| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library csslib.parser; | 5 library csslib.parser; |
| 6 | 6 |
| 7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
| 8 | 8 |
| 9 import 'package:source_maps/span.dart' show SourceFile, Span, FileSpan; | 9 import 'package:source_maps/span.dart' show SourceFile, Span, FileSpan; |
| 10 | 10 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 | 23 |
| 24 /** Used for parser lookup ahead (used for nested selectors Less support). */ | 24 /** Used for parser lookup ahead (used for nested selectors Less support). */ |
| 25 class ParserState extends TokenizerState { | 25 class ParserState extends TokenizerState { |
| 26 final Token peekToken; | 26 final Token peekToken; |
| 27 final Token previousToken; | 27 final Token previousToken; |
| 28 | 28 |
| 29 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer) | 29 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer) |
| 30 : super(tokenizer); | 30 : super(tokenizer); |
| 31 } | 31 } |
| 32 | 32 |
| 33 void _createMessages({List errors, List options}) { | 33 void _createMessages({List<Message> errors, List<String> options}) { |
| 34 if (errors == null) errors = []; | 34 if (errors == null) errors = []; |
| 35 | 35 |
| 36 if (options == null) { | 36 if (options == null) { |
| 37 options = ['--no-colors', 'memory']; | 37 options = ['--no-colors', 'memory']; |
| 38 } | 38 } |
| 39 var opt = PreprocessorOptions.parse(options); | 39 var opt = PreprocessorOptions.parse(options); |
| 40 messages = new Messages(options: opt, printHandler: errors.add); | 40 messages = new Messages(options: opt, printHandler: errors.add); |
| 41 } | 41 } |
| 42 | 42 |
| 43 /** CSS checked mode enabled. */ | 43 /** CSS checked mode enabled. */ |
| 44 bool get isChecked => messages.options.checked; | 44 bool get isChecked => messages.options.checked; |
| 45 | 45 |
| 46 // TODO(terry): Remove nested name parameter. | 46 // TODO(terry): Remove nested name parameter. |
| 47 /** Parse and analyze the CSS file. */ | 47 /** Parse and analyze the CSS file. */ |
| 48 StyleSheet compile(var input, {List errors, List options, bool nested: true, | 48 StyleSheet compile(var input, {List<Message> errors, List<String> options, |
| 49 bool polyfill: false, List<StyleSheet> includes: null}) { | 49 bool nested: true, |
| 50 bool polyfill: false, |
| 51 List<StyleSheet> includes: null}) { |
| 52 |
| 50 if (includes == null) { | 53 if (includes == null) { |
| 51 includes = []; | 54 includes = []; |
| 52 } | 55 } |
| 53 | 56 |
| 54 var source = _inputAsString(input); | 57 var source = _inputAsString(input); |
| 55 | 58 |
| 56 _createMessages(errors: errors, options: options); | 59 _createMessages(errors: errors, options: options); |
| 57 | 60 |
| 58 var file = new SourceFile.text(null, source); | 61 var file = new SourceFile.text(null, source); |
| 59 | 62 |
| 60 var tree = new _Parser(file, source).parse(); | 63 var tree = new _Parser(file, source).parse(); |
| 61 | 64 |
| 62 analyze([tree], errors: errors, options: options); | 65 analyze([tree], errors: errors, options: options); |
| 63 | 66 |
| 64 if (polyfill) { | 67 if (polyfill) { |
| 65 var processCss = new PolyFill(messages, true); | 68 var processCss = new PolyFill(messages, true); |
| 66 processCss.process(tree, includes: includes); | 69 processCss.process(tree, includes: includes); |
| 67 } | 70 } |
| 68 | 71 |
| 69 return tree; | 72 return tree; |
| 70 } | 73 } |
| 71 | 74 |
| 72 /** Analyze the CSS file. */ | 75 /** Analyze the CSS file. */ |
| 73 void analyze(List<StyleSheet> styleSheets, {List errors, List options}) { | 76 void analyze(List<StyleSheet> styleSheets, |
| 77 {List<Message> errors, List<String> options}) { |
| 78 |
| 74 _createMessages(errors: errors, options: options); | 79 _createMessages(errors: errors, options: options); |
| 75 new Analyzer(styleSheets, messages).run(); | 80 new Analyzer(styleSheets, messages).run(); |
| 76 } | 81 } |
| 77 | 82 |
| 78 /** | 83 /** |
| 79 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String], | 84 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String], |
| 80 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional | 85 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional |
| 81 * [errors] list will contain each error/warning as a [Message]. | 86 * [errors] list will contain each error/warning as a [Message]. |
| 82 */ | 87 */ |
| 83 StyleSheet parse(var input, {List errors, List options}) { | 88 StyleSheet parse(var input, {List<Message> errors, List<String> options}) { |
| 84 var source = _inputAsString(input); | 89 var source = _inputAsString(input); |
| 85 | 90 |
| 86 _createMessages(errors: errors, options: options); | 91 _createMessages(errors: errors, options: options); |
| 87 | 92 |
| 88 var file = new SourceFile.text(null, source); | 93 var file = new SourceFile.text(null, source); |
| 89 | 94 |
| 90 return new _Parser(file, source).parse(); | 95 return new _Parser(file, source).parse(); |
| 91 } | 96 } |
| 92 | 97 |
| 93 /** | 98 /** |
| 94 * Parse the [input] CSS selector into a tree. The [input] can be a [String], | 99 * Parse the [input] CSS selector into a tree. The [input] can be a [String], |
| 95 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional | 100 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional |
| 96 * [errors] list will contain each error/warning as a [Message]. | 101 * [errors] list will contain each error/warning as a [Message]. |
| 97 */ | 102 */ |
| 98 StyleSheet selector(var input, {List errors}) { | 103 StyleSheet selector(var input, {List<Message> errors}) { |
| 99 var source = _inputAsString(input); | 104 var source = _inputAsString(input); |
| 100 | 105 |
| 101 _createMessages(errors: errors); | 106 _createMessages(errors: errors); |
| 102 | 107 |
| 103 var file = new SourceFile.text(null, source); | 108 var file = new SourceFile.text(null, source); |
| 104 | 109 |
| 105 return new _Parser(file, source).parseSelector(); | 110 return new _Parser(file, source).parseSelector(); |
| 106 } | 111 } |
| 107 | 112 |
| 108 String _inputAsString(var input) { | 113 String _inputAsString(var input) { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 final _Parser _parser; | 148 final _Parser _parser; |
| 144 | 149 |
| 145 Parser(SourceFile file, String text, {int start: 0, String baseUrl}) : | 150 Parser(SourceFile file, String text, {int start: 0, String baseUrl}) : |
| 146 _parser = new _Parser(file, text, start: start, baseUrl: baseUrl); | 151 _parser = new _Parser(file, text, start: start, baseUrl: baseUrl); |
| 147 | 152 |
| 148 StyleSheet parse() => _parser.parse(); | 153 StyleSheet parse() => _parser.parse(); |
| 149 } | 154 } |
| 150 | 155 |
| 151 /** A simple recursive descent parser for CSS. */ | 156 /** A simple recursive descent parser for CSS. */ |
| 152 class _Parser { | 157 class _Parser { |
| 153 Tokenizer tokenizer; | 158 final Tokenizer tokenizer; |
| 154 | 159 |
| 155 /** Base url of CSS file. */ | 160 /** Base url of CSS file. */ |
| 156 final String _baseUrl; | 161 final String _baseUrl; |
| 157 | 162 |
| 158 /** | 163 /** |
| 159 * File containing the source being parsed, used to report errors with | 164 * File containing the source being parsed, used to report errors with |
| 160 * source-span locations. | 165 * source-span locations. |
| 161 */ | 166 */ |
| 162 final SourceFile file; | 167 final SourceFile file; |
| 163 | 168 |
| (...skipping 1013 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1177 } while (_maybeEat(TokenKind.COMMA)); | 1182 } while (_maybeEat(TokenKind.COMMA)); |
| 1178 | 1183 |
| 1179 if (selectors.length > 0) { | 1184 if (selectors.length > 0) { |
| 1180 return new SelectorGroup(selectors, _makeSpan(start)); | 1185 return new SelectorGroup(selectors, _makeSpan(start)); |
| 1181 } | 1186 } |
| 1182 } | 1187 } |
| 1183 | 1188 |
| 1184 /** | 1189 /** |
| 1185 * Return list of selectors | 1190 * Return list of selectors |
| 1186 */ | 1191 */ |
| 1187 processSelector() { | 1192 Selector processSelector() { |
| 1188 List<SimpleSelectorSequence> simpleSequences = []; | 1193 var simpleSequences = <SimpleSelectorSequence>[]; |
| 1189 int start = _peekToken.start; | 1194 var start = _peekToken.start; |
| 1190 while (true) { | 1195 while (true) { |
| 1191 // First item is never descendant make sure it's COMBINATOR_NONE. | 1196 // First item is never descendant make sure it's COMBINATOR_NONE. |
| 1192 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0); | 1197 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0); |
| 1193 if (selectorItem != null) { | 1198 if (selectorItem != null) { |
| 1194 simpleSequences.add(selectorItem); | 1199 simpleSequences.add(selectorItem); |
| 1195 } else { | 1200 } else { |
| 1196 break; | 1201 break; |
| 1197 } | 1202 } |
| 1198 } | 1203 } |
| 1199 | 1204 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1337 } | 1342 } |
| 1338 | 1343 |
| 1339 return false; | 1344 return false; |
| 1340 } | 1345 } |
| 1341 | 1346 |
| 1342 /** | 1347 /** |
| 1343 * type_selector | universal | HASH | class | attrib | pseudo | 1348 * type_selector | universal | HASH | class | attrib | pseudo |
| 1344 */ | 1349 */ |
| 1345 simpleSelectorTail() { | 1350 simpleSelectorTail() { |
| 1346 // Check for HASH | class | attrib | pseudo | negation | 1351 // Check for HASH | class | attrib | pseudo | negation |
| 1347 int start = _peekToken.start; | 1352 var start = _peekToken.start; |
| 1348 switch (_peek()) { | 1353 switch (_peek()) { |
| 1349 case TokenKind.HASH: | 1354 case TokenKind.HASH: |
| 1350 _eat(TokenKind.HASH); | 1355 _eat(TokenKind.HASH); |
| 1351 | 1356 |
| 1352 bool hasWhiteSpace = false; | 1357 var hasWhiteSpace = false; |
| 1353 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { | 1358 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { |
| 1354 _warning("Not a valid ID selector expected #id", _makeSpan(start)); | 1359 _warning("Not a valid ID selector expected #id", _makeSpan(start)); |
| 1355 hasWhiteSpace = true; | 1360 hasWhiteSpace = true; |
| 1356 } | 1361 } |
| 1357 if (_peekIdentifier()) { | 1362 if (_peekIdentifier()) { |
| 1358 var id = identifier(); | 1363 var id = identifier(); |
| 1359 if (hasWhiteSpace) { | 1364 if (hasWhiteSpace) { |
| 1360 // Generate bad selector id (normalized). | 1365 // Generate bad selector id (normalized). |
| 1361 id.name = " ${id.name}"; | 1366 id.name = " ${id.name}"; |
| 1362 } | 1367 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1388 _peekToken.span); | 1393 _peekToken.span); |
| 1389 _next(); | 1394 _next(); |
| 1390 break; | 1395 break; |
| 1391 } | 1396 } |
| 1392 } | 1397 } |
| 1393 | 1398 |
| 1394 processPseudoSelector(int start) { | 1399 processPseudoSelector(int start) { |
| 1395 // :pseudo-class ::pseudo-element | 1400 // :pseudo-class ::pseudo-element |
| 1396 // TODO(terry): '::' should be token. | 1401 // TODO(terry): '::' should be token. |
| 1397 _eat(TokenKind.COLON); | 1402 _eat(TokenKind.COLON); |
| 1398 bool pseudoElement = _maybeEat(TokenKind.COLON); | 1403 var pseudoElement = _maybeEat(TokenKind.COLON); |
| 1399 | 1404 |
| 1400 // TODO(terry): If no identifier specified consider optimizing out the | 1405 // TODO(terry): If no identifier specified consider optimizing out the |
| 1401 // : or :: and making this a normal selector. For now, | 1406 // : or :: and making this a normal selector. For now, |
| 1402 // create an empty pseudoName. | 1407 // create an empty pseudoName. |
| 1403 var pseudoName; | 1408 var pseudoName; |
| 1404 if (_peekIdentifier()) { | 1409 if (_peekIdentifier()) { |
| 1405 pseudoName = identifier(); | 1410 pseudoName = identifier(); |
| 1406 } else { | 1411 } else { |
| 1407 return null; | 1412 return null; |
| 1408 } | 1413 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1449 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". | 1454 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". |
| 1450 * | 1455 * |
| 1451 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ | 1456 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ |
| 1452 * | 1457 * |
| 1453 * num [0-9]+|[0-9]*\.[0-9]+ | 1458 * num [0-9]+|[0-9]*\.[0-9]+ |
| 1454 * PLUS '+' | 1459 * PLUS '+' |
| 1455 * DIMENSION {num}{ident} | 1460 * DIMENSION {num}{ident} |
| 1456 * NUMBER {num} | 1461 * NUMBER {num} |
| 1457 */ | 1462 */ |
| 1458 processSelectorExpression() { | 1463 processSelectorExpression() { |
| 1459 int start = _peekToken.start; | 1464 var start = _peekToken.start; |
| 1460 | 1465 |
| 1461 var expression = new SelectorExpression(_makeSpan(start)); | 1466 var expression = new SelectorExpression(_makeSpan(start)); |
| 1462 | 1467 |
| 1463 Token termToken; | 1468 Token termToken; |
| 1464 var value; | 1469 var value; |
| 1465 | 1470 |
| 1466 // Special parsing for expressions in pseudo functions. Minus is used as | 1471 // Special parsing for expressions in pseudo functions. Minus is used as |
| 1467 // operator not identifier. | 1472 // operator not identifier. |
| 1468 tokenizer.selectorExpression = true; | 1473 tokenizer.selectorExpression = true; |
| 1469 | 1474 |
| 1470 bool keepParsing = true; | 1475 var keepParsing = true; |
| 1471 while (keepParsing) { | 1476 while (keepParsing) { |
| 1472 switch (_peek()) { | 1477 switch (_peek()) { |
| 1473 case TokenKind.PLUS: | 1478 case TokenKind.PLUS: |
| 1474 start = _peekToken.start; | 1479 start = _peekToken.start; |
| 1475 termToken = _next(); | 1480 termToken = _next(); |
| 1476 expression.add(new OperatorPlus(_makeSpan(start))); | 1481 expression.add(new OperatorPlus(_makeSpan(start))); |
| 1477 break; | 1482 break; |
| 1478 case TokenKind.MINUS: | 1483 case TokenKind.MINUS: |
| 1479 start = _peekToken.start; | 1484 start = _peekToken.start; |
| 1480 termToken = _next(); | 1485 termToken = _next(); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1532 // | 1537 // |
| 1533 // DASHMATCH: '|=' | 1538 // DASHMATCH: '|=' |
| 1534 // | 1539 // |
| 1535 // PREFIXMATCH: '^=' | 1540 // PREFIXMATCH: '^=' |
| 1536 // | 1541 // |
| 1537 // SUFFIXMATCH: '$=' | 1542 // SUFFIXMATCH: '$=' |
| 1538 // | 1543 // |
| 1539 // SUBSTRMATCH: '*=' | 1544 // SUBSTRMATCH: '*=' |
| 1540 // | 1545 // |
| 1541 // | 1546 // |
| 1542 processAttribute() { | 1547 AttributeSelector processAttribute() { |
| 1543 int start = _peekToken.start; | 1548 var start = _peekToken.start; |
| 1544 | 1549 |
| 1545 if (_maybeEat(TokenKind.LBRACK)) { | 1550 if (_maybeEat(TokenKind.LBRACK)) { |
| 1546 var attrName = identifier(); | 1551 var attrName = identifier(); |
| 1547 | 1552 |
| 1548 int op; | 1553 int op; |
| 1549 switch (_peek()) { | 1554 switch (_peek()) { |
| 1550 case TokenKind.EQUALS: | 1555 case TokenKind.EQUALS: |
| 1551 case TokenKind.INCLUDES: // ~= | 1556 case TokenKind.INCLUDES: // ~= |
| 1552 case TokenKind.DASH_MATCH: // |= | 1557 case TokenKind.DASH_MATCH: // |= |
| 1553 case TokenKind.PREFIX_MATCH: // ^= | 1558 case TokenKind.PREFIX_MATCH: // ^= |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1586 // | 1591 // |
| 1587 // property: IDENT [or IE hacks] | 1592 // property: IDENT [or IE hacks] |
| 1588 // prio: !important | 1593 // prio: !important |
| 1589 // expr: (see processExpr) | 1594 // expr: (see processExpr) |
| 1590 // | 1595 // |
| 1591 // Here are the ugly IE hacks we need to support: | 1596 // Here are the ugly IE hacks we need to support: |
| 1592 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon | 1597 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon |
| 1593 // *IDENT - IE7 or below | 1598 // *IDENT - IE7 or below |
| 1594 // _IDENT - IE6 property (automatically a valid ident) | 1599 // _IDENT - IE6 property (automatically a valid ident) |
| 1595 // | 1600 // |
| 1596 processDeclaration(List dartStyles) { | 1601 Declaration processDeclaration(List dartStyles) { |
| 1597 Declaration decl; | 1602 Declaration decl; |
| 1598 | 1603 |
| 1599 int start = _peekToken.start; | 1604 int start = _peekToken.start; |
| 1600 | 1605 |
| 1601 // IE7 hack of * before property name if so the property is IE7 or below. | 1606 // IE7 hack of * before property name if so the property is IE7 or below. |
| 1602 var ie7 = _peekKind(TokenKind.ASTERISK); | 1607 var ie7 = _peekKind(TokenKind.ASTERISK); |
| 1603 if (ie7) { | 1608 if (ie7) { |
| 1604 _next(); | 1609 _next(); |
| 1605 } | 1610 } |
| 1606 | 1611 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1630 | 1635 |
| 1631 Expressions exprs = processExpr(); | 1636 Expressions exprs = processExpr(); |
| 1632 | 1637 |
| 1633 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); | 1638 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); |
| 1634 } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) { | 1639 } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) { |
| 1635 // @include mixinName in the declaration area. | 1640 // @include mixinName in the declaration area. |
| 1636 var span = _makeSpan(start); | 1641 var span = _makeSpan(start); |
| 1637 var include = processInclude(span, eatSemiColon: false); | 1642 var include = processInclude(span, eatSemiColon: false); |
| 1638 decl = new IncludeMixinAtDeclaration(include, span); | 1643 decl = new IncludeMixinAtDeclaration(include, span); |
| 1639 } else if (_peekToken.kind == TokenKind.DIRECTIVE_EXTEND) { | 1644 } else if (_peekToken.kind == TokenKind.DIRECTIVE_EXTEND) { |
| 1640 List<SimpleSelectorSequence> simpleSequences = []; | 1645 var simpleSequences = <TreeNode>[]; |
| 1641 | 1646 |
| 1642 _next(); | 1647 _next(); |
| 1643 var span = _makeSpan(start); | 1648 var span = _makeSpan(start); |
| 1644 var selector = simpleSelector(); | 1649 var selector = simpleSelector(); |
| 1645 if (selector == null) { | 1650 if (selector == null) { |
| 1646 _warning("@extends expecting simple selector name", span); | 1651 _warning("@extends expecting simple selector name", span); |
| 1647 } else { | 1652 } else { |
| 1648 simpleSequences.add(selector); | 1653 simpleSequences.add(selector); |
| 1649 } | 1654 } |
| 1650 if (_peekKind(TokenKind.COLON)) { | 1655 if (_peekKind(TokenKind.COLON)) { |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1723 'padding-top': _paddingPartTop, | 1728 'padding-top': _paddingPartTop, |
| 1724 'padding-right': _paddingPartRight, | 1729 'padding-right': _paddingPartRight, |
| 1725 'padding-bottom': _paddingPartBottom | 1730 'padding-bottom': _paddingPartBottom |
| 1726 }; | 1731 }; |
| 1727 | 1732 |
| 1728 static const Map<String, int> _nameToFontWeight = const { | 1733 static const Map<String, int> _nameToFontWeight = const { |
| 1729 'bold' : FontWeight.bold, | 1734 'bold' : FontWeight.bold, |
| 1730 'normal' : FontWeight.normal | 1735 'normal' : FontWeight.normal |
| 1731 }; | 1736 }; |
| 1732 | 1737 |
| 1733 static _findStyle(String styleName) { | 1738 static int _findStyle(String styleName) => _stylesToDart[styleName]; |
| 1734 if (_stylesToDart.containsKey(styleName)) { | |
| 1735 return _stylesToDart[styleName]; | |
| 1736 } | |
| 1737 } | |
| 1738 | 1739 |
| 1739 _styleForDart(Identifier property, Expressions exprs, List dartStyles) { | 1740 DartStyleExpression _styleForDart(Identifier property, Expressions exprs, |
| 1740 int styleType = _findStyle(property.name.toLowerCase()); | 1741 List dartStyles) { |
| 1742 var styleType = _findStyle(property.name.toLowerCase()); |
| 1741 if (styleType != null) { | 1743 if (styleType != null) { |
| 1742 return buildDartStyleNode(styleType, exprs, dartStyles); | 1744 return buildDartStyleNode(styleType, exprs, dartStyles); |
| 1743 } | 1745 } |
| 1744 } | 1746 } |
| 1745 | 1747 |
| 1746 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) { | 1748 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) { |
| 1747 // Merge all font styles for this class selector. | 1749 // Merge all font styles for this class selector. |
| 1748 for (var dartStyle in dartStyles) { | 1750 for (var dartStyle in dartStyles) { |
| 1749 if (dartStyle.isFont) { | 1751 if (dartStyle.isFont) { |
| 1750 fontExpr = new FontExpression.merge(dartStyle, fontExpr); | 1752 fontExpr = new FontExpression.merge(dartStyle, fontExpr); |
| 1751 } | 1753 } |
| 1752 } | 1754 } |
| 1753 | 1755 |
| 1754 return fontExpr; | 1756 return fontExpr; |
| 1755 } | 1757 } |
| 1756 | 1758 |
| 1757 buildDartStyleNode(int styleType, Expressions exprs, List dartStyles) { | 1759 DartStyleExpression buildDartStyleNode(int styleType, Expressions exprs, |
| 1760 List dartStyles) { |
| 1761 |
| 1758 switch (styleType) { | 1762 switch (styleType) { |
| 1759 /* | 1763 /* |
| 1760 * Properties in order: | 1764 * Properties in order: |
| 1761 * | 1765 * |
| 1762 * font-style font-variant font-weight font-size/line-height font-family | 1766 * font-style font-variant font-weight font-size/line-height font-family |
| 1763 * | 1767 * |
| 1764 * The font-size and font-family values are required. If other values are | 1768 * The font-size and font-family values are required. If other values are |
| 1765 * missing; a default, if it exist, will be used. | 1769 * missing; a default, if it exist, will be used. |
| 1766 */ | 1770 */ |
| 1767 case _fontPartFont: | 1771 case _fontPartFont: |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1890 } | 1894 } |
| 1891 break; | 1895 break; |
| 1892 default: | 1896 default: |
| 1893 // Don't handle it. | 1897 // Don't handle it. |
| 1894 return null; | 1898 return null; |
| 1895 } | 1899 } |
| 1896 } | 1900 } |
| 1897 | 1901 |
| 1898 // TODO(terry): Look at handling width of thin, thick, etc. any none numbers | 1902 // TODO(terry): Look at handling width of thin, thick, etc. any none numbers |
| 1899 // to convert to a number. | 1903 // to convert to a number. |
| 1900 processOneNumber(Expressions exprs, int part) { | 1904 DartStyleExpression processOneNumber(Expressions exprs, int part) { |
| 1901 var value = marginValue(exprs.expressions[0]); | 1905 var value = marginValue(exprs.expressions[0]); |
| 1902 if (value != null) { | 1906 if (value != null) { |
| 1903 switch (part) { | 1907 switch (part) { |
| 1904 case _marginPartLeft: | 1908 case _marginPartLeft: |
| 1905 return new MarginExpression(exprs.span, left: value); | 1909 return new MarginExpression(exprs.span, left: value); |
| 1906 case _marginPartTop: | 1910 case _marginPartTop: |
| 1907 return new MarginExpression(exprs.span, top: value); | 1911 return new MarginExpression(exprs.span, top: value); |
| 1908 case _marginPartRight: | 1912 case _marginPartRight: |
| 1909 return new MarginExpression(exprs.span, right: value); | 1913 return new MarginExpression(exprs.span, right: value); |
| 1910 case _marginPartBottom: | 1914 case _marginPartBottom: |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1940 /** | 1944 /** |
| 1941 * Margins are of the format: | 1945 * Margins are of the format: |
| 1942 * | 1946 * |
| 1943 * top,right,bottom,left (4 parameters) | 1947 * top,right,bottom,left (4 parameters) |
| 1944 * top,right/left, bottom (3 parameters) | 1948 * top,right/left, bottom (3 parameters) |
| 1945 * top/bottom,right/left (2 parameters) | 1949 * top/bottom,right/left (2 parameters) |
| 1946 * top/right/bottom/left (1 parameter) | 1950 * top/right/bottom/left (1 parameter) |
| 1947 * | 1951 * |
| 1948 * The values of the margins can be a unit or unitless or auto. | 1952 * The values of the margins can be a unit or unitless or auto. |
| 1949 */ | 1953 */ |
| 1950 processFourNums(Expressions exprs) { | 1954 BoxEdge processFourNums(Expressions exprs) { |
| 1951 num top; | 1955 num top; |
| 1952 num right; | 1956 num right; |
| 1953 num bottom; | 1957 num bottom; |
| 1954 num left; | 1958 num left; |
| 1955 | 1959 |
| 1956 int totalExprs = exprs.expressions.length; | 1960 int totalExprs = exprs.expressions.length; |
| 1957 switch (totalExprs) { | 1961 switch (totalExprs) { |
| 1958 case 1: | 1962 case 1: |
| 1959 top = marginValue(exprs.expressions[0]); | 1963 top = marginValue(exprs.expressions[0]); |
| 1960 right = top; | 1964 right = top; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1993 } | 1997 } |
| 1994 } | 1998 } |
| 1995 | 1999 |
| 1996 // Expression grammar: | 2000 // Expression grammar: |
| 1997 // | 2001 // |
| 1998 // expression: term [ operator? term]* | 2002 // expression: term [ operator? term]* |
| 1999 // | 2003 // |
| 2000 // operator: '/' | ',' | 2004 // operator: '/' | ',' |
| 2001 // term: (see processTerm) | 2005 // term: (see processTerm) |
| 2002 // | 2006 // |
| 2003 processExpr([bool ieFilter = false]) { | 2007 Expressions processExpr([bool ieFilter = false]) { |
| 2004 int start = _peekToken.start; | 2008 var start = _peekToken.start; |
| 2005 Expressions expressions = new Expressions(_makeSpan(start)); | 2009 var expressions = new Expressions(_makeSpan(start)); |
| 2006 | 2010 |
| 2007 bool keepGoing = true; | 2011 var keepGoing = true; |
| 2008 var expr; | 2012 var expr; |
| 2009 while (keepGoing && (expr = processTerm(ieFilter)) != null) { | 2013 while (keepGoing && (expr = processTerm(ieFilter)) != null) { |
| 2010 var op; | 2014 var op; |
| 2011 | 2015 |
| 2012 int opStart = _peekToken.start; | 2016 var opStart = _peekToken.start; |
| 2013 | 2017 |
| 2014 switch (_peek()) { | 2018 switch (_peek()) { |
| 2015 case TokenKind.SLASH: | 2019 case TokenKind.SLASH: |
| 2016 op = new OperatorSlash(_makeSpan(opStart)); | 2020 op = new OperatorSlash(_makeSpan(opStart)); |
| 2017 break; | 2021 break; |
| 2018 case TokenKind.COMMA: | 2022 case TokenKind.COMMA: |
| 2019 op = new OperatorComma(_makeSpan(opStart)); | 2023 op = new OperatorComma(_makeSpan(opStart)); |
| 2020 break; | 2024 break; |
| 2021 case TokenKind.BACKSLASH: | 2025 case TokenKind.BACKSLASH: |
| 2022 // Backslash outside of string; detected IE8 or older signaled by \9 at | 2026 // Backslash outside of string; detected IE8 or older signaled by \9 at |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2054 keepGoing = false; | 2058 keepGoing = false; |
| 2055 } else { | 2059 } else { |
| 2056 _next(); | 2060 _next(); |
| 2057 } | 2061 } |
| 2058 } | 2062 } |
| 2059 } | 2063 } |
| 2060 | 2064 |
| 2061 return expressions; | 2065 return expressions; |
| 2062 } | 2066 } |
| 2063 | 2067 |
| 2064 static int MAX_UNICODE = int.parse('0x10FFFF'); | 2068 static final int MAX_UNICODE = int.parse('0x10FFFF'); |
| 2065 | 2069 |
| 2066 // Term grammar: | 2070 // Term grammar: |
| 2067 // | 2071 // |
| 2068 // term: | 2072 // term: |
| 2069 // unary_operator? | 2073 // unary_operator? |
| 2070 // [ term_value ] | 2074 // [ term_value ] |
| 2071 // | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor | 2075 // | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor |
| 2072 // | 2076 // |
| 2073 // term_value: | 2077 // term_value: |
| 2074 // NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | | 2078 // NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | |
| 2075 // TIME S* | FREQ S* | function | 2079 // TIME S* | FREQ S* | function |
| 2076 // | 2080 // |
| 2077 // NUMBER: {num} | 2081 // NUMBER: {num} |
| 2078 // PERCENTAGE: {num}% | 2082 // PERCENTAGE: {num}% |
| 2079 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] | 2083 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] |
| 2080 // EMS: {num}'em' | 2084 // EMS: {num}'em' |
| 2081 // EXS: {num}'ex' | 2085 // EXS: {num}'ex' |
| 2082 // ANGLE: {num}['deg' | 'rad' | 'grad'] | 2086 // ANGLE: {num}['deg' | 'rad' | 'grad'] |
| 2083 // TIME: {num}['ms' | 's'] | 2087 // TIME: {num}['ms' | 's'] |
| 2084 // FREQ: {num}['hz' | 'khz'] | 2088 // FREQ: {num}['hz' | 'khz'] |
| 2085 // function: IDENT '(' expr ')' | 2089 // function: IDENT '(' expr ')' |
| 2086 // | 2090 // |
| 2087 processTerm([bool ieFilter = false]) { | 2091 processTerm([bool ieFilter = false]) { |
| 2088 int start = _peekToken.start; | 2092 var start = _peekToken.start; |
| 2089 Token t; // token for term's value | 2093 Token t; // token for term's value |
| 2090 var value; // value of term (numeric values) | 2094 var value; // value of term (numeric values) |
| 2091 | 2095 |
| 2092 var unary = ""; | 2096 var unary = ""; |
| 2093 switch (_peek()) { | 2097 switch (_peek()) { |
| 2094 case TokenKind.HASH: | 2098 case TokenKind.HASH: |
| 2095 this._eat(TokenKind.HASH); | 2099 this._eat(TokenKind.HASH); |
| 2096 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { | 2100 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { |
| 2097 String hexText; | 2101 String hexText; |
| 2098 if (_peekKind(TokenKind.INTEGER)) { | 2102 if (_peekKind(TokenKind.INTEGER)) { |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2186 var propName = nameValue.name; | 2190 var propName = nameValue.name; |
| 2187 var errMsg = TokenKind.isPredefinedName(propName) ? | 2191 var errMsg = TokenKind.isPredefinedName(propName) ? |
| 2188 "Improper use of property value ${propName}" : | 2192 "Improper use of property value ${propName}" : |
| 2189 "Unknown property value ${propName}"; | 2193 "Unknown property value ${propName}"; |
| 2190 _warning(errMsg, _makeSpan(start)); | 2194 _warning(errMsg, _makeSpan(start)); |
| 2191 } | 2195 } |
| 2192 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); | 2196 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); |
| 2193 } | 2197 } |
| 2194 | 2198 |
| 2195 // Yes, process the color as an RGB value. | 2199 // Yes, process the color as an RGB value. |
| 2196 String rgbColor = TokenKind.decimalToHex( | 2200 var rgbColor = |
| 2197 TokenKind.colorValue(colorEntry), 6); | 2201 TokenKind.decimalToHex(TokenKind.colorValue(colorEntry), 6); |
| 2198 return _parseHex(rgbColor, _makeSpan(start)); | 2202 return _parseHex(rgbColor, _makeSpan(start)); |
| 2199 case TokenKind.UNICODE_RANGE: | 2203 case TokenKind.UNICODE_RANGE: |
| 2200 var first; | 2204 var first; |
| 2201 var second; | 2205 var second; |
| 2202 var firstNumber; | 2206 var firstNumber; |
| 2203 var secondNumber; | 2207 var secondNumber; |
| 2204 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true); | 2208 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true); |
| 2205 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) { | 2209 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) { |
| 2206 first = _previousToken.text; | 2210 first = _previousToken.text; |
| 2207 firstNumber = int.parse('0x$first'); | 2211 firstNumber = int.parse('0x$first'); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2241 expr.expressions[0] = varUsage; | 2245 expr.expressions[0] = varUsage; |
| 2242 return expr.expressions; | 2246 return expr.expressions; |
| 2243 } | 2247 } |
| 2244 break; | 2248 break; |
| 2245 } | 2249 } |
| 2246 | 2250 |
| 2247 return processDimension(t, value, _makeSpan(start)); | 2251 return processDimension(t, value, _makeSpan(start)); |
| 2248 } | 2252 } |
| 2249 | 2253 |
| 2250 /** Process all dimension units. */ | 2254 /** Process all dimension units. */ |
| 2251 processDimension(Token t, var value, Span span) { | 2255 LiteralTerm processDimension(Token t, var value, Span span) { |
| 2252 var term; | 2256 LiteralTerm term; |
| 2253 var unitType = this._peek(); | 2257 var unitType = this._peek(); |
| 2254 | 2258 |
| 2255 switch (unitType) { | 2259 switch (unitType) { |
| 2256 case TokenKind.UNIT_EM: | 2260 case TokenKind.UNIT_EM: |
| 2257 term = new EmTerm(value, t.text, span); | 2261 term = new EmTerm(value, t.text, span); |
| 2258 _next(); // Skip the unit | 2262 _next(); // Skip the unit |
| 2259 break; | 2263 break; |
| 2260 case TokenKind.UNIT_EX: | 2264 case TokenKind.UNIT_EX: |
| 2261 term = new ExTerm(value, t.text, span); | 2265 term = new ExTerm(value, t.text, span); |
| 2262 _next(); // Skip the unit | 2266 _next(); // Skip the unit |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2321 term = (value is Identifier) | 2325 term = (value is Identifier) |
| 2322 ? new LiteralTerm(value, value.name, span) | 2326 ? new LiteralTerm(value, value.name, span) |
| 2323 : new NumberTerm(value, t.text, span); | 2327 : new NumberTerm(value, t.text, span); |
| 2324 } | 2328 } |
| 2325 break; | 2329 break; |
| 2326 } | 2330 } |
| 2327 | 2331 |
| 2328 return term; | 2332 return term; |
| 2329 } | 2333 } |
| 2330 | 2334 |
| 2331 processQuotedString([bool urlString = false]) { | 2335 String processQuotedString([bool urlString = false]) { |
| 2332 int start = _peekToken.start; | 2336 var start = _peekToken.start; |
| 2333 | 2337 |
| 2334 // URI term sucks up everything inside of quotes(' or ") or between parens | 2338 // URI term sucks up everything inside of quotes(' or ") or between parens |
| 2335 int stopToken = urlString ? TokenKind.RPAREN : -1; | 2339 var stopToken = urlString ? TokenKind.RPAREN : -1; |
| 2336 switch (_peek()) { | 2340 switch (_peek()) { |
| 2337 case TokenKind.SINGLE_QUOTE: | 2341 case TokenKind.SINGLE_QUOTE: |
| 2338 stopToken = TokenKind.SINGLE_QUOTE; | 2342 stopToken = TokenKind.SINGLE_QUOTE; |
| 2339 start = _peekToken.start + 1; // Skip the quote might have whitespace. | 2343 start = _peekToken.start + 1; // Skip the quote might have whitespace. |
| 2340 _next(); // Skip the SINGLE_QUOTE. | 2344 _next(); // Skip the SINGLE_QUOTE. |
| 2341 break; | 2345 break; |
| 2342 case TokenKind.DOUBLE_QUOTE: | 2346 case TokenKind.DOUBLE_QUOTE: |
| 2343 stopToken = TokenKind.DOUBLE_QUOTE; | 2347 stopToken = TokenKind.DOUBLE_QUOTE; |
| 2344 start = _peekToken.start + 1; // Skip the quote might have whitespace. | 2348 start = _peekToken.start + 1; // Skip the quote might have whitespace. |
| 2345 _next(); // Skip the DOUBLE_QUOTE. | 2349 _next(); // Skip the DOUBLE_QUOTE. |
| 2346 break; | 2350 break; |
| 2347 default: | 2351 default: |
| 2348 if (urlString) { | 2352 if (urlString) { |
| 2349 if (_peek() == TokenKind.LPAREN) { | 2353 if (_peek() == TokenKind.LPAREN) { |
| 2350 _next(); // Skip the LPAREN. | 2354 _next(); // Skip the LPAREN. |
| 2351 start = _peekToken.start; | 2355 start = _peekToken.start; |
| 2352 } | 2356 } |
| 2353 stopToken = TokenKind.RPAREN; | 2357 stopToken = TokenKind.RPAREN; |
| 2354 } else { | 2358 } else { |
| 2355 _error('unexpected string', _makeSpan(start)); | 2359 _error('unexpected string', _makeSpan(start)); |
| 2356 } | 2360 } |
| 2357 break; | 2361 break; |
| 2358 } | 2362 } |
| 2359 | 2363 |
| 2360 // Gobble up everything until we hit our stop token. | 2364 // Gobble up everything until we hit our stop token. |
| 2361 int runningStart = _peekToken.start; | 2365 var runningStart = _peekToken.start; |
| 2362 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { | 2366 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { |
| 2363 var tok = _next(); | 2367 var tok = _next(); |
| 2364 } | 2368 } |
| 2365 | 2369 |
| 2366 // All characters between quotes is the string. | 2370 // All characters between quotes is the string. |
| 2367 int end = _peekToken.end; | 2371 var end = _peekToken.end; |
| 2368 var stringValue = (_peekToken.span as FileSpan).file.getText(start, | 2372 var stringValue = (_peekToken.span as FileSpan).file.getText(start, |
| 2369 end - 1); | 2373 end - 1); |
| 2370 | 2374 |
| 2371 if (stopToken != TokenKind.RPAREN) { | 2375 if (stopToken != TokenKind.RPAREN) { |
| 2372 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; | 2376 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; |
| 2373 } | 2377 } |
| 2374 | 2378 |
| 2375 return stringValue; | 2379 return stringValue; |
| 2376 } | 2380 } |
| 2377 | 2381 |
| 2378 // TODO(terry): Should probably understand IE's non-standard filter syntax to | 2382 // TODO(terry): Should probably understand IE's non-standard filter syntax to |
| 2379 // fully support calc, var(), etc. | 2383 // fully support calc, var(), etc. |
| 2380 /** | 2384 /** |
| 2381 * IE's filter property breaks CSS value parsing. IE's format can be: | 2385 * IE's filter property breaks CSS value parsing. IE's format can be: |
| 2382 * | 2386 * |
| 2383 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); | 2387 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); |
| 2384 * | 2388 * |
| 2385 * We'll just parse everything after the 'progid:' look for the left paren | 2389 * We'll just parse everything after the 'progid:' look for the left paren |
| 2386 * then parse to the right paren ignoring everything in between. | 2390 * then parse to the right paren ignoring everything in between. |
| 2387 */ | 2391 */ |
| 2388 processIEFilter(int startAfterProgidColon) { | 2392 processIEFilter(int startAfterProgidColon) { |
| 2389 int parens = 0; | 2393 var parens = 0; |
| 2390 | 2394 |
| 2391 while (_peek() != TokenKind.END_OF_FILE) { | 2395 while (_peek() != TokenKind.END_OF_FILE) { |
| 2392 switch (_peek()) { | 2396 switch (_peek()) { |
| 2393 case TokenKind.LPAREN: | 2397 case TokenKind.LPAREN: |
| 2394 _eat(TokenKind.LPAREN); | 2398 _eat(TokenKind.LPAREN); |
| 2395 parens++; | 2399 parens++; |
| 2396 break; | 2400 break; |
| 2397 case TokenKind.RPAREN: | 2401 case TokenKind.RPAREN: |
| 2398 _eat(TokenKind.RPAREN); | 2402 _eat(TokenKind.RPAREN); |
| 2399 if (--parens == 0) { | 2403 if (--parens == 0) { |
| 2400 var tok = tokenizer.makeIEFilter(startAfterProgidColon, | 2404 var tok = tokenizer.makeIEFilter(startAfterProgidColon, |
| 2401 _peekToken.start); | 2405 _peekToken.start); |
| 2402 return new LiteralTerm(tok.text, tok.text, tok.span); | 2406 return new LiteralTerm(tok.text, tok.text, tok.span); |
| 2403 } | 2407 } |
| 2404 break; | 2408 break; |
| 2405 default: | 2409 default: |
| 2406 _eat(_peek()); | 2410 _eat(_peek()); |
| 2407 } | 2411 } |
| 2408 } | 2412 } |
| 2409 } | 2413 } |
| 2410 | 2414 |
| 2411 // Function grammar: | 2415 // Function grammar: |
| 2412 // | 2416 // |
| 2413 // function: IDENT '(' expr ')' | 2417 // function: IDENT '(' expr ')' |
| 2414 // | 2418 // |
| 2415 processFunction(Identifier func) { | 2419 processFunction(Identifier func) { |
| 2416 int start = _peekToken.start; | 2420 var start = _peekToken.start; |
| 2417 | 2421 |
| 2418 String name = func.name; | 2422 var name = func.name; |
| 2419 | 2423 |
| 2420 switch (name) { | 2424 switch (name) { |
| 2421 case 'url': | 2425 case 'url': |
| 2422 // URI term sucks up everything inside of quotes(' or ") or between parens | 2426 // URI term sucks up everything inside of quotes(' or ") or between parens |
| 2423 String urlParam = processQuotedString(true); | 2427 var urlParam = processQuotedString(true); |
| 2424 | 2428 |
| 2425 // TODO(terry): Better error messge and checking for mismatched quotes. | 2429 // TODO(terry): Better error messge and checking for mismatched quotes. |
| 2426 if (_peek() == TokenKind.END_OF_FILE) { | 2430 if (_peek() == TokenKind.END_OF_FILE) { |
| 2427 _error("problem parsing URI", _peekToken.span); | 2431 _error("problem parsing URI", _peekToken.span); |
| 2428 } | 2432 } |
| 2429 | 2433 |
| 2430 if (_peek() == TokenKind.RPAREN) { | 2434 if (_peek() == TokenKind.RPAREN) { |
| 2431 _next(); | 2435 _next(); |
| 2432 } | 2436 } |
| 2433 | 2437 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 2463 if (!_maybeEat(TokenKind.RPAREN)) { | 2467 if (!_maybeEat(TokenKind.RPAREN)) { |
| 2464 _error("problem parsing function expected ), ", _peekToken.span); | 2468 _error("problem parsing function expected ), ", _peekToken.span); |
| 2465 } | 2469 } |
| 2466 | 2470 |
| 2467 return new FunctionTerm(name, name, expr, _makeSpan(start)); | 2471 return new FunctionTerm(name, name, expr, _makeSpan(start)); |
| 2468 } | 2472 } |
| 2469 | 2473 |
| 2470 return null; | 2474 return null; |
| 2471 } | 2475 } |
| 2472 | 2476 |
| 2473 identifier() { | 2477 Identifier identifier() { |
| 2474 var tok = _next(); | 2478 var tok = _next(); |
| 2475 | 2479 |
| 2476 if (!TokenKind.isIdentifier(tok.kind) && | 2480 if (!TokenKind.isIdentifier(tok.kind) && |
| 2477 !TokenKind.isKindIdentifier(tok.kind)) { | 2481 !TokenKind.isKindIdentifier(tok.kind)) { |
| 2478 if (isChecked) { | 2482 if (isChecked) { |
| 2479 _warning('expected identifier, but found $tok', tok.span); | 2483 _warning('expected identifier, but found $tok', tok.span); |
| 2480 } | 2484 } |
| 2481 return new Identifier("", _makeSpan(tok.start)); | 2485 return new Identifier("", _makeSpan(tok.start)); |
| 2482 } | 2486 } |
| 2483 | 2487 |
| 2484 return new Identifier(tok.text, _makeSpan(tok.start)); | 2488 return new Identifier(tok.text, _makeSpan(tok.start)); |
| 2485 } | 2489 } |
| 2486 | 2490 |
| 2487 // TODO(terry): Move this to base <= 36 and into shared code. | 2491 // TODO(terry): Move this to base <= 36 and into shared code. |
| 2488 static int _hexDigit(int c) { | 2492 static int _hexDigit(int c) { |
| 2489 if(c >= 48/*0*/ && c <= 57/*9*/) { | 2493 if (c >= 48/*0*/ && c <= 57/*9*/) { |
| 2490 return c - 48; | 2494 return c - 48; |
| 2491 } else if (c >= 97/*a*/ && c <= 102/*f*/) { | 2495 } else if (c >= 97/*a*/ && c <= 102/*f*/) { |
| 2492 return c - 87; | 2496 return c - 87; |
| 2493 } else if (c >= 65/*A*/ && c <= 70/*F*/) { | 2497 } else if (c >= 65/*A*/ && c <= 70/*F*/) { |
| 2494 return c - 55; | 2498 return c - 55; |
| 2495 } else { | 2499 } else { |
| 2496 return -1; | 2500 return -1; |
| 2497 } | 2501 } |
| 2498 } | 2502 } |
| 2499 | 2503 |
| 2500 HexColorTerm _parseHex(String hexText, Span span) { | 2504 HexColorTerm _parseHex(String hexText, Span span) { |
| 2501 int hexValue = 0; | 2505 var hexValue = 0; |
| 2502 | 2506 |
| 2503 for (int i = 0; i < hexText.length; i++) { | 2507 for (var i = 0; i < hexText.length; i++) { |
| 2504 var digit = _hexDigit(hexText.codeUnitAt(i)); | 2508 var digit = _hexDigit(hexText.codeUnitAt(i)); |
| 2505 if (digit < 0) { | 2509 if (digit < 0) { |
| 2506 _warning('Bad hex number', span); | 2510 _warning('Bad hex number', span); |
| 2507 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span); | 2511 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span); |
| 2508 } | 2512 } |
| 2509 hexValue = (hexValue << 4) + digit; | 2513 hexValue = (hexValue << 4) + digit; |
| 2510 } | 2514 } |
| 2511 | 2515 |
| 2512 // Make 3 character hex value #RRGGBB => #RGB iff: | 2516 // Make 3 character hex value #RRGGBB => #RGB iff: |
| 2513 // high/low nibble of RR is the same, high/low nibble of GG is the same and | 2517 // high/low nibble of RR is the same, high/low nibble of GG is the same and |
| (...skipping 14 matching lines...) Expand all Loading... |
| 2528 } | 2532 } |
| 2529 } | 2533 } |
| 2530 | 2534 |
| 2531 class ExpressionsProcessor { | 2535 class ExpressionsProcessor { |
| 2532 final Expressions _exprs; | 2536 final Expressions _exprs; |
| 2533 int _index = 0; | 2537 int _index = 0; |
| 2534 | 2538 |
| 2535 ExpressionsProcessor(this._exprs); | 2539 ExpressionsProcessor(this._exprs); |
| 2536 | 2540 |
| 2537 // TODO(terry): Only handles ##px unit. | 2541 // TODO(terry): Only handles ##px unit. |
| 2538 processFontSize() { | 2542 FontExpression processFontSize() { |
| 2539 /* font-size[/line-height] | 2543 /* font-size[/line-height] |
| 2540 * | 2544 * |
| 2541 * Possible size values: | 2545 * Possible size values: |
| 2542 * xx-small | 2546 * xx-small |
| 2543 * small | 2547 * small |
| 2544 * medium [default] | 2548 * medium [default] |
| 2545 * large | 2549 * large |
| 2546 * x-large | 2550 * x-large |
| 2547 * xx-large | 2551 * xx-large |
| 2548 * smaller | 2552 * smaller |
| 2549 * larger | 2553 * larger |
| 2550 * ##length in px, pt, etc. | 2554 * ##length in px, pt, etc. |
| 2551 * ##%, percent of parent elem's font-size | 2555 * ##%, percent of parent elem's font-size |
| 2552 * inherit | 2556 * inherit |
| 2553 */ | 2557 */ |
| 2554 LengthTerm size; | 2558 LengthTerm size; |
| 2555 LineHeight lineHt; | 2559 LineHeight lineHt; |
| 2556 bool nextIsLineHeight = false; | 2560 var nextIsLineHeight = false; |
| 2557 for (; _index < _exprs.expressions.length; _index++) { | 2561 for (; _index < _exprs.expressions.length; _index++) { |
| 2558 var expr = _exprs.expressions[_index]; | 2562 var expr = _exprs.expressions[_index]; |
| 2559 if (size == null && expr is LengthTerm) { | 2563 if (size == null && expr is LengthTerm) { |
| 2560 // font-size part. | 2564 // font-size part. |
| 2561 size = expr; | 2565 size = expr; |
| 2562 } else if (size != null) { | 2566 } else if (size != null) { |
| 2563 if (expr is OperatorSlash) { | 2567 if (expr is OperatorSlash) { |
| 2564 // LineHeight could follow? | 2568 // LineHeight could follow? |
| 2565 nextIsLineHeight = true; | 2569 nextIsLineHeight = true; |
| 2566 } else if (nextIsLineHeight && expr is LengthTerm) { | 2570 } else if (nextIsLineHeight && expr is LengthTerm) { |
| 2567 assert(expr.unit == TokenKind.UNIT_LENGTH_PX); | 2571 assert(expr.unit == TokenKind.UNIT_LENGTH_PX); |
| 2568 lineHt = new LineHeight(expr.value, inPixels: true); | 2572 lineHt = new LineHeight(expr.value, inPixels: true); |
| 2569 nextIsLineHeight = false; | 2573 nextIsLineHeight = false; |
| 2570 _index++; | 2574 _index++; |
| 2571 break; | 2575 break; |
| 2572 } else { | 2576 } else { |
| 2573 break; | 2577 break; |
| 2574 } | 2578 } |
| 2575 } else { | 2579 } else { |
| 2576 break; | 2580 break; |
| 2577 } | 2581 } |
| 2578 } | 2582 } |
| 2579 | 2583 |
| 2580 return new FontExpression(_exprs.span, size: size, lineHeight: lineHt); | 2584 return new FontExpression(_exprs.span, size: size, lineHeight: lineHt); |
| 2581 } | 2585 } |
| 2582 | 2586 |
| 2583 processFontFamily() { | 2587 FontExpression processFontFamily() { |
| 2584 final List<String> family = <String>[]; | 2588 var family = <String>[]; |
| 2585 | 2589 |
| 2586 /* Possible family values: | 2590 /* Possible family values: |
| 2587 * font-family: arial, Times new roman ,Lucida Sans Unicode,Courier; | 2591 * font-family: arial, Times new roman ,Lucida Sans Unicode,Courier; |
| 2588 * font-family: "Times New Roman", arial, Lucida Sans Unicode, Courier; | 2592 * font-family: "Times New Roman", arial, Lucida Sans Unicode, Courier; |
| 2589 */ | 2593 */ |
| 2590 bool moreFamilies = false; | 2594 var moreFamilies = false; |
| 2591 | 2595 |
| 2592 for (; _index < _exprs.expressions.length; _index++) { | 2596 for (; _index < _exprs.expressions.length; _index++) { |
| 2593 Expression expr = _exprs.expressions[_index]; | 2597 Expression expr = _exprs.expressions[_index]; |
| 2594 if (expr is LiteralTerm) { | 2598 if (expr is LiteralTerm) { |
| 2595 if (family.length == 0 || moreFamilies) { | 2599 if (family.length == 0 || moreFamilies) { |
| 2596 // It's font-family now. | 2600 // It's font-family now. |
| 2597 family.add(expr.toString()); | 2601 family.add(expr.toString()); |
| 2598 moreFamilies = false; | 2602 moreFamilies = false; |
| 2599 } else if (isChecked) { | 2603 } else if (isChecked) { |
| 2600 messages.warning('Only font-family can be a list', _exprs.span); | 2604 messages.warning('Only font-family can be a list', _exprs.span); |
| 2601 } | 2605 } |
| 2602 } else if (expr is OperatorComma && family.length > 0) { | 2606 } else if (expr is OperatorComma && family.length > 0) { |
| 2603 moreFamilies = true; | 2607 moreFamilies = true; |
| 2604 } else { | 2608 } else { |
| 2605 break; | 2609 break; |
| 2606 } | 2610 } |
| 2607 } | 2611 } |
| 2608 | 2612 |
| 2609 return new FontExpression(_exprs.span, family: family); | 2613 return new FontExpression(_exprs.span, family: family); |
| 2610 } | 2614 } |
| 2611 | 2615 |
| 2612 processFont() { | 2616 FontExpression processFont() { |
| 2613 var family; | 2617 List<String> family; |
| 2614 | 2618 |
| 2615 // Process all parts of the font expression. | 2619 // Process all parts of the font expression. |
| 2616 FontExpression fontSize; | 2620 FontExpression fontSize; |
| 2617 FontExpression fontFamily; | 2621 FontExpression fontFamily; |
| 2618 for (; _index < _exprs.expressions.length; _index++) { | 2622 for (; _index < _exprs.expressions.length; _index++) { |
| 2619 var expr = _exprs.expressions[_index]; | 2623 var expr = _exprs.expressions[_index]; |
| 2620 // Order is font-size font-family | 2624 // Order is font-size font-family |
| 2621 if (fontSize == null) { | 2625 if (fontSize == null) { |
| 2622 fontSize = processFontSize(); | 2626 fontSize = processFontSize(); |
| 2623 } | 2627 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 2638 } | 2642 } |
| 2639 | 2643 |
| 2640 /** | 2644 /** |
| 2641 * Escapes [text] for use in a CSS string. | 2645 * Escapes [text] for use in a CSS string. |
| 2642 * [single] specifies single quote `'` vs double quote `"`. | 2646 * [single] specifies single quote `'` vs double quote `"`. |
| 2643 */ | 2647 */ |
| 2644 String _escapeString(String text, {bool single: false}) { | 2648 String _escapeString(String text, {bool single: false}) { |
| 2645 StringBuffer result = null; | 2649 StringBuffer result = null; |
| 2646 | 2650 |
| 2647 for (int i = 0; i < text.length; i++) { | 2651 for (int i = 0; i < text.length; i++) { |
| 2648 int code = text.codeUnitAt(i); | 2652 var code = text.codeUnitAt(i); |
| 2649 var replace = null; | 2653 String replace = null; |
| 2650 switch (code) { | 2654 switch (code) { |
| 2651 case 34/*'"'*/: if (!single) replace = r'\"'; break; | 2655 case 34/*'"'*/: if (!single) replace = r'\"'; break; |
| 2652 case 39/*"'"*/: if (single) replace = r"\'"; break; | 2656 case 39/*"'"*/: if (single) replace = r"\'"; break; |
| 2653 } | 2657 } |
| 2654 | 2658 |
| 2655 if (replace != null && result == null) { | 2659 if (replace != null && result == null) { |
| 2656 result = new StringBuffer(text.substring(0, i)); | 2660 result = new StringBuffer(text.substring(0, i)); |
| 2657 } | 2661 } |
| 2658 | 2662 |
| 2659 if (result != null) result.write(replace != null ? replace : text[i]); | 2663 if (result != null) result.write(replace != null ? replace : text[i]); |
| 2660 } | 2664 } |
| 2661 | 2665 |
| 2662 return result == null ? text : result.toString(); | 2666 return result == null ? text : result.toString(); |
| 2663 } | 2667 } |
| OLD | NEW |