OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 library dart_printer_test; | |
6 | |
7 import 'dart:mirrors'; | |
8 import 'package:expect/expect.dart'; | |
9 import 'package:compiler/src/constants/values.dart'; | |
10 import 'package:compiler/src/dart_backend/backend_ast_nodes.dart'; | |
11 import 'package:compiler/src/dart_backend/backend_ast_to_frontend_ast.dart' | |
12 show TreePrinter; | |
13 import 'package:compiler/src/diagnostics/diagnostic_listener.dart'; | |
14 import 'package:compiler/src/diagnostics/messages.dart'; | |
15 import 'package:compiler/src/diagnostics/spannable.dart' show Spannable; | |
16 import 'package:compiler/src/parser/listener.dart'; | |
17 import 'package:compiler/src/parser/parser.dart'; | |
18 import 'package:compiler/src/scanner/scanner.dart'; | |
19 import 'package:compiler/src/tokens/token.dart'; | |
20 import 'package:compiler/src/tokens/token_constants.dart'; | |
21 import 'package:compiler/src/io/source_file.dart'; | |
22 import 'package:compiler/src/string_validator.dart'; | |
23 import 'package:compiler/src/tree/tree.dart' show DartString; | |
24 import 'package:compiler/src/tree/tree.dart' as tree; | |
25 import '../options_helper.dart'; | |
26 | |
27 /// For debugging the [AstBuilder] stack. Prints information about [x]. | |
28 void show(x) { | |
29 StringBuffer buf = new StringBuffer(); | |
30 Unparser unparser = new Unparser(buf); | |
31 void unparse(x) { | |
32 if (x is Expression) | |
33 unparser.writeExpression(x); | |
34 else if (x is TypeAnnotation) | |
35 unparser.writeType(x); | |
36 else if (x is Statement) | |
37 unparser.writeStatement(x); | |
38 else if (x is List) { | |
39 buf.write('['); | |
40 bool first = true; | |
41 for (var y in x) { | |
42 if (first) | |
43 first = false; | |
44 else | |
45 buf.write(', '); | |
46 unparse(y); | |
47 } | |
48 buf.write(']'); | |
49 } | |
50 } | |
51 unparse(x); | |
52 print("${x.runtimeType}: ${buf.toString()}"); | |
53 } | |
54 | |
55 class PrintDiagnosticListener implements DiagnosticReporter { | |
56 void log(message) { | |
57 print(message); | |
58 } | |
59 | |
60 void internalError(Spannable spannable, message) { | |
61 print(message); | |
62 } | |
63 | |
64 SourceSpan spanFromSpannable(Spannable node) { | |
65 return new SourceSpan(null, 0, 0); | |
66 } | |
67 | |
68 void reportFatalError(Spannable node, MessageKind errorCode, | |
69 [Map arguments = const {}]) { | |
70 print(errorCode); | |
71 throw new Error(); | |
72 } | |
73 | |
74 void reportError(Spannable node, MessageKind errorCode, | |
75 [Map arguments = const {}]) { | |
76 print(errorCode); | |
77 } | |
78 | |
79 void reportWarning(Spannable node, MessageKind errorCode, | |
80 [Map arguments = const {}]) { | |
81 print(errorCode); | |
82 } | |
83 | |
84 void reportHint(Spannable node, MessageKind errorCode, | |
85 [Map arguments = const {}]) { | |
86 print(errorCode); | |
87 } | |
88 | |
89 void reportInfo(Spannable node, MessageKind errorCode, | |
90 [Map arguments = const {}]) { | |
91 print(errorCode); | |
92 } | |
93 | |
94 withCurrentElement(element, f()) { | |
95 f(); | |
96 } | |
97 } | |
98 | |
99 class AstBuilder extends Listener { | |
100 final List stack = []; | |
101 final StringValidator stringValidator | |
102 = new StringValidator(new PrintDiagnosticListener()); | |
103 | |
104 String asName(e) { | |
105 if (e is Identifier) | |
106 return e.name; | |
107 else if (e == null) | |
108 return null; | |
109 else | |
110 throw 'Expression is not a name: ${e.runtimeType}'; | |
111 } | |
112 | |
113 TypeAnnotation asType(x) { | |
114 if (x is TypeAnnotation) | |
115 return x; | |
116 if (x is Identifier) | |
117 return new TypeAnnotation(x.name); | |
118 if (x == null) | |
119 return null; | |
120 else | |
121 throw "Not a type: ${x.runtimeType}"; | |
122 } | |
123 | |
124 Parameter asParameter(x) { | |
125 if (x is Parameter) | |
126 return x; | |
127 if (x is Identifier) | |
128 return new Parameter(x.name); | |
129 else | |
130 throw "Not a parameter: ${x.runtimeType}"; | |
131 } | |
132 | |
133 void push(node) { | |
134 stack.add(node); | |
135 } | |
136 dynamic peek() { | |
137 return stack.last; | |
138 } | |
139 dynamic pop([coerce(x) = null]) { | |
140 var x = stack.removeLast(); | |
141 if (coerce != null) | |
142 return coerce(x); | |
143 else | |
144 return x; | |
145 } | |
146 List popList(int count, [List result, coerce(x) = null]) { | |
147 if (result == null) | |
148 result = <Node>[]; | |
149 for (int i=0; i<count; i++) { | |
150 var x = stack[stack.length-count+i]; | |
151 if (coerce != null) { | |
152 x = coerce(x); | |
153 } | |
154 result.add(x); | |
155 } | |
156 stack.removeRange(stack.length-count, stack.length); | |
157 return result; | |
158 } | |
159 popTypeAnnotation() { | |
160 List<TypeAnnotation> args = pop(); | |
161 if (args == null) | |
162 return null; | |
163 String name = pop(asName); | |
164 return new TypeAnnotation(name, args); | |
165 } | |
166 | |
167 // EXPRESSIONS | |
168 endCascade() { | |
169 throw "Cascade not supported yet"; | |
170 } | |
171 endIdentifierList(int count) { | |
172 push(popList(count, <Identifier>[])); | |
173 } | |
174 endTypeList(int count) { | |
175 push(popList(count, <TypeAnnotation>[], asType)); | |
176 } | |
177 beginLiteralString(Token token) { | |
178 String source = token.value; | |
179 tree.StringQuoting quoting = StringValidator.quotingFromString(source); | |
180 push(quoting); | |
181 push(token); // collect token at the end | |
182 } | |
183 handleStringPart(Token token) { | |
184 push(token); // collect token at the end | |
185 } | |
186 endLiteralString(int interpCount) { | |
187 List parts = popList(2 * interpCount + 1, []); | |
188 tree.StringQuoting quoting = pop(); | |
189 List<Expression> members = <Expression>[]; | |
190 for (var i=0; i<parts.length; i++) { | |
191 var part = parts[i]; | |
192 if (part is Expression) { | |
193 members.add(part); | |
194 } else { | |
195 assert(part is Token); | |
196 DartString str = stringValidator.validateInterpolationPart( | |
197 part as Token, | |
198 quoting, | |
199 isFirst: i == 0, | |
200 isLast: i == parts.length - 1); | |
201 members.add(new Literal(new StringConstantValue(str))); | |
202 } | |
203 } | |
204 push(new StringConcat(members)); | |
205 } | |
206 handleStringJuxtaposition(int litCount) { | |
207 push(new StringConcat(popList(litCount, <Expression>[]))); | |
208 } | |
209 endArguments(int count, begin, end) { | |
210 push(popList(count, <Argument>[])); | |
211 } | |
212 handleNoArguments(token) { | |
213 push(null); | |
214 } | |
215 handleNoTypeArguments(token) { | |
216 push(<TypeAnnotation>[]); | |
217 } | |
218 endTypeArguments(int count, t, y) { | |
219 List<TypeAnnotation> args = <TypeAnnotation>[]; | |
220 for (var i=0; i<count; i++) { | |
221 args.add(popTypeAnnotation()); | |
222 } | |
223 push(args.reversed.toList(growable:false)); | |
224 } | |
225 handleVoidKeyword(token) { | |
226 push(new Identifier("void")); | |
227 push(<TypeAnnotation>[]); // prepare for popTypeAnnotation | |
228 } | |
229 handleQualified(Token period) { | |
230 String last = pop(asName); | |
231 String first = pop(asName); | |
232 push(new Identifier('$first.$last')); | |
233 } | |
234 endSend(t) { | |
235 List<Argument> arguments = pop(); | |
236 pop(); // typeArguments | |
237 if (arguments == null) | |
238 return; // not a function call | |
239 Expression selector = pop(); | |
240 push(new CallFunction(selector, arguments)); | |
241 } | |
242 endThrowExpression(t, tt) { | |
243 push(new Throw(pop())); | |
244 } | |
245 handleAssignmentExpression(Token token) { | |
246 Expression right = pop(); | |
247 Expression left = pop(); | |
248 push(new Assignment(left, token.value, right)); | |
249 } | |
250 handleBinaryExpression(Token token) { | |
251 Expression right = pop(); | |
252 Receiver left = pop(); | |
253 String tokenString = token.stringValue; | |
254 if (tokenString == '.') { | |
255 if (right is CallFunction) { | |
256 String name = (right.callee as Identifier).name; | |
257 push(new CallMethod(left, name, right.arguments)); | |
258 } else { | |
259 push(new FieldExpression(left, (right as Identifier).name)); | |
260 } | |
261 } else { | |
262 push(new BinaryOperator(left, tokenString, right)); | |
263 } | |
264 } | |
265 handleConditionalExpression(question, colon) { | |
266 Expression elseExpression = pop(); | |
267 Expression thenExpression = pop(); | |
268 Expression condition = pop(); | |
269 push(new Conditional(condition, thenExpression, elseExpression)); | |
270 } | |
271 handleIdentifier(Token t) { | |
272 push(new Identifier(t.value)); | |
273 } | |
274 handleOperator(t) { | |
275 push(new Identifier(t.value)); | |
276 } | |
277 handleIndexedExpression(open, close) { | |
278 Expression index = pop(); | |
279 Receiver object = pop(); | |
280 push(new IndexExpression(object, index)); | |
281 } | |
282 handleIsOperator(operathor, not, endToken) { | |
283 TypeAnnotation type = popTypeAnnotation(); | |
284 Expression exp = pop(); | |
285 TypeOperator r = new TypeOperator(exp, 'is', type); | |
286 if (not != null) { | |
287 push(new UnaryOperator('!', r)); | |
288 } else { | |
289 push(r); | |
290 } | |
291 } | |
292 handleAsOperator(operathor, endToken) { | |
293 TypeAnnotation type = popTypeAnnotation(); | |
294 Expression exp = pop(); | |
295 push(new TypeOperator(exp, 'as', type)); | |
296 } | |
297 handleLiteralBool(Token t) { | |
298 bool value = t.value == 'true'; | |
299 push(new Literal( | |
300 value ? new TrueConstantValue() : new FalseConstantValue())); | |
301 } | |
302 handleLiteralDouble(t) { | |
303 push(new Literal(new DoubleConstantValue(double.parse(t.value)))); | |
304 } | |
305 handleLiteralInt(Token t) { | |
306 push(new Literal(new IntConstantValue(int.parse(t.value)))); | |
307 } | |
308 handleLiteralNull(t) { | |
309 push(new Literal(new NullConstantValue())); | |
310 } | |
311 endLiteralSymbol(Token hash, int idCount) { | |
312 List<Identifier> ids = popList(idCount, <Identifier>[]); | |
313 push(new LiteralSymbol(ids.map((id) => id.name).join('.'))); | |
314 } | |
315 handleLiteralList(int count, begin, constKeyword, end) { | |
316 List<Expression> exps = popList(count, <Expression>[]); | |
317 List<TypeAnnotation> types = pop(); | |
318 assert(types.length <= 1); | |
319 push(new LiteralList(exps, | |
320 isConst: constKeyword != null, | |
321 typeArgument: types.length == 0 ? null : types[0] | |
322 )); | |
323 } | |
324 handleLiteralMap(int count, begin, constKeyword, end) { | |
325 List<LiteralMapEntry> entries = popList(count, <LiteralMapEntry>[]); | |
326 List<TypeAnnotation> types = pop(); | |
327 assert(types.length == 0 || types.length == 2); | |
328 push(new LiteralMap(entries, | |
329 isConst: constKeyword != null, | |
330 typeArguments: types | |
331 )); | |
332 } | |
333 endLiteralMapEntry(colon, endToken) { | |
334 Expression value = pop(); | |
335 Expression key = pop(); | |
336 push(new LiteralMapEntry(key,value)); | |
337 } | |
338 handleNamedArgument(colon) { | |
339 Expression exp = pop(); | |
340 Identifier name = pop(); | |
341 push(new NamedArgument(name.name, exp)); | |
342 } | |
343 endConstructorReference(Token start, Token period, Token end) { | |
344 if (period == null) { | |
345 push(null); // indicate missing constructor name | |
346 } | |
347 } | |
348 handleNewExpression(t) { | |
349 List<Argument> args = pop(); | |
350 String constructorName = pop(asName); | |
351 TypeAnnotation type = popTypeAnnotation(); | |
352 push(new CallNew(type, args, constructorName: constructorName)); | |
353 } | |
354 handleConstExpression(t) { | |
355 List<Argument> args = pop(); | |
356 String constructorName = pop(asName); | |
357 TypeAnnotation type = popTypeAnnotation(); | |
358 push(new CallNew(type, args, constructorName: constructorName, | |
359 isConst:true)); | |
360 } | |
361 handleParenthesizedExpression(t) { | |
362 // do nothing, just leave expression on top of stack | |
363 } | |
364 handleSuperExpression(t) { | |
365 push(new SuperReceiver()); | |
366 } | |
367 handleThisExpression(t) { | |
368 push(new This()); | |
369 } | |
370 handleUnaryPostfixAssignmentExpression(Token t) { | |
371 push(new Increment.postfix(pop(), t.value)); | |
372 } | |
373 handleUnaryPrefixAssignmentExpression(Token t) { | |
374 push(new Increment.prefix(pop(), t.value)); | |
375 } | |
376 handleUnaryPrefixExpression(Token t) { | |
377 push(new UnaryOperator(t.value, pop())); | |
378 } | |
379 | |
380 handleFunctionTypedFormalParameter(tok) { | |
381 // handled in endFormalParameter | |
382 } | |
383 endFormalParameter(thisKeyword) { | |
384 Expression defaultValue = null; | |
385 var x = pop(); | |
386 if (x is DefaultValue) { | |
387 defaultValue = x.expression; | |
388 x = pop(); | |
389 } | |
390 if (x is Parameters) { | |
391 String name = pop(asName); | |
392 TypeAnnotation returnType = popTypeAnnotation(); | |
393 push(new Parameter.function(name, returnType, x, defaultValue)); | |
394 } else { | |
395 String name = asName(x); | |
396 TypeAnnotation type = popTypeAnnotation(); | |
397 push(new Parameter(name, type:type, defaultValue:defaultValue)); | |
398 } | |
399 } | |
400 handleValuedFormalParameter(eq, tok) { | |
401 push(new DefaultValue(pop())); | |
402 } | |
403 endOptionalFormalParameters(int count, begin, end) { | |
404 bool isNamed = end.value == '}'; | |
405 push(popList(count, <Parameter>[], asParameter)); | |
406 push(isNamed); // Indicate optional parameters to endFormalParameters. | |
407 } | |
408 endFormalParameters(count, begin, end) { | |
409 if (count == 0) { | |
410 push(new Parameters([])); | |
411 return; | |
412 } | |
413 var last = pop(); // Detect if optional parameters are present. | |
414 if (last is bool) { // See endOptionalFormalParameters. | |
415 List<Parameter> optional = pop(); | |
416 List<Parameter> required = popList(count-1, <Parameter>[], asParameter); | |
417 push(new Parameters(required, optional, last)); | |
418 } else { | |
419 // No optional parameters. | |
420 List<Parameter> required = popList(count-1, <Parameter>[], asParameter); | |
421 required.add(last); | |
422 push(new Parameters(required)); | |
423 } | |
424 } | |
425 handleNoFormalParameters(tok) { | |
426 push(new Parameters([])); | |
427 } | |
428 | |
429 endUnnamedFunction(t) { | |
430 Statement body = pop(); | |
431 Parameters parameters = pop(); | |
432 push(new FunctionExpression(parameters, body)); | |
433 } | |
434 | |
435 handleNoType(Token token) { | |
436 push(null); | |
437 } | |
438 | |
439 endReturnStatement(bool hasExpression, begin, end) { | |
440 // This is also called for functions whose body is "=> expression" | |
441 if (hasExpression) { | |
442 push(new Return(pop())); | |
443 } else { | |
444 push(new Return()); | |
445 } | |
446 } | |
447 | |
448 endExpressionStatement(Token token) { | |
449 push(new ExpressionStatement(pop())); | |
450 } | |
451 | |
452 endDoWhileStatement(Token doKeyword, Token whileKeyword, Token end) { | |
453 Expression condition = pop(); | |
454 Statement body = pop(); | |
455 push(new DoWhile(body, condition)); | |
456 } | |
457 | |
458 endWhileStatement(Token whileKeyword, Token end) { | |
459 Statement body = pop(); | |
460 Expression condition = pop(); | |
461 push(new While(condition, body)); | |
462 } | |
463 | |
464 endBlock(int count, Token begin, Token end) { | |
465 push(new Block(popList(count, <Statement>[]))); | |
466 } | |
467 | |
468 endRethrowStatement(Token throwToken, Token endToken) { | |
469 push(new Rethrow()); | |
470 } | |
471 | |
472 endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) { | |
473 Statement finallyBlock = null; | |
474 if (finallyKeyword != null) { | |
475 finallyBlock = pop(); | |
476 } | |
477 List<CatchBlock> catchBlocks = popList(catchCount, <CatchBlock>[]); | |
478 Statement tryBlock = pop(); | |
479 push(new Try(tryBlock, catchBlocks, finallyBlock)); | |
480 } | |
481 | |
482 void handleCatchBlock(Token onKeyword, Token catchKeyword) { | |
483 Statement block = pop(); | |
484 String exceptionVar = null; | |
485 String stackVar = null; | |
486 if (catchKeyword != null) { | |
487 Parameters params = pop(); | |
488 exceptionVar = params.requiredParameters[0].name; | |
489 if (params.requiredParameters.length > 1) { | |
490 stackVar = params.requiredParameters[1].name; | |
491 } | |
492 } | |
493 TypeAnnotation type = onKeyword == null ? null : pop(); | |
494 push(new CatchBlock(block, | |
495 onType: type, | |
496 exceptionVar: exceptionVar, | |
497 stackVar: stackVar | |
498 )); | |
499 } | |
500 | |
501 endSwitchStatement(Token switchKeyword, Token end) { | |
502 List<SwitchCase> cases = pop(); | |
503 Expression expression = pop(); | |
504 push(new Switch(expression, cases)); | |
505 } | |
506 | |
507 endSwitchBlock(int caseCount, Token begin, Token end) { | |
508 push(popList(caseCount, <SwitchCase>[])); | |
509 } | |
510 | |
511 handleSwitchCase(int labelCount, int caseCount, Token defaultKeyword, | |
512 int statementCount, Token first, Token end) { | |
513 List<Statement> statements = popList(statementCount, <Statement>[]); | |
514 List<Expression> cases = popList(caseCount, <Expression>[]); | |
515 if (defaultKeyword != null) { | |
516 cases = null; | |
517 } | |
518 push(new SwitchCase(cases, statements)); | |
519 } | |
520 | |
521 handleCaseMatch(Token caseKeyword, Token colon) { | |
522 // do nothing, leave case expression on stack | |
523 } | |
524 | |
525 handleBreakStatement(bool hasTarget, Token breakKeyword, Token end) { | |
526 String target = hasTarget ? pop(asName) : null; | |
527 push(new Break(target)); | |
528 } | |
529 | |
530 handleContinueStatement(bool hasTarget, Token continueKeyword, Token end) { | |
531 String target = hasTarget ? pop(asName) : null; | |
532 push(new Continue(target)); | |
533 } | |
534 | |
535 handleEmptyStatement(Token token) { | |
536 push(new EmptyStatement()); | |
537 } | |
538 | |
539 | |
540 VariableDeclaration asVariableDeclaration(x) { | |
541 if (x is VariableDeclaration) | |
542 return x; | |
543 if (x is Identifier) | |
544 return new VariableDeclaration(x.name); | |
545 throw "Not a variable definition: ${x.runtimeType}"; | |
546 } | |
547 | |
548 endVariablesDeclaration(int count, Token end) { | |
549 List<VariableDeclaration> variables = | |
550 popList(count, <VariableDeclaration>[], asVariableDeclaration); | |
551 TypeAnnotation type = popTypeAnnotation(); | |
552 push(new VariableDeclarations(variables, | |
553 type: type, | |
554 isFinal: false, // TODO(asgerf): Parse modifiers. | |
555 isConst: false | |
556 )); | |
557 } | |
558 | |
559 endInitializer(Token assign) { | |
560 Expression init = pop(); | |
561 String name = pop(asName); | |
562 push(new VariableDeclaration(name, init)); | |
563 } | |
564 | |
565 endIfStatement(Token ifToken, Token elseToken) { | |
566 Statement elsePart = (elseToken == null) ? null : pop(); | |
567 Statement thenPart = pop(); | |
568 Expression condition = pop(); | |
569 push(new If(condition, thenPart, elsePart)); | |
570 } | |
571 | |
572 endForStatement(int updateCount, Token begin, Token end) { | |
573 Statement body = pop(); | |
574 List<Expression> updates = popList(updateCount, <Expression>[]); | |
575 ExpressionStatement condition = pop(); // parsed as expression statement | |
576 Expression exp = condition == null ? null : condition.expression; | |
577 Node initializer = pop(); | |
578 push(new For(initializer, exp, updates, body)); | |
579 } | |
580 | |
581 handleNoExpression(Token token) { | |
582 push(null); | |
583 } | |
584 | |
585 endForIn(Token await, Token begin, Token inKeyword, Token end) { | |
586 Statement body = pop(); | |
587 Expression exp = pop(); | |
588 Node declaredIdentifier = pop(); | |
589 push(new ForIn(declaredIdentifier, exp, body)); | |
590 } | |
591 | |
592 handleAssertStatement(Token assertKeyword, | |
593 Token commaToken, Token semicolonToken) { | |
594 Expression message; | |
595 if (commaToken != null) message = pop(); | |
596 Expression exp = pop(); | |
597 var arguments = [exp]; | |
598 if (message != null) arguments.add(message); | |
599 Expression call = new CallFunction(new Identifier("assert"), arguments); | |
600 push(new ExpressionStatement(call)); | |
601 } | |
602 | |
603 endLabeledStatement(int labelCount) { | |
604 Statement statement = pop(); | |
605 for (int i=0; i<labelCount; i++) { | |
606 String label = pop(asName); | |
607 statement = new LabeledStatement(label, statement); | |
608 } | |
609 push(statement); | |
610 } | |
611 | |
612 endFunctionDeclaration(Token end) { | |
613 Statement body = pop(); | |
614 Parameters parameters = pop(); | |
615 String name = pop(asName); | |
616 TypeAnnotation returnType = popTypeAnnotation(); | |
617 push(new FunctionDeclaration(new FunctionExpression(parameters, body, | |
618 name: name, | |
619 returnType: returnType))); | |
620 } | |
621 | |
622 endFunctionBody(int count, Token begin, Token end) { | |
623 push(new Block(popList(count, <Statement>[]))); | |
624 } | |
625 } | |
626 | |
627 class DefaultValue { | |
628 final Expression expression; | |
629 DefaultValue(this.expression); | |
630 } | |
631 | |
632 /// Compares ASTs for structural equality. | |
633 void checkDeepEqual(x, y) { | |
634 if (x is List && y is List) { | |
635 if (x.length != y.length) | |
636 return; | |
637 for (var i=0; i<x.length; i++) { | |
638 checkDeepEqual(x[i], y[i]); | |
639 } | |
640 } | |
641 else if (x is Node && y is Node) { | |
642 if (x.runtimeType != y.runtimeType) | |
643 throw new Error(); | |
644 InstanceMirror xm = reflect(x); | |
645 InstanceMirror ym = reflect(y); | |
646 for (Symbol name in xm.type.instanceMembers.keys) { | |
647 if (reflectClass(Object).declarations.containsKey(name)) { | |
648 continue; // do not check things from Object, such as hashCode | |
649 } | |
650 MethodMirror mm = xm.type.instanceMembers[name]; | |
651 if (mm.isGetter) { | |
652 var xv = xm.getField(name).reflectee; | |
653 var yv = ym.getField(name).reflectee; | |
654 checkDeepEqual(xv,yv); | |
655 } | |
656 } | |
657 } | |
658 else if (x is PrimitiveConstantValue && y is PrimitiveConstantValue) { | |
659 checkDeepEqual(x.primitiveValue, y.primitiveValue); | |
660 } | |
661 else if (x is DartString && y is DartString) { | |
662 if (x.slowToString() != y.slowToString()) { | |
663 throw new Error(); | |
664 } | |
665 } | |
666 else { | |
667 if (x != y) { | |
668 throw new Error(); | |
669 } | |
670 } | |
671 } | |
672 | |
673 Expression parseExpression(String code) { | |
674 SourceFile file = new StringSourceFile.fromName('', code); | |
675 Scanner scan = new Scanner(file); | |
676 Token tok = scan.tokenize(); | |
677 AstBuilder builder = new AstBuilder(); | |
678 Parser parser = new Parser(builder, new MockParserOptions()); | |
679 tok = parser.parseExpression(tok); | |
680 if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) { | |
681 throw "Parse error in $code"; | |
682 } | |
683 return builder.pop(); | |
684 } | |
685 Statement parseStatement(String code) { | |
686 SourceFile file = new StringSourceFile.fromName('', code); | |
687 Scanner scan = new Scanner(file); | |
688 Token tok = scan.tokenize(); | |
689 AstBuilder builder = new AstBuilder(); | |
690 Parser parser = new Parser(builder, new MockParserOptions()); | |
691 tok = parser.parseStatement(tok); | |
692 if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) { | |
693 throw "Parse error in $code"; | |
694 } | |
695 return builder.pop(); | |
696 } | |
697 | |
698 String unparseExpression(Expression exp) { | |
699 StringBuffer buf = new StringBuffer(); | |
700 new Unparser(buf).writeExpression(exp); | |
701 return buf.toString(); | |
702 } | |
703 String unparseStatement(Statement stmt) { | |
704 StringBuffer buf = new StringBuffer(); | |
705 new Unparser(buf).writeStatement(stmt); | |
706 return buf.toString(); | |
707 } | |
708 | |
709 /// Converts [exp] to an instance of the frontend AST and unparses that. | |
710 String frontUnparseExpression(Expression exp) { | |
711 tree.Node node = new TreePrinter().makeExpression(exp); | |
712 return tree.unparse(node); | |
713 } | |
714 /// Converts [stmt] to an instance of the frontend AST and unparses that. | |
715 String frontUnparseStatement(Statement stmt) { | |
716 tree.Node node = new TreePrinter().makeStatement(stmt); | |
717 return tree.unparse(node); | |
718 } | |
719 | |
720 /// Parses [code], unparses the resulting AST, then parses the unparsed text. | |
721 /// The ASTs from the first and second parse are then compared for structural | |
722 /// equality. Alternatively, if [expected] is not an empty string, the second | |
723 /// parse must match the AST of parsing [expected]. | |
724 void checkFn(String code, String expected, Function parse, Function unparse) { | |
725 var firstParse = parse(code); | |
726 String unparsed = unparse(firstParse); | |
727 try { | |
728 var secondParse = parse(unparsed); | |
729 var baseline = expected == "" ? firstParse : parse(expected); | |
730 checkDeepEqual(baseline, secondParse); | |
731 } catch (e, stack) { | |
732 Expect.fail('"$code" was unparsed as "$unparsed"'); | |
733 } | |
734 } | |
735 | |
736 void checkExpression(String code, [String expected="", String expected2=""]) { | |
737 checkFn(code, expected, parseExpression, unparseExpression); | |
738 checkFn(code, expected2, parseExpression, frontUnparseExpression); | |
739 } | |
740 void checkStatement(String code, [String expected="", String expected2=""]) { | |
741 checkFn(code, expected, parseStatement, unparseStatement); | |
742 checkFn(code, expected2, parseStatement, frontUnparseStatement); | |
743 } | |
744 | |
745 void debugTokens(String code) { | |
746 SourceFile file = new StringSourceFile.fromName('', code); | |
747 Scanner scan = new Scanner(file); | |
748 Token tok = scan.tokenize(); | |
749 while (tok.next != tok) { | |
750 print(tok.toString()); | |
751 tok = tok.next; | |
752 } | |
753 } | |
754 | |
755 void main() { | |
756 // To check if these tests are effective, one should manually alter | |
757 // something in [Unparser] and see if a test fails. | |
758 | |
759 checkExpression(" a + b + c"); | |
760 checkExpression("(a + b) + c"); | |
761 checkExpression(" a + (b + c)"); | |
762 | |
763 checkExpression(" a + b - c"); | |
764 checkExpression("(a + b) - c"); | |
765 checkExpression(" a + (b - c)"); | |
766 | |
767 checkExpression(" a - b + c"); | |
768 checkExpression("(a - b) + c"); | |
769 checkExpression(" a - (b + c)"); | |
770 | |
771 checkExpression(" a * b + c"); | |
772 checkExpression("(a * b) + c"); | |
773 checkExpression(" a * (b + c)"); | |
774 | |
775 checkExpression(" a + b * c"); | |
776 checkExpression("(a + b) * c"); | |
777 checkExpression(" a + (b * c)"); | |
778 | |
779 checkExpression(" a * b * c"); | |
780 checkExpression("(a * b) * c"); | |
781 checkExpression(" a * (b * c)"); | |
782 | |
783 checkExpression("a is T"); | |
784 checkExpression("a is! T"); | |
785 checkExpression("!(a is T)"); | |
786 | |
787 checkExpression("a is T.x"); | |
788 checkExpression("a is! T.x"); | |
789 checkExpression("!(a is T.x)"); | |
790 checkExpression("!(a is T).x"); | |
791 | |
792 checkExpression("a as T.x"); | |
793 checkExpression("(a as T).x"); | |
794 | |
795 checkExpression("a == b"); | |
796 checkExpression("a != b"); | |
797 checkExpression("!(a == b)", "a != b"); | |
798 | |
799 checkExpression("a && b ? c : d"); | |
800 checkExpression("(a && b) ? c : d"); | |
801 checkExpression("a && (b ? c : d)"); | |
802 | |
803 checkExpression("a || b ? c : d"); | |
804 checkExpression("(a || b) ? c : d"); | |
805 checkExpression("a || (b ? c : d)"); | |
806 | |
807 checkExpression(" a ? b : c && d"); | |
808 checkExpression(" a ? b : (c && d)"); | |
809 checkExpression("(a ? b : c) && d"); | |
810 | |
811 checkExpression(" a ? b : c = d"); | |
812 checkExpression(" a ? b : (c = d)"); | |
813 | |
814 checkExpression("(a == b) == c"); | |
815 checkExpression("a == (b == c)"); | |
816 | |
817 checkExpression(" a < b == c"); | |
818 checkExpression("(a < b) == c"); | |
819 checkExpression(" a < (b == c)"); | |
820 | |
821 checkExpression(" a == b < c"); | |
822 checkExpression("(a == b) < c"); | |
823 checkExpression(" a == (b < c)"); | |
824 | |
825 checkExpression("x.f()"); | |
826 checkExpression("(x.f)()"); | |
827 | |
828 checkExpression("x.f()()"); | |
829 checkExpression("(x.f)()()"); | |
830 | |
831 checkExpression("x.f().g()"); | |
832 checkExpression("(x.f)().g()"); | |
833 | |
834 checkExpression("x.f()"); | |
835 checkExpression("x.f(1 + 2)"); | |
836 checkExpression("x.f(1 + 2, 3 + 4)"); | |
837 checkExpression("x.f(1 + 2, foo:3 + 4)"); | |
838 checkExpression("x.f(1 + 2, foo:3 + 4, bar: 5)"); | |
839 checkExpression("x.f(foo:3 + 4)"); | |
840 checkExpression("x.f(foo:3 + 4, bar: 5)"); | |
841 | |
842 checkExpression("x.f.g.h"); | |
843 checkExpression("(x.f).g.h"); | |
844 checkExpression("(x.f.g).h"); | |
845 | |
846 checkExpression(" a = b + c"); | |
847 checkExpression(" a = (b + c)"); | |
848 checkExpression("(a = b) + c"); | |
849 | |
850 checkExpression("a + (b = c)"); | |
851 | |
852 checkExpression("dx * dx + dy * dy < r * r", | |
853 "((dx * dx) + (dy * dy)) < (r * r)"); | |
854 checkExpression("mid = left + right << 1", | |
855 "mid = ((left + right) << 1)"); | |
856 checkExpression("a + b % c * -d ^ e - f ~/ x & ++y / z++ | w > a ? b : c"); | |
857 checkExpression("a + b % c * -d ^ (e - f) ~/ x & ++y / z++ | w > a ? b : c"); | |
858 | |
859 checkExpression("'foo'"); | |
860 checkExpression("'foo' 'bar'", "'foobar'"); | |
861 | |
862 checkExpression("{}.length"); | |
863 checkExpression("{x: 1+2}.length"); | |
864 checkExpression("<String,int>{}.length"); | |
865 checkExpression("<String,int>{x: 1+2}.length"); | |
866 | |
867 checkExpression("[].length"); | |
868 checkExpression("[1+2].length"); | |
869 checkExpression("<num>[].length"); | |
870 checkExpression("<num>[1+2].length"); | |
871 | |
872 checkExpression("x + -y"); | |
873 checkExpression("x + --y"); | |
874 checkExpression("x++ + y"); | |
875 checkExpression("x + ++y"); | |
876 checkExpression("x-- - y"); | |
877 checkExpression("x-- - -y"); | |
878 checkExpression("x - --y"); | |
879 | |
880 checkExpression("x && !y"); | |
881 checkExpression("!x && y"); | |
882 checkExpression("!(x && y)"); | |
883 | |
884 checkExpression(" super + 1 * 2"); | |
885 checkExpression("(super + 1) * 2"); | |
886 checkExpression(" super + (1 * 2)"); | |
887 checkExpression("x + -super"); | |
888 checkExpression("x-- - -super"); | |
889 checkExpression("x - -super"); | |
890 checkExpression("x && !super"); | |
891 | |
892 checkExpression("super.f(1, 2) + 3"); | |
893 checkExpression("super.f + 3"); | |
894 | |
895 checkExpression(r"'foo\nbar'"); | |
896 checkExpression(r"'foo\r\nbar'"); | |
897 checkExpression(r"'foo\rbar'"); | |
898 checkExpression(r"'foo\'bar'"); | |
899 checkExpression(r"""'foo"bar'"""); | |
900 checkExpression(r"r'foo\nbar'"); | |
901 checkExpression("''"); | |
902 checkExpression("r''"); | |
903 | |
904 var sq = "'"; | |
905 var dq = '"'; | |
906 checkExpression("'$dq$dq' \"$sq$sq\""); | |
907 checkExpression("'$dq$dq$dq$dq' \"$sq$sq$sq$sq\""); | |
908 checkExpression(r"'\$\$\$\$\$\$\$\$\$'"); | |
909 checkExpression("'$dq$dq$dq' '\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n' \"$sq$sq$sq\""); | |
910 checkExpression("'$dq$dq$dq' '\\r\\r\\r\\r\\r\\r\\r\\r\\r\\r' \"$sq$sq$sq\""); | |
911 checkExpression("'$dq$dq$dq' '\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n' \"$sq$sq$sq\""); | |
912 | |
913 checkExpression(r"'$foo'"); | |
914 checkExpression(r"'${foo}x'"); | |
915 checkExpression(r"'${foo}x\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'"); | |
916 checkExpression(r"'abc' '${foo}' r'\\\\\\\'"); | |
917 | |
918 checkExpression(r"'${$x}'"); | |
919 checkExpression(r"'${$x}y'"); | |
920 checkExpression("null + null"); | |
921 | |
922 checkExpression("(x) => x", | |
923 '', | |
924 '(x){return x;}'); | |
925 checkStatement("fn(x) => x;", | |
926 '', | |
927 'fn(x){return x;}'); | |
928 | |
929 checkExpression("throw x"); | |
930 checkStatement("throw x;"); | |
931 | |
932 checkStatement("var x, y, z;"); | |
933 checkStatement("final x, y, z;"); | |
934 checkStatement("dynamic x, y, z;"); | |
935 checkStatement("String x, y, z;"); | |
936 checkStatement("List<int> x, y, z;"); | |
937 checkStatement("final dynamic x, y, z;"); | |
938 checkStatement("final String x, y, z;"); | |
939 checkStatement("final List<int> x, y, z;"); | |
940 | |
941 checkStatement("var x = y, z;"); | |
942 checkStatement("var x, y = z;"); | |
943 checkStatement("var x = y = z;"); | |
944 | |
945 // Note: We sometimes have to pass an expected string to account for | |
946 // block flattening which does not preserve structural AST equality | |
947 checkStatement("if (x) if (y) foo(); else bar(); "); | |
948 checkStatement("if (x) { if (y) foo(); } else bar(); "); | |
949 checkStatement("if (x) { if (y) foo(); else bar(); }", | |
950 "if (x) if (y) foo(); else bar(); "); | |
951 | |
952 checkStatement("if (x) while (y) if (z) foo(); else bar(); "); | |
953 checkStatement("if (x) while (y) { if (z) foo(); } else bar(); "); | |
954 checkStatement("if (x) while (y) { if (z) foo(); else bar(); }", | |
955 "if (x) while (y) if (z) foo(); else bar(); "); | |
956 | |
957 checkStatement("{var x = 1; {var x = 2;} return x;}"); | |
958 checkStatement("{var x = 1; {x = 2;} return x;}", | |
959 "{var x = 1; x = 2; return x;}", | |
960 "{var x = 1; x = 2; return x;}"); | |
961 | |
962 checkStatement("if (x) {var x = 1;}"); | |
963 | |
964 checkStatement("({'foo': 1}).bar();"); | |
965 checkStatement("({'foo': 1}).length;"); | |
966 checkStatement("({'foo': 1}).length + 1;"); | |
967 checkStatement("({'foo': 1})['foo'].toString();"); | |
968 checkStatement("({'foo': 1})['foo'] = 3;"); | |
969 checkStatement("({'foo': 1}['foo']());"); | |
970 checkStatement("({'foo': 1}['foo'])();"); | |
971 checkStatement("({'foo': 1})['foo'].x++;"); | |
972 checkStatement("({'foo': 1}) is Map;"); | |
973 checkStatement("({'foo': 1}) as Map;"); | |
974 checkStatement("({'foo': 1}) is util.Map;"); | |
975 checkStatement("({'foo': 1}) + 1;"); | |
976 | |
977 checkStatement("[1].bar();"); | |
978 checkStatement("1.bar();"); | |
979 checkStatement("'foo'.bar();"); | |
980 | |
981 checkStatement("do while(x); while (y);"); | |
982 checkStatement("{do; while(x); while (y);}"); | |
983 | |
984 checkStatement('switch(x) { case 1: case 2: return y; }'); | |
985 checkStatement('switch(x) { default: return y; }'); | |
986 checkStatement('switch(x) { case 1: x=y; default: return y; }'); | |
987 checkStatement('switch(x) { case 1: x=y; y=z; break; default: return y; }'); | |
988 | |
989 } | |
990 | |
OLD | NEW |