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

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

Powered by Google App Engine
This is Rietveld 408576698