| 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 /** | 5 /** |
| 6 * Pretty-prints Node tree in XML-like format. | 6 * Pretty-prints Node tree in XML-like format. |
| 7 * | 7 * |
| 8 * TODO(smok): Add main() to run from command-line to print out tree for given | 8 * TODO(smok): Add main() to run from command-line to print out tree for given |
| 9 * .dart file. | 9 * .dart file. |
| 10 */ | 10 */ |
| 11 class PrettyPrinter implements Visitor { | 11 class PrettyPrinter implements Visitor { |
| 12 | 12 |
| 13 /** String used to represent one level of indent. */ | 13 /** String used to represent one level of indent. */ |
| 14 static const String INDENT = " "; | 14 static const String INDENT = " "; |
| 15 | 15 |
| 16 StringBuffer sb; | 16 StringBuffer sb; |
| 17 int depth; | 17 Link<String> tagStack; |
| 18 /** Prefix for the type passed to the next [openNode()] call. */ | |
| 19 String nextTypePrefix; | |
| 20 /** Nodes that were opened with [openNode()] calls. */ | |
| 21 Link<String> currentNodeTypes; | |
| 22 | 18 |
| 23 PrettyPrinter() : | 19 PrettyPrinter() : |
| 24 sb = new StringBuffer(), | 20 sb = new StringBuffer(), |
| 25 depth = 0, | 21 tagStack = new EmptyLink<String>(); |
| 26 currentNodeTypes = new EmptyLink<String>(); | |
| 27 | 22 |
| 28 void pushCurrentNodeType(String nodeType) { | 23 void pushTag(String tag) { |
| 29 currentNodeTypes = currentNodeTypes.prepend(nodeType); | 24 tagStack = tagStack.prepend(tag); |
| 30 } | 25 } |
| 31 | 26 |
| 32 String popCurrentNodeType() { | 27 String popTag() { |
| 33 assert(!currentNodeTypes.isEmpty()); | 28 assert(!tagStack.isEmpty()); |
| 34 String currentNodeType = currentNodeTypes.head; | 29 String tag = tagStack.head; |
| 35 currentNodeTypes = currentNodeTypes.tail; | 30 tagStack = tagStack.tail; |
| 36 return currentNodeType; | 31 return tag; |
| 37 } | 32 } |
| 38 | 33 |
| 39 /** | 34 /** |
| 40 * Adds given string to result string. | 35 * Adds given string to result string. |
| 41 */ | 36 */ |
| 42 void add(SourceString string) { | 37 void add(SourceString string) { |
| 43 string.printOn(sb); | 38 string.printOn(sb); |
| 44 } | 39 } |
| 45 | 40 |
| 46 void addBeginAndEndTokensToParams(Node node, Map params) { | 41 void addBeginAndEndTokensToParams(Node node, Map params) { |
| 47 params['getBeginToken'] = tokenToStringOrNull(node.getBeginToken()); | 42 params['getBeginToken'] = tokenToStringOrNull(node.getBeginToken()); |
| 48 params['getEndToken'] = tokenToStringOrNull(node.getEndToken()); | 43 params['getEndToken'] = tokenToStringOrNull(node.getEndToken()); |
| 49 } | 44 } |
| 50 | 45 |
| 51 /** | 46 /** |
| 52 * Adds given node type to result string, increasing current depth by 1. | 47 * Adds given node type to result string. |
| 53 * The method "opens" the node, meaning that all output after calling | 48 * The method "opens" the node, meaning that all output after calling |
| 54 * this method and before calling closeNode() will represent contents | 49 * this method and before calling closeNode() will represent contents |
| 55 * of given node. | 50 * of given node. |
| 56 */ | 51 */ |
| 57 void openNode(Node node, String type, [Map params]) { | 52 void openNode(Node node, String type, [Map params]) { |
| 58 if (params === null) params = new Map(); | 53 if (params === null) params = new Map(); |
| 59 addCurrentIndent(); | 54 addCurrentIndent(); |
| 60 sb.add("<"); | 55 sb.add("<"); |
| 61 addBeginAndEndTokensToParams(node, params); | 56 addBeginAndEndTokensToParams(node, params); |
| 62 addTypeWithParams(type, params); | 57 addTypeWithParams(type, params); |
| 63 sb.add(">\n"); | 58 sb.add(">\n"); |
| 64 pushCurrentNodeType(type); | 59 pushTag(type); |
| 65 depth++; | |
| 66 } | 60 } |
| 67 | 61 |
| 68 /** | 62 /** |
| 69 * Adds given node to result string, depth is not affected. | 63 * Adds given node to result string. |
| 70 */ | 64 */ |
| 71 void openAndCloseNode(Node node, String type, [Map params]) { | 65 void openAndCloseNode(Node node, String type, [Map params]) { |
| 72 if (params === null) params = new Map(); | 66 if (params === null) params = new Map(); |
| 73 addCurrentIndent(); | 67 addCurrentIndent(); |
| 74 sb.add("<"); | 68 sb.add("<"); |
| 75 addBeginAndEndTokensToParams(node, params); | 69 addBeginAndEndTokensToParams(node, params); |
| 76 addTypeWithParams(type, params); | 70 addTypeWithParams(type, params); |
| 77 sb.add("/>\n"); | 71 sb.add("/>\n"); |
| 78 } | 72 } |
| 79 | 73 |
| 80 /** | 74 /** |
| 81 * Closes current node type, decreasing current depth by 1. | 75 * Closes current node type. |
| 82 */ | 76 */ |
| 83 void closeNode() { | 77 void closeNode() { |
| 84 depth--; | 78 String tag = popTag(); |
| 85 addCurrentIndent(); | 79 addCurrentIndent(); |
| 86 sb.add("</"); | 80 sb.add("</"); |
| 87 addTypeWithParams(popCurrentNodeType()); | 81 addTypeWithParams(tag); |
| 88 sb.add(">\n"); | 82 sb.add(">\n"); |
| 89 } | 83 } |
| 90 | 84 |
| 91 void addTypeWithParams(String type, [Map params]) { | 85 void addTypeWithParams(String type, [Map params]) { |
| 92 if (params === null) params = new Map(); | 86 if (params === null) params = new Map(); |
| 93 if (nextTypePrefix !== null) { | |
| 94 sb.add(nextTypePrefix); | |
| 95 nextTypePrefix = null; | |
| 96 } | |
| 97 sb.add("${type}"); | 87 sb.add("${type}"); |
| 98 // TODO(smok): Escape doublequotes in values. | |
| 99 params.forEach((k, v) { | 88 params.forEach((k, v) { |
| 100 sb.add(' $k='); | 89 String value; |
| 101 if (v !== null) { | 90 if (v !== null) { |
| 102 sb.add('"$v"'); | 91 value = v |
| 92 .replaceAll("<", "<") |
| 93 .replaceAll(">", ">") |
| 94 .replaceAll('"', "'"); |
| 103 } else { | 95 } else { |
| 104 sb.add('null'); | 96 value = "[null]"; |
| 105 } | 97 } |
| 98 sb.add(' $k="$value"'); |
| 106 }); | 99 }); |
| 107 } | 100 } |
| 108 | 101 |
| 109 void addCurrentIndent() { | 102 void addCurrentIndent() { |
| 110 for (int i = 0; i < depth; i++) { | 103 tagStack.forEach((_) { sb.add(INDENT); }); |
| 111 sb.add(INDENT); | |
| 112 } | |
| 113 } | 104 } |
| 114 | 105 |
| 115 /** | 106 /** |
| 116 * Pretty-prints given node tree into string. | 107 * Pretty-prints given node tree into string. |
| 117 */ | 108 */ |
| 118 static String prettyPrint(Node node) { | 109 static String prettyPrint(Node node) { |
| 119 var p = new PrettyPrinter(); | 110 var p = new PrettyPrinter(); |
| 120 node.accept(p); | 111 node.accept(p); |
| 121 return p.sb.toString(); | 112 return p.sb.toString(); |
| 122 } | 113 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 148 } | 139 } |
| 149 | 140 |
| 150 visitCatchBlock(CatchBlock node) { | 141 visitCatchBlock(CatchBlock node) { |
| 151 visitNodeWithChildren(node, "CatchBlock"); | 142 visitNodeWithChildren(node, "CatchBlock"); |
| 152 } | 143 } |
| 153 | 144 |
| 154 visitClassNode(ClassNode node) { | 145 visitClassNode(ClassNode node) { |
| 155 openNode(node, "ClassNode", { | 146 openNode(node, "ClassNode", { |
| 156 "extendsKeyword" : tokenToStringOrNull(node.extendsKeyword) | 147 "extendsKeyword" : tokenToStringOrNull(node.extendsKeyword) |
| 157 }); | 148 }); |
| 158 visitWithPrefix(node.name, "name:"); | 149 visitChildNode(node.name, "name"); |
| 159 visitWithPrefix(node.superclass, "superclass:"); | 150 visitChildNode(node.superclass, "superclass"); |
| 160 visitWithPrefix(node.interfaces, "interfaces:"); | 151 visitChildNode(node.interfaces, "interfaces"); |
| 161 visitWithPrefix(node.typeParameters, "typeParameters:"); | 152 visitChildNode(node.typeParameters, "typeParameters"); |
| 162 visitWithPrefix(node.defaultClause, "defaultClause:"); | 153 visitChildNode(node.defaultClause, "defaultClause"); |
| 163 closeNode(); | 154 closeNode(); |
| 164 } | 155 } |
| 165 | 156 |
| 166 visitConditional(Conditional node) { | 157 visitConditional(Conditional node) { |
| 167 visitNodeWithChildren(node, "Conditional"); | 158 visitNodeWithChildren(node, "Conditional"); |
| 168 } | 159 } |
| 169 | 160 |
| 170 visitContinueStatement(ContinueStatement node) { | 161 visitContinueStatement(ContinueStatement node) { |
| 171 visitNodeWithChildren(node, "ContinueStatement"); | 162 visitNodeWithChildren(node, "ContinueStatement"); |
| 172 } | 163 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 192 } | 183 } |
| 193 | 184 |
| 194 visitFunctionDeclaration(FunctionDeclaration node) { | 185 visitFunctionDeclaration(FunctionDeclaration node) { |
| 195 visitNodeWithChildren(node, "FunctionDeclaration"); | 186 visitNodeWithChildren(node, "FunctionDeclaration"); |
| 196 } | 187 } |
| 197 | 188 |
| 198 visitFunctionExpression(FunctionExpression node) { | 189 visitFunctionExpression(FunctionExpression node) { |
| 199 openNode(node, "FunctionExpression", { | 190 openNode(node, "FunctionExpression", { |
| 200 "getOrSet" : tokenToStringOrNull(node.getOrSet) | 191 "getOrSet" : tokenToStringOrNull(node.getOrSet) |
| 201 }); | 192 }); |
| 202 visitWithPrefix(node.modifiers, "modifiers:"); | 193 visitChildNode(node.modifiers, "modifiers"); |
| 203 visitWithPrefix(node.returnType, "returnType:"); | 194 visitChildNode(node.returnType, "returnType"); |
| 204 visitWithPrefix(node.name, "name:"); | 195 visitChildNode(node.name, "name"); |
| 205 visitWithPrefix(node.parameters, "parameters:"); | 196 visitChildNode(node.parameters, "parameters"); |
| 206 visitWithPrefix(node.initializers, "initializers:"); | 197 visitChildNode(node.initializers, "initializers"); |
| 207 visitWithPrefix(node.body, "body:"); | 198 visitChildNode(node.body, "body"); |
| 208 closeNode(); | 199 closeNode(); |
| 209 } | 200 } |
| 210 | 201 |
| 211 visitIdentifier(Identifier node) { | 202 visitIdentifier(Identifier node) { |
| 212 openAndCloseNode(node, "Identifier", {"token" : node.token.slowToString()}); | 203 openAndCloseNode(node, "Identifier", {"token" : node.token.slowToString()}); |
| 213 } | 204 } |
| 214 | 205 |
| 215 visitIf(If node) { | 206 visitIf(If node) { |
| 216 visitNodeWithChildren(node, "If"); | 207 visitNodeWithChildren(node, "If"); |
| 217 } | 208 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 241 visitLiteral(node, "LiteralInt"); | 232 visitLiteral(node, "LiteralInt"); |
| 242 } | 233 } |
| 243 | 234 |
| 244 /** Returns token string value or [null] if token is [null]. */ | 235 /** Returns token string value or [null] if token is [null]. */ |
| 245 tokenToStringOrNull(Token token) => token === null ? null : token.stringValue; | 236 tokenToStringOrNull(Token token) => token === null ? null : token.stringValue; |
| 246 | 237 |
| 247 visitLiteralList(LiteralList node) { | 238 visitLiteralList(LiteralList node) { |
| 248 openNode(node, "LiteralList", { | 239 openNode(node, "LiteralList", { |
| 249 "constKeyword" : tokenToStringOrNull(node.constKeyword) | 240 "constKeyword" : tokenToStringOrNull(node.constKeyword) |
| 250 }); | 241 }); |
| 251 visitWithPrefix(node.typeArguments, "typeArguments:"); | 242 visitChildNode(node.typeArguments, "typeArguments"); |
| 252 visitWithPrefix(node.elements, "elements:"); | 243 visitChildNode(node.elements, "elements"); |
| 253 closeNode(); | 244 closeNode(); |
| 254 } | 245 } |
| 255 | 246 |
| 256 visitLiteralMap(LiteralMap node) { | 247 visitLiteralMap(LiteralMap node) { |
| 257 visitNodeWithChildren(node, "LiteralMap"); | 248 visitNodeWithChildren(node, "LiteralMap"); |
| 258 } | 249 } |
| 259 | 250 |
| 260 visitLiteralMapEntry(LiteralMapEntry node) { | 251 visitLiteralMapEntry(LiteralMapEntry node) { |
| 261 visitNodeWithChildren(node, "LiteralMapEntry"); | 252 visitNodeWithChildren(node, "LiteralMapEntry"); |
| 262 } | 253 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 299 visitOperator(Operator node) { | 290 visitOperator(Operator node) { |
| 300 openAndCloseNode(node, "Operator", {"value" : node.token.slowToString()}); | 291 openAndCloseNode(node, "Operator", {"value" : node.token.slowToString()}); |
| 301 } | 292 } |
| 302 | 293 |
| 303 visitParenthesizedExpression(ParenthesizedExpression node) { | 294 visitParenthesizedExpression(ParenthesizedExpression node) { |
| 304 visitNodeWithChildren(node, "ParenthesizedExpression"); | 295 visitNodeWithChildren(node, "ParenthesizedExpression"); |
| 305 } | 296 } |
| 306 | 297 |
| 307 visitReturn(Return node) { | 298 visitReturn(Return node) { |
| 308 openNode(node, "Return"); | 299 openNode(node, "Return"); |
| 309 visitWithPrefix(node.expression, "expression:"); | 300 visitChildNode(node.expression, "expression"); |
| 310 closeNode(); | 301 closeNode(); |
| 311 } | 302 } |
| 312 | 303 |
| 313 visitScriptTag(ScriptTag node) { | 304 visitScriptTag(ScriptTag node) { |
| 314 visitNodeWithChildren(node, "ScriptTag"); | 305 visitNodeWithChildren(node, "ScriptTag"); |
| 315 } | 306 } |
| 316 | 307 |
| 317 /** Custom helper to visit given node and print its type with prefix. */ | 308 visitChildNode(Node node, String fieldName) { |
| 318 visitWithPrefix(Node node, String prefix) { | |
| 319 if (node === null) return; | 309 if (node === null) return; |
| 320 nextTypePrefix = prefix; | 310 addCurrentIndent(); |
| 311 sb.add("<$fieldName>\n"); |
| 312 pushTag(fieldName); |
| 321 node.accept(this); | 313 node.accept(this); |
| 314 popTag(); |
| 315 addCurrentIndent(); |
| 316 sb.add("</$fieldName>\n"); |
| 322 } | 317 } |
| 323 | 318 |
| 324 openSendNodeWithFields(Send node, String type) { | 319 openSendNodeWithFields(Send node, String type) { |
| 325 openNode(node, type, { | 320 openNode(node, type, { |
| 326 "isPrefix" : "${node.isPrefix}", | 321 "isPrefix" : "${node.isPrefix}", |
| 327 "isPostfix" : "${node.isPostfix}", | 322 "isPostfix" : "${node.isPostfix}", |
| 328 "isIndex" : "${node.isIndex}" | 323 "isIndex" : "${node.isIndex}" |
| 329 }); | 324 }); |
| 330 visitWithPrefix(node.receiver, "receiver:"); | 325 visitChildNode(node.receiver, "receiver"); |
| 331 visitWithPrefix(node.selector, "selector:"); | 326 visitChildNode(node.selector, "selector"); |
| 332 visitWithPrefix(node.argumentsNode, "argumentsNode:"); | 327 visitChildNode(node.argumentsNode, "argumentsNode"); |
| 333 } | 328 } |
| 334 | 329 |
| 335 visitSend(Send node) { | 330 visitSend(Send node) { |
| 336 openSendNodeWithFields(node, "Send"); | 331 openSendNodeWithFields(node, "Send"); |
| 337 closeNode(); | 332 closeNode(); |
| 338 } | 333 } |
| 339 | 334 |
| 340 visitSendSet(SendSet node) { | 335 visitSendSet(SendSet node) { |
| 341 openSendNodeWithFields(node, "SendSet"); | 336 openSendNodeWithFields(node, "SendSet"); |
| 342 visitWithPrefix(node.assignmentOperator, "assignmentOperator:"); | 337 visitChildNode(node.assignmentOperator, "assignmentOperator"); |
| 343 closeNode(); | 338 closeNode(); |
| 344 } | 339 } |
| 345 | 340 |
| 346 visitStringInterpolation(StringInterpolation node) { | 341 visitStringInterpolation(StringInterpolation node) { |
| 347 visitNodeWithChildren(node, "StringInterpolation"); | 342 visitNodeWithChildren(node, "StringInterpolation"); |
| 348 } | 343 } |
| 349 | 344 |
| 350 visitStringInterpolationPart(StringInterpolationPart node) { | 345 visitStringInterpolationPart(StringInterpolationPart node) { |
| 351 visitNodeWithChildren(node, "StringInterpolationPart"); | 346 visitNodeWithChildren(node, "StringInterpolationPart"); |
| 352 } | 347 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 366 visitThrow(Throw node) { | 361 visitThrow(Throw node) { |
| 367 visitNodeWithChildren(node, "Throw"); | 362 visitNodeWithChildren(node, "Throw"); |
| 368 } | 363 } |
| 369 | 364 |
| 370 visitTryStatement(TryStatement node) { | 365 visitTryStatement(TryStatement node) { |
| 371 visitNodeWithChildren(node, "TryStatement"); | 366 visitNodeWithChildren(node, "TryStatement"); |
| 372 } | 367 } |
| 373 | 368 |
| 374 visitTypeAnnotation(TypeAnnotation node) { | 369 visitTypeAnnotation(TypeAnnotation node) { |
| 375 openNode(node, "TypeAnnotation"); | 370 openNode(node, "TypeAnnotation"); |
| 376 visitWithPrefix(node.typeName, "typeName:"); | 371 visitChildNode(node.typeName, "typeName"); |
| 377 visitWithPrefix(node.typeArguments, "typeArguments:"); | 372 visitChildNode(node.typeArguments, "typeArguments"); |
| 378 closeNode(); | 373 closeNode(); |
| 379 } | 374 } |
| 380 | 375 |
| 381 visitTypedef(Typedef node) { | 376 visitTypedef(Typedef node) { |
| 382 visitNodeWithChildren(node, "Typedef"); | 377 visitNodeWithChildren(node, "Typedef"); |
| 383 } | 378 } |
| 384 | 379 |
| 385 visitTypeVariable(TypeVariable node) { | 380 visitTypeVariable(TypeVariable node) { |
| 386 openNode(node, "TypeVariable"); | 381 openNode(node, "TypeVariable"); |
| 387 visitWithPrefix(node.name, "name:"); | 382 visitChildNode(node.name, "name"); |
| 388 visitWithPrefix(node.bound, "bound:"); | 383 visitChildNode(node.bound, "bound"); |
| 389 closeNode(); | 384 closeNode(); |
| 390 } | 385 } |
| 391 | 386 |
| 392 visitVariableDefinitions(VariableDefinitions node) { | 387 visitVariableDefinitions(VariableDefinitions node) { |
| 393 openNode(node, "VariableDefinitions"); | 388 openNode(node, "VariableDefinitions"); |
| 394 visitWithPrefix(node.type, "type:"); | 389 visitChildNode(node.type, "type"); |
| 395 visitWithPrefix(node.modifiers, "modifiers:"); | 390 visitChildNode(node.modifiers, "modifiers"); |
| 396 visitWithPrefix(node.definitions, "definitions:"); | 391 visitChildNode(node.definitions, "definitions"); |
| 397 closeNode(); | 392 closeNode(); |
| 398 } | 393 } |
| 399 | 394 |
| 400 visitWhile(While node) { | 395 visitWhile(While node) { |
| 401 visitNodeWithChildren(node, "While"); | 396 visitNodeWithChildren(node, "While"); |
| 402 } | 397 } |
| 403 } | 398 } |
| OLD | NEW |