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 |