Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: pkg/csslib/lib/parser.dart

Issue 751453004: Improve the speed and memory efficiency of csslib parsing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/csslib/CHANGELOG.md ('k') | pkg/csslib/lib/src/token.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library csslib.parser; 5 library csslib.parser;
6 6
7 import 'dart:math' as math; 7 import 'dart:math' as math;
8 8
9 import 'package:source_span/source_span.dart'; 9 import 'package:source_span/source_span.dart';
10 10
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 : this.file = file, 189 : this.file = file,
190 _baseUrl = baseUrl, 190 _baseUrl = baseUrl,
191 tokenizer = new Tokenizer(file, text, true, start) { 191 tokenizer = new Tokenizer(file, text, true, start) {
192 _peekToken = tokenizer.next(); 192 _peekToken = tokenizer.next();
193 } 193 }
194 194
195 /** Main entry point for parsing an entire CSS file. */ 195 /** Main entry point for parsing an entire CSS file. */
196 StyleSheet parse() { 196 StyleSheet parse() {
197 List<TreeNode> productions = []; 197 List<TreeNode> productions = [];
198 198
199 int start = _peekToken.start; 199 var start = _peekToken.span;
200 while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) { 200 while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) {
201 // TODO(terry): Need to handle charset. 201 // TODO(terry): Need to handle charset.
202 var directive = processDirective(); 202 var directive = processDirective();
203 if (directive != null) { 203 if (directive != null) {
204 productions.add(directive); 204 productions.add(directive);
205 _maybeEat(TokenKind.SEMICOLON); 205 _maybeEat(TokenKind.SEMICOLON);
206 } else { 206 } else {
207 RuleSet ruleset = processRuleSet(); 207 RuleSet ruleset = processRuleSet();
208 if (ruleset != null) { 208 if (ruleset != null) {
209 productions.add(ruleset); 209 productions.add(ruleset);
210 } else { 210 } else {
211 break; 211 break;
212 } 212 }
213 } 213 }
214 } 214 }
215 215
216 checkEndOfFile(); 216 checkEndOfFile();
217 217
218 return new StyleSheet(productions, _makeSpan(start)); 218 return new StyleSheet(productions, _makeSpan(start));
219 } 219 }
220 220
221 /** Main entry point for parsing a simple selector sequence. */ 221 /** Main entry point for parsing a simple selector sequence. */
222 StyleSheet parseSelector() { 222 StyleSheet parseSelector() {
223 List<TreeNode> productions = []; 223 List<TreeNode> productions = [];
224 224
225 int start = _peekToken.start; 225 var start = _peekToken.span;
226 while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) { 226 while (!_maybeEat(TokenKind.END_OF_FILE) && !_peekKind(TokenKind.RBRACE)) {
227 var selector = processSelector(); 227 var selector = processSelector();
228 if (selector != null) { 228 if (selector != null) {
229 productions.add(selector); 229 productions.add(selector);
230 } 230 }
231 } 231 }
232 232
233 checkEndOfFile(); 233 checkEndOfFile();
234 234
235 return new StyleSheet.selector(productions, _makeSpan(start)); 235 return new StyleSheet.selector(productions, _makeSpan(start));
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 messages.error(message, location); 327 messages.error(message, location);
328 } 328 }
329 329
330 void _warning(String message, SourceSpan location) { 330 void _warning(String message, SourceSpan location) {
331 if (location == null) { 331 if (location == null) {
332 location = _peekToken.span; 332 location = _peekToken.span;
333 } 333 }
334 messages.warning(message, location); 334 messages.warning(message, location);
335 } 335 }
336 336
337 SourceSpan _makeSpan(int start) { 337 SourceSpan _makeSpan(FileSpan start) {
338 // TODO(terry): there are places where we are creating spans before we eat 338 // TODO(terry): there are places where we are creating spans before we eat
339 // the tokens, so using _previousToken.end is not always valid. 339 // the tokens, so using _previousToken is not always valid.
340 var end = _previousToken != null && _previousToken.end >= start 340 // TODO(nweiz): use < rather than compareTo when SourceSpan supports it.
341 ? _previousToken.end : _peekToken.end; 341 if (_previousToken == null || _previousToken.span.compareTo(start) < 0) {
342 return file.span(start, end); 342 return start;
343 }
344 return start.expand(_previousToken.span);
343 } 345 }
344 346
345 /////////////////////////////////////////////////////////////////// 347 ///////////////////////////////////////////////////////////////////
346 // Top level productions 348 // Top level productions
347 /////////////////////////////////////////////////////////////////// 349 ///////////////////////////////////////////////////////////////////
348 350
349 /** 351 /**
350 * The media_query_list production below replaces the media_list production 352 * The media_query_list production below replaces the media_list production
351 * from CSS2 the new grammar is: 353 * from CSS2 the new grammar is:
352 * 354 *
(...skipping 29 matching lines...) Expand all
382 firstTime = true; 384 firstTime = true;
383 } while ((!firstTime && mediaQuery != null) || firstTime); 385 } while ((!firstTime && mediaQuery != null) || firstTime);
384 386
385 return mediaQueries; 387 return mediaQueries;
386 } 388 }
387 389
388 MediaQuery processMediaQuery([bool startQuery = true]) { 390 MediaQuery processMediaQuery([bool startQuery = true]) {
389 // Grammar: [ONLY | NOT]? S* media_type S* 391 // Grammar: [ONLY | NOT]? S* media_type S*
390 // [ AND S* MediaExpr ]* | MediaExpr [ AND S* MediaExpr ]* 392 // [ AND S* MediaExpr ]* | MediaExpr [ AND S* MediaExpr ]*
391 393
392 int start = _peekToken.start; 394 var start = _peekToken.span;
393 395
394 // Is it a unary media operator? 396 // Is it a unary media operator?
395 var op = _peekToken.text; 397 var op = _peekToken.text;
396 var opLen = op.length; 398 var opLen = op.length;
397 var unaryOp = TokenKind.matchMediaOperator(op, 0, opLen); 399 var unaryOp = TokenKind.matchMediaOperator(op, 0, opLen);
398 if (unaryOp != -1) { 400 if (unaryOp != -1) {
399 if (isChecked) { 401 if (isChecked) {
400 if (startQuery && 402 if (startQuery &&
401 unaryOp != TokenKind.MEDIA_OP_NOT || 403 unaryOp != TokenKind.MEDIA_OP_NOT ||
402 unaryOp != TokenKind.MEDIA_OP_ONLY) { 404 unaryOp != TokenKind.MEDIA_OP_ONLY) {
403 _warning("Only the unary operators NOT and ONLY allowed", 405 _warning("Only the unary operators NOT and ONLY allowed",
404 _makeSpan(start)); 406 _makeSpan(start));
405 } 407 }
406 if (!startQuery && unaryOp != TokenKind.MEDIA_OP_AND) { 408 if (!startQuery && unaryOp != TokenKind.MEDIA_OP_AND) {
407 _warning("Only the binary AND operator allowed", _makeSpan(start)); 409 _warning("Only the binary AND operator allowed", _makeSpan(start));
408 } 410 }
409 } 411 }
410 _next(); 412 _next();
411 start = _peekToken.start; 413 start = _peekToken.span;
412 } 414 }
413 415
414 var type; 416 var type;
415 if (startQuery && unaryOp != TokenKind.MEDIA_OP_AND) { 417 if (startQuery && unaryOp != TokenKind.MEDIA_OP_AND) {
416 // Get the media type. 418 // Get the media type.
417 if (_peekIdentifier()) type = identifier(); 419 if (_peekIdentifier()) type = identifier();
418 } 420 }
419 421
420 var exprs = []; 422 var exprs = [];
421 423
(...skipping 12 matching lines...) Expand all
434 _next(); 436 _next();
435 } 437 }
436 } 438 }
437 439
438 if (unaryOp != -1 || type != null || exprs.length > 0) { 440 if (unaryOp != -1 || type != null || exprs.length > 0) {
439 return new MediaQuery(unaryOp, type, exprs, _makeSpan(start)); 441 return new MediaQuery(unaryOp, type, exprs, _makeSpan(start));
440 } 442 }
441 } 443 }
442 444
443 MediaExpression processMediaExpression([bool andOperator = false]) { 445 MediaExpression processMediaExpression([bool andOperator = false]) {
444 int start = _peekToken.start; 446 var start = _peekToken.span;
445 447
446 // Grammar: '(' S* media_feature S* [ ':' S* expr ]? ')' S* 448 // Grammar: '(' S* media_feature S* [ ':' S* expr ]? ')' S*
447 if (_maybeEat(TokenKind.LPAREN)) { 449 if (_maybeEat(TokenKind.LPAREN)) {
448 if (_peekIdentifier()) { 450 if (_peekIdentifier()) {
449 var feature = identifier(); // Media feature. 451 var feature = identifier(); // Media feature.
450 while (_maybeEat(TokenKind.COLON)) { 452 while (_maybeEat(TokenKind.COLON)) {
451 int startExpr = _peekToken.start; 453 var startExpr = _peekToken.span;
452 var exprs = processExpr(); 454 var exprs = processExpr();
453 if (_maybeEat(TokenKind.RPAREN)) { 455 if (_maybeEat(TokenKind.RPAREN)) {
454 return new MediaExpression(andOperator, feature, exprs, 456 return new MediaExpression(andOperator, feature, exprs,
455 _makeSpan(startExpr)); 457 _makeSpan(startExpr));
456 } else if (isChecked) { 458 } else if (isChecked) {
457 _warning("Missing parenthesis around media expression", 459 _warning("Missing parenthesis around media expression",
458 _makeSpan(start)); 460 _makeSpan(start));
459 return null; 461 return null;
460 } 462 }
461 } 463 }
(...skipping 15 matching lines...) Expand all
477 * keyframes: '@-webkit-keyframes ...' (see grammar below). 479 * keyframes: '@-webkit-keyframes ...' (see grammar below).
478 * font_face: '@font-face' '{' declarations '}' 480 * font_face: '@font-face' '{' declarations '}'
479 * namespace: '@namespace name url("xmlns") 481 * namespace: '@namespace name url("xmlns")
480 * host: '@host '{' ruleset '}' 482 * host: '@host '{' ruleset '}'
481 * mixin: '@mixin name [(args,...)] '{' declarations/ruleset '}' 483 * mixin: '@mixin name [(args,...)] '{' declarations/ruleset '}'
482 * include: '@include name [(@arg,@arg1)] 484 * include: '@include name [(@arg,@arg1)]
483 * '@include name [(@arg...)] 485 * '@include name [(@arg...)]
484 * content '@content' 486 * content '@content'
485 */ 487 */
486 processDirective() { 488 processDirective() {
487 int start = _peekToken.start; 489 var start = _peekToken.span;
488 490
489 var tokId = processVariableOrDirective(); 491 var tokId = processVariableOrDirective();
490 if (tokId is VarDefinitionDirective) return tokId; 492 if (tokId is VarDefinitionDirective) return tokId;
491 switch (tokId) { 493 switch (tokId) {
492 case TokenKind.DIRECTIVE_IMPORT: 494 case TokenKind.DIRECTIVE_IMPORT:
493 _next(); 495 _next();
494 496
495 // @import "uri_string" or @import url("uri_string") are identical; only 497 // @import "uri_string" or @import url("uri_string") are identical; only
496 // a url can follow an @import. 498 // a url can follow an @import.
497 String importStr; 499 String importStr;
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
696 698
697 var name; 699 var name;
698 if (_peekIdentifier()) { 700 if (_peekIdentifier()) {
699 name = identifier(); 701 name = identifier();
700 } 702 }
701 703
702 _eat(TokenKind.LBRACE); 704 _eat(TokenKind.LBRACE);
703 705
704 List<TreeNode> productions = []; 706 List<TreeNode> productions = [];
705 707
706 start = _peekToken.start; 708 start = _peekToken.span;
707 while (!_maybeEat(TokenKind.END_OF_FILE)) { 709 while (!_maybeEat(TokenKind.END_OF_FILE)) {
708 RuleSet ruleset = processRuleSet(); 710 RuleSet ruleset = processRuleSet();
709 if (ruleset == null) { 711 if (ruleset == null) {
710 break; 712 break;
711 } 713 }
712 productions.add(ruleset); 714 productions.add(ruleset);
713 } 715 }
714 716
715 _eat(TokenKind.RBRACE); 717 _eat(TokenKind.RBRACE);
716 718
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
748 } 750 }
749 } else { 751 } else {
750 namespaceUri = processQuotedString(false); 752 namespaceUri = processQuotedString(false);
751 } 753 }
752 } 754 }
753 755
754 return new NamespaceDirective(prefix != null ? prefix.name : '', 756 return new NamespaceDirective(prefix != null ? prefix.name : '',
755 namespaceUri, _makeSpan(start)); 757 namespaceUri, _makeSpan(start));
756 758
757 case TokenKind.DIRECTIVE_MIXIN: 759 case TokenKind.DIRECTIVE_MIXIN:
758 return processMixin(start); 760 return processMixin();
759 761
760 case TokenKind.DIRECTIVE_INCLUDE: 762 case TokenKind.DIRECTIVE_INCLUDE:
761 return processInclude( _makeSpan(start)); 763 return processInclude( _makeSpan(start));
762 764
763 case TokenKind.DIRECTIVE_CONTENT: 765 case TokenKind.DIRECTIVE_CONTENT:
764 // TODO(terry): TBD 766 // TODO(terry): TBD
765 _warning("@content not implemented.", _makeSpan(start)); 767 _warning("@content not implemented.", _makeSpan(start));
766 return null; 768 return null;
767 } 769 }
768 return null; 770 return null;
769 } 771 }
770 772
771 /** 773 /**
772 * Parse the mixin beginning token offset [start]. Returns a [MixinDefinition] 774 * Parse the mixin beginning token offset [start]. Returns a [MixinDefinition]
773 * node. 775 * node.
774 * 776 *
775 * Mixin grammar: 777 * Mixin grammar:
776 * 778 *
777 * @mixin IDENT [(args,...)] '{' 779 * @mixin IDENT [(args,...)] '{'
778 * [ruleset | property | directive]* 780 * [ruleset | property | directive]*
779 * '}' 781 * '}'
780 */ 782 */
781 MixinDefinition processMixin(int start) { 783 MixinDefinition processMixin() {
782 _next(); 784 _next();
783 785
784 var name = identifier(); 786 var name = identifier();
785 787
786 List<VarDefinitionDirective> params = []; 788 List<VarDefinitionDirective> params = [];
787 // Any parameters? 789 // Any parameters?
788 if (_maybeEat(TokenKind.LPAREN)) { 790 if (_maybeEat(TokenKind.LPAREN)) {
789 var mustHaveParam = false; 791 var mustHaveParam = false;
790 var keepGoing = true; 792 var keepGoing = true;
791 while (keepGoing) { 793 while (keepGoing) {
792 var varDef = processVariableOrDirective(mixinParameter: true); 794 var varDef = processVariableOrDirective(mixinParameter: true);
793 if (varDef is VarDefinitionDirective || varDef is VarDefinition) { 795 if (varDef is VarDefinitionDirective || varDef is VarDefinition) {
794 params.add(varDef); 796 params.add(varDef);
795 } else if (mustHaveParam) { 797 } else if (mustHaveParam) {
796 _warning("Expecting parameter", _makeSpan(_peekToken.start)); 798 _warning("Expecting parameter", _makeSpan(_peekToken.span));
797 keepGoing = false; 799 keepGoing = false;
798 } 800 }
799 if (_maybeEat(TokenKind.COMMA)) { 801 if (_maybeEat(TokenKind.COMMA)) {
800 mustHaveParam = true; 802 mustHaveParam = true;
801 continue; 803 continue;
802 } 804 }
803 keepGoing = !_maybeEat(TokenKind.RPAREN); 805 keepGoing = !_maybeEat(TokenKind.RPAREN);
804 } 806 }
805 } 807 }
806 808
807 _eat(TokenKind.LBRACE); 809 _eat(TokenKind.LBRACE);
808 810
809 List<TreeNode> productions = []; 811 List<TreeNode> productions = [];
810 List<TreeNode> declarations = []; 812 List<TreeNode> declarations = [];
811 var mixinDirective; 813 var mixinDirective;
812 814
813 start = _peekToken.start; 815 var start = _peekToken.span;
814 while (!_maybeEat(TokenKind.END_OF_FILE)) { 816 while (!_maybeEat(TokenKind.END_OF_FILE)) {
815 var directive = processDirective(); 817 var directive = processDirective();
816 if (directive != null) { 818 if (directive != null) {
817 productions.add(directive); 819 productions.add(directive);
818 continue; 820 continue;
819 } 821 }
820 822
821 var declGroup = processDeclarations(checkBrace: false); 823 var declGroup = processDeclarations(checkBrace: false);
822 var decls = []; 824 var decls = [];
823 if (declGroup.declarations.any((decl) { 825 if (declGroup.declarations.any((decl) {
824 return decl is Declaration && 826 return decl is Declaration &&
825 decl is! IncludeMixinAtDeclaration; 827 decl is! IncludeMixinAtDeclaration;
826 })) { 828 })) {
827 var newDecls = []; 829 var newDecls = [];
828 productions.forEach((include) { 830 productions.forEach((include) {
829 // If declGroup has items that are declarations then we assume 831 // If declGroup has items that are declarations then we assume
830 // this mixin is a declaration mixin not a top-level mixin. 832 // this mixin is a declaration mixin not a top-level mixin.
831 if (include is IncludeDirective) { 833 if (include is IncludeDirective) {
832 newDecls.add(new IncludeMixinAtDeclaration(include, 834 newDecls.add(new IncludeMixinAtDeclaration(include,
833 include.span)); 835 include.span));
834 } else { 836 } else {
835 _warning("Error mixing of top-level vs declarations mixins", 837 _warning("Error mixing of top-level vs declarations mixins",
836 _makeSpan(include)); 838 _makeSpan(include.span));
837 } 839 }
838 }); 840 });
839 declGroup.declarations.insertAll(0, newDecls); 841 declGroup.declarations.insertAll(0, newDecls);
840 productions = []; 842 productions = [];
841 } else { 843 } else {
842 // Declarations are just @includes make it a list of productions 844 // Declarations are just @includes make it a list of productions
843 // not a declaration group (anything else is a ruleset). Make it a 845 // not a declaration group (anything else is a ruleset). Make it a
844 // list of productions, not a declaration group. 846 // list of productions, not a declaration group.
845 for (var decl in declGroup.declarations) { 847 for (var decl in declGroup.declarations) {
846 productions.add(decl is IncludeMixinAtDeclaration ? 848 productions.add(decl is IncludeMixinAtDeclaration ?
(...skipping 28 matching lines...) Expand all
875 _eat(TokenKind.RBRACE); 877 _eat(TokenKind.RBRACE);
876 878
877 return mixinDirective; 879 return mixinDirective;
878 } 880 }
879 881
880 /** 882 /**
881 * Returns a VarDefinitionDirective or VarDefinition if a varaible otherwise 883 * Returns a VarDefinitionDirective or VarDefinition if a varaible otherwise
882 * return the token id of a directive or -1 if neither. 884 * return the token id of a directive or -1 if neither.
883 */ 885 */
884 processVariableOrDirective({bool mixinParameter: false}) { 886 processVariableOrDirective({bool mixinParameter: false}) {
885 int start = _peekToken.start; 887 var start = _peekToken.span;
886 888
887 var tokId = _peek(); 889 var tokId = _peek();
888 // Handle case for @ directive (where there's a whitespace between the @ 890 // Handle case for @ directive (where there's a whitespace between the @
889 // sign and the directive name. Technically, it's not valid grammar but 891 // sign and the directive name. Technically, it's not valid grammar but
890 // a number of CSS tests test for whitespace between @ and name. 892 // a number of CSS tests test for whitespace between @ and name.
891 if (tokId == TokenKind.AT) { 893 if (tokId == TokenKind.AT) {
892 Token tok = _next(); 894 Token tok = _next();
893 tokId = _peek(); 895 tokId = _peek();
894 if (_peekIdentifier()) { 896 if (_peekIdentifier()) {
895 // Is it a directive? 897 // Is it a directive?
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
1039 return null; 1041 return null;
1040 } else { 1042 } else {
1041 // Remember any messages from look ahead. 1043 // Remember any messages from look ahead.
1042 oldMessages.mergeMessages(messages); 1044 oldMessages.mergeMessages(messages);
1043 messages = oldMessages; 1045 messages = oldMessages;
1044 return selGroup; 1046 return selGroup;
1045 } 1047 }
1046 } 1048 }
1047 1049
1048 DeclarationGroup processDeclarations({bool checkBrace: true}) { 1050 DeclarationGroup processDeclarations({bool checkBrace: true}) {
1049 int start = _peekToken.start; 1051 var start = _peekToken.span;
1050 1052
1051 if (checkBrace) _eat(TokenKind.LBRACE); 1053 if (checkBrace) _eat(TokenKind.LBRACE);
1052 1054
1053 List decls = []; 1055 List decls = [];
1054 List dartStyles = []; // List of latest styles exposed to Dart. 1056 List dartStyles = []; // List of latest styles exposed to Dart.
1055 1057
1056 do { 1058 do {
1057 var selectorGroup = _nestedSelector(); 1059 var selectorGroup = _nestedSelector();
1058 while (selectorGroup != null) { 1060 while (selectorGroup != null) {
1059 // Nested selector so process as a ruleset. 1061 // Nested selector so process as a ruleset.
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1097 } 1099 }
1098 } 1100 }
1099 } 1101 }
1100 1102
1101 return new DeclarationGroup(decls, _makeSpan(start)); 1103 return new DeclarationGroup(decls, _makeSpan(start));
1102 } 1104 }
1103 1105
1104 List<DeclarationGroup> processMarginsDeclarations() { 1106 List<DeclarationGroup> processMarginsDeclarations() {
1105 List groups = []; 1107 List groups = [];
1106 1108
1107 int start = _peekToken.start; 1109 var start = _peekToken.span;
1108 1110
1109 _eat(TokenKind.LBRACE); 1111 _eat(TokenKind.LBRACE);
1110 1112
1111 List<Declaration> decls = []; 1113 List<Declaration> decls = [];
1112 List dartStyles = []; // List of latest styles exposed to Dart. 1114 List dartStyles = []; // List of latest styles exposed to Dart.
1113 1115
1114 do { 1116 do {
1115 switch (_peek()) { 1117 switch (_peek()) {
1116 case TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER: 1118 case TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER:
1117 case TokenKind.MARGIN_DIRECTIVE_TOPLEFT: 1119 case TokenKind.MARGIN_DIRECTIVE_TOPLEFT:
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
1182 1184
1183 if (decls.length > 0) { 1185 if (decls.length > 0) {
1184 groups.add(new DeclarationGroup(decls, _makeSpan(start))); 1186 groups.add(new DeclarationGroup(decls, _makeSpan(start)));
1185 } 1187 }
1186 1188
1187 return groups; 1189 return groups;
1188 } 1190 }
1189 1191
1190 SelectorGroup processSelectorGroup() { 1192 SelectorGroup processSelectorGroup() {
1191 List<Selector> selectors = []; 1193 List<Selector> selectors = [];
1192 int start = _peekToken.start; 1194 var start = _peekToken.span;
1193 1195
1194 do { 1196 do {
1195 Selector selector = processSelector(); 1197 Selector selector = processSelector();
1196 if (selector != null) { 1198 if (selector != null) {
1197 selectors.add(selector); 1199 selectors.add(selector);
1198 } 1200 }
1199 } while (_maybeEat(TokenKind.COMMA)); 1201 } while (_maybeEat(TokenKind.COMMA));
1200 1202
1201 if (selectors.length > 0) { 1203 if (selectors.length > 0) {
1202 return new SelectorGroup(selectors, _makeSpan(start)); 1204 return new SelectorGroup(selectors, _makeSpan(start));
1203 } 1205 }
1204 } 1206 }
1205 1207
1206 /** 1208 /**
1207 * Return list of selectors 1209 * Return list of selectors
1208 */ 1210 */
1209 Selector processSelector() { 1211 Selector processSelector() {
1210 var simpleSequences = <SimpleSelectorSequence>[]; 1212 var simpleSequences = <SimpleSelectorSequence>[];
1211 var start = _peekToken.start; 1213 var start = _peekToken.span;
1212 while (true) { 1214 while (true) {
1213 // First item is never descendant make sure it's COMBINATOR_NONE. 1215 // First item is never descendant make sure it's COMBINATOR_NONE.
1214 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0); 1216 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0);
1215 if (selectorItem != null) { 1217 if (selectorItem != null) {
1216 simpleSequences.add(selectorItem); 1218 simpleSequences.add(selectorItem);
1217 } else { 1219 } else {
1218 break; 1220 break;
1219 } 1221 }
1220 } 1222 }
1221 1223
1222 if (simpleSequences.length > 0) { 1224 if (simpleSequences.length > 0) {
1223 return new Selector(simpleSequences, _makeSpan(start)); 1225 return new Selector(simpleSequences, _makeSpan(start));
1224 } 1226 }
1225 } 1227 }
1226 1228
1227 simpleSelectorSequence(bool forceCombinatorNone) { 1229 simpleSelectorSequence(bool forceCombinatorNone) {
1228 var start = _peekToken.start; 1230 var start = _peekToken.span;
1229 var combinatorType = TokenKind.COMBINATOR_NONE; 1231 var combinatorType = TokenKind.COMBINATOR_NONE;
1230 var thisOperator = false; 1232 var thisOperator = false;
1231 1233
1232 switch (_peek()) { 1234 switch (_peek()) {
1233 case TokenKind.PLUS: 1235 case TokenKind.PLUS:
1234 _eat(TokenKind.PLUS); 1236 _eat(TokenKind.PLUS);
1235 combinatorType = TokenKind.COMBINATOR_PLUS; 1237 combinatorType = TokenKind.COMBINATOR_PLUS;
1236 break; 1238 break;
1237 case TokenKind.GREATER: 1239 case TokenKind.GREATER:
1238 _eat(TokenKind.GREATER); 1240 _eat(TokenKind.GREATER);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1294 * class 1296 * class
1295 * : '.' IDENT 1297 * : '.' IDENT
1296 */ 1298 */
1297 simpleSelector() { 1299 simpleSelector() {
1298 // TODO(terry): Natalie makes a good point parsing of namespace and element 1300 // TODO(terry): Natalie makes a good point parsing of namespace and element
1299 // are essentially the same (asterisk or identifier) other 1301 // are essentially the same (asterisk or identifier) other
1300 // than the error message for element. Should consolidate the 1302 // than the error message for element. Should consolidate the
1301 // code. 1303 // code.
1302 // TODO(terry): Need to handle attribute namespace too. 1304 // TODO(terry): Need to handle attribute namespace too.
1303 var first; 1305 var first;
1304 int start = _peekToken.start; 1306 var start = _peekToken.span;
1305 switch (_peek()) { 1307 switch (_peek()) {
1306 case TokenKind.ASTERISK: 1308 case TokenKind.ASTERISK:
1307 // Mark as universal namespace. 1309 // Mark as universal namespace.
1308 var tok = _next(); 1310 var tok = _next();
1309 first = new Wildcard(_makeSpan(tok.start)); 1311 first = new Wildcard(_makeSpan(tok.span));
1310 break; 1312 break;
1311 case TokenKind.IDENTIFIER: 1313 case TokenKind.IDENTIFIER:
1312 first = identifier(); 1314 first = identifier();
1313 break; 1315 break;
1314 default: 1316 default:
1315 // Expecting simple selector. 1317 // Expecting simple selector.
1316 // TODO(terry): Could be a synthesized token like value, etc. 1318 // TODO(terry): Could be a synthesized token like value, etc.
1317 if (TokenKind.isKindIdentifier(_peek())) { 1319 if (TokenKind.isKindIdentifier(_peek())) {
1318 first = identifier(); 1320 first = identifier();
1319 } else if (_peekKind(TokenKind.SEMICOLON)) { 1321 } else if (_peekKind(TokenKind.SEMICOLON)) {
1320 // Can't be a selector if we found a semi-colon. 1322 // Can't be a selector if we found a semi-colon.
1321 return null; 1323 return null;
1322 } 1324 }
1323 break; 1325 break;
1324 } 1326 }
1325 1327
1326 if (_maybeEat(TokenKind.NAMESPACE)) { 1328 if (_maybeEat(TokenKind.NAMESPACE)) {
1327 var element; 1329 var element;
1328 switch (_peek()) { 1330 switch (_peek()) {
1329 case TokenKind.ASTERISK: 1331 case TokenKind.ASTERISK:
1330 // Mark as universal element 1332 // Mark as universal element
1331 var tok = _next(); 1333 var tok = _next();
1332 element = new Wildcard(_makeSpan(tok.start)); 1334 element = new Wildcard(_makeSpan(tok.span));
1333 break; 1335 break;
1334 case TokenKind.IDENTIFIER: 1336 case TokenKind.IDENTIFIER:
1335 element = identifier(); 1337 element = identifier();
1336 break; 1338 break;
1337 default: 1339 default:
1338 _error('expected element name or universal(*), but found $_peekToken', 1340 _error('expected element name or universal(*), but found $_peekToken',
1339 _peekToken.span); 1341 _peekToken.span);
1340 break; 1342 break;
1341 } 1343 }
1342 1344
(...skipping 16 matching lines...) Expand all
1359 } 1361 }
1360 1362
1361 return false; 1363 return false;
1362 } 1364 }
1363 1365
1364 /** 1366 /**
1365 * type_selector | universal | HASH | class | attrib | pseudo 1367 * type_selector | universal | HASH | class | attrib | pseudo
1366 */ 1368 */
1367 simpleSelectorTail() { 1369 simpleSelectorTail() {
1368 // Check for HASH | class | attrib | pseudo | negation 1370 // Check for HASH | class | attrib | pseudo | negation
1369 var start = _peekToken.start; 1371 var start = _peekToken.span;
1370 switch (_peek()) { 1372 switch (_peek()) {
1371 case TokenKind.HASH: 1373 case TokenKind.HASH:
1372 _eat(TokenKind.HASH); 1374 _eat(TokenKind.HASH);
1373 1375
1374 var hasWhiteSpace = false; 1376 var hasWhiteSpace = false;
1375 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { 1377 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) {
1376 _warning("Not a valid ID selector expected #id", _makeSpan(start)); 1378 _warning("Not a valid ID selector expected #id", _makeSpan(start));
1377 hasWhiteSpace = true; 1379 hasWhiteSpace = true;
1378 } 1380 }
1379 if (_peekIdentifier()) { 1381 if (_peekIdentifier()) {
(...skipping 26 matching lines...) Expand all
1406 case TokenKind.LBRACK: 1408 case TokenKind.LBRACK:
1407 return processAttribute(); 1409 return processAttribute();
1408 case TokenKind.DOUBLE: 1410 case TokenKind.DOUBLE:
1409 _error('name must start with a alpha character, but found a number', 1411 _error('name must start with a alpha character, but found a number',
1410 _peekToken.span); 1412 _peekToken.span);
1411 _next(); 1413 _next();
1412 break; 1414 break;
1413 } 1415 }
1414 } 1416 }
1415 1417
1416 processPseudoSelector(int start) { 1418 processPseudoSelector(FileSpan start) {
1417 // :pseudo-class ::pseudo-element 1419 // :pseudo-class ::pseudo-element
1418 // TODO(terry): '::' should be token. 1420 // TODO(terry): '::' should be token.
1419 _eat(TokenKind.COLON); 1421 _eat(TokenKind.COLON);
1420 var pseudoElement = _maybeEat(TokenKind.COLON); 1422 var pseudoElement = _maybeEat(TokenKind.COLON);
1421 1423
1422 // TODO(terry): If no identifier specified consider optimizing out the 1424 // TODO(terry): If no identifier specified consider optimizing out the
1423 // : or :: and making this a normal selector. For now, 1425 // : or :: and making this a normal selector. For now,
1424 // create an empty pseudoName. 1426 // create an empty pseudoName.
1425 var pseudoName; 1427 var pseudoName;
1426 if (_peekIdentifier()) { 1428 if (_peekIdentifier()) {
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
1485 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". 1487 * In CSS3, the expressions are identifiers, strings, or of the form "an+b".
1486 * 1488 *
1487 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ 1489 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
1488 * 1490 *
1489 * num [0-9]+|[0-9]*\.[0-9]+ 1491 * num [0-9]+|[0-9]*\.[0-9]+
1490 * PLUS '+' 1492 * PLUS '+'
1491 * DIMENSION {num}{ident} 1493 * DIMENSION {num}{ident}
1492 * NUMBER {num} 1494 * NUMBER {num}
1493 */ 1495 */
1494 processSelectorExpression() { 1496 processSelectorExpression() {
1495 var start = _peekToken.start; 1497 var start = _peekToken.span;
1496 1498
1497 var expressions = []; 1499 var expressions = [];
1498 1500
1499 Token termToken; 1501 Token termToken;
1500 var value; 1502 var value;
1501 1503
1502 var keepParsing = true; 1504 var keepParsing = true;
1503 while (keepParsing) { 1505 while (keepParsing) {
1504 switch (_peek()) { 1506 switch (_peek()) {
1505 case TokenKind.PLUS: 1507 case TokenKind.PLUS:
1506 start = _peekToken.start; 1508 start = _peekToken.span;
1507 termToken = _next(); 1509 termToken = _next();
1508 expressions.add(new OperatorPlus(_makeSpan(start))); 1510 expressions.add(new OperatorPlus(_makeSpan(start)));
1509 break; 1511 break;
1510 case TokenKind.MINUS: 1512 case TokenKind.MINUS:
1511 start = _peekToken.start; 1513 start = _peekToken.span;
1512 termToken = _next(); 1514 termToken = _next();
1513 expressions.add(new OperatorMinus(_makeSpan(start))); 1515 expressions.add(new OperatorMinus(_makeSpan(start)));
1514 break; 1516 break;
1515 case TokenKind.INTEGER: 1517 case TokenKind.INTEGER:
1516 termToken = _next(); 1518 termToken = _next();
1517 value = int.parse(termToken.text); 1519 value = int.parse(termToken.text);
1518 break; 1520 break;
1519 case TokenKind.DOUBLE: 1521 case TokenKind.DOUBLE:
1520 termToken = _next(); 1522 termToken = _next();
1521 value = double.parse(termToken.text); 1523 value = double.parse(termToken.text);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1566 // DASHMATCH: '|=' 1568 // DASHMATCH: '|='
1567 // 1569 //
1568 // PREFIXMATCH: '^=' 1570 // PREFIXMATCH: '^='
1569 // 1571 //
1570 // SUFFIXMATCH: '$=' 1572 // SUFFIXMATCH: '$='
1571 // 1573 //
1572 // SUBSTRMATCH: '*=' 1574 // SUBSTRMATCH: '*='
1573 // 1575 //
1574 // 1576 //
1575 AttributeSelector processAttribute() { 1577 AttributeSelector processAttribute() {
1576 var start = _peekToken.start; 1578 var start = _peekToken.span;
1577 1579
1578 if (_maybeEat(TokenKind.LBRACK)) { 1580 if (_maybeEat(TokenKind.LBRACK)) {
1579 var attrName = identifier(); 1581 var attrName = identifier();
1580 1582
1581 int op; 1583 int op;
1582 switch (_peek()) { 1584 switch (_peek()) {
1583 case TokenKind.EQUALS: 1585 case TokenKind.EQUALS:
1584 case TokenKind.INCLUDES: // ~= 1586 case TokenKind.INCLUDES: // ~=
1585 case TokenKind.DASH_MATCH: // |= 1587 case TokenKind.DASH_MATCH: // |=
1586 case TokenKind.PREFIX_MATCH: // ^= 1588 case TokenKind.PREFIX_MATCH: // ^=
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1622 // expr: (see processExpr) 1624 // expr: (see processExpr)
1623 // 1625 //
1624 // Here are the ugly IE hacks we need to support: 1626 // Here are the ugly IE hacks we need to support:
1625 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon 1627 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon
1626 // *IDENT - IE7 or below 1628 // *IDENT - IE7 or below
1627 // _IDENT - IE6 property (automatically a valid ident) 1629 // _IDENT - IE6 property (automatically a valid ident)
1628 // 1630 //
1629 Declaration processDeclaration(List dartStyles) { 1631 Declaration processDeclaration(List dartStyles) {
1630 Declaration decl; 1632 Declaration decl;
1631 1633
1632 int start = _peekToken.start; 1634 var start = _peekToken.span;
1633 1635
1634 // IE7 hack of * before property name if so the property is IE7 or below. 1636 // IE7 hack of * before property name if so the property is IE7 or below.
1635 var ie7 = _peekKind(TokenKind.ASTERISK); 1637 var ie7 = _peekKind(TokenKind.ASTERISK);
1636 if (ie7) { 1638 if (ie7) {
1637 _next(); 1639 _next();
1638 } 1640 }
1639 1641
1640 // IDENT ':' expr '!important'? 1642 // IDENT ':' expr '!important'?
1641 if (TokenKind.isIdentifier(_peekToken.kind)) { 1643 if (TokenKind.isIdentifier(_peekToken.kind)) {
1642 var propertyIdent = identifier(); 1644 var propertyIdent = identifier();
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1674 1676
1675 _next(); 1677 _next();
1676 var span = _makeSpan(start); 1678 var span = _makeSpan(start);
1677 var selector = simpleSelector(); 1679 var selector = simpleSelector();
1678 if (selector == null) { 1680 if (selector == null) {
1679 _warning("@extends expecting simple selector name", span); 1681 _warning("@extends expecting simple selector name", span);
1680 } else { 1682 } else {
1681 simpleSequences.add(selector); 1683 simpleSequences.add(selector);
1682 } 1684 }
1683 if (_peekKind(TokenKind.COLON)) { 1685 if (_peekKind(TokenKind.COLON)) {
1684 var pseudoSelector = processPseudoSelector(_peekToken.start); 1686 var pseudoSelector = processPseudoSelector(_peekToken.span);
1685 if (pseudoSelector is PseudoElementSelector || 1687 if (pseudoSelector is PseudoElementSelector ||
1686 pseudoSelector is PseudoClassSelector) { 1688 pseudoSelector is PseudoClassSelector) {
1687 simpleSequences.add(pseudoSelector); 1689 simpleSequences.add(pseudoSelector);
1688 } else { 1690 } else {
1689 _warning("not a valid selector", span); 1691 _warning("not a valid selector", span);
1690 } 1692 }
1691 } 1693 }
1692 decl = new ExtendDeclaration(simpleSequences, span); 1694 decl = new ExtendDeclaration(simpleSequences, span);
1693 } 1695 }
1694 1696
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
2026 } 2028 }
2027 2029
2028 // Expression grammar: 2030 // Expression grammar:
2029 // 2031 //
2030 // expression: term [ operator? term]* 2032 // expression: term [ operator? term]*
2031 // 2033 //
2032 // operator: '/' | ',' 2034 // operator: '/' | ','
2033 // term: (see processTerm) 2035 // term: (see processTerm)
2034 // 2036 //
2035 Expressions processExpr([bool ieFilter = false]) { 2037 Expressions processExpr([bool ieFilter = false]) {
2036 var start = _peekToken.start; 2038 var start = _peekToken.span;
2037 var expressions = new Expressions(_makeSpan(start)); 2039 var expressions = new Expressions(_makeSpan(start));
2038 2040
2039 var keepGoing = true; 2041 var keepGoing = true;
2040 var expr; 2042 var expr;
2041 while (keepGoing && (expr = processTerm(ieFilter)) != null) { 2043 while (keepGoing && (expr = processTerm(ieFilter)) != null) {
2042 var op; 2044 var op;
2043 2045
2044 var opStart = _peekToken.start; 2046 var opStart = _peekToken.span;
2045 2047
2046 switch (_peek()) { 2048 switch (_peek()) {
2047 case TokenKind.SLASH: 2049 case TokenKind.SLASH:
2048 op = new OperatorSlash(_makeSpan(opStart)); 2050 op = new OperatorSlash(_makeSpan(opStart));
2049 break; 2051 break;
2050 case TokenKind.COMMA: 2052 case TokenKind.COMMA:
2051 op = new OperatorComma(_makeSpan(opStart)); 2053 op = new OperatorComma(_makeSpan(opStart));
2052 break; 2054 break;
2053 case TokenKind.BACKSLASH: 2055 case TokenKind.BACKSLASH:
2054 // Backslash outside of string; detected IE8 or older signaled by \9 at 2056 // Backslash outside of string; detected IE8 or older signaled by \9 at
2055 // end of an expression. 2057 // end of an expression.
2056 var ie8Start = _peekToken.start; 2058 var ie8Start = _peekToken.span;
2057 2059
2058 _next(); 2060 _next();
2059 if (_peekKind(TokenKind.INTEGER)) { 2061 if (_peekKind(TokenKind.INTEGER)) {
2060 var numToken = _next(); 2062 var numToken = _next();
2061 var value = int.parse(numToken.text); 2063 var value = int.parse(numToken.text);
2062 if (value == 9) { 2064 if (value == 9) {
2063 op = new IE8Term(_makeSpan(ie8Start)); 2065 op = new IE8Term(_makeSpan(ie8Start));
2064 } else if (isChecked) { 2066 } else if (isChecked) {
2065 _warning("\$value is not valid in an expression", _makeSpan(start)); 2067 _warning("\$value is not valid in an expression", _makeSpan(start));
2066 } 2068 }
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
2110 // PERCENTAGE: {num}% 2112 // PERCENTAGE: {num}%
2111 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] 2113 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc']
2112 // EMS: {num}'em' 2114 // EMS: {num}'em'
2113 // EXS: {num}'ex' 2115 // EXS: {num}'ex'
2114 // ANGLE: {num}['deg' | 'rad' | 'grad'] 2116 // ANGLE: {num}['deg' | 'rad' | 'grad']
2115 // TIME: {num}['ms' | 's'] 2117 // TIME: {num}['ms' | 's']
2116 // FREQ: {num}['hz' | 'khz'] 2118 // FREQ: {num}['hz' | 'khz']
2117 // function: IDENT '(' expr ')' 2119 // function: IDENT '(' expr ')'
2118 // 2120 //
2119 processTerm([bool ieFilter = false]) { 2121 processTerm([bool ieFilter = false]) {
2120 var start = _peekToken.start; 2122 var start = _peekToken.span;
2121 Token t; // token for term's value 2123 Token t; // token for term's value
2122 var value; // value of term (numeric values) 2124 var value; // value of term (numeric values)
2123 2125
2124 var unary = ""; 2126 var unary = "";
2125 switch (_peek()) { 2127 switch (_peek()) {
2126 case TokenKind.HASH: 2128 case TokenKind.HASH:
2127 this._eat(TokenKind.HASH); 2129 this._eat(TokenKind.HASH);
2128 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { 2130 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) {
2129 String hexText; 2131 String hexText;
2130 if (_peekKind(TokenKind.INTEGER)) { 2132 if (_peekKind(TokenKind.INTEGER)) {
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
2357 ? new LiteralTerm(value, value.name, span) 2359 ? new LiteralTerm(value, value.name, span)
2358 : new NumberTerm(value, t.text, span); 2360 : new NumberTerm(value, t.text, span);
2359 } 2361 }
2360 break; 2362 break;
2361 } 2363 }
2362 2364
2363 return term; 2365 return term;
2364 } 2366 }
2365 2367
2366 String processQuotedString([bool urlString = false]) { 2368 String processQuotedString([bool urlString = false]) {
2367 var start = _peekToken.start; 2369 var start = _peekToken.span;
2368 2370
2369 // URI term sucks up everything inside of quotes(' or ") or between parens 2371 // URI term sucks up everything inside of quotes(' or ") or between parens
2370 var stopToken = urlString ? TokenKind.RPAREN : -1; 2372 var stopToken = urlString ? TokenKind.RPAREN : -1;
2371 2373
2372 // Note: disable skipping whitespace tokens inside a string. 2374 // Note: disable skipping whitespace tokens inside a string.
2373 // TODO(jmesserly): the layering here feels wrong. 2375 // TODO(jmesserly): the layering here feels wrong.
2374 var skipWhitespace = tokenizer._skipWhitespace; 2376 var skipWhitespace = tokenizer._skipWhitespace;
2375 tokenizer._skipWhitespace = false; 2377 tokenizer._skipWhitespace = false;
2376 2378
2377 switch (_peek()) { 2379 switch (_peek()) {
2378 case TokenKind.SINGLE_QUOTE: 2380 case TokenKind.SINGLE_QUOTE:
2379 stopToken = TokenKind.SINGLE_QUOTE; 2381 stopToken = TokenKind.SINGLE_QUOTE;
2380 start = _peekToken.start + 1; // Skip the quote might have whitespace. 2382 _next(); // Skip the SINGLE_QUOTE.
2381 _next(); // Skip the SINGLE_QUOTE. 2383 start = _peekToken.span;
sra1 2014/12/02 23:43:45 Will the _peekToken always have a span starting im
2382 break; 2384 break;
2383 case TokenKind.DOUBLE_QUOTE: 2385 case TokenKind.DOUBLE_QUOTE:
2384 stopToken = TokenKind.DOUBLE_QUOTE; 2386 stopToken = TokenKind.DOUBLE_QUOTE;
2385 start = _peekToken.start + 1; // Skip the quote might have whitespace. 2387 _next(); // Skip the DOUBLE_QUOTE.
2386 _next(); // Skip the DOUBLE_QUOTE. 2388 start = _peekToken.span;
2387 break; 2389 break;
2388 default: 2390 default:
2389 if (urlString) { 2391 if (urlString) {
2390 if (_peek() == TokenKind.LPAREN) { 2392 if (_peek() == TokenKind.LPAREN) {
2391 _next(); // Skip the LPAREN. 2393 _next(); // Skip the LPAREN.
2392 start = _peekToken.start; 2394 start = _peekToken.span;
2393 } 2395 }
2394 stopToken = TokenKind.RPAREN; 2396 stopToken = TokenKind.RPAREN;
2395 } else { 2397 } else {
2396 _error('unexpected string', _makeSpan(start)); 2398 _error('unexpected string', _makeSpan(start));
2397 } 2399 }
2398 break; 2400 break;
2399 } 2401 }
2400 2402
2401 // Gobble up everything until we hit our stop token. 2403 // Gobble up everything until we hit our stop token.
2402 var runningStart = _peekToken.start;
2403
2404 var stringValue = new StringBuffer(); 2404 var stringValue = new StringBuffer();
2405 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { 2405 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) {
2406 stringValue.write(_next().text); 2406 stringValue.write(_next().text);
2407 } 2407 }
2408 2408
2409 tokenizer._skipWhitespace = skipWhitespace; 2409 tokenizer._skipWhitespace = skipWhitespace;
2410 2410
2411 // All characters between quotes is the string. 2411 // All characters between quotes is the string.
2412 if (stopToken != TokenKind.RPAREN) { 2412 if (stopToken != TokenKind.RPAREN) {
2413 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; 2413 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE;
2414 } 2414 }
2415 2415
2416 return stringValue.toString(); 2416 return stringValue.toString();
2417 } 2417 }
2418 2418
2419 // TODO(terry): Should probably understand IE's non-standard filter syntax to 2419 // TODO(terry): Should probably understand IE's non-standard filter syntax to
2420 // fully support calc, var(), etc. 2420 // fully support calc, var(), etc.
2421 /** 2421 /**
2422 * IE's filter property breaks CSS value parsing. IE's format can be: 2422 * IE's filter property breaks CSS value parsing. IE's format can be:
2423 * 2423 *
2424 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); 2424 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83');
2425 * 2425 *
2426 * We'll just parse everything after the 'progid:' look for the left paren 2426 * We'll just parse everything after the 'progid:' look for the left paren
2427 * then parse to the right paren ignoring everything in between. 2427 * then parse to the right paren ignoring everything in between.
2428 */ 2428 */
2429 processIEFilter(int startAfterProgidColon) { 2429 processIEFilter(FileSpan startAfterProgidColon) {
2430 var parens = 0; 2430 var parens = 0;
2431 2431
2432 while (_peek() != TokenKind.END_OF_FILE) { 2432 while (_peek() != TokenKind.END_OF_FILE) {
2433 switch (_peek()) { 2433 switch (_peek()) {
2434 case TokenKind.LPAREN: 2434 case TokenKind.LPAREN:
2435 _eat(TokenKind.LPAREN); 2435 _eat(TokenKind.LPAREN);
2436 parens++; 2436 parens++;
2437 break; 2437 break;
2438 case TokenKind.RPAREN: 2438 case TokenKind.RPAREN:
2439 _eat(TokenKind.RPAREN); 2439 _eat(TokenKind.RPAREN);
2440 if (--parens == 0) { 2440 if (--parens == 0) {
2441 var tok = tokenizer.makeIEFilter(startAfterProgidColon, 2441 var tok = tokenizer.makeIEFilter(startAfterProgidColon.start.offset,
2442 _peekToken.start); 2442 _peekToken.start);
2443 return new LiteralTerm(tok.text, tok.text, tok.span); 2443 return new LiteralTerm(tok.text, tok.text, tok.span);
2444 } 2444 }
2445 break; 2445 break;
2446 default: 2446 default:
2447 _eat(_peek()); 2447 _eat(_peek());
2448 } 2448 }
2449 } 2449 }
2450 } 2450 }
2451 2451
2452 // Function grammar: 2452 // Function grammar:
2453 // 2453 //
2454 // function: IDENT '(' expr ')' 2454 // function: IDENT '(' expr ')'
2455 // 2455 //
2456 processFunction(Identifier func) { 2456 processFunction(Identifier func) {
2457 var start = _peekToken.start; 2457 var start = _peekToken.span;
2458 2458
2459 var name = func.name; 2459 var name = func.name;
2460 2460
2461 switch (name) { 2461 switch (name) {
2462 case 'url': 2462 case 'url':
2463 // URI term sucks up everything inside of quotes(' or ") or between parens 2463 // URI term sucks up everything inside of quotes(' or ") or between parens
2464 var urlParam = processQuotedString(true); 2464 var urlParam = processQuotedString(true);
2465 2465
2466 // TODO(terry): Better error messge and checking for mismatched quotes. 2466 // TODO(terry): Better error messge and checking for mismatched quotes.
2467 if (_peek() == TokenKind.END_OF_FILE) { 2467 if (_peek() == TokenKind.END_OF_FILE) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2512 } 2512 }
2513 2513
2514 Identifier identifier() { 2514 Identifier identifier() {
2515 var tok = _next(); 2515 var tok = _next();
2516 2516
2517 if (!TokenKind.isIdentifier(tok.kind) && 2517 if (!TokenKind.isIdentifier(tok.kind) &&
2518 !TokenKind.isKindIdentifier(tok.kind)) { 2518 !TokenKind.isKindIdentifier(tok.kind)) {
2519 if (isChecked) { 2519 if (isChecked) {
2520 _warning('expected identifier, but found $tok', tok.span); 2520 _warning('expected identifier, but found $tok', tok.span);
2521 } 2521 }
2522 return new Identifier("", _makeSpan(tok.start)); 2522 return new Identifier("", _makeSpan(tok.span));
2523 } 2523 }
2524 2524
2525 return new Identifier(tok.text, _makeSpan(tok.start)); 2525 return new Identifier(tok.text, _makeSpan(tok.span));
2526 } 2526 }
2527 2527
2528 // TODO(terry): Move this to base <= 36 and into shared code. 2528 // TODO(terry): Move this to base <= 36 and into shared code.
2529 static int _hexDigit(int c) { 2529 static int _hexDigit(int c) {
2530 if (c >= 48/*0*/ && c <= 57/*9*/) { 2530 if (c >= 48/*0*/ && c <= 57/*9*/) {
2531 return c - 48; 2531 return c - 48;
2532 } else if (c >= 97/*a*/ && c <= 102/*f*/) { 2532 } else if (c >= 97/*a*/ && c <= 102/*f*/) {
2533 return c - 87; 2533 return c - 87;
2534 } else if (c >= 65/*A*/ && c <= 70/*F*/) { 2534 } else if (c >= 65/*A*/ && c <= 70/*F*/) {
2535 return c - 55; 2535 return c - 55;
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
2695 2695
2696 if (replace != null && result == null) { 2696 if (replace != null && result == null) {
2697 result = new StringBuffer(text.substring(0, i)); 2697 result = new StringBuffer(text.substring(0, i));
2698 } 2698 }
2699 2699
2700 if (result != null) result.write(replace != null ? replace : text[i]); 2700 if (result != null) result.write(replace != null ? replace : text[i]);
2701 } 2701 }
2702 2702
2703 return result == null ? text : result.toString(); 2703 return result == null ? text : result.toString();
2704 } 2704 }
OLDNEW
« no previous file with comments | « pkg/csslib/CHANGELOG.md ('k') | pkg/csslib/lib/src/token.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698