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

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

Issue 23819036: Support for @mixin, @include and @extend (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: mixin w/o parameters Created 7 years, 3 months 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
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_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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698