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

Side by Side Diff: utils/css/parser.dart

Issue 9695048: Template parser (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Siggi's comments Created 8 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 | Annotate | Revision Log
« no previous file with comments | « utils/css/generate.dart ('k') | utils/css/source.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, 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 3
4 /** 4 /**
5 * A simple recursive descent parser for CSS. 5 * A simple recursive descent parser for CSS.
6 */ 6 */
7 class Parser { 7 class Parser {
8 Tokenizer tokenizer; 8 Tokenizer tokenizer;
9 9
10 var _fs; // If non-null filesystem to read files. 10 var _fs; // If non-null filesystem to read files.
11 String _basePath; // Base path of CSS file. 11 String _basePath; // Base path of CSS file.
12 12
13 final lang.SourceFile source; 13 final SourceFile source;
14 14
15 lang.Token _previousToken; 15 Token _previousToken;
16 lang.Token _peekToken; 16 Token _peekToken;
17 17
18 // Communicating errors back to template parser.
19 // TODO(terry): Need a better mechanism (e.g., common World).
20 var _erroMsgRedirector;
21
18 Parser(this.source, [int start = 0, this._fs = null, this._basePath = null]) { 22 Parser(this.source, [int start = 0, this._fs = null, this._basePath = null]) {
19 tokenizer = new Tokenizer(source, true, start); 23 tokenizer = new Tokenizer(source, true, start);
20 _peekToken = tokenizer.next(); 24 _peekToken = tokenizer.next();
21 _previousToken = null; 25 _previousToken = null;
22 } 26 }
23 27
24 // Main entry point for parsing an entire CSS file. 28 // Main entry point for parsing an entire CSS file.
25 Stylesheet parse() { 29 // If nestedCSS is true when we're back at processing directives from top and
26 List<lang.Node> productions = []; 30 // we encounter a } then stop we're inside of a template e.g.,
31 //
32 // template ... {
33 // css {
34 // .item {
35 // left: 10px;
36 // }
37 // }
38 // <div>...</div>
39 // }
40 //
41 Stylesheet parse([bool nestedCSS = false, var erroMsgRedirector = null]) {
42 // TODO(terry): Hack for migrating CSS errors back to template errors.
43 _erroMsgRedirector = erroMsgRedirector;
44
45 List<ASTNode> productions = [];
27 46
28 int start = _peekToken.start; 47 int start = _peekToken.start;
29 while (!_maybeEat(TokenKind.END_OF_FILE)) { 48 while (!_maybeEat(TokenKind.END_OF_FILE) &&
49 (!nestedCSS && !_peekKind(TokenKind.RBRACE))) {
30 // TODO(terry): Need to handle charset, import, media and page. 50 // TODO(terry): Need to handle charset, import, media and page.
31 var directive = processDirective(); 51 var directive = processDirective();
32 if (directive != null) { 52 if (directive != null) {
33 productions.add(directive); 53 productions.add(directive);
34 } else { 54 } else {
35 productions.add(processRuleSet()); 55 RuleSet ruleset = processRuleSet();
56 if (ruleset != null) {
57 productions.add(ruleset);
58 } else {
59 break;
60 }
36 } 61 }
37 } 62 }
38 63
39 return new Stylesheet(productions, _makeSpan(start)); 64 return new Stylesheet(productions, _makeSpan(start));
40 } 65 }
41 66
42 /** Generate an error if [source] has not been completely consumed. */ 67 /** Generate an error if [source] has not been completely consumed. */
43 void checkEndOfFile() { 68 void checkEndOfFile() {
44 _eat(TokenKind.END_OF_FILE); 69 _eat(TokenKind.END_OF_FILE);
45 } 70 }
(...skipping 11 matching lines...) Expand all
57 } 82 }
58 } 83 }
59 84
60 /////////////////////////////////////////////////////////////////// 85 ///////////////////////////////////////////////////////////////////
61 // Basic support methods 86 // Basic support methods
62 /////////////////////////////////////////////////////////////////// 87 ///////////////////////////////////////////////////////////////////
63 int _peek() { 88 int _peek() {
64 return _peekToken.kind; 89 return _peekToken.kind;
65 } 90 }
66 91
67 lang.Token _next() { 92 Token _next() {
68 _previousToken = _peekToken; 93 _previousToken = _peekToken;
69 _peekToken = tokenizer.next(); 94 _peekToken = tokenizer.next();
70 return _previousToken; 95 return _previousToken;
71 } 96 }
72 97
73 bool _peekKind(int kind) { 98 bool _peekKind(int kind) {
74 return _peekToken.kind == kind; 99 return _peekToken.kind == kind;
75 } 100 }
76 101
77 /* Is the next token a legal identifier? This includes pseudo-keywords. */ 102 /* Is the next token a legal identifier? This includes pseudo-keywords. */
(...skipping 25 matching lines...) Expand all
103 var tok = _next(); 128 var tok = _next();
104 var message; 129 var message;
105 try { 130 try {
106 message = 'expected $expected, but found $tok'; 131 message = 'expected $expected, but found $tok';
107 } catch (final e) { 132 } catch (final e) {
108 message = 'parsing error expected $expected'; 133 message = 'parsing error expected $expected';
109 } 134 }
110 _error(message, tok.span); 135 _error(message, tok.span);
111 } 136 }
112 137
113 void _error(String message, [lang.SourceSpan location=null]) { 138 void _error(String message, [SourceSpan location=null]) {
114 if (location === null) { 139 if (location === null) {
115 location = _peekToken.span; 140 location = _peekToken.span;
116 } 141 }
117 142
118 lang.world.fatal(message, location); // syntax errors are fatal for now 143 if (_erroMsgRedirector == null) {
144 world.fatal(message, location); // syntax errors are fatal for now
145 } else {
146 String text = "";
147 if (location != null) {
148 text = location.toMessageString("");
149 }
150 _erroMsgRedirector.displayError("CSS error: \r${text}\r${message}");
151 }
119 } 152 }
120 153
121 void _warning(String message, [lang.SourceSpan location=null]) { 154 void _warning(String message, [SourceSpan location=null]) {
122 if (location === null) { 155 if (location === null) {
123 location = _peekToken.span; 156 location = _peekToken.span;
124 } 157 }
125 158
126 lang.world.warning(message, location); 159 world.warning(message, location);
127 } 160 }
128 161
129 lang.SourceSpan _makeSpan(int start) { 162 SourceSpan _makeSpan(int start) {
130 return new lang.SourceSpan(source, start, _previousToken.end); 163 return new SourceSpan(source, start, _previousToken.end);
131 } 164 }
132 165
133 /////////////////////////////////////////////////////////////////// 166 ///////////////////////////////////////////////////////////////////
134 // Top level productions 167 // Top level productions
135 /////////////////////////////////////////////////////////////////// 168 ///////////////////////////////////////////////////////////////////
136 169
137 // Templates are @{selectors} single line nothing else. 170 // Templates are @{selectors} single line nothing else.
138 SelectorGroup parseTemplate() { 171 SelectorGroup parseTemplate() {
139 SelectorGroup selectorGroup = null; 172 SelectorGroup selectorGroup = null;
140 if (!isPrematureEndOfFile()) { 173 if (!isPrematureEndOfFile()) {
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 if (_fs.fileExists('${_basePath}${filename}')) { 339 if (_fs.fileExists('${_basePath}${filename}')) {
307 String basePath = ""; 340 String basePath = "";
308 int idx = filename.lastIndexOf('/'); 341 int idx = filename.lastIndexOf('/');
309 if (idx >= 0) { 342 if (idx >= 0) {
310 basePath = filename.substring(0, idx + 1); 343 basePath = filename.substring(0, idx + 1);
311 } 344 }
312 basePath = '${_basePath}${basePath}'; 345 basePath = '${_basePath}${basePath}';
313 // Yes, let's parse this file as well. 346 // Yes, let's parse this file as well.
314 String fullFN = '${basePath}${filename}'; 347 String fullFN = '${basePath}${filename}';
315 String contents = _fs.readAll(fullFN); 348 String contents = _fs.readAll(fullFN);
316 Parser parser = new Parser(new lang.SourceFile(fullFN, contents), 0, 349 Parser parser = new Parser(new SourceFile(fullFN, contents), 0,
317 _fs, basePath); 350 _fs, basePath);
318 Stylesheet stylesheet = parser.parse(); 351 Stylesheet stylesheet = parser.parse();
319 return new IncludeDirective(filename, stylesheet, _makeSpan(start)); 352 return new IncludeDirective(filename, stylesheet, _makeSpan(start));
320 } 353 }
321 354
322 _error('file doesn\'t exist ${filename}', _peekToken.span); 355 _error('file doesn\'t exist ${filename}', _peekToken.span);
323 } 356 }
324 357
325 print("WARNING: @include doesn't work for uitest"); 358 print("WARNING: @include doesn't work for uitest");
326 return new IncludeDirective(filename, null, _makeSpan(start)); 359 return new IncludeDirective(filename, null, _makeSpan(start));
327 case TokenKind.DIRECTIVE_STYLET: 360 case TokenKind.DIRECTIVE_STYLET:
328 /* Stylet grammar: 361 /* Stylet grammar:
329 * 362 *
330 * @stylet IDENT '{' 363 * @stylet IDENT '{'
331 * ruleset 364 * ruleset
332 * '}' 365 * '}'
333 */ 366 */
334 _next(); 367 _next();
335 368
336 var name; 369 var name;
337 if (_peekIdentifier()) { 370 if (_peekIdentifier()) {
338 name = identifier(); 371 name = identifier();
339 } 372 }
340 373
341 _eat(TokenKind.LBRACE); 374 _eat(TokenKind.LBRACE);
342 375
343 List<lang.Node> productions = []; 376 List<ASTNode> productions = [];
344 377
345 int start = _peekToken.start; 378 start = _peekToken.start;
346 while (!_maybeEat(TokenKind.END_OF_FILE)) { 379 while (!_maybeEat(TokenKind.END_OF_FILE)) {
347 RuleSet ruleset = processRuleSet(); 380 RuleSet ruleset = processRuleSet();
348 if (ruleset == null) { 381 if (ruleset == null) {
349 break; 382 break;
350 } 383 }
351 productions.add(ruleset); 384 productions.add(ruleset);
352 } 385 }
353 386
354 _eat(TokenKind.RBRACE); 387 _eat(TokenKind.RBRACE);
355 388
356 return new StyletDirective(name, productions, _makeSpan(start)); 389 return new StyletDirective(name, productions, _makeSpan(start));
357 default: 390 default:
358 _error('unknown directive, found $_peekToken', _peekToken.span); 391 _error('unknown directive, found $_peekToken', _peekToken.span);
359 } 392 }
360 } 393 }
361 } 394 }
362 395
363 processRuleSet() { 396 RuleSet processRuleSet() {
364 int start = _peekToken.start; 397 int start = _peekToken.start;
365 398
366 SelectorGroup selGroup = processSelectorGroup(); 399 SelectorGroup selGroup = processSelectorGroup();
367 if (selGroup != null) { 400 if (selGroup != null) {
368 return new RuleSet(selGroup, processDeclarations(), _makeSpan(start)); 401 return new RuleSet(selGroup, processDeclarations(), _makeSpan(start));
369 } 402 }
370 } 403 }
371 404
372 DeclarationGroup processDeclarations() { 405 DeclarationGroup processDeclarations() {
373 int start = _peekToken.start; 406 int start = _peekToken.start;
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after
691 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] 724 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc']
692 // EMS: {num}'em' 725 // EMS: {num}'em'
693 // EXS: {num}'ex' 726 // EXS: {num}'ex'
694 // ANGLE: {num}['deg' | 'rad' | 'grad'] 727 // ANGLE: {num}['deg' | 'rad' | 'grad']
695 // TIME: {num}['ms' | 's'] 728 // TIME: {num}['ms' | 's']
696 // FREQ: {num}['hz' | 'khz'] 729 // FREQ: {num}['hz' | 'khz']
697 // function: IDENT '(' expr ')' 730 // function: IDENT '(' expr ')'
698 // 731 //
699 processTerm() { 732 processTerm() {
700 int start = _peekToken.start; 733 int start = _peekToken.start;
701 lang.Token t; // token for term's value 734 Token t; // token for term's value
702 var value; // value of term (numeric values) 735 var value; // value of term (numeric values)
703 736
704 var unary = ""; 737 var unary = "";
705 738
706 switch (_peek()) { 739 switch (_peek()) {
707 case TokenKind.HASH: 740 case TokenKind.HASH:
708 this._eat(TokenKind.HASH); 741 this._eat(TokenKind.HASH);
709 String hexText; 742 String hexText;
710 if (_peekKind(TokenKind.INTEGER)) { 743 if (_peekKind(TokenKind.INTEGER)) {
711 String hexText1 = _peekToken.text; 744 String hexText1 = _peekToken.text;
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
771 // FUNCTION 804 // FUNCTION
772 return processFunction(nameValue); 805 return processFunction(nameValue);
773 } else { 806 } else {
774 // TODO(terry): Need to have a list of known identifiers today only 807 // TODO(terry): Need to have a list of known identifiers today only
775 // 'from' is special. 808 // 'from' is special.
776 if (nameValue.name == 'from') { 809 if (nameValue.name == 'from') {
777 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); 810 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
778 } 811 }
779 812
780 // What kind of identifier is it? 813 // What kind of identifier is it?
781 int value;
782 try { 814 try {
783 // Named color? 815 // Named color?
784 value = TokenKind.matchColorName(nameValue.name); 816 int colorValue = TokenKind.matchColorName(nameValue.name);
785 817
786 // Yes, process the color as an RGB value. 818 // Yes, process the color as an RGB value.
787 String rgbColor = TokenKind.decimalToHex(value); 819 String rgbColor = TokenKind.decimalToHex(colorValue);
788 int value;
789 try { 820 try {
790 value = parseHex(rgbColor); 821 colorValue = parseHex(rgbColor);
791 } catch (HexNumberException hne) { 822 } catch (HexNumberException hne) {
792 _error('Bad hex number', _makeSpan(start)); 823 _error('Bad hex number', _makeSpan(start));
793 } 824 }
794 return new HexColorTerm(value, rgbColor, _makeSpan(start)); 825 return new HexColorTerm(colorValue, rgbColor, _makeSpan(start));
795 } catch (final error) { 826 } catch (final error) {
796 if (error is NoColorMatchException) { 827 if (error is NoColorMatchException) {
797 // TODO(terry): Other named things to match with validator? 828 // TODO(terry): Other named things to match with validator?
798 _warning('Unknown property value ${error.name}', _makeSpan(start)); 829
830 // TODO(terry): Disable call to _warning need one World class for
831 // both CSS parser and other parser (e.g., template)
832 // so all warnings, errors, options, etc. are driven
833 // from the one World.
834 // _warning('Unknown property value ${error.name}', _makeSpan(start));
799 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); 835 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start));
800 } 836 }
801 } 837 }
802 } 838 }
803 } 839 }
804 840
805 var term; 841 var term;
806 var unitType = this._peek(); 842 var unitType = this._peek();
807 843
808 switch (unitType) { 844 switch (unitType) {
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
971 1007
972 return result; 1008 return result;
973 } 1009 }
974 } 1010 }
975 1011
976 /** Not a hex number. */ 1012 /** Not a hex number. */
977 class HexNumberException implements Exception { 1013 class HexNumberException implements Exception {
978 HexNumberException(); 1014 HexNumberException();
979 } 1015 }
980 1016
OLDNEW
« no previous file with comments | « utils/css/generate.dart ('k') | utils/css/source.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698