Chromium Code Reviews| 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_maps/span.dart' show SourceFile, Span, FileSpan; | 9 import 'package:source_maps/span.dart' show SourceFile, Span, FileSpan; |
| 10 | 10 |
| (...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 422 return null; | 422 return null; |
| 423 } | 423 } |
| 424 } | 424 } |
| 425 } else if (isChecked) { | 425 } else if (isChecked) { |
| 426 _warning("Missing media feature in media expression", _makeSpan(start)); | 426 _warning("Missing media feature in media expression", _makeSpan(start)); |
| 427 return null; | 427 return null; |
| 428 } | 428 } |
| 429 } | 429 } |
| 430 } | 430 } |
| 431 | 431 |
| 432 // Directive grammar: | 432 /** |
| 433 // | 433 * Directive grammar: |
| 434 // import: '@import' [string | URI] media_list? | 434 * |
| 435 // media: '@media' media_query_list '{' ruleset '}' | 435 * import: '@import' [string | URI] media_list? |
| 436 // page: '@page' [':' IDENT]? '{' declarations '}' | 436 * media: '@media' media_query_list '{' ruleset '}' |
| 437 // stylet: '@stylet' IDENT '{' ruleset '}' | 437 * page: '@page' [':' IDENT]? '{' declarations '}' |
| 438 // media_query_list: IDENT [',' IDENT] | 438 * stylet: '@stylet' IDENT '{' ruleset '}' |
| 439 // keyframes: '@-webkit-keyframes ...' (see grammar below). | 439 * media_query_list: IDENT [',' IDENT] |
| 440 // font_face: '@font-face' '{' declarations '}' | 440 * keyframes: '@-webkit-keyframes ...' (see grammar below). |
| 441 // namespace: '@namespace name url("xmlns") | 441 * font_face: '@font-face' '{' declarations '}' |
| 442 // host: '@host '{' ruleset '}' | 442 * namespace: '@namespace name url("xmlns") |
| 443 * host: '@host '{' ruleset '}' | |
| 444 * mixin: '@mixin name [(args,...)] '{' declarations/ruleset '}' | |
| 445 * include: '@include name [(@arg,@arg1)] | |
| 446 * '@include name [(@arg...)] | |
| 447 * content '@content' | |
| 448 */ | |
| 443 processDirective() { | 449 processDirective() { |
|
nweiz
2013/09/18 22:40:54
Why is this (as well as many other productions) pu
terry
2013/10/09 03:40:33
The parser should be a hidden class (_Parser).
On
| |
| 444 int start = _peekToken.start; | 450 int start = _peekToken.start; |
| 445 | 451 |
| 446 var tokId = _peek(); | 452 var tokId = _peek(); |
| 447 // Handle case for @ directive (where there's a whitespace between the @ | 453 // Handle case for @ directive (where there's a whitespace between the @ |
| 448 // sign and the directive name. Technically, it's not valid grammar but | 454 // sign and the directive name. Technically, it's not valid grammar but |
| 449 // a number of CSS tests test for whitespace between @ and name. | 455 // a number of CSS tests test for whitespace between @ and name. |
| 450 if (tokId == TokenKind.AT) { | 456 if (tokId == TokenKind.AT) { |
| 451 Token tok = _next(); | 457 Token tok = _next(); |
| 452 tokId = _peek(); | 458 tokId = _peek(); |
| 453 if (_peekIdentifier()) { | 459 if (_peekIdentifier()) { |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 741 namespaceUri = func.text; | 747 namespaceUri = func.text; |
| 742 prefix = null; | 748 prefix = null; |
| 743 } | 749 } |
| 744 } else { | 750 } else { |
| 745 namespaceUri = processQuotedString(false); | 751 namespaceUri = processQuotedString(false); |
| 746 } | 752 } |
| 747 } | 753 } |
| 748 | 754 |
| 749 return new NamespaceDirective(prefix != null ? prefix.name : '', | 755 return new NamespaceDirective(prefix != null ? prefix.name : '', |
| 750 namespaceUri, _makeSpan(start)); | 756 namespaceUri, _makeSpan(start)); |
| 757 | |
| 758 case TokenKind.DIRECTIVE_MIXIN: | |
| 759 /* Stylet grammar: | |
|
nweiz
2013/09/18 22:40:54
"Stylet" -> "Mixin"
terry
2013/10/09 03:40:33
Done.
| |
| 760 * | |
| 761 * @mixin IDENT [(args,...)] '{' | |
| 762 * ruleset | properties | |
|
nweiz
2013/09/18 22:40:54
This should be "[ruleset | property | directive]*"
terry
2013/10/09 03:40:33
Done.
| |
| 763 * '}' | |
| 764 */ | |
| 765 _next(); | |
| 766 | |
| 767 var name; | |
| 768 if (_peekIdentifier()) { | |
|
Jennifer Messerly
2013/09/18 06:44:15
is identifier optional? it doesn't appear so from
terry
2013/10/09 03:40:33
It's not optional, it's required - changed.
On 20
| |
| 769 name = identifier(); | |
| 770 } | |
|
Jennifer Messerly
2013/09/18 06:44:15
TODO here about parsing the args?
terry
2013/10/09 03:40:33
Done.
| |
| 771 | |
| 772 _eat(TokenKind.LBRACE); | |
| 773 | |
| 774 List<TreeNode> productions = []; | |
| 775 List<TreeNode> declarations = []; | |
| 776 var mixinDirective; | |
| 777 | |
| 778 start = _peekToken.start; | |
| 779 while (!_maybeEat(TokenKind.END_OF_FILE)) { | |
| 780 var directive = processDirective(); | |
|
Jennifer Messerly
2013/09/18 06:44:15
is it possible to reuse any existing parser code h
terry
2013/10/09 03:40:33
Was able to simplify a bit but this is really hand
| |
| 781 if (directive != null) { | |
| 782 productions.add(directive); | |
| 783 _maybeEat(TokenKind.SEMICOLON); | |
|
nweiz
2013/09/18 22:40:54
Why are you using [_maybeEat] here? Any number of
terry
2013/10/09 03:40:33
Okay, I fixed it.
On 2013/09/18 22:40:54, nweiz w
| |
| 784 continue; | |
| 785 } | |
| 786 var selectorGroup = _nestedSelector(); | |
| 787 if (selectorGroup != null) { | |
| 788 // Nested selector so process as a ruleset. | |
| 789 var ruleset = processRuleSet(selectorGroup); | |
| 790 if (ruleset == null) { | |
| 791 mixinDirective = new MixinRulesetDirective(name.name, [], false, | |
| 792 productions, _makeSpan(start)); | |
| 793 break; | |
|
nweiz
2013/09/18 22:40:54
Why are you breaking here? Mixins can contain more
terry
2013/10/09 03:40:33
Was able to eliminate the whole nestedSelector chu
| |
| 794 } | |
| 795 productions.add(ruleset); | |
| 796 } | |
| 797 | |
| 798 var declGroup = processDeclarations(checkBrace: false); | |
| 799 | |
| 800 var decls = []; | |
| 801 if (declGroup.declarations.any((decl) { | |
| 802 return decl is Declaration && decl is! IncludeMixinAtDeclaration; | |
| 803 })) { | |
|
nweiz
2013/09/18 22:40:54
Style nit: if you're going to write this if statem
terry
2013/10/09 03:40:33
Ok, changed the indentation.
On 2013/09/18 22:40:5
| |
| 804 var newDecls = []; | |
| 805 productions.forEach((include) { | |
| 806 // If declGroup has items that are declarations then we assume | |
| 807 // this mixin is a declaration mixin not a top-level mixin. | |
|
nweiz
2013/09/18 22:40:54
This isn't a distinction that should exist.
terry
2013/10/09 03:40:33
Warnings are returned if a mixin has both decls an
| |
| 808 if (include is IncludeDirective) { | |
| 809 newDecls.add(new IncludeMixinAtDeclaration(include, include.span )); | |
|
Jennifer Messerly
2013/09/18 06:44:15
long line
terry
2013/10/09 03:40:33
Done.
| |
| 810 } else { | |
| 811 print("Error mixing of top-level vs declarations mixins"); | |
| 812 } | |
| 813 }); | |
| 814 declGroup.declarations.insertAll(0, newDecls); | |
| 815 productions = []; | |
| 816 } else { | |
| 817 // Declarations are just @includes make it a list of productions | |
| 818 // no a declaration group. | |
|
nweiz
2013/09/18 22:40:54
If I interpreted this sentence correctly, it shoul
terry
2013/10/09 03:40:33
Done.
| |
| 819 declGroup.declarations.forEach((decl) { | |
|
Jennifer Messerly
2013/09/18 06:44:15
I would probably just use a normal for-in loop her
terry
2013/10/09 03:40:33
Done.
| |
| 820 productions.add(decl is IncludeMixinAtDeclaration ? | |
| 821 decl.include : decl); | |
| 822 }); | |
| 823 declGroup.declarations.clear(); | |
| 824 } | |
| 825 | |
| 826 if (declGroup.declarations.isNotEmpty) { | |
| 827 if (productions.isEmpty) { | |
| 828 mixinDirective = new MixinDeclarationDirective(name.name, [], | |
| 829 false, declGroup, _makeSpan(start)); | |
| 830 break; | |
| 831 } else { | |
| 832 declGroup.declarations.forEach((decl) { | |
| 833 if (decl is IncludeMixinAtDeclaration) { | |
|
Jennifer Messerly
2013/09/18 06:44:15
this looks similar to:
productions.add(decl is
terry
2013/10/09 03:40:33
Done.
| |
| 834 productions.add(decl.include); | |
| 835 } else { | |
| 836 productions.add(decl); | |
| 837 } | |
| 838 }); | |
| 839 } | |
| 840 } else { | |
| 841 mixinDirective = new MixinRulesetDirective(name.name, [], false, | |
| 842 productions, _makeSpan(start)); | |
| 843 break; | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 _eat(TokenKind.RBRACE); | |
| 848 | |
| 849 return mixinDirective; | |
| 850 | |
| 851 case TokenKind.DIRECTIVE_INCLUDE: | |
| 852 return processInclude( _makeSpan(start)); | |
| 853 | |
| 854 case TokenKind.DIRECTIVE_CONTENT: | |
| 855 // TODO(terry): TBD | |
|
Jennifer Messerly
2013/09/18 06:44:15
issue warning about not implemented feature?
terry
2013/10/09 03:40:33
Done.
| |
| 751 } | 856 } |
| 752 } | 857 } |
| 753 | 858 |
| 859 IncludeDirective processInclude(Span span, [bool eatSemiColon = true]) { | |
|
Jennifer Messerly
2013/09/18 06:44:15
for bools it's kind of nice to use named arguments
terry
2013/10/09 03:40:33
Done.
| |
| 860 /* Stylet grammar: | |
|
nweiz
2013/09/18 22:40:54
"Stylet" -> "Include"
terry
2013/10/09 03:40:33
Done.
| |
| 861 * | |
| 862 * @include IDENT [(args,...)]; | |
| 863 */ | |
| 864 _next(); | |
| 865 | |
| 866 var name; | |
| 867 if (_peekIdentifier()) { | |
|
nweiz
2013/09/18 22:40:54
As in John's comment for @mixin, this shouldn't be
terry
2013/10/09 03:40:33
Right fixed and handled parameters too.
On 2013/09
| |
| 868 name = identifier(); | |
| 869 } | |
| 870 | |
| 871 if (eatSemiColon) { | |
| 872 _maybeEat(TokenKind.SEMICOLON); | |
| 873 } | |
| 874 | |
| 875 // TODO(terry): Handle parameters to mixin. | |
| 876 | |
| 877 return new IncludeDirective(name.name, [], span); | |
| 878 } | |
| 879 | |
| 754 RuleSet processRuleSet([SelectorGroup selectorGroup]) { | 880 RuleSet processRuleSet([SelectorGroup selectorGroup]) { |
| 755 if (selectorGroup == null) { | 881 if (selectorGroup == null) { |
| 756 selectorGroup = processSelectorGroup(); | 882 selectorGroup = processSelectorGroup(); |
| 757 } | 883 } |
| 758 if (selectorGroup != null) { | 884 if (selectorGroup != null) { |
| 759 return new RuleSet(selectorGroup, processDeclarations(), | 885 return new RuleSet(selectorGroup, processDeclarations(), |
| 760 selectorGroup.span); | 886 selectorGroup.span); |
| 761 } | 887 } |
| 762 } | 888 } |
| 763 | 889 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 802 messages = oldMessages; | 928 messages = oldMessages; |
| 803 return null; | 929 return null; |
| 804 } else { | 930 } else { |
| 805 // Remember any messages from look ahead. | 931 // Remember any messages from look ahead. |
| 806 oldMessages.mergeMessages(messages); | 932 oldMessages.mergeMessages(messages); |
| 807 messages = oldMessages; | 933 messages = oldMessages; |
| 808 return selGroup; | 934 return selGroup; |
| 809 } | 935 } |
| 810 } | 936 } |
| 811 | 937 |
| 812 processDeclarations({bool checkBrace: true}) { | 938 DeclarationGroup processDeclarations({bool checkBrace: true}) { |
| 813 int start = _peekToken.start; | 939 int start = _peekToken.start; |
| 814 | 940 |
| 815 if (checkBrace) _eat(TokenKind.LBRACE); | 941 if (checkBrace) _eat(TokenKind.LBRACE); |
| 816 | 942 |
| 817 List decls = []; | 943 List decls = []; |
| 818 List dartStyles = []; // List of latest styles exposed to Dart. | 944 List dartStyles = []; // List of latest styles exposed to Dart. |
| 819 | 945 |
| 820 do { | 946 do { |
| 821 var selectorGroup = _nestedSelector(); | 947 var selectorGroup = _nestedSelector(); |
| 822 while (selectorGroup != null) { | 948 while (selectorGroup != null) { |
| (...skipping 532 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1355 } | 1481 } |
| 1356 | 1482 |
| 1357 _eat(TokenKind.RBRACK); | 1483 _eat(TokenKind.RBRACK); |
| 1358 | 1484 |
| 1359 return new AttributeSelector(attrName, op, value, _makeSpan(start)); | 1485 return new AttributeSelector(attrName, op, value, _makeSpan(start)); |
| 1360 } | 1486 } |
| 1361 } | 1487 } |
| 1362 | 1488 |
| 1363 // Declaration grammar: | 1489 // Declaration grammar: |
| 1364 // | 1490 // |
| 1365 // declaration: property ':' expr prio? | 1491 // declaration: property ':' expr prio? |
|
nweiz
2013/09/18 22:40:54
Shouldn't this include variables and now @include
terry
2013/10/09 03:40:33
It does. See VAR_DEFINITION and DIRECTIVE_INCLUDE
| |
| 1366 // | 1492 // |
| 1367 // property: IDENT [or IE hacks] | 1493 // property: IDENT [or IE hacks] |
| 1368 // prio: !important | 1494 // prio: !important |
| 1369 // expr: (see processExpr) | 1495 // expr: (see processExpr) |
| 1370 // | 1496 // |
| 1371 // Here are the ugly IE hacks we need to support: | 1497 // Here are the ugly IE hacks we need to support: |
| 1372 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon | 1498 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon |
| 1373 // *IDENT - IE7 or below | 1499 // *IDENT - IE7 or below |
| 1374 // _IDENT - IE6 property (automatically a valid ident) | 1500 // _IDENT - IE6 property (automatically a valid ident) |
| 1375 // | 1501 // |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 1404 } else if (_peekToken.kind == TokenKind.VAR_DEFINITION) { | 1530 } else if (_peekToken.kind == TokenKind.VAR_DEFINITION) { |
| 1405 _next(); | 1531 _next(); |
| 1406 var definedName; | 1532 var definedName; |
| 1407 if (_peekIdentifier()) definedName = identifier(); | 1533 if (_peekIdentifier()) definedName = identifier(); |
| 1408 | 1534 |
| 1409 _eat(TokenKind.COLON); | 1535 _eat(TokenKind.COLON); |
| 1410 | 1536 |
| 1411 Expressions exprs = processExpr(); | 1537 Expressions exprs = processExpr(); |
| 1412 | 1538 |
| 1413 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); | 1539 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); |
| 1540 } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) { | |
| 1541 // @include mixinName in the declaration area. | |
| 1542 var span = _makeSpan(start); | |
| 1543 var include = processInclude(span, false); | |
| 1544 decl = new IncludeMixinAtDeclaration(include, span); | |
| 1414 } | 1545 } |
| 1415 | 1546 |
| 1416 return decl; | 1547 return decl; |
| 1417 } | 1548 } |
| 1418 | 1549 |
| 1419 /** List of styles exposed to the Dart UI framework. */ | 1550 /** List of styles exposed to the Dart UI framework. */ |
| 1420 static const int _fontPartFont= 0; | 1551 static const int _fontPartFont= 0; |
| 1421 static const int _fontPartVariant = 1; | 1552 static const int _fontPartVariant = 1; |
| 1422 static const int _fontPartWeight = 2; | 1553 static const int _fontPartWeight = 2; |
| 1423 static const int _fontPartSize = 3; | 1554 static const int _fontPartSize = 3; |
| (...skipping 976 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2400 | 2531 |
| 2401 if (replace != null && result == null) { | 2532 if (replace != null && result == null) { |
| 2402 result = new StringBuffer(text.substring(0, i)); | 2533 result = new StringBuffer(text.substring(0, i)); |
| 2403 } | 2534 } |
| 2404 | 2535 |
| 2405 if (result != null) result.write(replace != null ? replace : text[i]); | 2536 if (result != null) result.write(replace != null ? replace : text[i]); |
| 2406 } | 2537 } |
| 2407 | 2538 |
| 2408 return result == null ? text : result.toString(); | 2539 return result == null ? text : result.toString(); |
| 2409 } | 2540 } |
| OLD | NEW |