OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |