OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |