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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: pkg/csslib/lib/parser.dart
diff --git a/pkg/csslib/lib/parser.dart b/pkg/csslib/lib/parser.dart
index 9f7b3ff1de6992ca3436d664e45a148d9afb8a5d..e050350c55f98d5f35c5d2317f08b5d7998bfded 100644
--- a/pkg/csslib/lib/parser.dart
+++ b/pkg/csslib/lib/parser.dart
@@ -429,17 +429,23 @@ class Parser {
}
}
- // Directive grammar:
- //
- // import: '@import' [string | URI] media_list?
- // media: '@media' media_query_list '{' ruleset '}'
- // page: '@page' [':' IDENT]? '{' declarations '}'
- // stylet: '@stylet' IDENT '{' ruleset '}'
- // media_query_list: IDENT [',' IDENT]
- // keyframes: '@-webkit-keyframes ...' (see grammar below).
- // font_face: '@font-face' '{' declarations '}'
- // namespace: '@namespace name url("xmlns")
- // host: '@host '{' ruleset '}'
+ /**
+ * Directive grammar:
+ *
+ * import: '@import' [string | URI] media_list?
+ * media: '@media' media_query_list '{' ruleset '}'
+ * page: '@page' [':' IDENT]? '{' declarations '}'
+ * stylet: '@stylet' IDENT '{' ruleset '}'
+ * media_query_list: IDENT [',' IDENT]
+ * keyframes: '@-webkit-keyframes ...' (see grammar below).
+ * font_face: '@font-face' '{' declarations '}'
+ * namespace: '@namespace name url("xmlns")
+ * host: '@host '{' ruleset '}'
+ * mixin: '@mixin name [(args,...)] '{' declarations/ruleset '}'
+ * include: '@include name [(@arg,@arg1)]
+ * '@include name [(@arg...)]
+ * content '@content'
+ */
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
int start = _peekToken.start;
@@ -748,9 +754,129 @@ class Parser {
return new NamespaceDirective(prefix != null ? prefix.name : '',
namespaceUri, _makeSpan(start));
+
+ case TokenKind.DIRECTIVE_MIXIN:
+ /* Stylet grammar:
nweiz 2013/09/18 22:40:54 "Stylet" -> "Mixin"
terry 2013/10/09 03:40:33 Done.
+ *
+ * @mixin IDENT [(args,...)] '{'
+ * ruleset | properties
nweiz 2013/09/18 22:40:54 This should be "[ruleset | property | directive]*"
terry 2013/10/09 03:40:33 Done.
+ * '}'
+ */
+ _next();
+
+ var name;
+ 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
+ name = identifier();
+ }
Jennifer Messerly 2013/09/18 06:44:15 TODO here about parsing the args?
terry 2013/10/09 03:40:33 Done.
+
+ _eat(TokenKind.LBRACE);
+
+ List<TreeNode> productions = [];
+ List<TreeNode> declarations = [];
+ var mixinDirective;
+
+ start = _peekToken.start;
+ while (!_maybeEat(TokenKind.END_OF_FILE)) {
+ 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
+ if (directive != null) {
+ productions.add(directive);
+ _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
+ continue;
+ }
+ var selectorGroup = _nestedSelector();
+ if (selectorGroup != null) {
+ // Nested selector so process as a ruleset.
+ var ruleset = processRuleSet(selectorGroup);
+ if (ruleset == null) {
+ mixinDirective = new MixinRulesetDirective(name.name, [], false,
+ productions, _makeSpan(start));
+ 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
+ }
+ productions.add(ruleset);
+ }
+
+ var declGroup = processDeclarations(checkBrace: false);
+
+ var decls = [];
+ if (declGroup.declarations.any((decl) {
+ return decl is Declaration && decl is! IncludeMixinAtDeclaration;
+ })) {
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
+ var newDecls = [];
+ productions.forEach((include) {
+ // If declGroup has items that are declarations then we assume
+ // 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
+ if (include is IncludeDirective) {
+ 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.
+ } else {
+ print("Error mixing of top-level vs declarations mixins");
+ }
+ });
+ declGroup.declarations.insertAll(0, newDecls);
+ productions = [];
+ } else {
+ // Declarations are just @includes make it a list of productions
+ // 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.
+ 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.
+ productions.add(decl is IncludeMixinAtDeclaration ?
+ decl.include : decl);
+ });
+ declGroup.declarations.clear();
+ }
+
+ if (declGroup.declarations.isNotEmpty) {
+ if (productions.isEmpty) {
+ mixinDirective = new MixinDeclarationDirective(name.name, [],
+ false, declGroup, _makeSpan(start));
+ break;
+ } else {
+ declGroup.declarations.forEach((decl) {
+ 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.
+ productions.add(decl.include);
+ } else {
+ productions.add(decl);
+ }
+ });
+ }
+ } else {
+ mixinDirective = new MixinRulesetDirective(name.name, [], false,
+ productions, _makeSpan(start));
+ break;
+ }
+ }
+
+ _eat(TokenKind.RBRACE);
+
+ return mixinDirective;
+
+ case TokenKind.DIRECTIVE_INCLUDE:
+ return processInclude( _makeSpan(start));
+
+ case TokenKind.DIRECTIVE_CONTENT:
+ // 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.
}
}
+ 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.
+ /* Stylet grammar:
nweiz 2013/09/18 22:40:54 "Stylet" -> "Include"
terry 2013/10/09 03:40:33 Done.
+ *
+ * @include IDENT [(args,...)];
+ */
+ _next();
+
+ var name;
+ 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
+ name = identifier();
+ }
+
+ if (eatSemiColon) {
+ _maybeEat(TokenKind.SEMICOLON);
+ }
+
+ // TODO(terry): Handle parameters to mixin.
+
+ return new IncludeDirective(name.name, [], span);
+ }
+
RuleSet processRuleSet([SelectorGroup selectorGroup]) {
if (selectorGroup == null) {
selectorGroup = processSelectorGroup();
@@ -809,7 +935,7 @@ class Parser {
}
}
- processDeclarations({bool checkBrace: true}) {
+ DeclarationGroup processDeclarations({bool checkBrace: true}) {
int start = _peekToken.start;
if (checkBrace) _eat(TokenKind.LBRACE);
@@ -1411,6 +1537,11 @@ class Parser {
Expressions exprs = processExpr();
decl = new VarDefinition(definedName, exprs, _makeSpan(start));
+ } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) {
+ // @include mixinName in the declaration area.
+ var span = _makeSpan(start);
+ var include = processInclude(span, false);
+ decl = new IncludeMixinAtDeclaration(include, span);
}
return decl;

Powered by Google App Engine
This is Rietveld 408576698