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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js/builder.dart

Issue 246633006: Revert "JS templates" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 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 | « no previous file | sdk/lib/_internal/compiler/implementation/js/js.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) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 // Utilities for building JS ASTs at runtime. Contains a builder class 5 // Utilities for building JS ASTs at runtime. Contains a builder class
6 // and a parser that parses part of the language. 6 // and a parser that parses part of the language.
7 7
8 part of js; 8 part of js;
9 9
10
11 /**
12 * Global template manager. We should aim to have a fixed number of
13 * templates. This implies that we do not use js('xxx') to parse text that is
14 * constructed from values that depend on names in the Dart program.
15 *
16 * TODO(sra): Find the remaining places where js('xxx') used to parse an
17 * unbounded number of expression, or institute a cache policy.
18 */
19 TemplateManager templateManager = new TemplateManager();
20
21
22 /**
23
24 [js] is a singleton instace of JsBuilder. JsBuilder is a set of conveniences
25 for constructing JavaScript ASTs.
26
27 [string] and [number] are used to create leaf AST nodes:
28
29 var s = js.string('hello'); // s = new LiteralString('"hello"')
30 var n = js.number(123); // n = new LiteralNumber(123)
31
32 In the line above `a --> b` means Dart expression `a` evaluates to a JavaScript
33 AST that would pretty-print as `b`.
34
35 The [call] method constructs an Expression AST.
36
37 No argument
38
39 js('window.alert("hello")') --> window.alert("hello")
40
41 The input text can contain placeholders `#` that are replaced with provided
42 arguments. A single argument can be passed directly:
43
44 js('window.alert(#)', s) --> window.alert("hello")
45
46 Multiple arguments are passed as a list:
47
48 js('# + #', [s, s]) --> "hello" + "hello"
49
50 The [statement] method constructs a Statement AST, but is otherwise like the
51 [call] method. This constructs a Return AST:
52
53 var ret = js.statement('return #;', n); --> return 123;
54
55 A placeholder in a Statement context must be followed by a semicolon ';'. You
56 can think of a statement placeholder as being `#;` to explain why the output
57 still has one semicolon:
58
59 js.statement('if (happy) #;', ret)
60 -->
61 if (happy)
62 return 123;
63
64 If the placeholder is not followed by a semicolon, it is part of an expression.
65 Here the paceholder is in the position of the function in a function call:
66
67 var vFoo = new VariableUse('foo');
68 js.statement('if (happy) #("Happy!")', vFoo)
69 -->
70 if (happy)
71 foo("Happy!");
72
73 Generally, a placeholder in an expression position requires an Expression AST as
74 an argument and a placeholder in a statement position requires a Statement AST.
75 An expression will be converted to a Statement if needed by creating an
76 ExpessionStatement. A String argument will be converted into a VariableUse and
77 requires that the string is a JavaScript identifier.
78
79 js('# + 1', vFoo) --> foo + 1
80 js('# + 1', 'foo') --> foo + 1
81 js('# + 1', 'foo.bar') --> assertion failure
82
83 Some placeholder positions are _splicing contexts_. A function argument list is
84 a splicing expression context. A placeholder in a splicing expression context
85 can take a single Expression (or String, converted to VariableUse) or an
86 Iterable of Expressions (and/or Strings).
87
88 // non-splicing argument:
89 js('#(#)', ['say', s]) --> say("hello")
90 // splicing arguments:
91 js('#(#)', ['say', []]) --> say()
92 js('#(#)', ['say', [s]]) --> say("hello")
93 js('#(#)', ['say', [s, n]]) --> say("hello", 123)
94
95 A splicing context can be used to append 'lists' and add extra elements:
96
97 js('foo(#, #, 1)', [ ['a', n], s]) --> foo(a, 123, "hello", 1)
98 js('foo(#, #, 1)', [ ['a', n], [s, n]]) --> foo(a, 123, "hello", 123, 1)
99 js('foo(#, #, 1)', [ [], [s, n]]) --> foo("hello", 123, 1)
100 js('foo(#, #, 1)', [ [], [] ]) --> foo(1)
101
102 The generation of a compile-time optional argument expression can be chosen by
103 providing an empty or singleton list.
104
105 In addition to Expressions and Statements, there are Parameters, which occur
106 only in the parameter list of a function expression or declaration.
107 Placeholders in parameter positions behave like placeholders in Expression
108 positions, except only Parameter AST nodes are permitted. String arguments for
109 parameter placeholders are converted to Parameter AST nodes.
110
111 var pFoo = new Parameter('foo')
112 js('function(#) { return #; }', [pFoo, vFoo])
113 -->
114 function(foo) { return foo; }
115
116 Expressions and Parameters are not compatible with each other's context:
117
118 js('function(#) { return #; }', [vFoo, vFoo]) --> error
119 js('function(#) { return #; }', [pFoo, pFoo]) --> error
120
121 The parameter context is a splicing context. When combined with the
122 context-sensitive conversion of Strings, this simplifies the construction of
123 trampoline-like functions:
124
125 var args = ['a', 'b'];
126 js('function(#) { return f(this, #); }', [args, args])
127 -->
128 function(a, b) { return f(this, a, b); }
129
130 A statement placeholder in a Block is also in a splicing context. In addition
131 to splicing Iterables, statement placeholders in a Block will also splice a
132 Block or an EmptyStatement. This flattens nested blocks and allows blocks to be
133 appended.
134
135 var b1 = js.statement('{ 1; 2; }');
136 var sEmpty = new Emptystatement();
137 js.statement('{ #; #; #; #; }', [sEmpty, b1, b1, sEmpty])
138 -->
139 { 1; 2; 1; 2; }
140
141 A placeholder in the context of an if-statement condition also accepts a Dart
142 bool argument, which selects the then-part or else-part of the if-statement:
143
144 js.statement('if (#) return;', vFoo) --> if (foo) return;
145 js.statement('if (#) return;', true) --> return;
146 js.statement('if (#) return;', false) --> ; // empty statement
147 var eTrue = new LiteralBool(true);
148 js.statement('if (#) return;', eTrue) --> if (true) return;
149
150 Combined with block splicing, if-statement condition context placeholders allows
151 the creation of tenplates that select code depending on variables.
152
153 js.statement('{ 1; if (#) 2; else { 3; 4; } 5;}', true)
154 --> { 1; 2; 5; }
155
156 js.statement('{ 1; if (#) 2; else { 3; 4; } 5;}', false)
157 --> { 1; 3; 4; 5; }
158
159 A placeholder following a period in a property access is in a property access
160 context. This is just like an expression context, except String arguments are
161 converted to JavaScript property accesses. In JavaScript, `a.b` is short-hand
162 for `a["b"]`:
163
164 js('a[#]', vFoo) --> a[foo]
165 js('a[#]', s) --> a.hello (i.e. a["hello"]).
166 js('a[#]', 'x') --> a[x]
167
168 js('a.#', vFoo) --> a[foo]
169 js('a.#', s) --> a.hello (i.e. a["hello"])
170 js('a.#', 'x') --> a.x (i.e. a["x"])
171
172 (Question - should `.#` be restricted to permit only String arguments? The
173 template should probably be writted with `[]` if non-strings are accepted.)
174
175
176 Object initialiers allow placeholders in the key property name position:
177
178 js('{#:1, #:2}', [s, 'bye']) --> {hello: 1, bye: 2}
179
180
181 What is not implemented:
182
183 - Array initializers and object initializers could support splicing. In the
184 array case, we would need some way to know if an ArrayInitializer argument
185 should be splice or is intended as a single value.
186
187 - There are no placeholders in definition contexts:
188
189 function #(){}
190 var # = 1;
191
192 */
193 const JsBuilder js = const JsBuilder();
194
195
196 class JsBuilder { 10 class JsBuilder {
197 const JsBuilder(); 11 const JsBuilder();
198 12
199 /** 13 /**
200 * Parses a bit of JavaScript, and returns an expression. 14 * Parses a bit of JavaScript, and returns an expression.
201 * 15 *
202 * See the MiniJsParser class. 16 * See the MiniJsParser class.
203 * 17 *
204 * [arguments] can be a single [Node] (e.g. an [Expression] or [Statement]) or 18 * [expression] can be an [Expression] or a list of [Expression]s, which will
205 * a list of [Node]s, which will be interpolated into the source at the '#' 19 * be interpolated into the source at the '#' signs.
206 * signs.
207 */ 20 */
208 Expression call(String source, [var arguments]) { 21 Expression call(String source, [var expression]) {
209 Template template = _findExpressionTemplate(source); 22 var result = new MiniJsParser(source).expression();
210 if (arguments == null) return template.instantiate([]); 23 if (expression == null) return result;
211 return template.instantiate(arguments is List ? arguments : [arguments]); 24
25 List<Node> nodes;
26 if (expression is List) {
27 nodes = expression;
28 } else {
29 nodes = <Node>[expression];
30 }
31 if (nodes.length != result.interpolatedNodes.length) {
32 throw 'Unmatched number of interpolated expressions given ${nodes.length}'
33 ' expected ${result.interpolatedNodes.length}';
34 }
35 for (int i = 0; i < nodes.length; i++) {
36 result.interpolatedNodes[i].assign(nodes[i]);
37 }
38
39 return result.value;
212 } 40 }
213 41
214 /** 42 Statement statement(String source) {
215 * Parses a JavaScript Statement, otherwise just like [call]. 43 var result = new MiniJsParser(source).statement();
216 */ 44 // TODO(sra): Interpolation.
217 Statement statement(String source, [var arguments]) { 45 return result;
218 Template template = _findStatementTemplate(source);
219 if (arguments == null) return template.instantiate([]);
220 return template.instantiate(arguments is List ? arguments : [arguments]);
221 } 46 }
222 47
223 /** 48 // Parse JavaScript written in the JS foreign instruction.
224 * Parses JavaScript written in the `JS` foreign instruction. 49 Expression parseForeignJS(String source, [var expression]) {
225 * 50 // We can parse simple JS with the mini parser. At the moment we can't
226 * The [source] must be a JavaScript expression or a JavaScript throw 51 // handle JSON literals and function literals, both of which contain "{".
227 * statement. 52 if (source.contains("{") || source.startsWith("throw ")) {
228 */ 53 assert(expression == null);
229 Template parseForeignJS(String source) { 54 return new LiteralExpression(source);
230 // TODO(sra): Parse with extra validation to forbid `#` interpolation in
231 // functions, as this leads to unanticipated capture of temporaries that are
232 // reused after capture.
233 if (source.startsWith("throw ")) {
234 return _findStatementTemplate(source);
235 } else {
236 return _findExpressionTemplate(source);
237 } 55 }
56 return call(source, expression);
238 } 57 }
239 58
240 Template _findExpressionTemplate(String source) { 59 /// Creates a litteral js string from [value].
241 Template template = templateManager.lookupExpressionTemplate(source);
242 if (template == null) {
243 MiniJsParser parser = new MiniJsParser(source);
244 Expression expression = parser.expression();
245 template = templateManager.defineExpressionTemplate(source, expression);
246 }
247 return template;
248 }
249
250 Template _findStatementTemplate(String source) {
251 Template template = templateManager.lookupStatementTemplate(source);
252 if (template == null) {
253 MiniJsParser parser = new MiniJsParser(source);
254 Statement statement = parser.statement();
255 template = templateManager.defineStatementTemplate(source, statement);
256 }
257 return template;
258 }
259
260 /**
261 * Creates an Expression template without caching the result.
262 */
263 Template uncachedExpressionTemplate(String source) {
264 MiniJsParser parser = new MiniJsParser(source);
265 Expression expression = parser.expression();
266 return new Template(
267 source, expression, isExpression: true, forceCopy: false);
268 }
269
270 /**
271 * Create an Expression template which has [ast] as the result. This is used
272 * to wrap a generated AST in a zero-argument Template so it can be passed to
273 * context that expects a template.
274 */
275 Template expressionTemplateYielding(Node ast) {
276 return new Template.withExpressionResult(ast);
277 }
278
279 Template statementTemplateYielding(Node ast) {
280 return new Template.withStatementResult(ast);
281 }
282
283 /// Creates a literal js string from [value].
284 LiteralString escapedString(String value) { 60 LiteralString escapedString(String value) {
285 // Do not escape unicode characters and ' because they are allowed in the 61 // Do not escape unicode characters and ' because they are allowed in the
286 // string literal anyway. 62 // string literal anyway.
287 String escaped = 63 String escaped =
288 value.replaceAllMapped(new RegExp('\n|"|\\|\0|\b|\t|\v'), (match) { 64 value.replaceAllMapped(new RegExp('\n|"|\\|\0|\b|\t|\v'), (match) {
289 switch (match.group(0)) { 65 switch (match.group(0)) {
290 case "\n" : return r"\n"; 66 case "\n" : return r"\n";
291 case "\\" : return r"\\"; 67 case "\\" : return r"\\";
292 case "\"" : return r'\"'; 68 case "\"" : return r'\"';
293 case "\0" : return r"\0"; 69 case "\0" : return r"\0";
294 case "\b" : return r"\b"; 70 case "\b" : return r"\b";
295 case "\t" : return r"\t"; 71 case "\t" : return r"\t";
296 case "\f" : return r"\f"; 72 case "\f" : return r"\f";
297 case "\v" : return r"\v"; 73 case "\v" : return r"\v";
298 } 74 }
299 }); 75 });
300 LiteralString result = string(escaped); 76 LiteralString result = string(escaped);
301 // We don't escape ' under the assumption that the string is wrapped 77 // We don't escape ' under the assumption that the string is wrapped
302 // into ". Verify that assumption. 78 // into ". Verify that assumption.
303 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); 79 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0));
304 return result; 80 return result;
305 } 81 }
306 82
307 /// Creates a literal js string from [value]. 83 /// Creates a litteral js string from [value].
308 /// 84 ///
309 /// Note that this function only puts quotes around [value]. It does not do 85 /// Note that this function only puts quotes around [value]. It does not do
310 /// any escaping, so use only when you can guarantee that [value] does not 86 /// any escaping, so use only when you can guarantee that [value] does not
311 /// contain newlines or backslashes. For escaping the string use 87 /// contain newlines or backslashes. For escaping the string use
312 /// [escapedString]. 88 /// [escapedString].
313 LiteralString string(String value) => new LiteralString('"$value"'); 89 LiteralString string(String value) => new LiteralString('"$value"');
314 90
315 LiteralNumber number(num value) => new LiteralNumber('$value'); 91 LiteralNumber number(num value) => new LiteralNumber('$value');
316 92
93 If if_(condition, thenPart, [elsePart]) {
94 condition = toExpression(condition);
95 return (elsePart == null)
96 ? new If.noElse(condition, toStatement(thenPart))
97 : new If(condition, toStatement(thenPart), toStatement(elsePart));
98 }
99
100 Return return_([value]) {
101 return new Return(value == null ? null : toExpression(value));
102 }
103
104 Block block(statement) {
105 if (statement is Block) {
106 return statement;
107 } else if (statement is List) {
108 List<Statement> statements = statement
109 .map(toStatement)
110 .where((s) => s is !EmptyStatement)
111 .toList();
112 return new Block(statements);
113 } else {
114 return new Block(<Statement>[toStatement(statement)]);
115 }
116 }
117
118 Fun fun(parameters, body) {
119 Parameter toParameter(parameter) {
120 if (parameter is String) {
121 return new Parameter(parameter);
122 } else if (parameter is Parameter) {
123 return parameter;
124 } else {
125 throw new ArgumentError('parameter should be a String or a Parameter');
126 }
127 }
128 if (parameters is! List) {
129 parameters = [parameters];
130 }
131 return new Fun(parameters.map(toParameter).toList(), block(body));
132 }
133
134 VariableDeclarationList defineVar(String name, [initializer]) {
135 if (initializer != null) {
136 initializer = toExpression(initializer);
137 }
138 var declaration = new VariableDeclaration(name);
139 var initialization = [new VariableInitialization(declaration, initializer)];
140 return new VariableDeclarationList(initialization);
141 }
142
143 Statement toStatement(statement) {
144 if (statement is List) {
145 return block(statement);
146 } else if (statement is Node) {
147 return statement.toStatement();
148 } else {
149 throw new ArgumentError('statement');
150 }
151 }
152
153 Expression toExpression(expression) {
154 if (expression == null) {
155 return null;
156 } else if (expression is Expression) {
157 return expression;
158 } else if (expression is String) {
159 return this(expression);
160 } else if (expression is num) {
161 return new LiteralNumber('$expression');
162 } else if (expression is bool) {
163 return new LiteralBool(expression);
164 } else if (expression is Map) {
165 if (!expression.isEmpty) {
166 throw new ArgumentError('expression should be an empty Map');
167 }
168 return new ObjectInitializer([]);
169 } else if (expression is List) {
170 var values = new List<ArrayElement>.generate(expression.length,
171 (index) => new ArrayElement(index, toExpression(expression[index])));
172 return new ArrayInitializer(values.length, values);
173 } else {
174 throw new ArgumentError('expression should be an Expression, '
175 'a String, a num, a bool, a Map, or a List;');
176 }
177 }
178
179 ForIn forIn(String name, object, statement) {
180 return new ForIn(defineVar(name),
181 toExpression(object),
182 toStatement(statement));
183 }
184
185 For for_(init, condition, update, statement) {
186 return new For(
187 toExpression(init), toExpression(condition), toExpression(update),
188 toStatement(statement));
189 }
190
191 While while_(condition, statement) {
192 return new While(
193 toExpression(condition), toStatement(statement));
194 }
195
196 Try try_(body, {catchPart, finallyPart}) {
197 if (catchPart != null) catchPart = toStatement(catchPart);
198 if (finallyPart != null) finallyPart = toStatement(finallyPart);
199 return new Try(toStatement(body), catchPart, finallyPart);
200 }
201
317 Comment comment(String text) => new Comment(text); 202 Comment comment(String text) => new Comment(text);
318 } 203 }
319 204
205 const JsBuilder js = const JsBuilder();
206
320 LiteralString string(String value) => js.string(value); 207 LiteralString string(String value) => js.string(value);
321 208
322 class MiniJsParserError { 209 class MiniJsParserError {
323 MiniJsParserError(this.parser, this.message) { } 210 MiniJsParserError(this.parser, this.message) { }
324 211
325 final MiniJsParser parser; 212 final MiniJsParser parser;
326 final String message; 213 final String message;
327 214
328 String toString() { 215 String toString() {
329 int pos = parser.lastPosition; 216 int pos = parser.lastPosition;
(...skipping 20 matching lines...) Expand all
350 /// AST nodes. Handles: 237 /// AST nodes. Handles:
351 /// * identifiers. 238 /// * identifiers.
352 /// * dot access. 239 /// * dot access.
353 /// * method calls. 240 /// * method calls.
354 /// * [] access. 241 /// * [] access.
355 /// * array, string, regexp, boolean, null and numeric literals. 242 /// * array, string, regexp, boolean, null and numeric literals.
356 /// * most operators. 243 /// * most operators.
357 /// * brackets. 244 /// * brackets.
358 /// * var declarations. 245 /// * var declarations.
359 /// * operator precedence. 246 /// * operator precedence.
360 /// * anonymous funtions and named function expressions and declarations.
361 /// Notable things it can't do yet include: 247 /// Notable things it can't do yet include:
362 /// * some statements are still missing (do-while, while, switch). 248 /// * non-empty object literals.
249 /// * throw, return.
250 /// * statements, including any flow control (if, while, for, etc.)
363 /// 251 ///
364 /// It's a fairly standard recursive descent parser. 252 /// It's a fairly standard recursive descent parser.
365 /// 253 ///
366 /// Literal strings are passed through to the final JS source code unchanged, 254 /// Literal strings are passed through to the final JS source code unchanged,
367 /// including the choice of surrounding quotes, so if you parse 255 /// including the choice of surrounding quotes, so if you parse
368 /// r'var x = "foo\n\"bar\""' you will end up with 256 /// r'var x = "foo\n\"bar\""' you will end up with
369 /// var x = "foo\n\"bar\"" in the final program. \x and \u escapes are not 257 /// var x = "foo\n\"bar\"" in the final program. \x and \u escapes are not
370 /// allowed in string and regexp literals because the machinery for checking 258 /// allowed in string and regexp literals because the machinery for checking
371 /// their correctness is rather involved. 259 /// their correctness is rather involved.
372 class MiniJsParser { 260 class MiniJsParser {
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 } while (currentCode != delimiter); 395 } while (currentCode != delimiter);
508 position++; 396 position++;
509 return src.substring(lastPosition, position); 397 return src.substring(lastPosition, position);
510 } 398 }
511 399
512 void getToken() { 400 void getToken() {
513 skippedNewline = false; 401 skippedNewline = false;
514 for (;;) { 402 for (;;) {
515 if (position >= src.length) break; 403 if (position >= src.length) break;
516 int code = src.codeUnitAt(position); 404 int code = src.codeUnitAt(position);
517 // Skip '//' and '/*' style comments. 405 // Skip '//' style comment.
518 if (code == charCodes.$SLASH && 406 if (code == charCodes.$SLASH &&
519 position + 1 < src.length) { 407 position + 1 < src.length &&
520 if (src.codeUnitAt(position + 1) == charCodes.$SLASH) { 408 src.codeUnitAt(position + 1) == charCodes.$SLASH) {
521 int nextPosition = src.indexOf('\n', position); 409 int nextPosition = src.indexOf('\n', position);
522 if (nextPosition == -1) nextPosition = src.length; 410 if (nextPosition == -1) nextPosition = src.length;
523 position = nextPosition; 411 position = nextPosition;
524 continue; 412 } else {
525 } else if (src.codeUnitAt(position + 1) == charCodes.$STAR) { 413 if (category(code) != WHITESPACE) break;
526 int nextPosition = src.indexOf('*/', position + 2); 414 if (code == charCodes.$LF) skippedNewline = true;
527 if (nextPosition == -1) error('Unterminated comment'); 415 ++position;
528 position = nextPosition + 2;
529 continue;
530 }
531 } 416 }
532 if (category(code) != WHITESPACE) break;
533 if (code == charCodes.$LF) skippedNewline = true;
534 ++position;
535 } 417 }
536 418
537 if (position == src.length) { 419 if (position == src.length) {
538 lastCategory = NONE; 420 lastCategory = NONE;
539 lastToken = null; 421 lastToken = null;
540 lastPosition = position; 422 lastPosition = position;
541 return; 423 return;
542 } 424 }
543 int code = src.codeUnitAt(position); 425 int code = src.codeUnitAt(position);
544 lastPosition = position; 426 lastPosition = position;
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
620 502
621 void expectSemicolon() { 503 void expectSemicolon() {
622 if (acceptSemicolon()) return; 504 if (acceptSemicolon()) return;
623 error('Expected SEMICOLON'); 505 error('Expected SEMICOLON');
624 } 506 }
625 507
626 bool acceptSemicolon() { 508 bool acceptSemicolon() {
627 // Accept semicolon or automatically inserted semicolon before close brace. 509 // Accept semicolon or automatically inserted semicolon before close brace.
628 // Miniparser forbids other kinds of semicolon insertion. 510 // Miniparser forbids other kinds of semicolon insertion.
629 if (RBRACE == lastCategory) return true; 511 if (RBRACE == lastCategory) return true;
630 if (NONE == lastCategory) return true; // end of input
631 if (skippedNewline) { 512 if (skippedNewline) {
632 error('No automatic semicolon insertion at preceding newline'); 513 error('No automatic semicolon insertion at preceding newline');
633 } 514 }
634 return acceptCategory(SEMICOLON); 515 return acceptCategory(SEMICOLON);
635 } 516 }
636 517
637 bool acceptString(String string) { 518 bool acceptString(String string) {
638 if (lastToken == string) { 519 if (lastToken == string) {
639 getToken(); 520 getToken();
640 return true; 521 return true;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
680 } 561 }
681 return new ArrayInitializer(values.length, values); 562 return new ArrayInitializer(values.length, values);
682 } else if (last.startsWith("/")) { 563 } else if (last.startsWith("/")) {
683 String regexp = getDelimited(lastPosition); 564 String regexp = getDelimited(lastPosition);
684 getToken(); 565 getToken();
685 String flags = lastToken; 566 String flags = lastToken;
686 if (!acceptCategory(ALPHA)) flags = ""; 567 if (!acceptCategory(ALPHA)) flags = "";
687 Expression expression = new RegExpLiteral(regexp + flags); 568 Expression expression = new RegExpLiteral(regexp + flags);
688 return expression; 569 return expression;
689 } else if (acceptCategory(HASH)) { 570 } else if (acceptCategory(HASH)) {
690 InterpolatedExpression expression = 571 InterpolatedExpression expression = new InterpolatedExpression(null);
691 new InterpolatedExpression(interpolatedValues.length);
692 interpolatedValues.add(expression); 572 interpolatedValues.add(expression);
693 return expression; 573 return expression;
694 } else { 574 } else {
695 error("Expected primary expression"); 575 error("Expected primary expression");
696 return null; 576 return null;
697 } 577 }
698 } 578 }
699 579
700 Expression parseFunctionExpression() { 580 Expression parseFunctionExpression() {
701 String last = lastToken; 581 String last = lastToken;
702 if (acceptCategory(ALPHA)) { 582 if (acceptCategory(ALPHA)) {
703 String functionName = last; 583 String functionName = last;
704 return new NamedFunction(new VariableDeclaration(functionName), 584 return new NamedFunction(new VariableDeclaration(functionName),
705 parseFun()); 585 parseFun());
706 } 586 }
707 return parseFun(); 587 return parseFun();
708 } 588 }
709 589
710 Expression parseFun() { 590 Expression parseFun() {
711 List<Parameter> params = <Parameter>[]; 591 List<Parameter> params = <Parameter>[];
712
713 expectCategory(LPAREN); 592 expectCategory(LPAREN);
714 if (!acceptCategory(RPAREN)) { 593 String argumentName = lastToken;
715 for (;;) { 594 if (acceptCategory(ALPHA)) {
716 if (acceptCategory(HASH)) { 595 params.add(new Parameter(argumentName));
717 InterpolatedParameter parameter = 596 while (acceptCategory(COMMA)) {
718 new InterpolatedParameter(interpolatedValues.length); 597 argumentName = lastToken;
719 interpolatedValues.add(parameter); 598 expectCategory(ALPHA);
720 params.add(parameter); 599 params.add(new Parameter(argumentName));
721 } else {
722 String argumentName = lastToken;
723 expectCategory(ALPHA);
724 params.add(new Parameter(argumentName));
725 }
726 if (acceptCategory(COMMA)) continue;
727 expectCategory(RPAREN);
728 break;
729 } 600 }
730 } 601 }
731 602 expectCategory(RPAREN);
732 expectCategory(LBRACE); 603 expectCategory(LBRACE);
733 Block block = parseBlock(); 604 Block block = parseBlock();
734 return new Fun(params, block); 605 return new Fun(params, block);
735 } 606 }
736 607
737 Expression parseObjectInitializer() { 608 Expression parseObjectInitializer() {
738 List<Property> properties = <Property>[]; 609 List<Property> properties = <Property>[];
739 for (;;) { 610 for (;;) {
740 if (acceptCategory(RBRACE)) break; 611 if (acceptCategory(RBRACE)) break;
741 // Limited subset: keys are identifiers, no 'get' or 'set' properties. 612 // Limited subset: keys are identifiers, no 'get' or 'set' properties.
742 Literal propertyName; 613 Literal propertyName;
743 String identifier = lastToken; 614 String identifier = lastToken;
744 if (acceptCategory(ALPHA)) { 615 if (acceptCategory(ALPHA)) {
745 propertyName = new LiteralString('"$identifier"'); 616 propertyName = new LiteralString('"$identifier"');
746 } else if (acceptCategory(STRING)) { 617 } else if (acceptCategory(STRING)) {
747 propertyName = new LiteralString(identifier); 618 propertyName = new LiteralString(identifier);
748 } else if (acceptCategory(SYMBOL)) { // e.g. void
749 propertyName = new LiteralString('"$identifier"');
750 } else if (acceptCategory(HASH)) {
751 InterpolatedLiteral interpolatedLiteral =
752 new InterpolatedLiteral(interpolatedValues.length);
753 interpolatedValues.add(interpolatedLiteral);
754 propertyName = interpolatedLiteral;
755 } else { 619 } else {
756 error('Expected property name'); 620 error('Expected property name');
757 } 621 }
758 expectCategory(COLON); 622 expectCategory(COLON);
759 Expression value = parseAssignment(); 623 Expression value = parseAssignment();
760 properties.add(new Property(propertyName, value)); 624 properties.add(new Property(propertyName, value));
761 if (acceptCategory(RBRACE)) break; 625 if (acceptCategory(RBRACE)) break;
762 expectCategory(COMMA); 626 expectCategory(COMMA);
763 } 627 }
764 return new ObjectInitializer(properties); 628 return new ObjectInitializer(properties);
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
807 } else { 671 } else {
808 // JS allows new without (), but we don't. 672 // JS allows new without (), but we don't.
809 if (constructor) error("Parentheses are required for new"); 673 if (constructor) error("Parentheses are required for new");
810 break; 674 break;
811 } 675 }
812 } 676 }
813 return receiver; 677 return receiver;
814 } 678 }
815 679
816 Expression getDotRhs(Expression receiver) { 680 Expression getDotRhs(Expression receiver) {
817 if (acceptCategory(HASH)) {
818 InterpolatedSelector property =
819 new InterpolatedSelector(interpolatedValues.length);
820 interpolatedValues.add(property);
821 return new PropertyAccess(receiver, property);
822 }
823 String identifier = lastToken; 681 String identifier = lastToken;
824 // In ES5 keywords like delete and continue are allowed as property 682 // In ES5 keywords like delete and continue are allowed as property
825 // names, and the IndexedDB API uses that, so we need to allow it here. 683 // names, and the IndexedDB API uses that, so we need to allow it here.
826 if (acceptCategory(SYMBOL)) { 684 if (acceptCategory(SYMBOL)) {
827 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) { 685 if (!OPERATORS_THAT_LOOK_LIKE_IDENTIFIERS.contains(identifier)) {
828 error("Expected alphanumeric identifier"); 686 error("Expected alphanumeric identifier");
829 } 687 }
830 } else { 688 } else {
831 expectCategory(ALPHA); 689 expectCategory(ALPHA);
832 } 690 }
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
964 } else { 822 } else {
965 return parseExpression(); 823 return parseExpression();
966 } 824 }
967 } 825 }
968 826
969 Expression expression() { 827 Expression expression() {
970 Expression expression = parseVarDeclarationOrExpression(); 828 Expression expression = parseVarDeclarationOrExpression();
971 if (lastCategory != NONE || position != src.length) { 829 if (lastCategory != NONE || position != src.length) {
972 error("Unparsed junk: ${categoryToString(lastCategory)}"); 830 error("Unparsed junk: ${categoryToString(lastCategory)}");
973 } 831 }
832 if (!interpolatedValues.isEmpty) {
833 return new JSExpression(expression, interpolatedValues);
834 }
974 return expression; 835 return expression;
975 } 836 }
976 837
977 Statement statement() { 838 Statement statement() {
978 Statement statement = parseStatement(); 839 Statement statement = parseStatement();
979 if (lastCategory != NONE || position != src.length) { 840 if (lastCategory != NONE || position != src.length) {
980 error("Unparsed junk: ${categoryToString(lastCategory)}"); 841 error("Unparsed junk: ${categoryToString(lastCategory)}");
981 } 842 }
982 // TODO(sra): interpolated capture here? 843 // TODO(sra): interpolated capture here?
983 return statement; 844 return statement;
984 } 845 }
985 846
986 Block parseBlock() { 847 Block parseBlock() {
987 List<Statement> statements = <Statement>[]; 848 List<Statement> statements = <Statement>[];
988 849
989 while (!acceptCategory(RBRACE)) { 850 while (!acceptCategory(RBRACE)) {
990 Statement statement = parseStatement(); 851 Statement statement = parseStatement();
991 statements.add(statement); 852 statements.add(statement);
992 } 853 }
993 return new Block(statements); 854 return new Block(statements);
994 } 855 }
995 856
996 Statement parseStatement() { 857 Statement parseStatement() {
997 if (acceptCategory(LBRACE)) return parseBlock(); 858 if (acceptCategory(LBRACE)) return parseBlock();
998 859
999 if (acceptCategory(SEMICOLON)) return new EmptyStatement();
1000
1001 if (lastCategory == ALPHA) { 860 if (lastCategory == ALPHA) {
1002 if (acceptString('return')) return parseReturn(); 861 if (acceptString('return')) return parseReturn();
1003 862
1004 if (acceptString('throw')) return parseThrow(); 863 if (acceptString('throw')) return parseThrow();
1005 864
1006 if (acceptString('break')) { 865 if (acceptString('break')) {
1007 return parseBreakOrContinue((label) => new Break(label)); 866 return parseBreakOrContinue((label) => new Break(label));
1008 } 867 }
1009 868
1010 if (acceptString('continue')) { 869 if (acceptString('continue')) {
1011 return parseBreakOrContinue((label) => new Continue(label)); 870 return parseBreakOrContinue((label) => new Continue(label));
1012 } 871 }
1013 872
1014 if (acceptString('if')) return parseIfThenElse(); 873 if (acceptString('if')) return parseIfThenElse();
1015 874
1016 if (acceptString('for')) return parseFor(); 875 if (acceptString('for')) return parseFor();
1017 876
1018 if (acceptString('function')) return parseFunctionDeclaration(); 877 if (acceptString('function')) return parseFunctionDeclaration();
1019 878
1020 if (acceptString('try')) return parseTry();
1021
1022 if (acceptString('var')) { 879 if (acceptString('var')) {
1023 Expression declarations = parseVariableDeclarationList(); 880 Expression declarations = parseVariableDeclarationList();
1024 expectSemicolon(); 881 expectSemicolon();
1025 return new ExpressionStatement(declarations); 882 return new ExpressionStatement(declarations);
1026 } 883 }
1027 884
1028 if (lastToken == 'case' || 885 if (lastToken == 'case' ||
1029 lastToken == 'do' || 886 lastToken == 'do' ||
1030 lastToken == 'while' || 887 lastToken == 'while' ||
1031 lastToken == 'switch' || 888 lastToken == 'switch' ||
889 lastToken == 'try' ||
1032 lastToken == 'with') { 890 lastToken == 'with') {
1033 error('Not implemented in mini parser'); 891 error('Not implemented in mini parser');
1034 } 892 }
1035 } 893 }
1036 894
895 if (acceptCategory(HASH)) {
896 InterpolatedStatement statement = new InterpolatedStatement(null);
897 interpolatedValues.add(statement);
898 return statement;
899 }
1037 900
1038 // TODO: label: statement 901 // TODO: label: statement
1039 902
1040 bool checkForInterpolatedStatement = lastCategory == HASH;
1041
1042 Expression expression = parseExpression(); 903 Expression expression = parseExpression();
1043 expectSemicolon(); 904 expectSemicolon();
1044
1045 if (checkForInterpolatedStatement) {
1046 // 'Promote' the interpolated expression `#;` to an interpolated
1047 // statement.
1048 if (expression is InterpolatedExpression) {
1049 assert(identical(interpolatedValues.last, expression));
1050 InterpolatedStatement statement =
1051 new InterpolatedStatement(expression.name);
1052 interpolatedValues[interpolatedValues.length - 1] = statement;
1053 return statement;
1054 }
1055 }
1056
1057 return new ExpressionStatement(expression); 905 return new ExpressionStatement(expression);
1058 } 906 }
1059 907
1060 Statement parseReturn() { 908 Statement parseReturn() {
1061 if (acceptSemicolon()) return new Return(); 909 if (acceptSemicolon()) return new Return();
1062 Expression expression = parseExpression(); 910 Expression expression = parseExpression();
1063 expectSemicolon(); 911 expectSemicolon();
1064 return new Return(expression); 912 return new Return(expression);
1065 } 913 }
1066 914
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1122 return finishFor(null); 970 return finishFor(null);
1123 } 971 }
1124 972
1125 if (acceptString('var')) { 973 if (acceptString('var')) {
1126 String identifier = lastToken; 974 String identifier = lastToken;
1127 expectCategory(ALPHA); 975 expectCategory(ALPHA);
1128 if (acceptString('in')) { 976 if (acceptString('in')) {
1129 Expression objectExpression = parseExpression(); 977 Expression objectExpression = parseExpression();
1130 expectCategory(RPAREN); 978 expectCategory(RPAREN);
1131 Statement body = parseStatement(); 979 Statement body = parseStatement();
1132 return new ForIn( 980 return new ForIn(js.defineVar(identifier), objectExpression, body);
1133 new VariableDeclarationList([
1134 new VariableInitialization(
1135 new VariableDeclaration(identifier), null)]),
1136 objectExpression,
1137 body);
1138 } 981 }
1139 Expression declarations = finishVariableDeclarationList(identifier); 982 Expression declarations = finishVariableDeclarationList(identifier);
1140 expectCategory(SEMICOLON); 983 expectCategory(SEMICOLON);
1141 return finishFor(declarations); 984 return finishFor(declarations);
1142 } 985 }
1143 986
1144 Expression init = parseExpression(); 987 Expression init = parseExpression();
1145 expectCategory(SEMICOLON); 988 expectCategory(SEMICOLON);
1146 return finishFor(init); 989 return finishFor(init);
1147 } 990 }
1148 991
1149 Statement parseFunctionDeclaration() { 992 Statement parseFunctionDeclaration() {
1150 String name = lastToken; 993 String name = lastToken;
1151 expectCategory(ALPHA); 994 expectCategory(ALPHA);
1152 Expression fun = parseFun(); 995 Expression fun = parseFun();
1153 return new FunctionDeclaration(new VariableDeclaration(name), fun); 996 return new FunctionDeclaration(new VariableDeclaration(name), fun);
1154 } 997 }
998 }
1155 999
1156 Statement parseTry() { 1000 /**
1157 expectCategory(LBRACE); 1001 * Clone a JSExpression node into an expression where all children
1158 Block body = parseBlock(); 1002 * have been cloned, and [InterpolatedExpression]s have been replaced
1159 String token = lastToken; 1003 * with real [Expression].
1160 Catch catchPart = null; 1004 */
1161 if (acceptString('catch')) catchPart = parseCatch(); 1005 class UninterpolateJSExpression extends BaseVisitor<Node> {
1162 Block finallyPart = null; 1006 final List<Expression> arguments;
1163 if (acceptString('finally')) { 1007 int argumentIndex = 0;
1164 expectCategory(LBRACE); 1008
1165 finallyPart = parseBlock(); 1009 UninterpolateJSExpression(this.arguments);
1166 } else { 1010
1167 if (catchPart == null) error("expected 'finally'"); 1011 void error(message) {
1168 } 1012 throw message;
1169 return new Try(body, catchPart, finallyPart);
1170 } 1013 }
1171 1014
1172 Catch parseCatch() { 1015 Node visitNode(Node node) {
1173 expectCategory(LPAREN); 1016 error('Cannot handle $node');
1174 String identifier = lastToken; 1017 return null;
1175 expectCategory(ALPHA); 1018 }
1176 expectCategory(RPAREN); 1019
1177 expectCategory(LBRACE); 1020 Node copyPosition(Node oldNode, Node newNode) {
1178 Block body = parseBlock(); 1021 newNode.sourcePosition = oldNode.sourcePosition;
1179 return new Catch(new VariableDeclaration(identifier), body); 1022 newNode.endSourcePosition = oldNode.endSourcePosition;
1023 return newNode;
1024 }
1025
1026 Node visit(Node node) {
1027 return node == null ? null : node.accept(this);
1028 }
1029
1030 List<Node> visitList(List<Node> list) {
1031 return list.map((e) => visit(e)).toList();
1032 }
1033
1034 Node visitLiteralString(LiteralString node) {
1035 return node;
1036 }
1037
1038 Node visitVariableUse(VariableUse node) {
1039 return node;
1040 }
1041
1042 Node visitAccess(PropertyAccess node) {
1043 return copyPosition(node,
1044 new PropertyAccess(visit(node.receiver), visit(node.selector)));
1045 }
1046
1047 Node visitCall(Call node) {
1048 return copyPosition(node,
1049 new Call(visit(node.target), visitList(node.arguments)));
1050 }
1051
1052 Node visitInterpolatedExpression(InterpolatedExpression expression) {
1053 return arguments[argumentIndex++];
1054 }
1055
1056 Node visitInterpolatedStatement(InterpolatedStatement statement) {
1057 return arguments[argumentIndex++];
1058 }
1059
1060 Node visitJSExpression(JSExpression expression) {
1061 assert(argumentIndex == 0);
1062 Node result = visit(expression.value);
1063 if (argumentIndex != arguments.length) {
1064 error("Invalid number of arguments");
1065 }
1066 assert(result is! JSExpression);
1067 return result;
1068 }
1069
1070 Node visitLiteralExpression(LiteralExpression node) {
1071 assert(argumentIndex == 0);
1072 return copyPosition(node,
1073 new LiteralExpression.withData(node.template, arguments));
1074 }
1075
1076 Node visitAssignment(Assignment node) {
1077 return copyPosition(node,
1078 new Assignment._internal(visit(node.leftHandSide),
1079 visit(node.compoundTarget),
1080 visit(node.value)));
1081 }
1082
1083 Node visitRegExpLiteral(RegExpLiteral node) {
1084 return node;
1085 }
1086
1087 Node visitLiteralNumber(LiteralNumber node) {
1088 return node;
1089 }
1090
1091 Node visitBinary(Binary node) {
1092 return copyPosition(node,
1093 new Binary(node.op, visit(node.left), visit(node.right)));
1094 }
1095
1096 Node visitPrefix(Prefix node) {
1097 return copyPosition(node,
1098 new Prefix(node.op, visit(node.argument)));
1099 }
1100
1101 Node visitPostfix(Postfix node) {
1102 return copyPosition(node,
1103 new Postfix(node.op, visit(node.argument)));
1104 }
1105
1106 Node visitNew(New node) {
1107 return copyPosition(node,
1108 new New(visit(node.target), visitList(node.arguments)));
1109 }
1110
1111 Node visitArrayInitializer(ArrayInitializer node) {
1112 return copyPosition(node,
1113 new ArrayInitializer(node.length, visitList(node.elements)));
1114 }
1115
1116 Node visitArrayElement(ArrayElement node) {
1117 return copyPosition(node,
1118 new ArrayElement(node.index, visit(node.value)));
1119 }
1120
1121 Node visitConditional(Conditional node) {
1122 return copyPosition(node,
1123 new Conditional(visit(node.condition),
1124 visit(node.then),
1125 visit(node.otherwise)));
1126 }
1127
1128 Node visitLiteralNull(LiteralNull node) {
1129 return node;
1130 }
1131
1132 Node visitLiteralBool(LiteralBool node) {
1133 return node;
1180 } 1134 }
1181 } 1135 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/_internal/compiler/implementation/js/js.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698