Chromium Code Reviews| 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 |