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

Side by Side Diff: lib/parser.dart

Issue 998843003: pkg/csslib: formatting (Closed) Base URL: https://github.com/dart-lang/csslib@master
Patch Set: Created 5 years, 9 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
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_span/source_span.dart'; 9 import 'package:source_span/source_span.dart';
10 10
11 import "visitor.dart"; 11 import "visitor.dart";
12 import 'src/messages.dart'; 12 import 'src/messages.dart';
13 import 'src/options.dart'; 13 import 'src/options.dart';
14 14
15 part 'src/analyzer.dart'; 15 part 'src/analyzer.dart';
16 part 'src/polyfill.dart'; 16 part 'src/polyfill.dart';
17 part 'src/property.dart'; 17 part 'src/property.dart';
18 part 'src/token.dart'; 18 part 'src/token.dart';
19 part 'src/tokenizer_base.dart'; 19 part 'src/tokenizer_base.dart';
20 part 'src/tokenizer.dart'; 20 part 'src/tokenizer.dart';
21 part 'src/tokenkind.dart'; 21 part 'src/tokenkind.dart';
22 22
23
24 /** Used for parser lookup ahead (used for nested selectors Less support). */ 23 /** Used for parser lookup ahead (used for nested selectors Less support). */
25 class ParserState extends TokenizerState { 24 class ParserState extends TokenizerState {
26 final Token peekToken; 25 final Token peekToken;
27 final Token previousToken; 26 final Token previousToken;
28 27
29 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer) 28 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer)
30 : super(tokenizer); 29 : super(tokenizer);
31 } 30 }
32 31
33 // TODO(jmesserly): this should not be global 32 // TODO(jmesserly): this should not be global
34 void _createMessages({List<Message> errors, List<String> options}) { 33 void _createMessages({List<Message> errors, List<String> options}) {
35 if (errors == null) errors = []; 34 if (errors == null) errors = [];
36 35
37 if (options == null) { 36 if (options == null) {
38 options = ['--no-colors', 'memory']; 37 options = ['--no-colors', 'memory'];
39 } 38 }
40 var opt = PreprocessorOptions.parse(options); 39 var opt = PreprocessorOptions.parse(options);
41 messages = new Messages(options: opt, printHandler: errors.add); 40 messages = new Messages(options: opt, printHandler: errors.add);
42 } 41 }
43 42
44 /** CSS checked mode enabled. */ 43 /** CSS checked mode enabled. */
45 bool get isChecked => messages.options.checked; 44 bool get isChecked => messages.options.checked;
46 45
47 // TODO(terry): Remove nested name parameter. 46 // TODO(terry): Remove nested name parameter.
48 /** Parse and analyze the CSS file. */ 47 /** Parse and analyze the CSS file. */
49 StyleSheet compile(input, {List<Message> errors, List<String> options, 48 StyleSheet compile(input, {List<Message> errors, List<String> options,
50 bool nested: true, 49 bool nested: true, bool polyfill: false, List<StyleSheet> includes: null}) {
51 bool polyfill: false,
52 List<StyleSheet> includes: null}) {
53
54 if (includes == null) { 50 if (includes == null) {
55 includes = []; 51 includes = [];
56 } 52 }
57 53
58 var source = _inputAsString(input); 54 var source = _inputAsString(input);
59 55
60 _createMessages(errors: errors, options: options); 56 _createMessages(errors: errors, options: options);
61 57
62 var file = new SourceFile(source); 58 var file = new SourceFile(source);
63 59
64 var tree = new _Parser(file, source).parse(); 60 var tree = new _Parser(file, source).parse();
65 61
66 analyze([tree], errors: errors, options: options); 62 analyze([tree], errors: errors, options: options);
67 63
68 if (polyfill) { 64 if (polyfill) {
69 var processCss = new PolyFill(messages, true); 65 var processCss = new PolyFill(messages, true);
70 processCss.process(tree, includes: includes); 66 processCss.process(tree, includes: includes);
71 } 67 }
72 68
73 return tree; 69 return tree;
74 } 70 }
75 71
76 /** Analyze the CSS file. */ 72 /** Analyze the CSS file. */
77 void analyze(List<StyleSheet> styleSheets, 73 void analyze(List<StyleSheet> styleSheets,
78 {List<Message> errors, List<String> options}) { 74 {List<Message> errors, List<String> options}) {
79
80 _createMessages(errors: errors, options: options); 75 _createMessages(errors: errors, options: options);
81 new Analyzer(styleSheets, messages).run(); 76 new Analyzer(styleSheets, messages).run();
82 } 77 }
83 78
84 /** 79 /**
85 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String], 80 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String],
86 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional 81 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional
87 * [errors] list will contain each error/warning as a [Message]. 82 * [errors] list will contain each error/warning as a [Message].
88 */ 83 */
89 StyleSheet parse(input, {List<Message> errors, List<String> options}) { 84 StyleSheet parse(input, {List<Message> errors, List<String> options}) {
(...skipping 10 matching lines...) Expand all
100 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional 95 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional
101 * [errors] list will contain each error/warning as a [Message]. 96 * [errors] list will contain each error/warning as a [Message].
102 */ 97 */
103 // TODO(jmesserly): should rename "parseSelector" and return Selector 98 // TODO(jmesserly): should rename "parseSelector" and return Selector
104 StyleSheet selector(input, {List<Message> errors}) { 99 StyleSheet selector(input, {List<Message> errors}) {
105 var source = _inputAsString(input); 100 var source = _inputAsString(input);
106 101
107 _createMessages(errors: errors); 102 _createMessages(errors: errors);
108 103
109 var file = new SourceFile(source); 104 var file = new SourceFile(source);
110 return (new _Parser(file, source) 105 return (new _Parser(file, source)..tokenizer.inSelector = true)
111 ..tokenizer.inSelector = true)
112 .parseSelector(); 106 .parseSelector();
113 } 107 }
114 108
115 SelectorGroup parseSelectorGroup(input, {List<Message> errors}) { 109 SelectorGroup parseSelectorGroup(input, {List<Message> errors}) {
116 var source = _inputAsString(input); 110 var source = _inputAsString(input);
117 111
118 _createMessages(errors: errors); 112 _createMessages(errors: errors);
119 113
120 var file = new SourceFile(source); 114 var file = new SourceFile(source);
121 return (new _Parser(file, source) 115 return (new _Parser(file, source)
122 // TODO(jmesserly): this fix should be applied to the parser. It's tricky 116 // TODO(jmesserly): this fix should be applied to the parser. It's tricky
123 // because by the time the flag is set one token has already been fetched. 117 // because by the time the flag is set one token has already been fetched.
124 ..tokenizer.inSelector = true) 118 ..tokenizer.inSelector = true).processSelectorGroup();
125 .processSelectorGroup();
126 } 119 }
127 120
128 String _inputAsString(input) { 121 String _inputAsString(input) {
129 String source; 122 String source;
130 123
131 if (input is String) { 124 if (input is String) {
132 source = input; 125 source = input;
133 } else if (input is List) { 126 } else if (input is List) {
134 // TODO(terry): The parse function needs an "encoding" argument and will 127 // TODO(terry): The parse function needs an "encoding" argument and will
135 // default to whatever encoding CSS defaults to. 128 // default to whatever encoding CSS defaults to.
(...skipping 20 matching lines...) Expand all
156 return source; 149 return source;
157 } 150 }
158 151
159 // TODO(terry): Consider removing this class when all usages can be eliminated 152 // TODO(terry): Consider removing this class when all usages can be eliminated
160 // or replaced with compile API. 153 // or replaced with compile API.
161 /** Public parsing interface for csslib. */ 154 /** Public parsing interface for csslib. */
162 class Parser { 155 class Parser {
163 final _Parser _parser; 156 final _Parser _parser;
164 157
165 // TODO(jmesserly): having file and text is redundant. 158 // TODO(jmesserly): having file and text is redundant.
166 Parser(SourceFile file, String text, {int start: 0, String baseUrl}) : 159 Parser(SourceFile file, String text, {int start: 0, String baseUrl})
167 _parser = new _Parser(file, text, start: start, baseUrl: baseUrl); 160 : _parser = new _Parser(file, text, start: start, baseUrl: baseUrl);
168 161
169 StyleSheet parse() => _parser.parse(); 162 StyleSheet parse() => _parser.parse();
170 } 163 }
171 164
172 /** A simple recursive descent parser for CSS. */ 165 /** A simple recursive descent parser for CSS. */
173 class _Parser { 166 class _Parser {
174 final Tokenizer tokenizer; 167 final Tokenizer tokenizer;
175 168
176 /** Base url of CSS file. */ 169 /** Base url of CSS file. */
177 final String _baseUrl; 170 final String _baseUrl;
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 } 249 }
257 } 250 }
258 251
259 /////////////////////////////////////////////////////////////////// 252 ///////////////////////////////////////////////////////////////////
260 // Basic support methods 253 // Basic support methods
261 /////////////////////////////////////////////////////////////////// 254 ///////////////////////////////////////////////////////////////////
262 int _peek() { 255 int _peek() {
263 return _peekToken.kind; 256 return _peekToken.kind;
264 } 257 }
265 258
266 Token _next({unicodeRange : false}) { 259 Token _next({unicodeRange: false}) {
267 _previousToken = _peekToken; 260 _previousToken = _peekToken;
268 _peekToken = tokenizer.next(unicodeRange: unicodeRange); 261 _peekToken = tokenizer.next(unicodeRange: unicodeRange);
269 return _previousToken; 262 return _previousToken;
270 } 263 }
271 264
272 bool _peekKind(int kind) { 265 bool _peekKind(int kind) {
273 return _peekToken.kind == kind; 266 return _peekToken.kind == kind;
274 } 267 }
275 268
276 /* Is the next token a legal identifier? This includes pseudo-keywords. */ 269 /* Is the next token a legal identifier? This includes pseudo-keywords. */
277 bool _peekIdentifier() { 270 bool _peekIdentifier() {
278 return TokenKind.isIdentifier(_peekToken.kind); 271 return TokenKind.isIdentifier(_peekToken.kind);
279 } 272 }
280 273
281 /** Marks the parser/tokenizer look ahead to support Less nested selectors. */ 274 /** Marks the parser/tokenizer look ahead to support Less nested selectors. */
282 ParserState get _mark => 275 ParserState get _mark =>
283 new ParserState(_peekToken, _previousToken, tokenizer); 276 new ParserState(_peekToken, _previousToken, tokenizer);
284 277
285 /** Restores the parser/tokenizer state to state remembered by _mark. */ 278 /** Restores the parser/tokenizer state to state remembered by _mark. */
286 void _restore(ParserState markedData) { 279 void _restore(ParserState markedData) {
287 tokenizer.restore(markedData); 280 tokenizer.restore(markedData);
288 _peekToken = markedData.peekToken; 281 _peekToken = markedData.peekToken;
289 _previousToken = markedData.previousToken; 282 _previousToken = markedData.previousToken;
290 } 283 }
291 284
292 bool _maybeEat(int kind, {unicodeRange : false}) { 285 bool _maybeEat(int kind, {unicodeRange: false}) {
293 if (_peekToken.kind == kind) { 286 if (_peekToken.kind == kind) {
294 _previousToken = _peekToken; 287 _previousToken = _peekToken;
295 _peekToken = tokenizer.next(unicodeRange: unicodeRange); 288 _peekToken = tokenizer.next(unicodeRange: unicodeRange);
296 return true; 289 return true;
297 } else { 290 } else {
298 return false; 291 return false;
299 } 292 }
300 } 293 }
301 294
302 void _eat(int kind, {unicodeRange : false}) { 295 void _eat(int kind, {unicodeRange: false}) {
303 if (!_maybeEat(kind, unicodeRange: unicodeRange)) { 296 if (!_maybeEat(kind, unicodeRange: unicodeRange)) {
304 _errorExpected(TokenKind.kindToString(kind)); 297 _errorExpected(TokenKind.kindToString(kind));
305 } 298 }
306 } 299 }
307 300
308 void _eatSemicolon() { 301 void _eatSemicolon() {
309 _eat(TokenKind.SEMICOLON); 302 _eat(TokenKind.SEMICOLON);
310 } 303 }
311 304
312 void _errorExpected(String expected) { 305 void _errorExpected(String expected) {
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 // [ AND S* MediaExpr ]* | MediaExpr [ AND S* MediaExpr ]* 385 // [ AND S* MediaExpr ]* | MediaExpr [ AND S* MediaExpr ]*
393 386
394 var start = _peekToken.span; 387 var start = _peekToken.span;
395 388
396 // Is it a unary media operator? 389 // Is it a unary media operator?
397 var op = _peekToken.text; 390 var op = _peekToken.text;
398 var opLen = op.length; 391 var opLen = op.length;
399 var unaryOp = TokenKind.matchMediaOperator(op, 0, opLen); 392 var unaryOp = TokenKind.matchMediaOperator(op, 0, opLen);
400 if (unaryOp != -1) { 393 if (unaryOp != -1) {
401 if (isChecked) { 394 if (isChecked) {
402 if (startQuery && 395 if (startQuery && unaryOp != TokenKind.MEDIA_OP_NOT ||
403 unaryOp != TokenKind.MEDIA_OP_NOT ||
404 unaryOp != TokenKind.MEDIA_OP_ONLY) { 396 unaryOp != TokenKind.MEDIA_OP_ONLY) {
405 _warning("Only the unary operators NOT and ONLY allowed", 397 _warning("Only the unary operators NOT and ONLY allowed",
406 _makeSpan(start)); 398 _makeSpan(start));
407 } 399 }
408 if (!startQuery && unaryOp != TokenKind.MEDIA_OP_AND) { 400 if (!startQuery && unaryOp != TokenKind.MEDIA_OP_AND) {
409 _warning("Only the binary AND operator allowed", _makeSpan(start)); 401 _warning("Only the binary AND operator allowed", _makeSpan(start));
410 } 402 }
411 } 403 }
412 _next(); 404 _next();
413 start = _peekToken.span; 405 start = _peekToken.span;
(...skipping 27 matching lines...) Expand all
441 return new MediaQuery(unaryOp, type, exprs, _makeSpan(start)); 433 return new MediaQuery(unaryOp, type, exprs, _makeSpan(start));
442 } 434 }
443 } 435 }
444 436
445 MediaExpression processMediaExpression([bool andOperator = false]) { 437 MediaExpression processMediaExpression([bool andOperator = false]) {
446 var start = _peekToken.span; 438 var start = _peekToken.span;
447 439
448 // Grammar: '(' S* media_feature S* [ ':' S* expr ]? ')' S* 440 // Grammar: '(' S* media_feature S* [ ':' S* expr ]? ')' S*
449 if (_maybeEat(TokenKind.LPAREN)) { 441 if (_maybeEat(TokenKind.LPAREN)) {
450 if (_peekIdentifier()) { 442 if (_peekIdentifier()) {
451 var feature = identifier(); // Media feature. 443 var feature = identifier(); // Media feature.
452 while (_maybeEat(TokenKind.COLON)) { 444 while (_maybeEat(TokenKind.COLON)) {
453 var startExpr = _peekToken.span; 445 var startExpr = _peekToken.span;
454 var exprs = processExpr(); 446 var exprs = processExpr();
455 if (_maybeEat(TokenKind.RPAREN)) { 447 if (_maybeEat(TokenKind.RPAREN)) {
456 return new MediaExpression(andOperator, feature, exprs, 448 return new MediaExpression(
457 _makeSpan(startExpr)); 449 andOperator, feature, exprs, _makeSpan(startExpr));
458 } else if (isChecked) { 450 } else if (isChecked) {
459 _warning("Missing parenthesis around media expression", 451 _warning("Missing parenthesis around media expression",
460 _makeSpan(start)); 452 _makeSpan(start));
461 return null; 453 return null;
462 } 454 }
463 } 455 }
464 } else if (isChecked) { 456 } else if (isChecked) {
465 _warning("Missing media feature in media expression", _makeSpan(start)); 457 _warning("Missing media feature in media expression", _makeSpan(start));
466 return null; 458 return null;
467 } 459 }
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 } 574 }
583 575
584 // Any pseudo page? 576 // Any pseudo page?
585 var pseudoPage; 577 var pseudoPage;
586 if (_maybeEat(TokenKind.COLON)) { 578 if (_maybeEat(TokenKind.COLON)) {
587 if (_peekIdentifier()) { 579 if (_peekIdentifier()) {
588 pseudoPage = identifier(); 580 pseudoPage = identifier();
589 // TODO(terry): Normalize pseudoPage to lowercase. 581 // TODO(terry): Normalize pseudoPage to lowercase.
590 if (isChecked && 582 if (isChecked &&
591 !(pseudoPage.name == 'left' || 583 !(pseudoPage.name == 'left' ||
592 pseudoPage.name == 'right' || 584 pseudoPage.name == 'right' ||
593 pseudoPage.name == 'first')) { 585 pseudoPage.name == 'first')) {
594 _warning("Pseudo page must be left, top or first", 586 _warning(
595 pseudoPage.span); 587 "Pseudo page must be left, top or first", pseudoPage.span);
596 return null; 588 return null;
597 } 589 }
598 } 590 }
599 } 591 }
600 592
601 String pseudoName = pseudoPage is Identifier ? pseudoPage.name : ''; 593 String pseudoName = pseudoPage is Identifier ? pseudoPage.name : '';
602 String ident = name is Identifier ? name.name : ''; 594 String ident = name is Identifier ? name.name : '';
603 return new PageDirective(ident, pseudoName, 595 return new PageDirective(
604 processMarginsDeclarations(), _makeSpan(start)); 596 ident, pseudoName, processMarginsDeclarations(), _makeSpan(start));
605 597
606 case TokenKind.DIRECTIVE_CHARSET: 598 case TokenKind.DIRECTIVE_CHARSET:
607 // @charset S* STRING S* ';' 599 // @charset S* STRING S* ';'
608 _next(); 600 _next();
609 601
610 var charEncoding = processQuotedString(false); 602 var charEncoding = processQuotedString(false);
611 if (isChecked && charEncoding == null) { 603 if (isChecked && charEncoding == null) {
612 // Missing character encoding. 604 // Missing character encoding.
613 _warning('missing character encoding string', _makeSpan(start)); 605 _warning('missing character encoding string', _makeSpan(start));
614 } 606 }
(...skipping 18 matching lines...) Expand all
633 */ 625 */
634 case TokenKind.DIRECTIVE_KEYFRAMES: 626 case TokenKind.DIRECTIVE_KEYFRAMES:
635 case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES: 627 case TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES:
636 case TokenKind.DIRECTIVE_MOZ_KEYFRAMES: 628 case TokenKind.DIRECTIVE_MOZ_KEYFRAMES:
637 case TokenKind.DIRECTIVE_O_KEYFRAMES: 629 case TokenKind.DIRECTIVE_O_KEYFRAMES:
638 // TODO(terry): Remove workaround when bug 8270 is fixed. 630 // TODO(terry): Remove workaround when bug 8270 is fixed.
639 case TokenKind.DIRECTIVE_MS_KEYFRAMES: 631 case TokenKind.DIRECTIVE_MS_KEYFRAMES:
640 if (tokId == TokenKind.DIRECTIVE_MS_KEYFRAMES && isChecked) { 632 if (tokId == TokenKind.DIRECTIVE_MS_KEYFRAMES && isChecked) {
641 _warning('@-ms-keyframes should be @keyframes', _makeSpan(start)); 633 _warning('@-ms-keyframes should be @keyframes', _makeSpan(start));
642 } 634 }
643 // TODO(terry): End of workaround. 635 // TODO(terry): End of workaround.
644 636
645 /* Key frames grammar: 637 /* Key frames grammar:
646 * 638 *
647 * @[browser]? keyframes [IDENT|STRING] '{' keyframes-blocks '}'; 639 * @[browser]? keyframes [IDENT|STRING] '{' keyframes-blocks '}';
648 * 640 *
649 * browser: [-webkit-, -moz-, -ms-, -o-] 641 * browser: [-webkit-, -moz-, -ms-, -o-]
650 * 642 *
651 * keyframes-blocks: 643 * keyframes-blocks:
652 * [keyframe-selectors '{' declarations '}']* ; 644 * [keyframe-selectors '{' declarations '}']* ;
653 * 645 *
(...skipping 15 matching lines...) Expand all
669 Expressions selectors = new Expressions(_makeSpan(start)); 661 Expressions selectors = new Expressions(_makeSpan(start));
670 662
671 do { 663 do {
672 var term = processTerm(); 664 var term = processTerm();
673 665
674 // TODO(terry): Only allow from, to and PERCENTAGE ... 666 // TODO(terry): Only allow from, to and PERCENTAGE ...
675 667
676 selectors.add(term); 668 selectors.add(term);
677 } while (_maybeEat(TokenKind.COMMA)); 669 } while (_maybeEat(TokenKind.COMMA));
678 670
679 keyframe.add(new KeyFrameBlock(selectors, processDeclarations(), 671 keyframe.add(new KeyFrameBlock(
680 _makeSpan(start))); 672 selectors, processDeclarations(), _makeSpan(start)));
681
682 } while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile()); 673 } while (!_maybeEat(TokenKind.RBRACE) && !isPrematureEndOfFile());
683 674
684 return keyframe; 675 return keyframe;
685 676
686 case TokenKind.DIRECTIVE_FONTFACE: 677 case TokenKind.DIRECTIVE_FONTFACE:
687 _next(); 678 _next();
688 return new FontFaceDirective(processDeclarations(), _makeSpan(start)); 679 return new FontFaceDirective(processDeclarations(), _makeSpan(start));
689 680
690 case TokenKind.DIRECTIVE_STYLET: 681 case TokenKind.DIRECTIVE_STYLET:
691 /* Stylet grammar: 682 /* Stylet grammar:
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
746 if (func is UriTerm) { 737 if (func is UriTerm) {
747 // @namespace url(""); 738 // @namespace url("");
748 namespaceUri = func.text; 739 namespaceUri = func.text;
749 prefix = null; 740 prefix = null;
750 } 741 }
751 } else { 742 } else {
752 namespaceUri = processQuotedString(false); 743 namespaceUri = processQuotedString(false);
753 } 744 }
754 } 745 }
755 746
756 return new NamespaceDirective(prefix != null ? prefix.name : '', 747 return new NamespaceDirective(
757 namespaceUri, _makeSpan(start)); 748 prefix != null ? prefix.name : '', namespaceUri, _makeSpan(start));
758 749
759 case TokenKind.DIRECTIVE_MIXIN: 750 case TokenKind.DIRECTIVE_MIXIN:
760 return processMixin(); 751 return processMixin();
761 752
762 case TokenKind.DIRECTIVE_INCLUDE: 753 case TokenKind.DIRECTIVE_INCLUDE:
763 return processInclude( _makeSpan(start)); 754 return processInclude(_makeSpan(start));
764 755
765 case TokenKind.DIRECTIVE_CONTENT: 756 case TokenKind.DIRECTIVE_CONTENT:
766 // TODO(terry): TBD 757 // TODO(terry): TBD
767 _warning("@content not implemented.", _makeSpan(start)); 758 _warning("@content not implemented.", _makeSpan(start));
768 return null; 759 return null;
769 } 760 }
770 return null; 761 return null;
771 } 762 }
772 763
773 /** 764 /**
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
816 while (!_maybeEat(TokenKind.END_OF_FILE)) { 807 while (!_maybeEat(TokenKind.END_OF_FILE)) {
817 var directive = processDirective(); 808 var directive = processDirective();
818 if (directive != null) { 809 if (directive != null) {
819 productions.add(directive); 810 productions.add(directive);
820 continue; 811 continue;
821 } 812 }
822 813
823 var declGroup = processDeclarations(checkBrace: false); 814 var declGroup = processDeclarations(checkBrace: false);
824 var decls = []; 815 var decls = [];
825 if (declGroup.declarations.any((decl) { 816 if (declGroup.declarations.any((decl) {
826 return decl is Declaration && 817 return decl is Declaration && decl is! IncludeMixinAtDeclaration;
827 decl is! IncludeMixinAtDeclaration;
828 })) { 818 })) {
829 var newDecls = []; 819 var newDecls = [];
830 productions.forEach((include) { 820 productions.forEach((include) {
831 // If declGroup has items that are declarations then we assume 821 // If declGroup has items that are declarations then we assume
832 // this mixin is a declaration mixin not a top-level mixin. 822 // this mixin is a declaration mixin not a top-level mixin.
833 if (include is IncludeDirective) { 823 if (include is IncludeDirective) {
834 newDecls.add(new IncludeMixinAtDeclaration(include, 824 newDecls.add(new IncludeMixinAtDeclaration(include, include.span));
835 include.span));
836 } else { 825 } else {
837 _warning("Error mixing of top-level vs declarations mixins", 826 _warning("Error mixing of top-level vs declarations mixins",
838 _makeSpan(include.span)); 827 _makeSpan(include.span));
839 } 828 }
840 }); 829 });
841 declGroup.declarations.insertAll(0, newDecls); 830 declGroup.declarations.insertAll(0, newDecls);
842 productions = []; 831 productions = [];
843 } else { 832 } else {
844 // Declarations are just @includes make it a list of productions 833 // Declarations are just @includes make it a list of productions
845 // not a declaration group (anything else is a ruleset). Make it a 834 // not a declaration group (anything else is a ruleset). Make it a
846 // list of productions, not a declaration group. 835 // list of productions, not a declaration group.
847 for (var decl in declGroup.declarations) { 836 for (var decl in declGroup.declarations) {
848 productions.add(decl is IncludeMixinAtDeclaration ? 837 productions
849 decl.include : decl); 838 .add(decl is IncludeMixinAtDeclaration ? decl.include : decl);
850 }; 839 }
840 ;
851 declGroup.declarations.clear(); 841 declGroup.declarations.clear();
852 } 842 }
853 843
854 if (declGroup.declarations.isNotEmpty) { 844 if (declGroup.declarations.isNotEmpty) {
855 if (productions.isEmpty) { 845 if (productions.isEmpty) {
856 mixinDirective = new MixinDeclarationDirective(name.name, params, 846 mixinDirective = new MixinDeclarationDirective(
857 false, declGroup, _makeSpan(start)); 847 name.name, params, false, declGroup, _makeSpan(start));
858 break; 848 break;
859 } else { 849 } else {
860 for (var decl in declGroup.declarations) { 850 for (var decl in declGroup.declarations) {
861 productions.add(decl is IncludeMixinAtDeclaration ? 851 productions
862 decl.include : decl); 852 .add(decl is IncludeMixinAtDeclaration ? decl.include : decl);
863 } 853 }
864 } 854 }
865 } else { 855 } else {
866 mixinDirective = new MixinRulesetDirective(name.name, params, 856 mixinDirective = new MixinRulesetDirective(
867 false, productions, _makeSpan(start)); 857 name.name, params, false, productions, _makeSpan(start));
868 break; 858 break;
869 } 859 }
870 } 860 }
871 861
872 if (productions.isNotEmpty) { 862 if (productions.isNotEmpty) {
873 mixinDirective = new MixinRulesetDirective(name.name, params, 863 mixinDirective = new MixinRulesetDirective(
874 false, productions, _makeSpan(start)); 864 name.name, params, false, productions, _makeSpan(start));
875 } 865 }
876 866
877 _eat(TokenKind.RBRACE); 867 _eat(TokenKind.RBRACE);
878 868
879 return mixinDirective; 869 return mixinDirective;
880 } 870 }
881 871
882 /** 872 /**
883 * Returns a VarDefinitionDirective or VarDefinition if a varaible otherwise 873 * Returns a VarDefinitionDirective or VarDefinition if a varaible otherwise
884 * return the token id of a directive or -1 if neither. 874 * return the token id of a directive or -1 if neither.
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
987 } 977 }
988 978
989 return new IncludeDirective(name.name, params, span); 979 return new IncludeDirective(name.name, params, span);
990 } 980 }
991 981
992 RuleSet processRuleSet([SelectorGroup selectorGroup]) { 982 RuleSet processRuleSet([SelectorGroup selectorGroup]) {
993 if (selectorGroup == null) { 983 if (selectorGroup == null) {
994 selectorGroup = processSelectorGroup(); 984 selectorGroup = processSelectorGroup();
995 } 985 }
996 if (selectorGroup != null) { 986 if (selectorGroup != null) {
997 return new RuleSet(selectorGroup, processDeclarations(), 987 return new RuleSet(
998 selectorGroup.span); 988 selectorGroup, processDeclarations(), selectorGroup.span);
999 } 989 }
1000 } 990 }
1001 991
1002 /** 992 /**
1003 * Look ahead to see if what should be a declaration is really a selector. 993 * Look ahead to see if what should be a declaration is really a selector.
1004 * If it's a selector than it's a nested selector. This support's Less' 994 * If it's a selector than it's a nested selector. This support's Less'
1005 * nested selector syntax (requires a look ahead). E.g., 995 * nested selector syntax (requires a look ahead). E.g.,
1006 * 996 *
1007 * div { 997 * div {
1008 * width : 20px; 998 * width : 20px;
(...skipping 15 matching lines...) Expand all
1024 */ 1014 */
1025 SelectorGroup _nestedSelector() { 1015 SelectorGroup _nestedSelector() {
1026 Messages oldMessages = messages; 1016 Messages oldMessages = messages;
1027 _createMessages(); 1017 _createMessages();
1028 1018
1029 var markedData = _mark; 1019 var markedData = _mark;
1030 1020
1031 // Look a head do we have a nested selector instead of a declaration? 1021 // Look a head do we have a nested selector instead of a declaration?
1032 SelectorGroup selGroup = processSelectorGroup(); 1022 SelectorGroup selGroup = processSelectorGroup();
1033 1023
1034 var nestedSelector = selGroup != null && _peekKind(TokenKind.LBRACE) && 1024 var nestedSelector = selGroup != null &&
1025 _peekKind(TokenKind.LBRACE) &&
1035 messages.messages.isEmpty; 1026 messages.messages.isEmpty;
1036 1027
1037 if (!nestedSelector) { 1028 if (!nestedSelector) {
1038 // Not a selector so restore the world. 1029 // Not a selector so restore the world.
1039 _restore(markedData); 1030 _restore(markedData);
1040 messages = oldMessages; 1031 messages = oldMessages;
1041 return null; 1032 return null;
1042 } else { 1033 } else {
1043 // Remember any messages from look ahead. 1034 // Remember any messages from look ahead.
1044 oldMessages.mergeMessages(messages); 1035 oldMessages.mergeMessages(messages);
1045 messages = oldMessages; 1036 messages = oldMessages;
1046 return selGroup; 1037 return selGroup;
1047 } 1038 }
1048 } 1039 }
1049 1040
1050 DeclarationGroup processDeclarations({bool checkBrace: true}) { 1041 DeclarationGroup processDeclarations({bool checkBrace: true}) {
1051 var start = _peekToken.span; 1042 var start = _peekToken.span;
1052 1043
1053 if (checkBrace) _eat(TokenKind.LBRACE); 1044 if (checkBrace) _eat(TokenKind.LBRACE);
1054 1045
1055 List decls = []; 1046 List decls = [];
1056 List dartStyles = []; // List of latest styles exposed to Dart. 1047 List dartStyles = []; // List of latest styles exposed to Dart.
1057 1048
1058 do { 1049 do {
1059 var selectorGroup = _nestedSelector(); 1050 var selectorGroup = _nestedSelector();
1060 while (selectorGroup != null) { 1051 while (selectorGroup != null) {
1061 // Nested selector so process as a ruleset. 1052 // Nested selector so process as a ruleset.
1062 var ruleset = processRuleSet(selectorGroup); 1053 var ruleset = processRuleSet(selectorGroup);
1063 decls.add(ruleset); 1054 decls.add(ruleset);
1064 selectorGroup = _nestedSelector(); 1055 selectorGroup = _nestedSelector();
1065 } 1056 }
1066 1057
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1104 } 1095 }
1105 1096
1106 List<DeclarationGroup> processMarginsDeclarations() { 1097 List<DeclarationGroup> processMarginsDeclarations() {
1107 List groups = []; 1098 List groups = [];
1108 1099
1109 var start = _peekToken.span; 1100 var start = _peekToken.span;
1110 1101
1111 _eat(TokenKind.LBRACE); 1102 _eat(TokenKind.LBRACE);
1112 1103
1113 List<Declaration> decls = []; 1104 List<Declaration> decls = [];
1114 List dartStyles = []; // List of latest styles exposed to Dart. 1105 List dartStyles = []; // List of latest styles exposed to Dart.
1115 1106
1116 do { 1107 do {
1117 switch (_peek()) { 1108 switch (_peek()) {
1118 case TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER: 1109 case TokenKind.MARGIN_DIRECTIVE_TOPLEFTCORNER:
1119 case TokenKind.MARGIN_DIRECTIVE_TOPLEFT: 1110 case TokenKind.MARGIN_DIRECTIVE_TOPLEFT:
1120 case TokenKind.MARGIN_DIRECTIVE_TOPCENTER: 1111 case TokenKind.MARGIN_DIRECTIVE_TOPCENTER:
1121 case TokenKind.MARGIN_DIRECTIVE_TOPRIGHT: 1112 case TokenKind.MARGIN_DIRECTIVE_TOPRIGHT:
1122 case TokenKind.MARGIN_DIRECTIVE_TOPRIGHTCORNER: 1113 case TokenKind.MARGIN_DIRECTIVE_TOPRIGHTCORNER:
1123 case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFTCORNER: 1114 case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFTCORNER:
1124 case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFT: 1115 case TokenKind.MARGIN_DIRECTIVE_BOTTOMLEFT:
(...skipping 10 matching lines...) Expand all
1135 // margin : 1126 // margin :
1136 // margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S* 1127 // margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
1137 // 1128 //
1138 // margin_sym : @top-left-corner, @top-left, @bottom-left, etc. 1129 // margin_sym : @top-left-corner, @top-left, @bottom-left, etc.
1139 var marginSym = _peek(); 1130 var marginSym = _peek();
1140 1131
1141 _next(); 1132 _next();
1142 1133
1143 var declGroup = processDeclarations(); 1134 var declGroup = processDeclarations();
1144 if (declGroup != null) { 1135 if (declGroup != null) {
1145 groups.add(new MarginGroup(marginSym, declGroup.declarations, 1136 groups.add(new MarginGroup(
1146 _makeSpan(start))); 1137 marginSym, declGroup.declarations, _makeSpan(start)));
1147 } 1138 }
1148 break; 1139 break;
1149 default: 1140 default:
1150 Declaration decl = processDeclaration(dartStyles); 1141 Declaration decl = processDeclaration(dartStyles);
1151 if (decl != null) { 1142 if (decl != null) {
1152 if (decl.hasDartStyle) { 1143 if (decl.hasDartStyle) {
1153 var newDartStyle = decl.dartStyle; 1144 var newDartStyle = decl.dartStyle;
1154 1145
1155 // Replace or add latest Dart style. 1146 // Replace or add latest Dart style.
1156 bool replaced = false; 1147 bool replaced = false;
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1241 combinatorType = TokenKind.COMBINATOR_GREATER; 1232 combinatorType = TokenKind.COMBINATOR_GREATER;
1242 break; 1233 break;
1243 case TokenKind.TILDE: 1234 case TokenKind.TILDE:
1244 _eat(TokenKind.TILDE); 1235 _eat(TokenKind.TILDE);
1245 combinatorType = TokenKind.COMBINATOR_TILDE; 1236 combinatorType = TokenKind.COMBINATOR_TILDE;
1246 break; 1237 break;
1247 case TokenKind.AMPERSAND: 1238 case TokenKind.AMPERSAND:
1248 _eat(TokenKind.AMPERSAND); 1239 _eat(TokenKind.AMPERSAND);
1249 thisOperator = true; 1240 thisOperator = true;
1250 break; 1241 break;
1251 } 1242 }
1252 1243
1253 // Check if WHITESPACE existed between tokens if so we're descendent. 1244 // Check if WHITESPACE existed between tokens if so we're descendent.
1254 if (combinatorType == TokenKind.COMBINATOR_NONE && !forceCombinatorNone) { 1245 if (combinatorType == TokenKind.COMBINATOR_NONE && !forceCombinatorNone) {
1255 if (this._previousToken != null && 1246 if (this._previousToken != null &&
1256 this._previousToken.end != this._peekToken.start) { 1247 this._previousToken.end != this._peekToken.start) {
1257 combinatorType = TokenKind.COMBINATOR_DESCENDANT; 1248 combinatorType = TokenKind.COMBINATOR_DESCENDANT;
1258 } 1249 }
1259 } 1250 }
1260 1251
1261 var span = _makeSpan(start); 1252 var span = _makeSpan(start);
1262 var simpleSel = thisOperator ? 1253 var simpleSel = thisOperator
1263 new ElementSelector(new ThisOperator(span), span) : simpleSelector(); 1254 ? new ElementSelector(new ThisOperator(span), span)
1255 : simpleSelector();
1264 if (simpleSel == null && 1256 if (simpleSel == null &&
1265 (combinatorType == TokenKind.COMBINATOR_PLUS || 1257 (combinatorType == TokenKind.COMBINATOR_PLUS ||
1266 combinatorType == TokenKind.COMBINATOR_GREATER || 1258 combinatorType == TokenKind.COMBINATOR_GREATER ||
1267 combinatorType == TokenKind.COMBINATOR_TILDE)) { 1259 combinatorType == TokenKind.COMBINATOR_TILDE)) {
1268 // For "+ &", "~ &" or "> &" a selector sequence with no name is needed 1260 // For "+ &", "~ &" or "> &" a selector sequence with no name is needed
1269 // so that the & will have a combinator too. This is needed to 1261 // so that the & will have a combinator too. This is needed to
1270 // disambiguate selector expressions: 1262 // disambiguate selector expressions:
1271 // .foo&:hover combinator before & is NONE 1263 // .foo&:hover combinator before & is NONE
1272 // .foo & combinator before & is DESCDENDANT 1264 // .foo & combinator before & is DESCDENDANT
1273 // .foo > & combinator before & is GREATER 1265 // .foo > & combinator before & is GREATER
1274 simpleSel = new ElementSelector(new Identifier("", span), span); 1266 simpleSel = new ElementSelector(new Identifier("", span), span);
1275 } 1267 }
1276 if (simpleSel != null) { 1268 if (simpleSel != null) {
1277 return new SimpleSelectorSequence(simpleSel, span, combinatorType); 1269 return new SimpleSelectorSequence(simpleSel, span, combinatorType);
1278 } 1270 }
1279 } 1271 }
1280 1272
1281 /** 1273 /**
1282 * Simple selector grammar: 1274 * Simple selector grammar:
1283 * 1275 *
1284 * simple_selector_sequence 1276 * simple_selector_sequence
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1335 break; 1327 break;
1336 case TokenKind.IDENTIFIER: 1328 case TokenKind.IDENTIFIER:
1337 element = identifier(); 1329 element = identifier();
1338 break; 1330 break;
1339 default: 1331 default:
1340 _error('expected element name or universal(*), but found $_peekToken', 1332 _error('expected element name or universal(*), but found $_peekToken',
1341 _peekToken.span); 1333 _peekToken.span);
1342 break; 1334 break;
1343 } 1335 }
1344 1336
1345 return new NamespaceSelector(first, 1337 return new NamespaceSelector(
1346 new ElementSelector(element, element.span), _makeSpan(start)); 1338 first, new ElementSelector(element, element.span), _makeSpan(start));
1347 } else if (first != null) { 1339 } else if (first != null) {
1348 return new ElementSelector(first, _makeSpan(start)); 1340 return new ElementSelector(first, _makeSpan(start));
1349 } else { 1341 } else {
1350 // Check for HASH | class | attrib | pseudo | negation 1342 // Check for HASH | class | attrib | pseudo | negation
1351 return simpleSelectorTail(); 1343 return simpleSelectorTail();
1352 } 1344 }
1353 } 1345 }
1354 1346
1355 bool _anyWhiteSpaceBeforePeekToken(int kind) { 1347 bool _anyWhiteSpaceBeforePeekToken(int kind) {
1356 if (_previousToken != null && _peekToken != null && 1348 if (_previousToken != null &&
1349 _peekToken != null &&
1357 _previousToken.kind == kind) { 1350 _previousToken.kind == kind) {
1358 // If end of previous token isn't same as the start of peek token then 1351 // If end of previous token isn't same as the start of peek token then
1359 // there's something between these tokens probably whitespace. 1352 // there's something between these tokens probably whitespace.
1360 return _previousToken.end != _peekToken.start; 1353 return _previousToken.end != _peekToken.start;
1361 } 1354 }
1362 1355
1363 return false; 1356 return false;
1364 } 1357 }
1365 1358
1366 /** 1359 /**
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
1427 var pseudoName; 1420 var pseudoName;
1428 if (_peekIdentifier()) { 1421 if (_peekIdentifier()) {
1429 pseudoName = identifier(); 1422 pseudoName = identifier();
1430 } else { 1423 } else {
1431 return null; 1424 return null;
1432 } 1425 }
1433 1426
1434 // Functional pseudo? 1427 // Functional pseudo?
1435 1428
1436 if (_peekToken.kind == TokenKind.LPAREN) { 1429 if (_peekToken.kind == TokenKind.LPAREN) {
1437
1438 if (!pseudoElement && pseudoName.name.toLowerCase() == 'not') { 1430 if (!pseudoElement && pseudoName.name.toLowerCase() == 'not') {
1439 _eat(TokenKind.LPAREN); 1431 _eat(TokenKind.LPAREN);
1440 1432
1441 // Negation : ':NOT(' S* negation_arg S* ')' 1433 // Negation : ':NOT(' S* negation_arg S* ')'
1442 var negArg = simpleSelector(); 1434 var negArg = simpleSelector();
1443 1435
1444 _eat(TokenKind.RPAREN); 1436 _eat(TokenKind.RPAREN);
1445 return new NegationSelector(negArg, _makeSpan(start)); 1437 return new NegationSelector(negArg, _makeSpan(start));
1446 } else { 1438 } else {
1447 // Special parsing for expressions in pseudo functions. Minus is used 1439 // Special parsing for expressions in pseudo functions. Minus is used
(...skipping 11 matching lines...) Expand all
1459 tokenizer.inSelectorExpression = false; 1451 tokenizer.inSelectorExpression = false;
1460 1452
1461 // Used during selector look-a-head if not a SelectorExpression is 1453 // Used during selector look-a-head if not a SelectorExpression is
1462 // bad. 1454 // bad.
1463 if (expr is! SelectorExpression) { 1455 if (expr is! SelectorExpression) {
1464 _errorExpected("CSS expression"); 1456 _errorExpected("CSS expression");
1465 return null; 1457 return null;
1466 } 1458 }
1467 1459
1468 _eat(TokenKind.RPAREN); 1460 _eat(TokenKind.RPAREN);
1469 return (pseudoElement) ? 1461 return (pseudoElement)
1470 new PseudoElementFunctionSelector(pseudoName, expr, span) : 1462 ? new PseudoElementFunctionSelector(pseudoName, expr, span)
1471 new PseudoClassFunctionSelector(pseudoName, expr, span); 1463 : new PseudoClassFunctionSelector(pseudoName, expr, span);
1472 } 1464 }
1473 } 1465 }
1474 1466
1475 // TODO(terry): Need to handle specific pseudo class/element name and 1467 // TODO(terry): Need to handle specific pseudo class/element name and
1476 // backward compatible names that are : as well as :: as well as 1468 // backward compatible names that are : as well as :: as well as
1477 // parameters. Current, spec uses :: for pseudo-element and : for 1469 // parameters. Current, spec uses :: for pseudo-element and : for
1478 // pseudo-class. However, CSS2.1 allows for : to specify old 1470 // pseudo-class. However, CSS2.1 allows for : to specify old
1479 // pseudo-elements (:first-line, :first-letter, :before and :after) any 1471 // pseudo-elements (:first-line, :first-letter, :before and :after) any
1480 // new pseudo-elements defined would require a ::. 1472 // new pseudo-elements defined would require a ::.
1481 return pseudoElement ? 1473 return pseudoElement
1482 new PseudoElementSelector(pseudoName, _makeSpan(start)) : 1474 ? new PseudoElementSelector(pseudoName, _makeSpan(start))
1483 new PseudoClassSelector(pseudoName, _makeSpan(start)); 1475 : new PseudoClassSelector(pseudoName, _makeSpan(start));
1484 } 1476 }
1485 1477
1486 /** 1478 /**
1487 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". 1479 * In CSS3, the expressions are identifiers, strings, or of the form "an+b".
1488 * 1480 *
1489 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ 1481 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
1490 * 1482 *
1491 * num [0-9]+|[0-9]*\.[0-9]+ 1483 * num [0-9]+|[0-9]*\.[0-9]+
1492 * PLUS '+' 1484 * PLUS '+'
1493 * DIMENSION {num}{ident} 1485 * DIMENSION {num}{ident}
(...skipping 30 matching lines...) Expand all
1524 break; 1516 break;
1525 case TokenKind.SINGLE_QUOTE: 1517 case TokenKind.SINGLE_QUOTE:
1526 value = processQuotedString(false); 1518 value = processQuotedString(false);
1527 value = "'${_escapeString(value, single: true)}'"; 1519 value = "'${_escapeString(value, single: true)}'";
1528 return new LiteralTerm(value, value, _makeSpan(start)); 1520 return new LiteralTerm(value, value, _makeSpan(start));
1529 case TokenKind.DOUBLE_QUOTE: 1521 case TokenKind.DOUBLE_QUOTE:
1530 value = processQuotedString(false); 1522 value = processQuotedString(false);
1531 value = '"${_escapeString(value)}"'; 1523 value = '"${_escapeString(value)}"';
1532 return new LiteralTerm(value, value, _makeSpan(start)); 1524 return new LiteralTerm(value, value, _makeSpan(start));
1533 case TokenKind.IDENTIFIER: 1525 case TokenKind.IDENTIFIER:
1534 value = identifier(); // Snarf up the ident we'll remap, maybe. 1526 value = identifier(); // Snarf up the ident we'll remap, maybe.
1535 break; 1527 break;
1536 default: 1528 default:
1537 keepParsing = false; 1529 keepParsing = false;
1538 } 1530 }
1539 1531
1540 if (keepParsing && value != null) { 1532 if (keepParsing && value != null) {
1541 var unitTerm; 1533 var unitTerm;
1542 // Don't process the dimension if MINUS or PLUS is next. 1534 // Don't process the dimension if MINUS or PLUS is next.
1543 if (_peek() != TokenKind.MINUS && _peek() != TokenKind.PLUS) { 1535 if (_peek() != TokenKind.MINUS && _peek() != TokenKind.PLUS) {
1544 unitTerm = processDimension(termToken, value, _makeSpan(start)); 1536 unitTerm = processDimension(termToken, value, _makeSpan(start));
(...skipping 30 matching lines...) Expand all
1575 // 1567 //
1576 // 1568 //
1577 AttributeSelector processAttribute() { 1569 AttributeSelector processAttribute() {
1578 var start = _peekToken.span; 1570 var start = _peekToken.span;
1579 1571
1580 if (_maybeEat(TokenKind.LBRACK)) { 1572 if (_maybeEat(TokenKind.LBRACK)) {
1581 var attrName = identifier(); 1573 var attrName = identifier();
1582 1574
1583 int op; 1575 int op;
1584 switch (_peek()) { 1576 switch (_peek()) {
1585 case TokenKind.EQUALS: 1577 case TokenKind.EQUALS:
1586 case TokenKind.INCLUDES: // ~= 1578 case TokenKind.INCLUDES: // ~=
1587 case TokenKind.DASH_MATCH: // |= 1579 case TokenKind.DASH_MATCH: // |=
1588 case TokenKind.PREFIX_MATCH: // ^= 1580 case TokenKind.PREFIX_MATCH: // ^=
1589 case TokenKind.SUFFIX_MATCH: // $= 1581 case TokenKind.SUFFIX_MATCH: // $=
1590 case TokenKind.SUBSTRING_MATCH: // *= 1582 case TokenKind.SUBSTRING_MATCH: // *=
1591 op = _peek(); 1583 op = _peek();
1592 _next(); 1584 _next();
1593 break; 1585 break;
1594 default: 1586 default:
1595 op = TokenKind.NO_MATCH; 1587 op = TokenKind.NO_MATCH;
1596 } 1588 }
1597 1589
1598 var value; 1590 var value;
1599 if (op != TokenKind.NO_MATCH) { 1591 if (op != TokenKind.NO_MATCH) {
1600 // Operator hit so we require a value too. 1592 // Operator hit so we require a value too.
1601 if (_peekIdentifier()) { 1593 if (_peekIdentifier()) {
1602 value = identifier(); 1594 value = identifier();
1603 } else { 1595 } else {
1604 value = processQuotedString(false); 1596 value = processQuotedString(false);
1605 } 1597 }
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1647 1639
1648 _eat(TokenKind.COLON); 1640 _eat(TokenKind.COLON);
1649 1641
1650 Expressions exprs = processExpr(ieFilterProperty); 1642 Expressions exprs = processExpr(ieFilterProperty);
1651 1643
1652 var dartComposite = _styleForDart(propertyIdent, exprs, dartStyles); 1644 var dartComposite = _styleForDart(propertyIdent, exprs, dartStyles);
1653 1645
1654 // Handle !important (prio) 1646 // Handle !important (prio)
1655 var importantPriority = _maybeEat(TokenKind.IMPORTANT); 1647 var importantPriority = _maybeEat(TokenKind.IMPORTANT);
1656 1648
1657 decl = new Declaration(propertyIdent, exprs, dartComposite, 1649 decl = new Declaration(
1658 _makeSpan(start), important: importantPriority, ie7: ie7); 1650 propertyIdent, exprs, dartComposite, _makeSpan(start),
1651 important: importantPriority, ie7: ie7);
1659 } else if (_peekToken.kind == TokenKind.VAR_DEFINITION) { 1652 } else if (_peekToken.kind == TokenKind.VAR_DEFINITION) {
1660 _next(); 1653 _next();
1661 var definedName; 1654 var definedName;
1662 if (_peekIdentifier()) definedName = identifier(); 1655 if (_peekIdentifier()) definedName = identifier();
1663 1656
1664 _eat(TokenKind.COLON); 1657 _eat(TokenKind.COLON);
1665 1658
1666 Expressions exprs = processExpr(); 1659 Expressions exprs = processExpr();
1667 1660
1668 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); 1661 decl = new VarDefinition(definedName, exprs, _makeSpan(start));
(...skipping 22 matching lines...) Expand all
1691 _warning("not a valid selector", span); 1684 _warning("not a valid selector", span);
1692 } 1685 }
1693 } 1686 }
1694 decl = new ExtendDeclaration(simpleSequences, span); 1687 decl = new ExtendDeclaration(simpleSequences, span);
1695 } 1688 }
1696 1689
1697 return decl; 1690 return decl;
1698 } 1691 }
1699 1692
1700 /** List of styles exposed to the Dart UI framework. */ 1693 /** List of styles exposed to the Dart UI framework. */
1701 static const int _fontPartFont= 0; 1694 static const int _fontPartFont = 0;
1702 static const int _fontPartVariant = 1; 1695 static const int _fontPartVariant = 1;
1703 static const int _fontPartWeight = 2; 1696 static const int _fontPartWeight = 2;
1704 static const int _fontPartSize = 3; 1697 static const int _fontPartSize = 3;
1705 static const int _fontPartFamily = 4; 1698 static const int _fontPartFamily = 4;
1706 static const int _fontPartStyle = 5; 1699 static const int _fontPartStyle = 5;
1707 static const int _marginPartMargin = 6; 1700 static const int _marginPartMargin = 6;
1708 static const int _marginPartLeft = 7; 1701 static const int _marginPartLeft = 7;
1709 static const int _marginPartTop = 8; 1702 static const int _marginPartTop = 8;
1710 static const int _marginPartRight = 9; 1703 static const int _marginPartRight = 9;
1711 static const int _marginPartBottom = 10; 1704 static const int _marginPartBottom = 10;
(...skipping 10 matching lines...) Expand all
1722 static const int _borderPartBottomWidth = 21; 1715 static const int _borderPartBottomWidth = 21;
1723 static const int _heightPart = 22; 1716 static const int _heightPart = 22;
1724 static const int _widthPart = 23; 1717 static const int _widthPart = 23;
1725 static const int _paddingPartPadding = 24; 1718 static const int _paddingPartPadding = 24;
1726 static const int _paddingPartLeft = 25; 1719 static const int _paddingPartLeft = 25;
1727 static const int _paddingPartTop = 26; 1720 static const int _paddingPartTop = 26;
1728 static const int _paddingPartRight = 27; 1721 static const int _paddingPartRight = 27;
1729 static const int _paddingPartBottom = 28; 1722 static const int _paddingPartBottom = 28;
1730 1723
1731 static const Map<String, int> _stylesToDart = const { 1724 static const Map<String, int> _stylesToDart = const {
1732 'font': _fontPartFont, 1725 'font': _fontPartFont,
1733 'font-family': _fontPartFamily, 1726 'font-family': _fontPartFamily,
1734 'font-size': _fontPartSize, 1727 'font-size': _fontPartSize,
1735 'font-style': _fontPartStyle, 1728 'font-style': _fontPartStyle,
1736 'font-variant': _fontPartVariant, 1729 'font-variant': _fontPartVariant,
1737 'font-weight': _fontPartWeight, 1730 'font-weight': _fontPartWeight,
1738 'line-height': _lineHeightPart, 1731 'line-height': _lineHeightPart,
1739 'margin': _marginPartMargin, 1732 'margin': _marginPartMargin,
1740 'margin-left': _marginPartLeft, 1733 'margin-left': _marginPartLeft,
1741 'margin-right': _marginPartRight, 1734 'margin-right': _marginPartRight,
1742 'margin-top': _marginPartTop, 1735 'margin-top': _marginPartTop,
1743 'margin-bottom': _marginPartBottom, 1736 'margin-bottom': _marginPartBottom,
1744 'border': _borderPartBorder, 1737 'border': _borderPartBorder,
1745 'border-left': _borderPartLeft, 1738 'border-left': _borderPartLeft,
1746 'border-right': _borderPartRight, 1739 'border-right': _borderPartRight,
1747 'border-top': _borderPartTop, 1740 'border-top': _borderPartTop,
1748 'border-bottom': _borderPartBottom, 1741 'border-bottom': _borderPartBottom,
1749 'border-width': _borderPartWidth, 1742 'border-width': _borderPartWidth,
1750 'border-left-width': _borderPartLeftWidth, 1743 'border-left-width': _borderPartLeftWidth,
1751 'border-top-width': _borderPartTopWidth, 1744 'border-top-width': _borderPartTopWidth,
1752 'border-right-width': _borderPartRightWidth, 1745 'border-right-width': _borderPartRightWidth,
1753 'border-bottom-width': _borderPartBottomWidth, 1746 'border-bottom-width': _borderPartBottomWidth,
1754 'height': _heightPart, 1747 'height': _heightPart,
1755 'width': _widthPart, 1748 'width': _widthPart,
1756 'padding': _paddingPartPadding, 1749 'padding': _paddingPartPadding,
1757 'padding-left': _paddingPartLeft, 1750 'padding-left': _paddingPartLeft,
1758 'padding-top': _paddingPartTop, 1751 'padding-top': _paddingPartTop,
1759 'padding-right': _paddingPartRight, 1752 'padding-right': _paddingPartRight,
1760 'padding-bottom': _paddingPartBottom 1753 'padding-bottom': _paddingPartBottom
1761 }; 1754 };
1762 1755
1763 static const Map<String, int> _nameToFontWeight = const { 1756 static const Map<String, int> _nameToFontWeight = const {
1764 'bold' : FontWeight.bold, 1757 'bold': FontWeight.bold,
1765 'normal' : FontWeight.normal 1758 'normal': FontWeight.normal
1766 }; 1759 };
1767 1760
1768 static int _findStyle(String styleName) => _stylesToDart[styleName]; 1761 static int _findStyle(String styleName) => _stylesToDart[styleName];
1769 1762
1770 DartStyleExpression _styleForDart(Identifier property, Expressions exprs, 1763 DartStyleExpression _styleForDart(
1771 List dartStyles) { 1764 Identifier property, Expressions exprs, List dartStyles) {
1772 var styleType = _findStyle(property.name.toLowerCase()); 1765 var styleType = _findStyle(property.name.toLowerCase());
1773 if (styleType != null) { 1766 if (styleType != null) {
1774 return buildDartStyleNode(styleType, exprs, dartStyles); 1767 return buildDartStyleNode(styleType, exprs, dartStyles);
1775 } 1768 }
1776 } 1769 }
1777 1770
1778 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) { 1771 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) {
1779 // Merge all font styles for this class selector. 1772 // Merge all font styles for this class selector.
1780 for (var dartStyle in dartStyles) { 1773 for (var dartStyle in dartStyles) {
1781 if (dartStyle.isFont) { 1774 if (dartStyle.isFont) {
1782 fontExpr = new FontExpression.merge(dartStyle, fontExpr); 1775 fontExpr = new FontExpression.merge(dartStyle, fontExpr);
1783 } 1776 }
1784 } 1777 }
1785 1778
1786 return fontExpr; 1779 return fontExpr;
1787 } 1780 }
1788 1781
1789 DartStyleExpression buildDartStyleNode(int styleType, Expressions exprs, 1782 DartStyleExpression buildDartStyleNode(
1790 List dartStyles) { 1783 int styleType, Expressions exprs, List dartStyles) {
1791
1792 switch (styleType) { 1784 switch (styleType) {
1793 /* 1785 /*
1794 * Properties in order: 1786 * Properties in order:
1795 * 1787 *
1796 * font-style font-variant font-weight font-size/line-height font-family 1788 * font-style font-variant font-weight font-size/line-height font-family
1797 * 1789 *
1798 * The font-size and font-family values are required. If other values are 1790 * The font-size and font-family values are required. If other values are
1799 * missing; a default, if it exist, will be used. 1791 * missing; a default, if it exist, will be used.
1800 */ 1792 */
1801 case _fontPartFont: 1793 case _fontPartFont:
1802 var processor = new ExpressionsProcessor(exprs); 1794 var processor = new ExpressionsProcessor(exprs);
1803 return _mergeFontStyles(processor.processFont(), dartStyles); 1795 return _mergeFontStyles(processor.processFont(), dartStyles);
1804 case _fontPartFamily: 1796 case _fontPartFamily:
1805 var processor = new ExpressionsProcessor(exprs); 1797 var processor = new ExpressionsProcessor(exprs);
1806 1798
1807 try { 1799 try {
1808 return _mergeFontStyles(processor.processFontFamily(), dartStyles); 1800 return _mergeFontStyles(processor.processFontFamily(), dartStyles);
1809 } catch (fontException) { 1801 } catch (fontException) {
1810 _error(fontException, _peekToken.span); 1802 _error(fontException, _peekToken.span);
1811 } 1803 }
1812 break; 1804 break;
1813 case _fontPartSize: 1805 case _fontPartSize:
(...skipping 23 matching lines...) Expand all
1837 * bolder 1829 * bolder
1838 * lighter 1830 * lighter
1839 * 100 - 900 1831 * 100 - 900
1840 * inherit 1832 * inherit
1841 */ 1833 */
1842 // TODO(terry): Only 'normal', 'bold', or values of 100-900 supoorted 1834 // TODO(terry): Only 'normal', 'bold', or values of 100-900 supoorted
1843 // need to handle bolder, lighter, and inherit. See 1835 // need to handle bolder, lighter, and inherit. See
1844 // https://github.com/dart-lang/csslib/issues/1 1836 // https://github.com/dart-lang/csslib/issues/1
1845 var expr = exprs.expressions[0]; 1837 var expr = exprs.expressions[0];
1846 if (expr is NumberTerm) { 1838 if (expr is NumberTerm) {
1847 var fontExpr = new FontExpression(expr.span, 1839 var fontExpr = new FontExpression(expr.span, weight: expr.value);
1848 weight: expr.value);
1849 return _mergeFontStyles(fontExpr, dartStyles); 1840 return _mergeFontStyles(fontExpr, dartStyles);
1850 } else if (expr is LiteralTerm) { 1841 } else if (expr is LiteralTerm) {
1851 int weight = _nameToFontWeight[expr.value.toString()]; 1842 int weight = _nameToFontWeight[expr.value.toString()];
1852 if (weight != null) { 1843 if (weight != null) {
1853 var fontExpr = new FontExpression(expr.span, weight: weight); 1844 var fontExpr = new FontExpression(expr.span, weight: weight);
1854 return _mergeFontStyles(fontExpr, dartStyles); 1845 return _mergeFontStyles(fontExpr, dartStyles);
1855 } 1846 }
1856 } 1847 }
1857 break; 1848 break;
1858 case _lineHeightPart: 1849 case _lineHeightPart:
1859 num lineHeight; 1850 num lineHeight;
1860 if (exprs.expressions.length == 1) { 1851 if (exprs.expressions.length == 1) {
1861 var expr = exprs.expressions[0]; 1852 var expr = exprs.expressions[0];
1862 if (expr is UnitTerm) { 1853 if (expr is UnitTerm) {
1863 UnitTerm unitTerm = expr; 1854 UnitTerm unitTerm = expr;
1864 // TODO(terry): Need to handle other units and LiteralTerm normal 1855 // TODO(terry): Need to handle other units and LiteralTerm normal
1865 // See https://github.com/dart-lang/csslib/issues/2. 1856 // See https://github.com/dart-lang/csslib/issues/2.
1866 if (unitTerm.unit == TokenKind.UNIT_LENGTH_PX || 1857 if (unitTerm.unit == TokenKind.UNIT_LENGTH_PX ||
1867 unitTerm.unit == TokenKind.UNIT_LENGTH_PT) { 1858 unitTerm.unit == TokenKind.UNIT_LENGTH_PT) {
1868 var fontExpr = new FontExpression(expr.span, 1859 var fontExpr = new FontExpression(expr.span,
1869 lineHeight: new LineHeight(expr.value, inPixels: true)); 1860 lineHeight: new LineHeight(expr.value, inPixels: true));
1870 return _mergeFontStyles(fontExpr, dartStyles); 1861 return _mergeFontStyles(fontExpr, dartStyles);
1871 } else if (isChecked) { 1862 } else if (isChecked) {
1872 _warning("Unexpected unit for line-height", expr.span); 1863 _warning("Unexpected unit for line-height", expr.span);
1873 } 1864 }
1874 } else if (expr is NumberTerm) { 1865 } else if (expr is NumberTerm) {
1875 var fontExpr = new FontExpression(expr.span, 1866 var fontExpr = new FontExpression(expr.span,
1876 lineHeight: new LineHeight(expr.value, inPixels: false)); 1867 lineHeight: new LineHeight(expr.value, inPixels: false));
1877 return _mergeFontStyles(fontExpr, dartStyles); 1868 return _mergeFontStyles(fontExpr, dartStyles);
(...skipping 14 matching lines...) Expand all
1892 } 1883 }
1893 break; 1884 break;
1894 case _borderPartWidth: 1885 case _borderPartWidth:
1895 var v = marginValue(exprs.expressions[0]); 1886 var v = marginValue(exprs.expressions[0]);
1896 if (v != null) { 1887 if (v != null) {
1897 final box = new BoxEdge.uniform(v); 1888 final box = new BoxEdge.uniform(v);
1898 return new BorderExpression.boxEdge(exprs.span, box); 1889 return new BorderExpression.boxEdge(exprs.span, box);
1899 } 1890 }
1900 break; 1891 break;
1901 case _paddingPartPadding: 1892 case _paddingPartPadding:
1902 return new PaddingExpression.boxEdge(exprs.span, 1893 return new PaddingExpression.boxEdge(
1903 processFourNums(exprs)); 1894 exprs.span, processFourNums(exprs));
1904 case _marginPartLeft: 1895 case _marginPartLeft:
1905 case _marginPartTop: 1896 case _marginPartTop:
1906 case _marginPartRight: 1897 case _marginPartRight:
1907 case _marginPartBottom: 1898 case _marginPartBottom:
1908 case _borderPartLeft: 1899 case _borderPartLeft:
1909 case _borderPartTop: 1900 case _borderPartTop:
1910 case _borderPartRight: 1901 case _borderPartRight:
1911 case _borderPartBottom: 1902 case _borderPartBottom:
1912 case _borderPartLeftWidth: 1903 case _borderPartLeftWidth:
1913 case _borderPartTopWidth: 1904 case _borderPartTopWidth:
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
1993 top = marginValue(exprs.expressions[0]); 1984 top = marginValue(exprs.expressions[0]);
1994 right = top; 1985 right = top;
1995 bottom = top; 1986 bottom = top;
1996 left = top; 1987 left = top;
1997 break; 1988 break;
1998 case 2: 1989 case 2:
1999 top = marginValue(exprs.expressions[0]); 1990 top = marginValue(exprs.expressions[0]);
2000 bottom = top; 1991 bottom = top;
2001 right = marginValue(exprs.expressions[1]); 1992 right = marginValue(exprs.expressions[1]);
2002 left = right; 1993 left = right;
2003 break; 1994 break;
2004 case 3: 1995 case 3:
2005 top = marginValue(exprs.expressions[0]); 1996 top = marginValue(exprs.expressions[0]);
2006 right = marginValue(exprs.expressions[1]); 1997 right = marginValue(exprs.expressions[1]);
2007 left = right; 1998 left = right;
2008 bottom = marginValue(exprs.expressions[2]); 1999 bottom = marginValue(exprs.expressions[2]);
2009 break; 2000 break;
2010 case 4: 2001 case 4:
2011 top = marginValue(exprs.expressions[0]); 2002 top = marginValue(exprs.expressions[0]);
2012 right = marginValue(exprs.expressions[1]); 2003 right = marginValue(exprs.expressions[1]);
2013 bottom = marginValue(exprs.expressions[2]); 2004 bottom = marginValue(exprs.expressions[2]);
(...skipping 25 matching lines...) Expand all
2039 var expressions = new Expressions(_makeSpan(start)); 2030 var expressions = new Expressions(_makeSpan(start));
2040 2031
2041 var keepGoing = true; 2032 var keepGoing = true;
2042 var expr; 2033 var expr;
2043 while (keepGoing && (expr = processTerm(ieFilter)) != null) { 2034 while (keepGoing && (expr = processTerm(ieFilter)) != null) {
2044 var op; 2035 var op;
2045 2036
2046 var opStart = _peekToken.span; 2037 var opStart = _peekToken.span;
2047 2038
2048 switch (_peek()) { 2039 switch (_peek()) {
2049 case TokenKind.SLASH: 2040 case TokenKind.SLASH:
2050 op = new OperatorSlash(_makeSpan(opStart)); 2041 op = new OperatorSlash(_makeSpan(opStart));
2051 break; 2042 break;
2052 case TokenKind.COMMA: 2043 case TokenKind.COMMA:
2053 op = new OperatorComma(_makeSpan(opStart)); 2044 op = new OperatorComma(_makeSpan(opStart));
2054 break; 2045 break;
2055 case TokenKind.BACKSLASH: 2046 case TokenKind.BACKSLASH:
2056 // Backslash outside of string; detected IE8 or older signaled by \9 at 2047 // Backslash outside of string; detected IE8 or older signaled by \9 a t
2057 // end of an expression. 2048 // end of an expression.
2058 var ie8Start = _peekToken.span; 2049 var ie8Start = _peekToken.span;
2059 2050
2060 _next(); 2051 _next();
2061 if (_peekKind(TokenKind.INTEGER)) { 2052 if (_peekKind(TokenKind.INTEGER)) {
2062 var numToken = _next(); 2053 var numToken = _next();
2063 var value = int.parse(numToken.text); 2054 var value = int.parse(numToken.text);
2064 if (value == 9) { 2055 if (value == 9) {
2065 op = new IE8Term(_makeSpan(ie8Start)); 2056 op = new IE8Term(_makeSpan(ie8Start));
2066 } else if (isChecked) { 2057 } else if (isChecked) {
2067 _warning("\$value is not valid in an expression", _makeSpan(start)); 2058 _warning(
2059 "\$value is not valid in an expression", _makeSpan(start));
2060 }
2068 } 2061 }
2069 } 2062 break;
2070 break;
2071 } 2063 }
2072 2064
2073 if (expr != null) { 2065 if (expr != null) {
2074 if (expr is List) { 2066 if (expr is List) {
2075 expr.forEach((exprItem) { 2067 expr.forEach((exprItem) {
2076 expressions.add(exprItem); 2068 expressions.add(exprItem);
2077 }); 2069 });
2078 } else { 2070 } else {
2079 expressions.add(expr); 2071 expressions.add(expr);
2080 } 2072 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
2113 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] 2105 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc']
2114 // EMS: {num}'em' 2106 // EMS: {num}'em'
2115 // EXS: {num}'ex' 2107 // EXS: {num}'ex'
2116 // ANGLE: {num}['deg' | 'rad' | 'grad'] 2108 // ANGLE: {num}['deg' | 'rad' | 'grad']
2117 // TIME: {num}['ms' | 's'] 2109 // TIME: {num}['ms' | 's']
2118 // FREQ: {num}['hz' | 'khz'] 2110 // FREQ: {num}['hz' | 'khz']
2119 // function: IDENT '(' expr ')' 2111 // function: IDENT '(' expr ')'
2120 // 2112 //
2121 processTerm([bool ieFilter = false]) { 2113 processTerm([bool ieFilter = false]) {
2122 var start = _peekToken.span; 2114 var start = _peekToken.span;
2123 Token t; // token for term's value 2115 Token t; // token for term's value
2124 var value; // value of term (numeric values) 2116 var value; // value of term (numeric values)
2125 2117
2126 var unary = ""; 2118 var unary = "";
2127 switch (_peek()) { 2119 switch (_peek()) {
2128 case TokenKind.HASH: 2120 case TokenKind.HASH:
2129 this._eat(TokenKind.HASH); 2121 this._eat(TokenKind.HASH);
2130 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { 2122 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) {
2131 String hexText; 2123 String hexText;
2132 if (_peekKind(TokenKind.INTEGER)) { 2124 if (_peekKind(TokenKind.INTEGER)) {
2133 String hexText1 = _peekToken.text; 2125 String hexText1 = _peekToken.text;
2134 _next(); 2126 _next();
2135 if (_peekIdentifier()) { 2127 if (_peekIdentifier()) {
2136 hexText = '$hexText1${identifier().name}'; 2128 hexText = '$hexText1${identifier().name}';
2129 } else {
2130 hexText = hexText1;
2131 }
2132 } else if (_peekIdentifier()) {
2133 hexText = identifier().name;
2134 }
2135 if (hexText != null) {
2136 return _parseHex(hexText, _makeSpan(start));
2137 }
2138 }
2139
2140 if (isChecked) {
2141 _warning("Expected hex number", _makeSpan(start));
2142 }
2143 // Construct the bad hex value with a #<space>number.
2144 return _parseHex(" ${processTerm().text}", _makeSpan(start));
2145 case TokenKind.INTEGER:
2146 t = _next();
2147 value = int.parse("${unary}${t.text}");
2148 break;
2149 case TokenKind.DOUBLE:
2150 t = _next();
2151 value = double.parse("${unary}${t.text}");
2152 break;
2153 case TokenKind.SINGLE_QUOTE:
2154 value = processQuotedString(false);
2155 value = "'${_escapeString(value, single: true)}'";
2156 return new LiteralTerm(value, value, _makeSpan(start));
2157 case TokenKind.DOUBLE_QUOTE:
2158 value = processQuotedString(false);
2159 value = '"${_escapeString(value)}"';
2160 return new LiteralTerm(value, value, _makeSpan(start));
2161 case TokenKind.LPAREN:
2162 _next();
2163
2164 GroupTerm group = new GroupTerm(_makeSpan(start));
2165
2166 var term;
2167 do {
2168 term = processTerm();
2169 if (term != null && term is LiteralTerm) {
2170 group.add(term);
2171 }
2172 } while (term != null &&
2173 !_maybeEat(TokenKind.RPAREN) &&
2174 !isPrematureEndOfFile());
2175
2176 return group;
2177 case TokenKind.LBRACK:
2178 _next();
2179
2180 var term = processTerm();
2181 if (!(term is NumberTerm)) {
2182 _error('Expecting a positive number', _makeSpan(start));
2183 }
2184
2185 _eat(TokenKind.RBRACK);
2186
2187 return new ItemTerm(term.value, term.text, _makeSpan(start));
2188 case TokenKind.IDENTIFIER:
2189 var nameValue = identifier(); // Snarf up the ident we'll remap, maybe.
2190
2191 if (!ieFilter && _maybeEat(TokenKind.LPAREN)) {
2192 // FUNCTION
2193 return processFunction(nameValue);
2194 }
2195 if (ieFilter) {
2196 if (_maybeEat(TokenKind.COLON) &&
2197 nameValue.name.toLowerCase() == 'progid') {
2198 // IE filter:progid:
2199 return processIEFilter(start);
2137 } else { 2200 } else {
2138 hexText = hexText1; 2201 // Handle filter:<name> where name is any filter e.g., alpha, chroma ,
2202 // Wave, blur, etc.
2203 return processIEFilter(start);
2139 } 2204 }
2140 } else if (_peekIdentifier()) {
2141 hexText = identifier().name;
2142 } 2205 }
2143 if (hexText != null) { 2206
2144 return _parseHex(hexText, _makeSpan(start)); 2207 // TODO(terry): Need to have a list of known identifiers today only
2208 // 'from' is special.
2209 if (nameValue.name == 'from') {
2210 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
2145 } 2211 }
2146 }
2147 2212
2148 if (isChecked) { 2213 // What kind of identifier is it, named color?
2149 _warning("Expected hex number", _makeSpan(start)); 2214 var colorEntry = TokenKind.matchColorName(nameValue.name);
2150 } 2215 if (colorEntry == null) {
2151 // Construct the bad hex value with a #<space>number. 2216 if (isChecked) {
2152 return _parseHex(" ${processTerm().text}", _makeSpan(start)); 2217 var propName = nameValue.name;
2153 case TokenKind.INTEGER: 2218 var errMsg = TokenKind.isPredefinedName(propName)
2154 t = _next(); 2219 ? "Improper use of property value ${propName}"
2155 value = int.parse("${unary}${t.text}"); 2220 : "Unknown property value ${propName}";
2156 break; 2221 _warning(errMsg, _makeSpan(start));
2157 case TokenKind.DOUBLE: 2222 }
2158 t = _next(); 2223 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
2159 value = double.parse("${unary}${t.text}"); 2224 }
2160 break;
2161 case TokenKind.SINGLE_QUOTE:
2162 value = processQuotedString(false);
2163 value = "'${_escapeString(value, single: true)}'";
2164 return new LiteralTerm(value, value, _makeSpan(start));
2165 case TokenKind.DOUBLE_QUOTE:
2166 value = processQuotedString(false);
2167 value = '"${_escapeString(value)}"';
2168 return new LiteralTerm(value, value, _makeSpan(start));
2169 case TokenKind.LPAREN:
2170 _next();
2171 2225
2172 GroupTerm group = new GroupTerm(_makeSpan(start)); 2226 // Yes, process the color as an RGB value.
2173 2227 var rgbColor =
2174 var term; 2228 TokenKind.decimalToHex(TokenKind.colorValue(colorEntry), 6);
2175 do { 2229 return _parseHex(rgbColor, _makeSpan(start));
2176 term = processTerm(); 2230 case TokenKind.UNICODE_RANGE:
2177 if (term != null && term is LiteralTerm) { 2231 var first;
2178 group.add(term); 2232 var second;
2179 } 2233 var firstNumber;
2180 } while (term != null && !_maybeEat(TokenKind.RPAREN) && 2234 var secondNumber;
2181 !isPrematureEndOfFile()); 2235 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true);
2182 2236 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
2183 return group; 2237 first = _previousToken.text;
2184 case TokenKind.LBRACK: 2238 firstNumber = int.parse('0x$first');
2185 _next(); 2239 if (firstNumber > MAX_UNICODE) {
2186 2240 _error("unicode range must be less than 10FFFF", _makeSpan(start));
2187 var term = processTerm(); 2241 }
2188 if (!(term is NumberTerm)) { 2242 if (_maybeEat(TokenKind.MINUS, unicodeRange: true)) {
2189 _error('Expecting a positive number', _makeSpan(start)); 2243 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
2190 } 2244 second = _previousToken.text;
2191 2245 secondNumber = int.parse('0x$second');
2192 _eat(TokenKind.RBRACK); 2246 if (secondNumber > MAX_UNICODE) {
2193 2247 _error(
2194 return new ItemTerm(term.value, term.text, _makeSpan(start)); 2248 "unicode range must be less than 10FFFF", _makeSpan(start));
2195 case TokenKind.IDENTIFIER: 2249 }
2196 var nameValue = identifier(); // Snarf up the ident we'll remap, maybe. 2250 if (firstNumber > secondNumber) {
2197 2251 _error("unicode first range can not be greater than last",
2198 if (!ieFilter && _maybeEat(TokenKind.LPAREN)) { 2252 _makeSpan(start));
2199 // FUNCTION 2253 }
2200 return processFunction(nameValue);
2201 } if (ieFilter) {
2202 if (_maybeEat(TokenKind.COLON) &&
2203 nameValue.name.toLowerCase() == 'progid') {
2204 // IE filter:progid:
2205 return processIEFilter(start);
2206 } else {
2207 // Handle filter:<name> where name is any filter e.g., alpha, chroma,
2208 // Wave, blur, etc.
2209 return processIEFilter(start);
2210 }
2211 }
2212
2213 // TODO(terry): Need to have a list of known identifiers today only
2214 // 'from' is special.
2215 if (nameValue.name == 'from') {
2216 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
2217 }
2218
2219 // What kind of identifier is it, named color?
2220 var colorEntry = TokenKind.matchColorName(nameValue.name);
2221 if (colorEntry == null) {
2222 if (isChecked) {
2223 var propName = nameValue.name;
2224 var errMsg = TokenKind.isPredefinedName(propName) ?
2225 "Improper use of property value ${propName}" :
2226 "Unknown property value ${propName}";
2227 _warning(errMsg, _makeSpan(start));
2228 }
2229 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
2230 }
2231
2232 // Yes, process the color as an RGB value.
2233 var rgbColor =
2234 TokenKind.decimalToHex(TokenKind.colorValue(colorEntry), 6);
2235 return _parseHex(rgbColor, _makeSpan(start));
2236 case TokenKind.UNICODE_RANGE:
2237 var first;
2238 var second;
2239 var firstNumber;
2240 var secondNumber;
2241 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true);
2242 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
2243 first = _previousToken.text;
2244 firstNumber = int.parse('0x$first');
2245 if (firstNumber > MAX_UNICODE) {
2246 _error("unicode range must be less than 10FFFF", _makeSpan(start));
2247 }
2248 if (_maybeEat(TokenKind.MINUS, unicodeRange: true)) {
2249 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) {
2250 second = _previousToken.text;
2251 secondNumber = int.parse('0x$second');
2252 if (secondNumber > MAX_UNICODE) {
2253 _error("unicode range must be less than 10FFFF",
2254 _makeSpan(start));
2255 }
2256 if (firstNumber > secondNumber) {
2257 _error("unicode first range can not be greater than last",
2258 _makeSpan(start));
2259 } 2254 }
2260 } 2255 }
2261 } 2256 } else if (_maybeEat(TokenKind.HEX_RANGE, unicodeRange: true)) {
2262 } else if (_maybeEat(TokenKind.HEX_RANGE, unicodeRange: true)) { 2257 first = _previousToken.text;
2263 first = _previousToken.text;
2264 }
2265
2266 return new UnicodeRangeTerm(first, second, _makeSpan(start));
2267 case TokenKind.AT:
2268 if (messages.options.lessSupport) {
2269 _next();
2270
2271 var expr = processExpr();
2272 if (isChecked && expr.expressions.length > 1) {
2273 _error("only @name for Less syntax", _peekToken.span);
2274 } 2258 }
2275 2259
2276 var param = expr.expressions[0]; 2260 return new UnicodeRangeTerm(first, second, _makeSpan(start));
2277 var varUsage = new VarUsage(param.text, [], _makeSpan(start)); 2261 case TokenKind.AT:
2278 expr.expressions[0] = varUsage; 2262 if (messages.options.lessSupport) {
2279 return expr.expressions; 2263 _next();
2280 } 2264
2281 break; 2265 var expr = processExpr();
2266 if (isChecked && expr.expressions.length > 1) {
2267 _error("only @name for Less syntax", _peekToken.span);
2268 }
2269
2270 var param = expr.expressions[0];
2271 var varUsage = new VarUsage(param.text, [], _makeSpan(start));
2272 expr.expressions[0] = varUsage;
2273 return expr.expressions;
2274 }
2275 break;
2282 } 2276 }
2283 2277
2284 return processDimension(t, value, _makeSpan(start)); 2278 return processDimension(t, value, _makeSpan(start));
2285 } 2279 }
2286 2280
2287 /** Process all dimension units. */ 2281 /** Process all dimension units. */
2288 LiteralTerm processDimension(Token t, var value, SourceSpan span) { 2282 LiteralTerm processDimension(Token t, var value, SourceSpan span) {
2289 LiteralTerm term; 2283 LiteralTerm term;
2290 var unitType = this._peek(); 2284 var unitType = this._peek();
2291 2285
2292 switch (unitType) { 2286 switch (unitType) {
2293 case TokenKind.UNIT_EM: 2287 case TokenKind.UNIT_EM:
2294 term = new EmTerm(value, t.text, span); 2288 term = new EmTerm(value, t.text, span);
2295 _next(); // Skip the unit 2289 _next(); // Skip the unit
2296 break; 2290 break;
2297 case TokenKind.UNIT_EX: 2291 case TokenKind.UNIT_EX:
2298 term = new ExTerm(value, t.text, span); 2292 term = new ExTerm(value, t.text, span);
2299 _next(); // Skip the unit 2293 _next(); // Skip the unit
2300 break; 2294 break;
2301 case TokenKind.UNIT_LENGTH_PX: 2295 case TokenKind.UNIT_LENGTH_PX:
2302 case TokenKind.UNIT_LENGTH_CM: 2296 case TokenKind.UNIT_LENGTH_CM:
2303 case TokenKind.UNIT_LENGTH_MM: 2297 case TokenKind.UNIT_LENGTH_MM:
2304 case TokenKind.UNIT_LENGTH_IN: 2298 case TokenKind.UNIT_LENGTH_IN:
2305 case TokenKind.UNIT_LENGTH_PT: 2299 case TokenKind.UNIT_LENGTH_PT:
2306 case TokenKind.UNIT_LENGTH_PC: 2300 case TokenKind.UNIT_LENGTH_PC:
2307 term = new LengthTerm(value, t.text, span, unitType); 2301 term = new LengthTerm(value, t.text, span, unitType);
2308 _next(); // Skip the unit 2302 _next(); // Skip the unit
2309 break; 2303 break;
2310 case TokenKind.UNIT_ANGLE_DEG: 2304 case TokenKind.UNIT_ANGLE_DEG:
2311 case TokenKind.UNIT_ANGLE_RAD: 2305 case TokenKind.UNIT_ANGLE_RAD:
2312 case TokenKind.UNIT_ANGLE_GRAD: 2306 case TokenKind.UNIT_ANGLE_GRAD:
2313 case TokenKind.UNIT_ANGLE_TURN: 2307 case TokenKind.UNIT_ANGLE_TURN:
2314 term = new AngleTerm(value, t.text, span, unitType); 2308 term = new AngleTerm(value, t.text, span, unitType);
2315 _next(); // Skip the unit 2309 _next(); // Skip the unit
2316 break; 2310 break;
2317 case TokenKind.UNIT_TIME_MS: 2311 case TokenKind.UNIT_TIME_MS:
2318 case TokenKind.UNIT_TIME_S: 2312 case TokenKind.UNIT_TIME_S:
2319 term = new TimeTerm(value, t.text, span, unitType); 2313 term = new TimeTerm(value, t.text, span, unitType);
2320 _next(); // Skip the unit 2314 _next(); // Skip the unit
2321 break; 2315 break;
2322 case TokenKind.UNIT_FREQ_HZ: 2316 case TokenKind.UNIT_FREQ_HZ:
2323 case TokenKind.UNIT_FREQ_KHZ: 2317 case TokenKind.UNIT_FREQ_KHZ:
2324 term = new FreqTerm(value, t.text, span, unitType); 2318 term = new FreqTerm(value, t.text, span, unitType);
2325 _next(); // Skip the unit 2319 _next(); // Skip the unit
2326 break; 2320 break;
2327 case TokenKind.PERCENT: 2321 case TokenKind.PERCENT:
2328 term = new PercentageTerm(value, t.text, span); 2322 term = new PercentageTerm(value, t.text, span);
2329 _next(); // Skip the % 2323 _next(); // Skip the %
2330 break; 2324 break;
2331 case TokenKind.UNIT_FRACTION: 2325 case TokenKind.UNIT_FRACTION:
2332 term = new FractionTerm(value, t.text, span); 2326 term = new FractionTerm(value, t.text, span);
2333 _next(); // Skip the unit 2327 _next(); // Skip the unit
2334 break; 2328 break;
2335 case TokenKind.UNIT_RESOLUTION_DPI: 2329 case TokenKind.UNIT_RESOLUTION_DPI:
2336 case TokenKind.UNIT_RESOLUTION_DPCM: 2330 case TokenKind.UNIT_RESOLUTION_DPCM:
2337 case TokenKind.UNIT_RESOLUTION_DPPX: 2331 case TokenKind.UNIT_RESOLUTION_DPPX:
2338 term = new ResolutionTerm(value, t.text, span, unitType); 2332 term = new ResolutionTerm(value, t.text, span, unitType);
2339 _next(); // Skip the unit 2333 _next(); // Skip the unit
2340 break; 2334 break;
2341 case TokenKind.UNIT_CH: 2335 case TokenKind.UNIT_CH:
2342 term = new ChTerm(value, t.text, span, unitType); 2336 term = new ChTerm(value, t.text, span, unitType);
2343 _next(); // Skip the unit 2337 _next(); // Skip the unit
2344 break; 2338 break;
2345 case TokenKind.UNIT_REM: 2339 case TokenKind.UNIT_REM:
2346 term = new RemTerm(value, t.text, span, unitType); 2340 term = new RemTerm(value, t.text, span, unitType);
2347 _next(); // Skip the unit 2341 _next(); // Skip the unit
2348 break; 2342 break;
2349 case TokenKind.UNIT_VIEWPORT_VW: 2343 case TokenKind.UNIT_VIEWPORT_VW:
2350 case TokenKind.UNIT_VIEWPORT_VH: 2344 case TokenKind.UNIT_VIEWPORT_VH:
2351 case TokenKind.UNIT_VIEWPORT_VMIN: 2345 case TokenKind.UNIT_VIEWPORT_VMIN:
2352 case TokenKind.UNIT_VIEWPORT_VMAX: 2346 case TokenKind.UNIT_VIEWPORT_VMAX:
2353 term = new ViewportTerm(value, t.text, span, unitType); 2347 term = new ViewportTerm(value, t.text, span, unitType);
2354 _next(); // Skip the unit 2348 _next(); // Skip the unit
2355 break; 2349 break;
2356 default: 2350 default:
2357 if (value != null && t != null) { 2351 if (value != null && t != null) {
2358 term = (value is Identifier) 2352 term = (value is Identifier)
2359 ? new LiteralTerm(value, value.name, span) 2353 ? new LiteralTerm(value, value.name, span)
2360 : new NumberTerm(value, t.text, span); 2354 : new NumberTerm(value, t.text, span);
2361 } 2355 }
2362 break; 2356 break;
2363 } 2357 }
2364 2358
2365 return term; 2359 return term;
2366 } 2360 }
2367 2361
2368 String processQuotedString([bool urlString = false]) { 2362 String processQuotedString([bool urlString = false]) {
2369 var start = _peekToken.span; 2363 var start = _peekToken.span;
2370 2364
2371 // URI term sucks up everything inside of quotes(' or ") or between parens 2365 // URI term sucks up everything inside of quotes(' or ") or between parens
2372 var stopToken = urlString ? TokenKind.RPAREN : -1; 2366 var stopToken = urlString ? TokenKind.RPAREN : -1;
2373 2367
2374 // Note: disable skipping whitespace tokens inside a string. 2368 // Note: disable skipping whitespace tokens inside a string.
2375 // TODO(jmesserly): the layering here feels wrong. 2369 // TODO(jmesserly): the layering here feels wrong.
2376 var skipWhitespace = tokenizer._skipWhitespace; 2370 var skipWhitespace = tokenizer._skipWhitespace;
2377 tokenizer._skipWhitespace = false; 2371 tokenizer._skipWhitespace = false;
2378 2372
2379 switch (_peek()) { 2373 switch (_peek()) {
2380 case TokenKind.SINGLE_QUOTE: 2374 case TokenKind.SINGLE_QUOTE:
2381 stopToken = TokenKind.SINGLE_QUOTE; 2375 stopToken = TokenKind.SINGLE_QUOTE;
2382 _next(); // Skip the SINGLE_QUOTE. 2376 _next(); // Skip the SINGLE_QUOTE.
2383 start = _peekToken.span; 2377 start = _peekToken.span;
2384 break; 2378 break;
2385 case TokenKind.DOUBLE_QUOTE: 2379 case TokenKind.DOUBLE_QUOTE:
2386 stopToken = TokenKind.DOUBLE_QUOTE; 2380 stopToken = TokenKind.DOUBLE_QUOTE;
2387 _next(); // Skip the DOUBLE_QUOTE. 2381 _next(); // Skip the DOUBLE_QUOTE.
2388 start = _peekToken.span; 2382 start = _peekToken.span;
2389 break; 2383 break;
2390 default: 2384 default:
2391 if (urlString) { 2385 if (urlString) {
2392 if (_peek() == TokenKind.LPAREN) { 2386 if (_peek() == TokenKind.LPAREN) {
2393 _next(); // Skip the LPAREN. 2387 _next(); // Skip the LPAREN.
2394 start = _peekToken.span; 2388 start = _peekToken.span;
2389 }
2390 stopToken = TokenKind.RPAREN;
2391 } else {
2392 _error('unexpected string', _makeSpan(start));
2395 } 2393 }
2396 stopToken = TokenKind.RPAREN; 2394 break;
2397 } else {
2398 _error('unexpected string', _makeSpan(start));
2399 }
2400 break;
2401 } 2395 }
2402 2396
2403 // Gobble up everything until we hit our stop token. 2397 // Gobble up everything until we hit our stop token.
2404 var stringValue = new StringBuffer(); 2398 var stringValue = new StringBuffer();
2405 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { 2399 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) {
2406 stringValue.write(_next().text); 2400 stringValue.write(_next().text);
2407 } 2401 }
2408 2402
2409 tokenizer._skipWhitespace = skipWhitespace; 2403 tokenizer._skipWhitespace = skipWhitespace;
2410 2404
2411 // All characters between quotes is the string. 2405 // All characters between quotes is the string.
2412 if (stopToken != TokenKind.RPAREN) { 2406 if (stopToken != TokenKind.RPAREN) {
2413 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; 2407 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE;
2414 } 2408 }
2415 2409
2416 return stringValue.toString(); 2410 return stringValue.toString();
2417 } 2411 }
2418 2412
2419 // TODO(terry): Should probably understand IE's non-standard filter syntax to 2413 // TODO(terry): Should probably understand IE's non-standard filter syntax to
2420 // fully support calc, var(), etc. 2414 // fully support calc, var(), etc.
2421 /** 2415 /**
2422 * IE's filter property breaks CSS value parsing. IE's format can be: 2416 * IE's filter property breaks CSS value parsing. IE's format can be:
2423 * 2417 *
2424 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); 2418 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83');
2425 * 2419 *
2426 * We'll just parse everything after the 'progid:' look for the left paren 2420 * We'll just parse everything after the 'progid:' look for the left paren
2427 * then parse to the right paren ignoring everything in between. 2421 * then parse to the right paren ignoring everything in between.
2428 */ 2422 */
2429 processIEFilter(FileSpan startAfterProgidColon) { 2423 processIEFilter(FileSpan startAfterProgidColon) {
2430 var parens = 0; 2424 var parens = 0;
2431 2425
2432 while (_peek() != TokenKind.END_OF_FILE) { 2426 while (_peek() != TokenKind.END_OF_FILE) {
2433 switch (_peek()) { 2427 switch (_peek()) {
2434 case TokenKind.LPAREN: 2428 case TokenKind.LPAREN:
2435 _eat(TokenKind.LPAREN); 2429 _eat(TokenKind.LPAREN);
2436 parens++; 2430 parens++;
2437 break; 2431 break;
2438 case TokenKind.RPAREN: 2432 case TokenKind.RPAREN:
2439 _eat(TokenKind.RPAREN); 2433 _eat(TokenKind.RPAREN);
2440 if (--parens == 0) { 2434 if (--parens == 0) {
2441 var tok = tokenizer.makeIEFilter(startAfterProgidColon.start.offset, 2435 var tok = tokenizer.makeIEFilter(
2442 _peekToken.start); 2436 startAfterProgidColon.start.offset, _peekToken.start);
2443 return new LiteralTerm(tok.text, tok.text, tok.span); 2437 return new LiteralTerm(tok.text, tok.text, tok.span);
2444 } 2438 }
2445 break; 2439 break;
2446 default: 2440 default:
2447 _eat(_peek()); 2441 _eat(_peek());
2448 } 2442 }
2449 } 2443 }
2450 } 2444 }
2451 2445
2452 // Function grammar: 2446 // Function grammar:
2453 // 2447 //
2454 // function: IDENT '(' expr ')' 2448 // function: IDENT '(' expr ')'
2455 // 2449 //
2456 processFunction(Identifier func) { 2450 processFunction(Identifier func) {
2457 var start = _peekToken.span; 2451 var start = _peekToken.span;
2458 2452
2459 var name = func.name; 2453 var name = func.name;
2460 2454
2461 switch (name) { 2455 switch (name) {
2462 case 'url': 2456 case 'url':
2463 // URI term sucks up everything inside of quotes(' or ") or between parens 2457 // URI term sucks up everything inside of quotes(' or ") or between pare ns
2464 var urlParam = processQuotedString(true); 2458 var urlParam = processQuotedString(true);
2465 2459
2466 // TODO(terry): Better error messge and checking for mismatched quotes. 2460 // TODO(terry): Better error messge and checking for mismatched quotes.
2467 if (_peek() == TokenKind.END_OF_FILE) { 2461 if (_peek() == TokenKind.END_OF_FILE) {
2468 _error("problem parsing URI", _peekToken.span); 2462 _error("problem parsing URI", _peekToken.span);
2469 } 2463 }
2470 2464
2471 if (_peek() == TokenKind.RPAREN) { 2465 if (_peek() == TokenKind.RPAREN) {
2472 _next(); 2466 _next();
2473 } 2467 }
2474 2468
2475 return new UriTerm(urlParam, _makeSpan(start)); 2469 return new UriTerm(urlParam, _makeSpan(start));
2476 case 'calc': 2470 case 'calc':
2477 // TODO(terry): Implement expression handling... 2471 // TODO(terry): Implement expression handling...
2478 break; 2472 break;
2479 case 'var': 2473 case 'var':
2480 // TODO(terry): Consider handling var in IE specific filter/progid. This 2474 // TODO(terry): Consider handling var in IE specific filter/progid. Thi s
2481 // will require parsing entire IE specific syntax e.g., 2475 // will require parsing entire IE specific syntax e.g.,
2482 // param = value or progid:com_id, etc. for example: 2476 // param = value or progid:com_id, etc. for example:
2483 // 2477 //
2484 // var-blur: Blur(Add = 0, Direction = 225, Strength = 10); 2478 // var-blur: Blur(Add = 0, Direction = 225, Strength = 10);
2485 // var-gradient: progid:DXImageTransform.Microsoft.gradient" 2479 // var-gradient: progid:DXImageTransform.Microsoft.gradient"
2486 // (GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); 2480 // (GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
2487 var expr = processExpr(); 2481 var expr = processExpr();
2488 if (!_maybeEat(TokenKind.RPAREN)) { 2482 if (!_maybeEat(TokenKind.RPAREN)) {
2489 _error("problem parsing var expected ), ", _peekToken.span); 2483 _error("problem parsing var expected ), ", _peekToken.span);
2490 } 2484 }
2491 if (isChecked && 2485 if (isChecked &&
2492 expr.expressions.where((e) => e is OperatorComma).length > 1) { 2486 expr.expressions.where((e) => e is OperatorComma).length > 1) {
2493 _error("too many parameters to var()", _peekToken.span); 2487 _error("too many parameters to var()", _peekToken.span);
2494 } 2488 }
2495 2489
2496 var paramName = expr.expressions[0].text; 2490 var paramName = expr.expressions[0].text;
2497 2491
2498 // [0] - var name, [1] - OperatorComma, [2] - default value. 2492 // [0] - var name, [1] - OperatorComma, [2] - default value.
2499 var defaultValues = expr.expressions.length >= 3 2493 var defaultValues =
2500 ? expr.expressions.sublist(2) : []; 2494 expr.expressions.length >= 3 ? expr.expressions.sublist(2) : [];
2501 return new VarUsage(paramName, defaultValues, _makeSpan(start)); 2495 return new VarUsage(paramName, defaultValues, _makeSpan(start));
2502 default: 2496 default:
2503 var expr = processExpr(); 2497 var expr = processExpr();
2504 if (!_maybeEat(TokenKind.RPAREN)) { 2498 if (!_maybeEat(TokenKind.RPAREN)) {
2505 _error("problem parsing function expected ), ", _peekToken.span); 2499 _error("problem parsing function expected ), ", _peekToken.span);
2506 } 2500 }
2507 2501
2508 return new FunctionTerm(name, name, expr, _makeSpan(start)); 2502 return new FunctionTerm(name, name, expr, _makeSpan(start));
2509 } 2503 }
2510 2504
2511 return null; 2505 return null;
2512 } 2506 }
2513 2507
2514 Identifier identifier() { 2508 Identifier identifier() {
2515 var tok = _next(); 2509 var tok = _next();
2516 2510
2517 if (!TokenKind.isIdentifier(tok.kind) && 2511 if (!TokenKind.isIdentifier(tok.kind) &&
2518 !TokenKind.isKindIdentifier(tok.kind)) { 2512 !TokenKind.isKindIdentifier(tok.kind)) {
2519 if (isChecked) { 2513 if (isChecked) {
2520 _warning('expected identifier, but found $tok', tok.span); 2514 _warning('expected identifier, but found $tok', tok.span);
2521 } 2515 }
2522 return new Identifier("", _makeSpan(tok.span)); 2516 return new Identifier("", _makeSpan(tok.span));
2523 } 2517 }
2524 2518
2525 return new Identifier(tok.text, _makeSpan(tok.span)); 2519 return new Identifier(tok.text, _makeSpan(tok.span));
2526 } 2520 }
2527 2521
2528 // TODO(terry): Move this to base <= 36 and into shared code. 2522 // TODO(terry): Move this to base <= 36 and into shared code.
2529 static int _hexDigit(int c) { 2523 static int _hexDigit(int c) {
2530 if (c >= 48/*0*/ && c <= 57/*9*/) { 2524 if (c >= 48 /*0*/ && c <= 57 /*9*/) {
2531 return c - 48; 2525 return c - 48;
2532 } else if (c >= 97/*a*/ && c <= 102/*f*/) { 2526 } else if (c >= 97 /*a*/ && c <= 102 /*f*/) {
2533 return c - 87; 2527 return c - 87;
2534 } else if (c >= 65/*A*/ && c <= 70/*F*/) { 2528 } else if (c >= 65 /*A*/ && c <= 70 /*F*/) {
2535 return c - 55; 2529 return c - 55;
2536 } else { 2530 } else {
2537 return -1; 2531 return -1;
2538 } 2532 }
2539 } 2533 }
2540 2534
2541 HexColorTerm _parseHex(String hexText, SourceSpan span) { 2535 HexColorTerm _parseHex(String hexText, SourceSpan span) {
2542 var hexValue = 0; 2536 var hexValue = 0;
2543 2537
2544 for (var i = 0; i < hexText.length; i++) { 2538 for (var i = 0; i < hexText.length; i++) {
2545 var digit = _hexDigit(hexText.codeUnitAt(i)); 2539 var digit = _hexDigit(hexText.codeUnitAt(i));
2546 if (digit < 0) { 2540 if (digit < 0) {
2547 _warning('Bad hex number', span); 2541 _warning('Bad hex number', span);
2548 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span); 2542 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span);
2549 } 2543 }
2550 hexValue = (hexValue << 4) + digit; 2544 hexValue = (hexValue << 4) + digit;
2551 } 2545 }
2552 2546
2553 // Make 3 character hex value #RRGGBB => #RGB iff: 2547 // Make 3 character hex value #RRGGBB => #RGB iff:
2554 // high/low nibble of RR is the same, high/low nibble of GG is the same and 2548 // high/low nibble of RR is the same, high/low nibble of GG is the same and
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
2682 * Escapes [text] for use in a CSS string. 2676 * Escapes [text] for use in a CSS string.
2683 * [single] specifies single quote `'` vs double quote `"`. 2677 * [single] specifies single quote `'` vs double quote `"`.
2684 */ 2678 */
2685 String _escapeString(String text, {bool single: false}) { 2679 String _escapeString(String text, {bool single: false}) {
2686 StringBuffer result = null; 2680 StringBuffer result = null;
2687 2681
2688 for (int i = 0; i < text.length; i++) { 2682 for (int i = 0; i < text.length; i++) {
2689 var code = text.codeUnitAt(i); 2683 var code = text.codeUnitAt(i);
2690 String replace = null; 2684 String replace = null;
2691 switch (code) { 2685 switch (code) {
2692 case 34/*'"'*/: if (!single) replace = r'\"'; break; 2686 case 34 /*'"'*/ :
2693 case 39/*"'"*/: if (single) replace = r"\'"; break; 2687 if (!single) replace = r'\"';
2688 break;
2689 case 39 /*"'"*/ :
2690 if (single) replace = r"\'";
2691 break;
2694 } 2692 }
2695 2693
2696 if (replace != null && result == null) { 2694 if (replace != null && result == null) {
2697 result = new StringBuffer(text.substring(0, i)); 2695 result = new StringBuffer(text.substring(0, i));
2698 } 2696 }
2699 2697
2700 if (result != null) result.write(replace != null ? replace : text[i]); 2698 if (result != null) result.write(replace != null ? replace : text[i]);
2701 } 2699 }
2702 2700
2703 return result == null ? text : result.toString(); 2701 return result == null ? text : result.toString();
2704 } 2702 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698