Chromium Code Reviews| 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; |