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

Side by Side Diff: pkg/js_ast/lib/src/printer.dart

Issue 1081313003: Improve precision of JS printer callbacks (2nd try) (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Updated cf. comments. Created 5 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 | « pkg/compiler/lib/src/js/js.dart ('k') | pkg/js_ast/test/printer_callback_test.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) 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 part of js_ast; 5 part of js_ast;
6 6
7 7
8 class JavaScriptPrintingOptions { 8 class JavaScriptPrintingOptions {
9 final bool shouldCompressOutput; 9 final bool shouldCompressOutput;
10 final bool minifyLocalVariables; 10 final bool minifyLocalVariables;
11 final bool preferSemicolonToNewlineInMinifiedOutput; 11 final bool preferSemicolonToNewlineInMinifiedOutput;
12 12
13 JavaScriptPrintingOptions( 13 JavaScriptPrintingOptions(
14 {this.shouldCompressOutput: false, 14 {this.shouldCompressOutput: false,
15 this.minifyLocalVariables: false, 15 this.minifyLocalVariables: false,
16 this.preferSemicolonToNewlineInMinifiedOutput: false}); 16 this.preferSemicolonToNewlineInMinifiedOutput: false});
17 } 17 }
18 18
19 19
20 /// An environment in which JavaScript printing is done. Provides emitting of 20 /// An environment in which JavaScript printing is done. Provides emitting of
21 /// text and pre- and post-visit callbacks. 21 /// text and pre- and post-visit callbacks.
22 abstract class JavaScriptPrintingContext { 22 abstract class JavaScriptPrintingContext {
23 /// Signals an error. This should happen only for serious internal errors. 23 /// Signals an error. This should happen only for serious internal errors.
24 void error(String message) { throw message; } 24 void error(String message) { throw message; }
25 25
26 /// Adds [string] to the output. 26 /// Adds [string] to the output.
27 void emit(String string); 27 void emit(String string);
28 28
29 /// Callback immediately before printing [node]. Whitespace may be printed 29 /// Callback for the start of printing of [node]. [startPosition] is the
30 /// after this callback before the first non-whitespace character for [node]. 30 /// position of the first non-whitespace character of [node].
31 void enterNode(Node node) {} 31 ///
32 /// Callback after printing the last character representing [node]. 32 /// [enterNode] is called in pre-traversal order.
33 void exitNode(Node node) {} 33 void enterNode(Node node, int startPosition) {}
34
35 /// Callback for the end of printing of [node]. [startPosition] is the
36 /// position of the first non-whitespace character of [node] (also provided
37 /// in the [enterNode] callback), [endPosition] is the position immediately
38 /// following the last character of [node]. [closingPosition] is the
39 /// position of the ending delimiter of [node]. This is only provided for
40 /// [Fun] nodes and is `null` otherwise.
41 ///
42 /// [enterNode] is called in post-traversal order.
43 void exitNode(Node node,
44 int startPosition,
45 int endPosition,
46 int closingPosition) {}
34 } 47 }
35 48
36 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests. 49 /// A simple implementation of [JavaScriptPrintingContext] suitable for tests.
37 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext { 50 class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext {
38 final StringBuffer buffer = new StringBuffer(); 51 final StringBuffer buffer = new StringBuffer();
39 52
40 void emit(String string) { 53 void emit(String string) {
41 buffer.write(string); 54 buffer.write(string);
42 } 55 }
43 56
44 String getText() => buffer.toString(); 57 String getText() => buffer.toString();
45 } 58 }
46 59
47 60
48 class Printer implements NodeVisitor { 61 class Printer implements NodeVisitor {
49 final JavaScriptPrintingOptions options; 62 final JavaScriptPrintingOptions options;
50 final JavaScriptPrintingContext context; 63 final JavaScriptPrintingContext context;
51 final bool shouldCompressOutput; 64 final bool shouldCompressOutput;
52 final DanglingElseVisitor danglingElseVisitor; 65 final DanglingElseVisitor danglingElseVisitor;
53 final LocalNamer localNamer; 66 final LocalNamer localNamer;
54 67
68 int _charCount = 0;
55 bool inForInit = false; 69 bool inForInit = false;
56 bool atStatementBegin = false; 70 bool atStatementBegin = false;
57 bool pendingSemicolon = false; 71 bool pendingSemicolon = false;
58 bool pendingSpace = false; 72 bool pendingSpace = false;
59 73
60 // The current indentation level. 74 // The current indentation level.
61 int _indentLevel = 0; 75 int _indentLevel = 0;
62 // A cache of all indentation strings used so far. 76 // A cache of all indentation strings used so far.
63 List<String> _indentList = <String>[""]; 77 List<String> _indentList = <String>[""];
64 78
(...skipping 26 matching lines...) Expand all
91 } 105 }
92 106
93 void indentMore() { 107 void indentMore() {
94 _indentLevel++; 108 _indentLevel++;
95 } 109 }
96 110
97 void indentLess() { 111 void indentLess() {
98 _indentLevel--; 112 _indentLevel--;
99 } 113 }
100 114
101
102 /// Always emit a newline, even under `enableMinification`. 115 /// Always emit a newline, even under `enableMinification`.
103 void forceLine() { 116 void forceLine() {
104 out("\n"); 117 out("\n", isWhitespace: true);
105 } 118 }
119
106 /// Emits a newline for readability. 120 /// Emits a newline for readability.
107 void lineOut() { 121 void lineOut() {
108 if (!shouldCompressOutput) forceLine(); 122 if (!shouldCompressOutput) forceLine();
109 } 123 }
124
110 void spaceOut() { 125 void spaceOut() {
111 if (!shouldCompressOutput) out(" "); 126 if (!shouldCompressOutput) out(" ", isWhitespace: true);
112 } 127 }
113 128
114 String lastAddedString = null; 129 String lastAddedString = null;
130
115 int get lastCharCode { 131 int get lastCharCode {
116 if (lastAddedString == null) return 0; 132 if (lastAddedString == null) return 0;
117 assert(lastAddedString.length != ""); 133 assert(lastAddedString.length != "");
118 return lastAddedString.codeUnitAt(lastAddedString.length - 1); 134 return lastAddedString.codeUnitAt(lastAddedString.length - 1);
119 } 135 }
120 136
121 void out(String str) { 137 void out(String str, {bool isWhitespace: false}) {
122 if (str != "") { 138 if (str != "") {
123 if (pendingSemicolon) { 139 if (pendingSemicolon) {
124 if (!shouldCompressOutput) { 140 if (!shouldCompressOutput) {
125 context.emit(";"); 141 _emit(";");
126 } else if (str != "}") { 142 } else if (str != "}") {
127 // We want to output newline instead of semicolon because it makes 143 // We want to output newline instead of semicolon because it makes
128 // the raw stack traces much easier to read and it also makes line- 144 // the raw stack traces much easier to read and it also makes line-
129 // based tools like diff work much better. JavaScript will 145 // based tools like diff work much better. JavaScript will
130 // automatically insert the semicolon at the newline if it means a 146 // automatically insert the semicolon at the newline if it means a
131 // parsing error is avoided, so we can only do this trick if the 147 // parsing error is avoided, so we can only do this trick if the
132 // next line is not something that can be glued onto a valid 148 // next line is not something that can be glued onto a valid
133 // expression to make a new valid expression. 149 // expression to make a new valid expression.
134 150
135 // If we're using the new emitter where most pretty printed code 151 // If we're using the new emitter where most pretty printed code
136 // is escaped in strings, it is a lot easier to deal with semicolons 152 // is escaped in strings, it is a lot easier to deal with semicolons
137 // than newlines because the former doesn't need escaping. 153 // than newlines because the former doesn't need escaping.
138 if (options.preferSemicolonToNewlineInMinifiedOutput || 154 if (options.preferSemicolonToNewlineInMinifiedOutput ||
139 expressionContinuationRegExp.hasMatch(str)) { 155 expressionContinuationRegExp.hasMatch(str)) {
140 context.emit(";"); 156 _emit(";");
141 } else { 157 } else {
142 context.emit("\n"); 158 _emit("\n");
143 } 159 }
144 } 160 }
145 } 161 }
146 if (pendingSpace && 162 if (pendingSpace &&
147 (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) { 163 (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) {
148 context.emit(" "); 164 _emit(" ");
149 } 165 }
150 pendingSpace = false; 166 pendingSpace = false;
151 pendingSemicolon = false; 167 pendingSemicolon = false;
152 context.emit(str); 168 if (!isWhitespace) {
169 enterNode();
170 }
171 _emit(str);
153 lastAddedString = str; 172 lastAddedString = str;
154 } 173 }
155 } 174 }
156 175
157 void outLn(String str) { 176 void outLn(String str) {
158 out(str); 177 out(str);
159 lineOut(); 178 lineOut();
160 } 179 }
161 180
162 void outSemicolonLn() { 181 void outSemicolonLn() {
163 if (shouldCompressOutput) { 182 if (shouldCompressOutput) {
164 pendingSemicolon = true; 183 pendingSemicolon = true;
165 } else { 184 } else {
166 out(";"); 185 out(";");
167 forceLine(); 186 forceLine();
168 } 187 }
169 } 188 }
170 189
171 void outIndent(String str) { indent(); out(str); } 190 void outIndent(String str) {
172 void outIndentLn(String str) { indent(); outLn(str); } 191 indent();
192 out(str);
193 }
194
195 void outIndentLn(String str) {
196 indent();
197 outLn(str);
198 }
199
173 void indent() { 200 void indent() {
174 if (!shouldCompressOutput) { 201 if (!shouldCompressOutput) {
175 out(indentation); 202 out(indentation, isWhitespace: true);
176 } 203 }
177 } 204 }
178 205
179 visit(Node node) { 206 EnterExitNode currentNode;
180 context.enterNode(node); 207
181 node.accept(this); 208 void _emit(String text) {
182 context.exitNode(node); 209 context.emit(text);
210 _charCount += text.length;
183 } 211 }
184 212
185 visitCommaSeparated(List<Node> nodes, int hasRequiredType, 213 void startNode(Node node) {
186 {bool newInForInit, bool newAtStatementBegin}) { 214 currentNode = new EnterExitNode(currentNode, node);
215 }
216
217 void enterNode() {
218 currentNode.addToNode(context, _charCount);
219 }
220
221 void endNode(Node node) {
222 assert(currentNode.node == node);
223 currentNode = currentNode.exitNode(context, _charCount);
224 }
225
226 void visit(Node node) {
227 startNode(node);
228 node.accept(this);
229 endNode(node);
230 }
231
232 void visitCommaSeparated(List<Node> nodes, int hasRequiredType,
233 {bool newInForInit, bool newAtStatementBegin}) {
187 for (int i = 0; i < nodes.length; i++) { 234 for (int i = 0; i < nodes.length; i++) {
188 if (i != 0) { 235 if (i != 0) {
189 atStatementBegin = false; 236 atStatementBegin = false;
190 out(","); 237 out(",");
191 spaceOut(); 238 spaceOut();
192 } 239 }
193 visitNestedExpression(nodes[i], hasRequiredType, 240 visitNestedExpression(nodes[i], hasRequiredType,
194 newInForInit: newInForInit, 241 newInForInit: newInForInit,
195 newAtStatementBegin: newAtStatementBegin); 242 newAtStatementBegin: newAtStatementBegin);
196 } 243 }
197 } 244 }
198 245
199 visitAll(List<Node> nodes) { 246 void visitAll(List<Node> nodes) {
200 nodes.forEach(visit); 247 nodes.forEach(visit);
201 } 248 }
202 249
203 visitProgram(Program program) { 250 @override
204 visitAll(program.body); 251 void visitProgram(Program program) {
252 if (program.body.isNotEmpty) {
253 visitAll(program.body);
254 }
205 } 255 }
206 256
207 Statement unwrapBlockIfSingleStatement(Statement body) { 257 Statement unwrapBlockIfSingleStatement(Statement body) {
208 Statement result = body; 258 Statement result = body;
209 while (result is Block) { 259 while (result is Block) {
210 Block block = result; 260 Block block = result;
211 if (block.statements.length != 1) break; 261 if (block.statements.length != 1) break;
212 result = block.statements.single; 262 result = block.statements.single;
213 } 263 }
214 return result; 264 return result;
215 } 265 }
216 266
217 bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) { 267 bool blockBody(Statement body, {bool needsSeparation, bool needsNewline}) {
218 if (body is Block) { 268 if (body is Block) {
219 spaceOut(); 269 spaceOut();
220 blockOut(body, shouldIndent: false, needsNewline: needsNewline); 270 blockOut(body, shouldIndent: false, needsNewline: needsNewline);
221 return true; 271 return true;
222 } 272 }
223 if (shouldCompressOutput && needsSeparation) { 273 if (shouldCompressOutput && needsSeparation) {
224 // If [shouldCompressOutput] is false, then the 'lineOut' will insert 274 // If [shouldCompressOutput] is false, then the 'lineOut' will insert
225 // the separation. 275 // the separation.
226 out(" "); 276 out(" ", isWhitespace: true);
227 } else { 277 } else {
228 lineOut(); 278 lineOut();
229 } 279 }
230 indentMore(); 280 indentMore();
231 visit(body); 281 visit(body);
232 indentLess(); 282 indentLess();
233 return false; 283 return false;
234 } 284 }
235 285
236 void blockOutWithoutBraces(Node node) { 286 void blockOutWithoutBraces(Node node) {
237 if (node is Block) { 287 if (node is Block) {
238 context.enterNode(node); 288 startNode(node);
239 Block block = node; 289 Block block = node;
240 block.statements.forEach(blockOutWithoutBraces); 290 block.statements.forEach(blockOutWithoutBraces);
241 context.exitNode(node); 291 endNode(node);
242 } else { 292 } else {
243 visit(node); 293 visit(node);
244 } 294 }
245 } 295 }
246 296
247 void blockOut(Block node, {bool shouldIndent, bool needsNewline}) { 297 int blockOut(Block node, {bool shouldIndent, bool needsNewline}) {
248 if (shouldIndent) indent(); 298 if (shouldIndent) indent();
249 context.enterNode(node); 299 startNode(node);
250 out("{"); 300 out("{");
251 lineOut(); 301 lineOut();
252 indentMore(); 302 indentMore();
253 node.statements.forEach(blockOutWithoutBraces); 303 node.statements.forEach(blockOutWithoutBraces);
254 indentLess(); 304 indentLess();
255 indent(); 305 indent();
256 out("}"); 306 out("}");
257 context.exitNode(node); 307 int closingPosition = _charCount - 1;
308 endNode(node);
258 if (needsNewline) lineOut(); 309 if (needsNewline) lineOut();
310 return closingPosition;
259 } 311 }
260 312
261 visitBlock(Block block) { 313 @override
314 void visitBlock(Block block) {
262 blockOut(block, shouldIndent: true, needsNewline: true); 315 blockOut(block, shouldIndent: true, needsNewline: true);
263 } 316 }
264 317
265 visitExpressionStatement(ExpressionStatement expressionStatement) { 318 @override
319 void visitExpressionStatement(ExpressionStatement node) {
266 indent(); 320 indent();
267 visitNestedExpression(expressionStatement.expression, EXPRESSION, 321 visitNestedExpression(node.expression, EXPRESSION,
268 newInForInit: false, newAtStatementBegin: true); 322 newInForInit: false, newAtStatementBegin: true);
269 outSemicolonLn(); 323 outSemicolonLn();
270 } 324 }
271 325
272 visitEmptyStatement(EmptyStatement nop) { 326 @override
327 void visitEmptyStatement(EmptyStatement node) {
273 outIndentLn(";"); 328 outIndentLn(";");
274 } 329 }
275 330
276 void ifOut(If node, bool shouldIndent) { 331 void ifOut(If node, bool shouldIndent) {
277 Statement then = unwrapBlockIfSingleStatement(node.then); 332 Statement then = unwrapBlockIfSingleStatement(node.then);
278 Statement elsePart = node.otherwise; 333 Statement elsePart = node.otherwise;
279 bool hasElse = node.hasElse; 334 bool hasElse = node.hasElse;
280 335
281 // Handle dangling elses and a work-around for Android 4.0 stock browser. 336 // Handle dangling elses and a work-around for Android 4.0 stock browser.
282 // Android 4.0 requires braces for a single do-while in the `then` branch. 337 // Android 4.0 requires braces for a single do-while in the `then` branch.
(...skipping 23 matching lines...) Expand all
306 if (elsePart is If) { 361 if (elsePart is If) {
307 pendingSpace = true; 362 pendingSpace = true;
308 ifOut(elsePart, false); 363 ifOut(elsePart, false);
309 } else { 364 } else {
310 blockBody(unwrapBlockIfSingleStatement(elsePart), 365 blockBody(unwrapBlockIfSingleStatement(elsePart),
311 needsSeparation: true, needsNewline: true); 366 needsSeparation: true, needsNewline: true);
312 } 367 }
313 } 368 }
314 } 369 }
315 370
316 visitIf(If node) { 371 @override
372 void visitIf(If node) {
317 ifOut(node, true); 373 ifOut(node, true);
318 } 374 }
319 375
320 visitFor(For loop) { 376 @override
377 void visitFor(For loop) {
321 outIndent("for"); 378 outIndent("for");
322 spaceOut(); 379 spaceOut();
323 out("("); 380 out("(");
324 if (loop.init != null) { 381 if (loop.init != null) {
325 visitNestedExpression(loop.init, EXPRESSION, 382 visitNestedExpression(loop.init, EXPRESSION,
326 newInForInit: true, newAtStatementBegin: false); 383 newInForInit: true, newAtStatementBegin: false);
327 } 384 }
328 out(";"); 385 out(";");
329 if (loop.condition != null) { 386 if (loop.condition != null) {
330 spaceOut(); 387 spaceOut();
331 visitNestedExpression(loop.condition, EXPRESSION, 388 visitNestedExpression(loop.condition, EXPRESSION,
332 newInForInit: false, newAtStatementBegin: false); 389 newInForInit: false, newAtStatementBegin: false);
333 } 390 }
334 out(";"); 391 out(";");
335 if (loop.update != null) { 392 if (loop.update != null) {
336 spaceOut(); 393 spaceOut();
337 visitNestedExpression(loop.update, EXPRESSION, 394 visitNestedExpression(loop.update, EXPRESSION,
338 newInForInit: false, newAtStatementBegin: false); 395 newInForInit: false, newAtStatementBegin: false);
339 } 396 }
340 out(")"); 397 out(")");
341 blockBody(unwrapBlockIfSingleStatement(loop.body), 398 blockBody(unwrapBlockIfSingleStatement(loop.body),
342 needsSeparation: false, needsNewline: true); 399 needsSeparation: false, needsNewline: true);
343 } 400 }
344 401
345 visitForIn(ForIn loop) { 402 @override
403 void visitForIn(ForIn loop) {
346 outIndent("for"); 404 outIndent("for");
347 spaceOut(); 405 spaceOut();
348 out("("); 406 out("(");
349 visitNestedExpression(loop.leftHandSide, EXPRESSION, 407 visitNestedExpression(loop.leftHandSide, EXPRESSION,
350 newInForInit: true, newAtStatementBegin: false); 408 newInForInit: true, newAtStatementBegin: false);
351 out(" in"); 409 out(" in");
352 pendingSpace = true; 410 pendingSpace = true;
353 visitNestedExpression(loop.object, EXPRESSION, 411 visitNestedExpression(loop.object, EXPRESSION,
354 newInForInit: false, newAtStatementBegin: false); 412 newInForInit: false, newAtStatementBegin: false);
355 out(")"); 413 out(")");
356 blockBody(unwrapBlockIfSingleStatement(loop.body), 414 blockBody(unwrapBlockIfSingleStatement(loop.body),
357 needsSeparation: false, needsNewline: true); 415 needsSeparation: false, needsNewline: true);
358 } 416 }
359 417
360 visitWhile(While loop) { 418 @override
419 void visitWhile(While loop) {
361 outIndent("while"); 420 outIndent("while");
362 spaceOut(); 421 spaceOut();
363 out("("); 422 out("(");
364 visitNestedExpression(loop.condition, EXPRESSION, 423 visitNestedExpression(loop.condition, EXPRESSION,
365 newInForInit: false, newAtStatementBegin: false); 424 newInForInit: false, newAtStatementBegin: false);
366 out(")"); 425 out(")");
367 blockBody(unwrapBlockIfSingleStatement(loop.body), 426 blockBody(unwrapBlockIfSingleStatement(loop.body),
368 needsSeparation: false, needsNewline: true); 427 needsSeparation: false, needsNewline: true);
369 } 428 }
370 429
371 visitDo(Do loop) { 430 @override
431 void visitDo(Do loop) {
372 outIndent("do"); 432 outIndent("do");
373 if (blockBody(unwrapBlockIfSingleStatement(loop.body), 433 if (blockBody(unwrapBlockIfSingleStatement(loop.body),
374 needsSeparation: true, needsNewline: false)) { 434 needsSeparation: true, needsNewline: false)) {
375 spaceOut(); 435 spaceOut();
376 } else { 436 } else {
377 indent(); 437 indent();
378 } 438 }
379 out("while"); 439 out("while");
380 spaceOut(); 440 spaceOut();
381 out("("); 441 out("(");
382 visitNestedExpression(loop.condition, EXPRESSION, 442 visitNestedExpression(loop.condition, EXPRESSION,
383 newInForInit: false, newAtStatementBegin: false); 443 newInForInit: false, newAtStatementBegin: false);
384 out(")"); 444 out(")");
385 outSemicolonLn(); 445 outSemicolonLn();
386 } 446 }
387 447
388 visitContinue(Continue node) { 448 @override
449 void visitContinue(Continue node) {
389 if (node.targetLabel == null) { 450 if (node.targetLabel == null) {
390 outIndent("continue"); 451 outIndent("continue");
391 } else { 452 } else {
392 outIndent("continue ${node.targetLabel}"); 453 outIndent("continue ${node.targetLabel}");
393 } 454 }
394 outSemicolonLn(); 455 outSemicolonLn();
395 } 456 }
396 457
397 visitBreak(Break node) { 458 @override
459 void visitBreak(Break node) {
398 if (node.targetLabel == null) { 460 if (node.targetLabel == null) {
399 outIndent("break"); 461 outIndent("break");
400 } else { 462 } else {
401 outIndent("break ${node.targetLabel}"); 463 outIndent("break ${node.targetLabel}");
402 } 464 }
403 outSemicolonLn(); 465 outSemicolonLn();
404 } 466 }
405 467
406 visitReturn(Return node) { 468 @override
469 void visitReturn(Return node) {
407 if (node.value == null) { 470 if (node.value == null) {
408 outIndent("return"); 471 outIndent("return");
409 } else { 472 } else {
410 outIndent("return"); 473 outIndent("return");
411 pendingSpace = true; 474 pendingSpace = true;
412 visitNestedExpression(node.value, EXPRESSION, 475 visitNestedExpression(node.value, EXPRESSION,
413 newInForInit: false, newAtStatementBegin: false); 476 newInForInit: false, newAtStatementBegin: false);
414 } 477 }
415 outSemicolonLn(); 478 outSemicolonLn();
416 } 479 }
417 480
418 visitDartYield(DartYield node) { 481 @override
482 void visitDartYield(DartYield node) {
419 if (node.hasStar) { 483 if (node.hasStar) {
420 outIndent("yield*"); 484 outIndent("yield*");
421 } else { 485 } else {
422 outIndent("yield"); 486 outIndent("yield");
423 } 487 }
424 pendingSpace = true; 488 pendingSpace = true;
425 visitNestedExpression(node.expression, EXPRESSION, 489 visitNestedExpression(node.expression, EXPRESSION,
426 newInForInit: false, newAtStatementBegin: false); 490 newInForInit: false, newAtStatementBegin: false);
427 outSemicolonLn(); 491 outSemicolonLn();
428 } 492 }
429 493
430 494 @override
431 visitThrow(Throw node) { 495 void visitThrow(Throw node) {
432 outIndent("throw"); 496 outIndent("throw");
433 pendingSpace = true; 497 pendingSpace = true;
434 visitNestedExpression(node.expression, EXPRESSION, 498 visitNestedExpression(node.expression, EXPRESSION,
435 newInForInit: false, newAtStatementBegin: false); 499 newInForInit: false, newAtStatementBegin: false);
436 outSemicolonLn(); 500 outSemicolonLn();
437 } 501 }
438 502
439 visitTry(Try node) { 503 @override
504 void visitTry(Try node) {
440 outIndent("try"); 505 outIndent("try");
441 blockBody(node.body, needsSeparation: true, needsNewline: false); 506 blockBody(node.body, needsSeparation: true, needsNewline: false);
442 if (node.catchPart != null) { 507 if (node.catchPart != null) {
443 visit(node.catchPart); 508 visit(node.catchPart);
444 } 509 }
445 if (node.finallyPart != null) { 510 if (node.finallyPart != null) {
446 spaceOut(); 511 spaceOut();
447 out("finally"); 512 out("finally");
448 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true); 513 blockBody(node.finallyPart, needsSeparation: true, needsNewline: true);
449 } else { 514 } else {
450 lineOut(); 515 lineOut();
451 } 516 }
452 } 517 }
453 518
454 visitCatch(Catch node) { 519 @override
520 void visitCatch(Catch node) {
455 spaceOut(); 521 spaceOut();
456 out("catch"); 522 out("catch");
457 spaceOut(); 523 spaceOut();
458 out("("); 524 out("(");
459 visitNestedExpression(node.declaration, EXPRESSION, 525 visitNestedExpression(node.declaration, EXPRESSION,
460 newInForInit: false, newAtStatementBegin: false); 526 newInForInit: false, newAtStatementBegin: false);
461 out(")"); 527 out(")");
462 blockBody(node.body, needsSeparation: false, needsNewline: false); 528 blockBody(node.body, needsSeparation: false, needsNewline: false);
463 } 529 }
464 530
465 visitSwitch(Switch node) { 531 @override
532 void visitSwitch(Switch node) {
466 outIndent("switch"); 533 outIndent("switch");
467 spaceOut(); 534 spaceOut();
468 out("("); 535 out("(");
469 visitNestedExpression(node.key, EXPRESSION, 536 visitNestedExpression(node.key, EXPRESSION,
470 newInForInit: false, newAtStatementBegin: false); 537 newInForInit: false, newAtStatementBegin: false);
471 out(")"); 538 out(")");
472 spaceOut(); 539 spaceOut();
473 outLn("{"); 540 outLn("{");
474 indentMore(); 541 indentMore();
475 visitAll(node.cases); 542 visitAll(node.cases);
476 indentLess(); 543 indentLess();
477 outIndentLn("}"); 544 outIndentLn("}");
478 } 545 }
479 546
480 visitCase(Case node) { 547 @override
548 void visitCase(Case node) {
481 outIndent("case"); 549 outIndent("case");
482 pendingSpace = true; 550 pendingSpace = true;
483 visitNestedExpression(node.expression, EXPRESSION, 551 visitNestedExpression(node.expression, EXPRESSION,
484 newInForInit: false, newAtStatementBegin: false); 552 newInForInit: false, newAtStatementBegin: false);
485 outLn(":"); 553 outLn(":");
486 if (!node.body.statements.isEmpty) { 554 if (!node.body.statements.isEmpty) {
487 indentMore(); 555 indentMore();
488 blockOutWithoutBraces(node.body); 556 blockOutWithoutBraces(node.body);
489 indentLess(); 557 indentLess();
490 } 558 }
491 } 559 }
492 560
493 visitDefault(Default node) { 561 @override
562 void visitDefault(Default node) {
494 outIndentLn("default:"); 563 outIndentLn("default:");
495 if (!node.body.statements.isEmpty) { 564 if (!node.body.statements.isEmpty) {
496 indentMore(); 565 indentMore();
497 blockOutWithoutBraces(node.body); 566 blockOutWithoutBraces(node.body);
498 indentLess(); 567 indentLess();
499 } 568 }
500 } 569 }
501 570
502 visitLabeledStatement(LabeledStatement node) { 571 @override
572 void visitLabeledStatement(LabeledStatement node) {
503 Statement body = unwrapBlockIfSingleStatement(node.body); 573 Statement body = unwrapBlockIfSingleStatement(node.body);
504 // `label: break label;` 574 // `label: break label;`
505 // Does not work on IE. The statement is a nop, so replace it by an empty 575 // Does not work on IE. The statement is a nop, so replace it by an empty
506 // statement. 576 // statement.
507 // See: 577 // See:
508 // https://connect.microsoft.com/IE/feedback/details/891889/parser-bugs 578 // https://connect.microsoft.com/IE/feedback/details/891889/parser-bugs
509 if (body is Break && body.targetLabel == node.label) { 579 if (body is Break && body.targetLabel == node.label) {
510 visit(new EmptyStatement()); 580 visit(new EmptyStatement());
511 return; 581 return;
512 } 582 }
513 outIndent("${node.label}:"); 583 outIndent("${node.label}:");
514 blockBody(body, needsSeparation: false, needsNewline: true); 584 blockBody(body, needsSeparation: false, needsNewline: true);
515 } 585 }
516 586
517 void functionOut(Fun fun, Node name, VarCollector vars) { 587 int functionOut(Fun fun, Node name, VarCollector vars) {
518 out("function"); 588 out("function");
519 if (name != null) { 589 if (name != null) {
520 out(" "); 590 out(" ");
521 // Name must be a [Decl]. Therefore only test for primary expressions. 591 // Name must be a [Decl]. Therefore only test for primary expressions.
522 visitNestedExpression(name, PRIMARY, 592 visitNestedExpression(name, PRIMARY,
523 newInForInit: false, newAtStatementBegin: false); 593 newInForInit: false, newAtStatementBegin: false);
524 } 594 }
525 localNamer.enterScope(vars); 595 localNamer.enterScope(vars);
526 out("("); 596 out("(");
527 if (fun.params != null) { 597 if (fun.params != null) {
528 visitCommaSeparated(fun.params, PRIMARY, 598 visitCommaSeparated(fun.params, PRIMARY,
529 newInForInit: false, newAtStatementBegin: false); 599 newInForInit: false, newAtStatementBegin: false);
530 } 600 }
531 out(")"); 601 out(")");
532 switch (fun.asyncModifier) { 602 switch (fun.asyncModifier) {
533 case const AsyncModifier.sync(): 603 case const AsyncModifier.sync():
534 break; 604 break;
535 case const AsyncModifier.async(): 605 case const AsyncModifier.async():
536 out(' async'); 606 out(' ', isWhitespace: true);
607 out('async');
537 break; 608 break;
538 case const AsyncModifier.syncStar(): 609 case const AsyncModifier.syncStar():
539 out(' sync*'); 610 out(' ', isWhitespace: true);
611 out('sync*');
540 break; 612 break;
541 case const AsyncModifier.asyncStar(): 613 case const AsyncModifier.asyncStar():
542 out(' async*'); 614 out(' ', isWhitespace: true);
615 out('async*');
543 break; 616 break;
544 } 617 }
545 blockBody(fun.body, needsSeparation: false, needsNewline: false); 618 spaceOut();
619 int closingPosition =
620 blockOut(fun.body, shouldIndent: false, needsNewline: false);
546 localNamer.leaveScope(); 621 localNamer.leaveScope();
622 return closingPosition;
623
547 } 624 }
548 625
626 @override
549 visitFunctionDeclaration(FunctionDeclaration declaration) { 627 visitFunctionDeclaration(FunctionDeclaration declaration) {
550 VarCollector vars = new VarCollector(); 628 VarCollector vars = new VarCollector();
551 vars.visitFunctionDeclaration(declaration); 629 vars.visitFunctionDeclaration(declaration);
552 indent(); 630 indent();
553 functionOut(declaration.function, declaration.name, vars); 631 functionOut(declaration.function, declaration.name, vars);
554 lineOut(); 632 lineOut();
555 } 633 }
556 634
557 visitNestedExpression(Expression node, int requiredPrecedence, 635 visitNestedExpression(Expression node, int requiredPrecedence,
558 {bool newInForInit, bool newAtStatementBegin}) { 636 {bool newInForInit, bool newAtStatementBegin}) {
(...skipping 14 matching lines...) Expand all
573 out("("); 651 out("(");
574 visit(node); 652 visit(node);
575 out(")"); 653 out(")");
576 } else { 654 } else {
577 inForInit = newInForInit; 655 inForInit = newInForInit;
578 atStatementBegin = newAtStatementBegin; 656 atStatementBegin = newAtStatementBegin;
579 visit(node); 657 visit(node);
580 } 658 }
581 } 659 }
582 660
661 @override
583 visitVariableDeclarationList(VariableDeclarationList list) { 662 visitVariableDeclarationList(VariableDeclarationList list) {
584 out("var "); 663 out("var ");
585 visitCommaSeparated(list.declarations, ASSIGNMENT, 664 visitCommaSeparated(list.declarations, ASSIGNMENT,
586 newInForInit: inForInit, newAtStatementBegin: false); 665 newInForInit: inForInit, newAtStatementBegin: false);
587 } 666 }
588 667
668 @override
589 visitAssignment(Assignment assignment) { 669 visitAssignment(Assignment assignment) {
590 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE, 670 visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE,
591 newInForInit: inForInit, 671 newInForInit: inForInit,
592 newAtStatementBegin: atStatementBegin); 672 newAtStatementBegin: atStatementBegin);
593 if (assignment.value != null) { 673 if (assignment.value != null) {
594 spaceOut(); 674 spaceOut();
595 String op = assignment.op; 675 String op = assignment.op;
596 if (op != null) out(op); 676 if (op != null) out(op);
597 out("="); 677 out("=");
598 spaceOut(); 678 spaceOut();
599 visitNestedExpression(assignment.value, ASSIGNMENT, 679 visitNestedExpression(assignment.value, ASSIGNMENT,
600 newInForInit: inForInit, 680 newInForInit: inForInit,
601 newAtStatementBegin: false); 681 newAtStatementBegin: false);
602 } 682 }
603 } 683 }
604 684
685 @override
605 visitVariableInitialization(VariableInitialization initialization) { 686 visitVariableInitialization(VariableInitialization initialization) {
606 visitAssignment(initialization); 687 visitAssignment(initialization);
607 } 688 }
608 689
690 @override
609 visitConditional(Conditional cond) { 691 visitConditional(Conditional cond) {
610 visitNestedExpression(cond.condition, LOGICAL_OR, 692 visitNestedExpression(cond.condition, LOGICAL_OR,
611 newInForInit: inForInit, 693 newInForInit: inForInit,
612 newAtStatementBegin: atStatementBegin); 694 newAtStatementBegin: atStatementBegin);
613 spaceOut(); 695 spaceOut();
614 out("?"); 696 out("?");
615 spaceOut(); 697 spaceOut();
616 // The then part is allowed to have an 'in'. 698 // The then part is allowed to have an 'in'.
617 visitNestedExpression(cond.then, ASSIGNMENT, 699 visitNestedExpression(cond.then, ASSIGNMENT,
618 newInForInit: false, newAtStatementBegin: false); 700 newInForInit: false, newAtStatementBegin: false);
619 spaceOut(); 701 spaceOut();
620 out(":"); 702 out(":");
621 spaceOut(); 703 spaceOut();
622 visitNestedExpression(cond.otherwise, ASSIGNMENT, 704 visitNestedExpression(cond.otherwise, ASSIGNMENT,
623 newInForInit: inForInit, newAtStatementBegin: false); 705 newInForInit: inForInit, newAtStatementBegin: false);
624 } 706 }
625 707
708 @override
626 visitNew(New node) { 709 visitNew(New node) {
627 out("new "); 710 out("new ");
628 visitNestedExpression(node.target, CALL, 711 visitNestedExpression(node.target, CALL,
629 newInForInit: inForInit, newAtStatementBegin: false); 712 newInForInit: inForInit, newAtStatementBegin: false);
630 out("("); 713 out("(");
631 visitCommaSeparated(node.arguments, ASSIGNMENT, 714 visitCommaSeparated(node.arguments, ASSIGNMENT,
632 newInForInit: false, newAtStatementBegin: false); 715 newInForInit: false, newAtStatementBegin: false);
633 out(")"); 716 out(")");
634 } 717 }
635 718
719 @override
636 visitCall(Call call) { 720 visitCall(Call call) {
637 visitNestedExpression(call.target, LEFT_HAND_SIDE, 721 visitNestedExpression(call.target, LEFT_HAND_SIDE,
638 newInForInit: inForInit, 722 newInForInit: inForInit,
639 newAtStatementBegin: atStatementBegin); 723 newAtStatementBegin: atStatementBegin);
640 out("("); 724 out("(");
641 visitCommaSeparated(call.arguments, ASSIGNMENT, 725 visitCommaSeparated(call.arguments, ASSIGNMENT,
642 newInForInit: false, newAtStatementBegin: false); 726 newInForInit: false, newAtStatementBegin: false);
643 out(")"); 727 out(")");
644 } 728 }
645 729
646 visitBinary(Binary binary) { 730 @override
731 void visitBinary(Binary binary) {
647 Expression left = binary.left; 732 Expression left = binary.left;
648 Expression right = binary.right; 733 Expression right = binary.right;
649 String op = binary.op; 734 String op = binary.op;
650 int leftPrecedenceRequirement; 735 int leftPrecedenceRequirement;
651 int rightPrecedenceRequirement; 736 int rightPrecedenceRequirement;
652 bool leftSpace = true; // left<HERE>op right 737 bool leftSpace = true; // left<HERE>op right
653 switch (op) { 738 switch (op) {
654 case ',': 739 case ',':
655 // x, (y, z) <=> (x, y), z. 740 // x, (y, z) <=> (x, y), z.
656 leftPrecedenceRequirement = EXPRESSION; 741 leftPrecedenceRequirement = EXPRESSION;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 context.error("Forgot operator: $op"); 810 context.error("Forgot operator: $op");
726 } 811 }
727 812
728 visitNestedExpression(left, leftPrecedenceRequirement, 813 visitNestedExpression(left, leftPrecedenceRequirement,
729 newInForInit: inForInit, 814 newInForInit: inForInit,
730 newAtStatementBegin: atStatementBegin); 815 newAtStatementBegin: atStatementBegin);
731 816
732 if (op == "in" || op == "instanceof") { 817 if (op == "in" || op == "instanceof") {
733 // There are cases where the space is not required but without further 818 // There are cases where the space is not required but without further
734 // analysis we cannot know. 819 // analysis we cannot know.
735 out(" "); 820 out(" ", isWhitespace: true);
736 out(op); 821 out(op);
737 out(" "); 822 out(" ", isWhitespace: true);
738 } else { 823 } else {
739 if (leftSpace) spaceOut(); 824 if (leftSpace) spaceOut();
740 out(op); 825 out(op);
741 spaceOut(); 826 spaceOut();
742 } 827 }
743 visitNestedExpression(right, rightPrecedenceRequirement, 828 visitNestedExpression(right, rightPrecedenceRequirement,
744 newInForInit: inForInit, 829 newInForInit: inForInit,
745 newAtStatementBegin: false); 830 newAtStatementBegin: false);
746 } 831 }
747 832
748 visitPrefix(Prefix unary) { 833 @override
834 void visitPrefix(Prefix unary) {
749 String op = unary.op; 835 String op = unary.op;
750 switch (op) { 836 switch (op) {
751 case "delete": 837 case "delete":
752 case "void": 838 case "void":
753 case "typeof": 839 case "typeof":
754 // There are cases where the space is not required but without further 840 // There are cases where the space is not required but without further
755 // analysis we cannot know. 841 // analysis we cannot know.
756 out(op); 842 out(op);
757 out(" "); 843 out(" ", isWhitespace: true);
758 break; 844 break;
759 case "+": 845 case "+":
760 case "++": 846 case "++":
761 if (lastCharCode == charCodes.$PLUS) out(" "); 847 if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true);
762 out(op); 848 out(op);
763 break; 849 break;
764 case "-": 850 case "-":
765 case "--": 851 case "--":
766 if (lastCharCode == charCodes.$MINUS) out(" "); 852 if (lastCharCode == charCodes.$MINUS) out(" ", isWhitespace: true);
767 out(op); 853 out(op);
768 break; 854 break;
769 default: 855 default:
770 out(op); 856 out(op);
771 } 857 }
772 visitNestedExpression(unary.argument, UNARY, 858 visitNestedExpression(unary.argument, UNARY,
773 newInForInit: inForInit, newAtStatementBegin: false); 859 newInForInit: inForInit, newAtStatementBegin: false);
774 } 860 }
775 861
776 visitPostfix(Postfix postfix) { 862 @override
863 void visitPostfix(Postfix postfix) {
777 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE, 864 visitNestedExpression(postfix.argument, LEFT_HAND_SIDE,
778 newInForInit: inForInit, 865 newInForInit: inForInit,
779 newAtStatementBegin: atStatementBegin); 866 newAtStatementBegin: atStatementBegin);
780 out(postfix.op); 867 out(postfix.op);
781 } 868 }
782 869
783 visitVariableUse(VariableUse ref) { 870 @override
871 void visitVariableUse(VariableUse ref) {
784 out(localNamer.getName(ref.name)); 872 out(localNamer.getName(ref.name));
785 } 873 }
786 874
787 visitThis(This node) { 875 @override
876 void visitThis(This node) {
788 out("this"); 877 out("this");
789 } 878 }
790 879
791 visitVariableDeclaration(VariableDeclaration decl) { 880 @override
881 void visitVariableDeclaration(VariableDeclaration decl) {
792 out(localNamer.getName(decl.name)); 882 out(localNamer.getName(decl.name));
793 } 883 }
794 884
795 visitParameter(Parameter param) { 885 @override
886 void visitParameter(Parameter param) {
796 out(localNamer.getName(param.name)); 887 out(localNamer.getName(param.name));
797 } 888 }
798 889
799 bool isDigit(int charCode) { 890 bool isDigit(int charCode) {
800 return charCodes.$0 <= charCode && charCode <= charCodes.$9; 891 return charCodes.$0 <= charCode && charCode <= charCodes.$9;
801 } 892 }
802 893
803 bool isValidJavaScriptId(String field) { 894 bool isValidJavaScriptId(String field) {
804 if (field.length < 3) return false; 895 if (field.length < 3) return false;
805 // Ignore the leading and trailing string-delimiter. 896 // Ignore the leading and trailing string-delimiter.
806 for (int i = 1; i < field.length - 1; i++) { 897 for (int i = 1; i < field.length - 1; i++) {
807 // TODO(floitsch): allow more characters. 898 // TODO(floitsch): allow more characters.
808 int charCode = field.codeUnitAt(i); 899 int charCode = field.codeUnitAt(i);
809 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z || 900 if (!(charCodes.$a <= charCode && charCode <= charCodes.$z ||
810 charCodes.$A <= charCode && charCode <= charCodes.$Z || 901 charCodes.$A <= charCode && charCode <= charCodes.$Z ||
811 charCode == charCodes.$$ || 902 charCode == charCodes.$$ ||
812 charCode == charCodes.$_ || 903 charCode == charCodes.$_ ||
813 i != 1 && isDigit(charCode))) { 904 i != 1 && isDigit(charCode))) {
814 return false; 905 return false;
815 } 906 }
816 } 907 }
817 // TODO(floitsch): normally we should also check that the field is not a 908 // TODO(floitsch): normally we should also check that the field is not a
818 // reserved word. We don't generate fields with reserved word names except 909 // reserved word. We don't generate fields with reserved word names except
819 // for 'super'. 910 // for 'super'.
820 if (field == '"super"') return false; 911 if (field == '"super"') return false;
821 return true; 912 return true;
822 } 913 }
823 914
824 visitAccess(PropertyAccess access) { 915 @override
916 void visitAccess(PropertyAccess access) {
825 visitNestedExpression(access.receiver, CALL, 917 visitNestedExpression(access.receiver, CALL,
826 newInForInit: inForInit, 918 newInForInit: inForInit,
827 newAtStatementBegin: atStatementBegin); 919 newAtStatementBegin: atStatementBegin);
828 Node selector = access.selector; 920 Node selector = access.selector;
829 if (selector is LiteralString) { 921 if (selector is LiteralString) {
830 LiteralString selectorString = selector; 922 LiteralString selectorString = selector;
831 String fieldWithQuotes = selectorString.value; 923 String fieldWithQuotes = selectorString.value;
832 if (isValidJavaScriptId(fieldWithQuotes)) { 924 if (isValidJavaScriptId(fieldWithQuotes)) {
833 if (access.receiver is LiteralNumber) out(" "); 925 if (access.receiver is LiteralNumber) out(" ", isWhitespace: true);
834 out("."); 926 out(".");
835 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1)); 927 out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1));
836 return; 928 return;
837 } 929 }
838 } 930 }
839 out("["); 931 out("[");
840 visitNestedExpression(selector, EXPRESSION, 932 visitNestedExpression(selector, EXPRESSION,
841 newInForInit: false, newAtStatementBegin: false); 933 newInForInit: false, newAtStatementBegin: false);
842 out("]"); 934 out("]");
843 } 935 }
844 936
845 visitNamedFunction(NamedFunction namedFunction) { 937 @override
938 void visitNamedFunction(NamedFunction namedFunction) {
846 VarCollector vars = new VarCollector(); 939 VarCollector vars = new VarCollector();
847 vars.visitNamedFunction(namedFunction); 940 vars.visitNamedFunction(namedFunction);
848 functionOut(namedFunction.function, namedFunction.name, vars); 941 startNode(namedFunction.function);
942 currentNode.closingPosition =
943 functionOut(namedFunction.function, namedFunction.name, vars);
944 endNode(namedFunction.function);
849 } 945 }
850 946
851 visitFun(Fun fun) { 947 @override
948 void visitFun(Fun fun) {
852 VarCollector vars = new VarCollector(); 949 VarCollector vars = new VarCollector();
853 vars.visitFun(fun); 950 vars.visitFun(fun);
854 functionOut(fun, null, vars); 951 currentNode.closingPosition = functionOut(fun, null, vars);
855 } 952 }
856 953
857 visitLiteralBool(LiteralBool node) { 954 @override
955 void visitLiteralBool(LiteralBool node) {
858 out(node.value ? "true" : "false"); 956 out(node.value ? "true" : "false");
859 } 957 }
860 958
861 visitLiteralString(LiteralString node) { 959 @override
960 void visitLiteralString(LiteralString node) {
862 out(node.value); 961 out(node.value);
863 } 962 }
864 963
865 visitLiteralNumber(LiteralNumber node) { 964 @override
965 void visitLiteralNumber(LiteralNumber node) {
866 int charCode = node.value.codeUnitAt(0); 966 int charCode = node.value.codeUnitAt(0);
867 if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) { 967 if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) {
868 out(" "); 968 out(" ", isWhitespace: true);
869 } 969 }
870 out(node.value); 970 out(node.value);
871 } 971 }
872 972
873 visitLiteralNull(LiteralNull node) { 973 @override
974 void visitLiteralNull(LiteralNull node) {
874 out("null"); 975 out("null");
875 } 976 }
876 977
877 visitArrayInitializer(ArrayInitializer node) { 978 @override
979 void visitArrayInitializer(ArrayInitializer node) {
878 out("["); 980 out("[");
879 List<Expression> elements = node.elements; 981 List<Expression> elements = node.elements;
880 for (int i = 0; i < elements.length; i++) { 982 for (int i = 0; i < elements.length; i++) {
881 Expression element = elements[i]; 983 Expression element = elements[i];
882 if (element is ArrayHole) { 984 if (element is ArrayHole) {
883 // Note that array holes must have a trailing "," even if they are 985 // Note that array holes must have a trailing "," even if they are
884 // in last position. Otherwise `[,]` (having length 1) would become 986 // in last position. Otherwise `[,]` (having length 1) would become
885 // equal to `[]` (the empty array) 987 // equal to `[]` (the empty array)
886 // and [1,,] (array with 1 and a hole) would become [1,] = [1]. 988 // and [1,,] (array with 1 and a hole) would become [1,] = [1].
887 out(","); 989 out(",");
888 continue; 990 continue;
889 } 991 }
890 if (i != 0) spaceOut(); 992 if (i != 0) spaceOut();
891 visitNestedExpression(element, ASSIGNMENT, 993 visitNestedExpression(element, ASSIGNMENT,
892 newInForInit: false, newAtStatementBegin: false); 994 newInForInit: false, newAtStatementBegin: false);
893 // We can skip the trailing "," for the last element (since it's not 995 // We can skip the trailing "," for the last element (since it's not
894 // an array hole). 996 // an array hole).
895 if (i != elements.length - 1) out(","); 997 if (i != elements.length - 1) out(",");
896 } 998 }
897 out("]"); 999 out("]");
898 } 1000 }
899 1001
900 visitArrayHole(ArrayHole node) { 1002 @override
1003 void visitArrayHole(ArrayHole node) {
901 throw "Unreachable"; 1004 throw "Unreachable";
902 } 1005 }
903 1006
904 visitObjectInitializer(ObjectInitializer node) { 1007 @override
1008 void visitObjectInitializer(ObjectInitializer node) {
905 // Print all the properties on one line until we see a function-valued 1009 // Print all the properties on one line until we see a function-valued
906 // property. Ideally, we would use a proper pretty-printer to make the 1010 // property. Ideally, we would use a proper pretty-printer to make the
907 // decision based on layout. 1011 // decision based on layout.
908 List<Property> properties = node.properties; 1012 List<Property> properties = node.properties;
909 out("{"); 1013 out("{");
910 indentMore(); 1014 indentMore();
911 for (int i = 0; i < properties.length; i++) { 1015 for (int i = 0; i < properties.length; i++) {
912 Expression value = properties[i].value; 1016 Expression value = properties[i].value;
913 if (i != 0) { 1017 if (i != 0) {
914 out(","); 1018 out(",");
915 if (node.isOneLiner) spaceOut(); 1019 if (node.isOneLiner) spaceOut();
916 } 1020 }
917 if (!node.isOneLiner) { 1021 if (!node.isOneLiner) {
918 forceLine(); 1022 forceLine();
919 indent(); 1023 indent();
920 } 1024 }
921 visit(properties[i]); 1025 visit(properties[i]);
922 } 1026 }
923 indentLess(); 1027 indentLess();
924 if (!node.isOneLiner && !properties.isEmpty) { 1028 if (!node.isOneLiner && !properties.isEmpty) {
925 lineOut(); 1029 lineOut();
926 indent(); 1030 indent();
927 } 1031 }
928 out("}"); 1032 out("}");
929 } 1033 }
930 1034
931 visitProperty(Property node) { 1035 @override
1036 void visitProperty(Property node) {
932 if (node.name is LiteralString) { 1037 if (node.name is LiteralString) {
933 LiteralString nameString = node.name; 1038 LiteralString nameString = node.name;
934 String name = nameString.value; 1039 String name = nameString.value;
935 if (isValidJavaScriptId(name)) { 1040 if (isValidJavaScriptId(name)) {
936 out(name.substring(1, name.length - 1)); 1041 out(name.substring(1, name.length - 1));
937 } else { 1042 } else {
938 out(name); 1043 out(name);
939 } 1044 }
940 } else { 1045 } else {
941 assert(node.name is LiteralNumber); 1046 assert(node.name is LiteralNumber);
942 LiteralNumber nameNumber = node.name; 1047 LiteralNumber nameNumber = node.name;
943 out(nameNumber.value); 1048 out(nameNumber.value);
944 } 1049 }
945 out(":"); 1050 out(":");
946 spaceOut(); 1051 spaceOut();
947 visitNestedExpression(node.value, ASSIGNMENT, 1052 visitNestedExpression(node.value, ASSIGNMENT,
948 newInForInit: false, newAtStatementBegin: false); 1053 newInForInit: false, newAtStatementBegin: false);
949 } 1054 }
950 1055
951 visitRegExpLiteral(RegExpLiteral node) { 1056 @override
1057 void visitRegExpLiteral(RegExpLiteral node) {
952 out(node.pattern); 1058 out(node.pattern);
953 } 1059 }
954 1060
955 visitLiteralExpression(LiteralExpression node) { 1061 @override
1062 void visitLiteralExpression(LiteralExpression node) {
956 String template = node.template; 1063 String template = node.template;
957 List<Expression> inputs = node.inputs; 1064 List<Expression> inputs = node.inputs;
958 1065
959 List<String> parts = template.split('#'); 1066 List<String> parts = template.split('#');
960 int inputsLength = inputs == null ? 0 : inputs.length; 1067 int inputsLength = inputs == null ? 0 : inputs.length;
961 if (parts.length != inputsLength + 1) { 1068 if (parts.length != inputsLength + 1) {
962 context.error('Wrong number of arguments for JS: $template'); 1069 context.error('Wrong number of arguments for JS: $template');
963 } 1070 }
964 // Code that uses JS must take care of operator precedences, and 1071 // Code that uses JS must take care of operator precedences, and
965 // put parenthesis if needed. 1072 // put parenthesis if needed.
966 out(parts[0]); 1073 out(parts[0]);
967 for (int i = 0; i < inputsLength; i++) { 1074 for (int i = 0; i < inputsLength; i++) {
968 visit(inputs[i]); 1075 visit(inputs[i]);
969 out(parts[i + 1]); 1076 out(parts[i + 1]);
970 } 1077 }
971 } 1078 }
972 1079
973 visitLiteralStatement(LiteralStatement node) { 1080 @override
1081 void visitLiteralStatement(LiteralStatement node) {
974 outLn(node.code); 1082 outLn(node.code);
975 } 1083 }
976 1084
977 visitInterpolatedNode(InterpolatedNode node) { 1085 void visitInterpolatedNode(InterpolatedNode node) {
978 out('#${node.nameOrPosition}'); 1086 out('#${node.nameOrPosition}');
979 } 1087 }
980 1088
981 visitInterpolatedExpression(InterpolatedExpression node) => 1089 @override
1090 void visitInterpolatedExpression(InterpolatedExpression node) =>
982 visitInterpolatedNode(node); 1091 visitInterpolatedNode(node);
983 1092
984 visitInterpolatedLiteral(InterpolatedLiteral node) => 1093 @override
1094 void visitInterpolatedLiteral(InterpolatedLiteral node) =>
985 visitInterpolatedNode(node); 1095 visitInterpolatedNode(node);
986 1096
987 visitInterpolatedParameter(InterpolatedParameter node) => 1097 @override
1098 void visitInterpolatedParameter(InterpolatedParameter node) =>
988 visitInterpolatedNode(node); 1099 visitInterpolatedNode(node);
989 1100
990 visitInterpolatedSelector(InterpolatedSelector node) => 1101 @override
1102 void visitInterpolatedSelector(InterpolatedSelector node) =>
991 visitInterpolatedNode(node); 1103 visitInterpolatedNode(node);
992 1104
993 visitInterpolatedStatement(InterpolatedStatement node) { 1105 @override
1106 void visitInterpolatedStatement(InterpolatedStatement node) {
994 outLn('#${node.nameOrPosition}'); 1107 outLn('#${node.nameOrPosition}');
995 } 1108 }
996 1109
997 visitInterpolatedDeclaration(InterpolatedDeclaration node) { 1110 @override
1111 void visitInterpolatedDeclaration(InterpolatedDeclaration node) {
998 visitInterpolatedNode(node); 1112 visitInterpolatedNode(node);
999 } 1113 }
1000 1114
1115 @override
1001 void visitComment(Comment node) { 1116 void visitComment(Comment node) {
1002 if (shouldCompressOutput) return; 1117 if (shouldCompressOutput) return;
1003 String comment = node.comment.trim(); 1118 String comment = node.comment.trim();
1004 if (comment.isEmpty) return; 1119 if (comment.isEmpty) return;
1005 for (var line in comment.split('\n')) { 1120 for (var line in comment.split('\n')) {
1006 if (comment.startsWith('//')) { 1121 if (comment.startsWith('//')) {
1007 outIndentLn(line.trim()); 1122 outIndentLn(line.trim());
1008 } else { 1123 } else {
1009 outIndentLn('// ${line.trim()}'); 1124 outIndentLn('// ${line.trim()}');
1010 } 1125 }
1011 } 1126 }
1012 } 1127 }
1013 1128
1129 @override
1014 void visitAwait(Await node) { 1130 void visitAwait(Await node) {
1015 out("await "); 1131 out("await ");
1016 visit(node.expression); 1132 visit(node.expression);
1017 } 1133 }
1018 } 1134 }
1019 1135
1020 1136
1021 class OrderedSet<T> { 1137 class OrderedSet<T> {
1022 final Set<T> set; 1138 final Set<T> set;
1023 final List<T> list; 1139 final List<T> list;
1024 1140
1025 OrderedSet() : set = new Set<T>(), list = <T>[]; 1141 OrderedSet() : set = new Set<T>(), list = <T>[];
1026 1142
1027 void add(T x) { 1143 void add(T x) {
1028 if (!set.contains(x)) { 1144 if (set.add(x)) {
1029 set.add(x); 1145 // [Set.add] returns `true` if 'x' was added.
1030 list.add(x); 1146 list.add(x);
1031 } 1147 }
1032 } 1148 }
1033 1149
1034 void forEach(void fun(T x)) { 1150 void forEach(void fun(T x)) {
1035 list.forEach(fun); 1151 list.forEach(fun);
1036 } 1152 }
1037 } 1153 }
1038 1154
1039 // Collects all the var declarations in the function. We need to do this in a 1155 // Collects all the var declarations in the function. We need to do this in a
(...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after
1267 codes.add(nthLetter((n ~/ nameSpaceSize) % LETTERS)); 1383 codes.add(nthLetter((n ~/ nameSpaceSize) % LETTERS));
1268 } 1384 }
1269 codes.add(charCodes.$0 + digit); 1385 codes.add(charCodes.$0 + digit);
1270 newName = new String.fromCharCodes(codes); 1386 newName = new String.fromCharCodes(codes);
1271 } 1387 }
1272 assert(new RegExp(r'[a-zA-Z][a-zA-Z0-9]*').hasMatch(newName)); 1388 assert(new RegExp(r'[a-zA-Z][a-zA-Z0-9]*').hasMatch(newName));
1273 maps.last[oldName] = newName; 1389 maps.last[oldName] = newName;
1274 return newName; 1390 return newName;
1275 } 1391 }
1276 } 1392 }
1393
1394 /// Information pertaining the enter and exit callbacks for [node].
1395 class EnterExitNode {
1396 final EnterExitNode parent;
1397 final Node node;
1398
1399 int startPosition;
1400 int closingPosition;
1401
1402 EnterExitNode(this.parent, this.node);
1403
1404 void addToNode(JavaScriptPrintingContext context, int position) {
1405 if (startPosition == null) {
1406 // [position] is the start position of [node].
1407 if (parent != null) {
1408 // This might be the start position of the parent as well.
1409 parent.addToNode(context, position);
1410 }
1411 startPosition = position;
1412 context.enterNode(node, position);
1413 }
1414 }
1415
1416 EnterExitNode exitNode(JavaScriptPrintingContext context, int position) {
1417 // Enter must happen before exit.
1418 addToNode(context, position);
1419 context.exitNode(node, startPosition, position, closingPosition);
1420 return parent;
1421 }
1422 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js/js.dart ('k') | pkg/js_ast/test/printer_callback_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698