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

Unified Diff: tests/compiler/dart2js/dart_printer_test.dart

Issue 250523003: dart2dart backend: AST and unparser for backend. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tests/compiler/dart2js/dart_printer_test.dart
diff --git a/tests/compiler/dart2js/dart_printer_test.dart b/tests/compiler/dart2js/dart_printer_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c57b97c8136ff2dc81c443ca028a56cd51a7bab9
--- /dev/null
+++ b/tests/compiler/dart2js/dart_printer_test.dart
@@ -0,0 +1,940 @@
+library dart_printer_test;
+
+import '../../../sdk/lib/_internal/compiler/implementation/dart_backend/dart_printer.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/scanner/scannerlib.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/source_file.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart';
+import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart' show DartString;
+import 'dart:mirrors';
+import '../../../sdk/lib/_internal/compiler/implementation/tree/tree.dart' as tree;
+import '../../../sdk/lib/_internal/compiler/implementation/string_validator.dart';
+
+/// For debugging the [AstBuilder] stack. Prints information about [x].
+void show(x) {
+ StringBuffer buf = new StringBuffer();
+ Unparser unparser = new Unparser(buf);
+ void unparse(x) {
+ if (x is Expression)
+ unparser.writeExpression(x);
+ else if (x is TypeAnnotation)
+ unparser.writeType(x);
+ else if (x is Statement)
+ unparser.writeStatement(x);
+ else if (x is List) {
+ buf.write('[');
+ bool first = true;
+ for (var y in x) {
+ if (first)
+ first = false;
+ else
+ buf.write(', ');
+ unparse(y);
+ }
+ buf.write(']');
+ }
+ }
+ unparse(x);
+ print("${x.runtimeType}: ${buf.toString()}");
+}
+
+class PrintDiagnosticListener implements DiagnosticListener {
+ void log(message) {
+ print(message);
+ }
+
+ void internalError(Spannable spannable, message) {
+ print(message);
+ }
+
+ SourceSpan spanFromSpannable(Spannable node) {
+ return new SourceSpan(null, 0, 0);
+ }
+
+ void reportFatalError(Spannable node, MessageKind errorCode,
+ [Map arguments = const {}]) {
+ print(errorCode);
+ throw new Error();
+ }
+
+ void reportError(Spannable node, MessageKind errorCode,
+ [Map arguments = const {}]) {
+ print(errorCode);
+ }
+
+ void reportWarning(Spannable node, MessageKind errorCode,
+ [Map arguments = const {}]) {
+ print(errorCode);
+ }
+
+ void reportHint(Spannable node, MessageKind errorCode,
+ [Map arguments = const {}]) {
+ print(errorCode);
+ }
+
+ void reportInfo(Spannable node, MessageKind errorCode,
+ [Map arguments = const {}]) {
+ print(errorCode);
+ }
+
+ withCurrentElement(element, f()) {
+ f();
+ }
+}
+
+class AstBuilder extends Listener {
+ final List stack = [];
+ final StringValidator stringValidator
+ = new StringValidator(new PrintDiagnosticListener());
+
+ String asName(e) {
+ if (e is Identifier)
+ return e.name;
+ else if (e == null)
+ return null;
+ else
+ throw 'Expression is not a name: ${e.runtimeType}';
+ }
+
+ TypeAnnotation asType(x) {
+ if (x is TypeAnnotation)
+ return x;
+ if (x is Identifier)
+ return new TypeAnnotation(x.name);
+ if (x == null)
+ return null;
+ else
+ throw "Not a type: ${x.runtimeType}";
+ }
+
+ Parameter asParameter(x) {
+ if (x is Parameter)
+ return x;
+ if (x is Identifier)
+ return new Parameter(x.name);
+ else
+ throw "Not a parameter: ${x.runtimeType}";
+ }
+
+ void push(node) {
+ stack.add(node);
+ }
+ dynamic peek() {
+ return stack.last;
+ }
+ dynamic pop([coerce(x) = null]) {
+ var x = stack.removeLast();
+ if (coerce != null)
+ return coerce(x);
+ else
+ return x;
+ }
+ List popList(int count, [List result, coerce(x) = null]) {
+ if (result == null)
+ result = <Node>[];
+ for (int i=0; i<count; i++) {
+ var x = stack[stack.length-count+i];
+ if (coerce != null) {
+ x = coerce(x);
+ }
+ result.add(x);
+ }
+ stack.removeRange(stack.length-count, stack.length);
+ return result;
+ }
+ popTypeAnnotation() {
+ List<TypeAnnotation> args = pop();
+ if (args == null)
+ return null;
+ String name = pop(asName);
+ return new TypeAnnotation(name, args);
+ }
+
+ // EXPRESSIONS
+ endCascade() {
+ throw "Cascade not supported yet";
+ }
+ endIdentifierList(int count) {
+ push(popList(count, <Identifier>[]));
+ }
+ endTypeList(int count) {
+ push(popList(count, <TypeAnnotation>[], asType));
+ }
+ beginLiteralString(Token token) {
+ String source = token.value;
+ tree.StringQuoting quoting = StringValidator.quotingFromString(source);
+ push(quoting);
+ push(token); // collect token at the end
+ }
+ handleStringPart(Token token) {
+ push(token); // collect token at the end
+ }
+ endLiteralString(int interpCount) {
+ List parts = popList(2 * interpCount + 1, []);
+ tree.StringQuoting quoting = pop();
+ List<Expression> members = <Expression>[];
+ for (var i=0; i<parts.length; i++) {
+ var part = parts[i];
+ if (part is Expression) {
+ members.add(part);
+ } else {
+ assert(part is Token);
+ DartString str = stringValidator.validateInterpolationPart(
+ part as Token,
+ quoting,
+ isFirst: i == 0,
+ isLast: i == parts.length - 1);
+ members.add(new Literal(new StringConstant(str)));
+ }
+ }
+ push(new StringConcat(members));
+ }
+ handleStringJuxtaposition(int litCount) {
+ push(new StringConcat(popList(litCount, <Expression>[])));
+ }
+ endArguments(int count, begin, end) {
+ push(popList(count, <Argument>[]));
+ }
+ handleNoArguments(token) {
+ push(null);
+ }
+ handleNoTypeArguments(token) {
+ push(<TypeAnnotation>[]);
+ }
+ endTypeArguments(int count, t, y) {
+ List<TypeAnnotation> args = <TypeAnnotation>[];
+ for (var i=0; i<count; i++) {
+ args.add(popTypeAnnotation());
+ }
+ push(args.reversed.toList(growable:false));
+ }
+ handleVoidKeyword(token) {
+ push(new Identifier("void"));
+ push(<TypeAnnotation>[]); // prepare for popTypeAnnotation
+ }
+ handleQualified(Token period) {
+ String last = pop(asName);
+ String first = pop(asName);
+ push(new Identifier('$first.$last'));
+ }
+ endSend(t) {
+ List<Argument> arguments = pop();
+ if (arguments == null)
+ return; // not a function call
+ Expression selector = pop();
+ push(new CallFunction(selector, arguments));
+ }
+ endThrowExpression(t, tt) {
+ push(new Throw(pop()));
+ }
+ handleAssignmentExpression(Token token) {
+ Expression right = pop();
+ Expression left = pop();
+ push(new Assignment(left, token.value, right));
+ }
+ handleBinaryExpression(Token token) {
+ Expression right = pop();
+ Receiver left = pop();
+ String tokenString = token.stringValue;
+ if (tokenString == '.') {
+ if (right is CallFunction) {
+ String name = (right.callee as Identifier).name;
+ push(new CallMethod(left, name, right.arguments));
+ } else {
+ push(new FieldExpression(left, (right as Identifier).name));
+ }
+ } else {
+ push(new BinaryOperator(left, tokenString, right));
+ }
+ }
+ handleConditionalExpression(question, colon) {
+ Expression elseExpression = pop();
+ Expression thenExpression = pop();
+ Expression condition = pop();
+ push(new Conditional(condition, thenExpression, elseExpression));
+ }
+ handleIdentifier(Token t) {
+ push(new Identifier(t.value));
+ }
+ handleOperator(t) {
+ push(new Identifier(t.value));
+ }
+ handleIndexedExpression(open, close) {
+ Expression index = pop();
+ Receiver object = pop();
+ push(new IndexExpression(object, index));
+ }
+ handleIsOperator(operathor, not, endToken) {
+ TypeAnnotation type = popTypeAnnotation();
+ Expression exp = pop();
+ TypeOperator r = new TypeOperator(exp, 'is', type);
+ if (not != null) {
+ push(new UnaryOperator('!', r));
+ } else {
+ push(r);
+ }
+ }
+ handleAsOperator(operathor, endToken) {
+ TypeAnnotation type = popTypeAnnotation();
+ Expression exp = pop();
+ push(new TypeOperator(exp, 'as', type));
+ }
+ handleLiteralBool(Token t) {
+ bool value = t.value == 'true';
+ push(new Literal(value ? new TrueConstant() : new FalseConstant()));
+ }
+ handleLiteralDouble(t) {
+ push(new Literal(new DoubleConstant(double.parse(t.value))));
+ }
+ handleLiteralInt(Token t) {
+ push(new Literal(new IntConstant(int.parse(t.value))));
+ }
+ handleLiteralNull(t) {
+ push(new Literal(new NullConstant()));
+ }
+ endLiteralSymbol(Token hash, int idCount) {
+ List<Identifier> ids = popList(idCount, <Identifier>[]);
+ push(new LiteralSymbol(ids.map((id) => id.name).join('.')));
+ }
+ handleLiteralList(int count, begin, constKeyword, end) {
+ List<Expression> exps = popList(count, <Expression>[]);
+ List<TypeAnnotation> types = pop();
+ assert(types.length <= 1);
+ push(new LiteralList(exps,
+ isConst: constKeyword != null,
+ typeArgument: types.length == 0 ? null : types[0]
+ ));
+ }
+ handleLiteralMap(int count, begin, constKeyword, end) {
+ List<LiteralMapEntry> entries = popList(count, <LiteralMapEntry>[]);
+ List<TypeAnnotation> types = pop();
+ assert(types.length == 0 || types.length == 2);
+ push(new LiteralMap(entries,
+ isConst: constKeyword != null,
+ typeArguments: types
+ ));
+ }
+ endLiteralMapEntry(colon, endToken) {
+ Expression value = pop();
+ Expression key = pop();
+ push(new LiteralMapEntry(key,value));
+ }
+ handleNamedArgument(colon) {
+ Expression exp = pop();
+ Identifier name = pop();
+ push(new NamedArgument(name.name, exp));
+ }
+ endConstructorReference(Token start, Token period, Token end) {
+ if (period == null) {
+ push(null); // indicate missing constructor name
+ }
+ }
+ handleNewExpression(t) {
+ List<Argument> args = pop();
+ String constructorName = pop(asName);
+ TypeAnnotation type = popTypeAnnotation();
+ push(new CallNew(type, args, constructorName: constructorName));
+ }
+ handleConstExpression(t) {
+ List<Argument> args = pop();
+ String constructorName = pop(asName);
+ TypeAnnotation type = popTypeAnnotation();
+ push(new CallNew(type, args, constructorName: constructorName,
+ isConst:true));
+ }
+ handleParenthesizedExpression(t) {
+ // do nothing, just leave expression on top of stack
+ }
+ handleSuperExpression(t) {
+ push(new SuperReceiver());
+ }
+ handleThisExpression(t) {
+ push(new This());
+ }
+ handleUnaryPostfixAssignmentExpression(Token t) {
+ push(new Increment.postfix(pop(), t.value));
+ }
+ handleUnaryPrefixAssignmentExpression(Token t) {
+ push(new Increment.prefix(pop(), t.value));
+ }
+ handleUnaryPrefixExpression(Token t) {
+ push(new UnaryOperator(t.value, pop()));
+ }
+
+ handleFunctionTypedFormalParameter(tok) {
+ // handled in endFormalParameter
+ }
+ endFormalParameter(thisKeyword) {
+ Expression defaultValue = null;
+ var x = pop();
+ if (x is DefaultValue) {
+ defaultValue = x.expression;
+ x = pop();
+ }
+ if (x is Parameters) {
+ String name = pop(asName);
+ TypeAnnotation returnType = popTypeAnnotation();
+ push(new Parameter.function(name, returnType, x, defaultValue));
+ } else {
+ String name = asName(x);
+ TypeAnnotation type = popTypeAnnotation();
+ push(new Parameter(name, type:type, defaultValue:defaultValue));
+ }
+ }
+ handleValuedFormalParameter(eq, tok) {
+ push(new DefaultValue(pop()));
+ }
+ endOptionalFormalParameters(int count, begin, end) {
+ bool isNamed = end.value == '}';
+ push(popList(count, <Parameter>[], asParameter));
+ push(isNamed); // Indicate optional parameters to endFormalParameters.
+ }
+ endFormalParameters(count, begin, end) {
+ if (count == 0) {
+ push(new Parameters([]));
+ return;
+ }
+ var last = pop(); // Detect if optional parameters are present.
+ if (last is bool) { // See endOptionalFormalParameters.
+ List<Parameter> optional = pop();
+ List<Parameter> required = popList(count-1, <Parameter>[], asParameter);
+ push(new Parameters(required, optional, last));
+ } else {
+ // No optional parameters.
+ List<Parameter> required = popList(count-1, <Parameter>[], asParameter);
+ required.add(last);
+ push(new Parameters(required));
+ }
+ }
+ handleNoFormalParameters(tok) {
+ push(new Parameters([]));
+ }
+ endUnamedFunction(t) {
+ Statement body = pop();
+ Parameters parameters = pop();
+ push(new FunctionExpression(parameters, body));
+ }
+ handleNoType(Token token) {
+ push(null);
+ }
+
+ endReturnStatement(bool hasExpression, begin, end) {
+ // This is also called for functions whose body is "=> expression"
+ if (hasExpression) {
+ push(new Return(pop()));
+ } else {
+ push(new Return());
+ }
+ }
+
+ endExpressionStatement(Token token) {
+ push(new ExpressionStatement(pop()));
+ }
+
+ endDoWhileStatement(Token doKeyword, Token whileKeyword, Token end) {
+ Expression condition = pop();
+ Statement body = pop();
+ push(new DoWhile(body, condition));
+ }
+
+ endWhileStatement(Token whileKeyword, Token end) {
+ Statement body = pop();
+ Expression condition = pop();
+ push(new While(condition, body));
+ }
+
+ endBlock(int count, Token begin, Token end) {
+ push(new Block(popList(count, <Statement>[])));
+ }
+
+ endRethrowStatement(Token throwToken, Token endToken) {
+ push(new Rethrow());
+ }
+
+ endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) {
+ Statement finallyBlock = null;
+ if (finallyKeyword != null) {
+ finallyBlock = pop();
+ }
+ List<CatchBlock> catchBlocks = popList(catchCount, <CatchBlock>[]);
+ Statement tryBlock = pop();
+ push(new Try(tryBlock, catchBlocks, finallyBlock));
+ }
+
+ void handleCatchBlock(Token onKeyword, Token catchKeyword) {
+ Statement block = pop();
+ String exceptionVar = null;
+ String stackVar = null;
+ if (catchKeyword != null) {
+ Parameters params = pop();
+ exceptionVar = params.requiredParameters[0].name;
+ if (params.requiredParameters.length > 1) {
+ stackVar = params.requiredParameters[1].name;
+ }
+ }
+ TypeAnnotation type = onKeyword == null ? null : pop();
+ push(new CatchBlock(block,
+ onType: type,
+ exceptionVar: exceptionVar,
+ stackVar: stackVar
+ ));
+ }
+
+ endSwitchStatement(Token switchKeyword, Token end) {
+ List<SwitchCase> cases = pop();
+ Expression expression = pop();
+ push(new Switch(expression, cases));
+ }
+
+ endSwitchBlock(int caseCount, Token begin, Token end) {
+ push(popList(caseCount, <SwitchCase>[]));
+ }
+
+ handleSwitchCase(int labelCount, int caseCount, Token defaultKeyword,
+ int statementCount, Token first, Token end) {
+ List<Statement> statements = popList(statementCount, <Statement>[]);
+ List<Expression> cases = popList(caseCount, <Expression>[]);
+ if (defaultKeyword != null) {
+ cases = null;
+ }
+ push(new SwitchCase(cases, statements));
+ }
+
+ handleCaseMatch(Token caseKeyword, Token colon) {
+ // do nothing, leave case expression on stack
+ }
+
+ handleBreakStatement(bool hasTarget, Token breakKeyword, Token end) {
+ String target = hasTarget ? pop(asName) : null;
+ push(new Break(target));
+ }
+
+ handleContinueStatement(bool hasTarget, Token continueKeyword, Token end) {
+ String target = hasTarget ? pop(asName) : null;
+ push(new Continue(target));
+ }
+
+ handleEmptyStatement(Token token) {
+ push(new EmptyStatement());
+ }
+
+
+ VariableDefinition asVariableDefinition(x) {
+ if (x is VariableDefinition)
+ return x;
+ if (x is Identifier)
+ return new VariableDefinition(x.name);
+ throw "Not a variable definition: ${x.runtimeType}";
+ }
+
+ endVariablesDeclaration(int count, Token end) {
+ List<VariableDefinition> variables =
+ popList(count, <VariableDefinition>[], asVariableDefinition);
+ TypeAnnotation type = popTypeAnnotation();
+ push(new VariableDefinitions(variables,
+ type: type,
+ isFinal: false, // TODO(asgerf): Parse modifiers.
+ isConst: false
+ ));
+ }
+
+ endInitializer(Token assign) {
+ Expression init = pop();
+ String name = pop(asName);
+ push(new VariableDefinition(name, init));
+ }
+
+ endIfStatement(Token ifToken, Token elseToken) {
+ Statement elsePart = (elseToken == null) ? null : pop();
+ Statement thenPart = pop();
+ Expression condition = pop();
+ push(new If(condition, thenPart, elsePart));
+ }
+
+ endForStatement(int updateCount, Token begin, Token end) {
+ Statement body = pop();
+ List<Expression> updates = popList(updateCount, <Expression>[]);
+ ExpressionStatement condition = pop(); // parsed as expression statement
+ Expression exp = condition == null ? null : condition.expression;
+ Node initializer = pop();
+ push(new For(initializer, exp, updates, body));
+ }
+
+ handleNoExpression(Token token) {
+ push(null);
+ }
+
+ endForIn(Token begin, Token inKeyword, Token end) {
+ Statement body = pop();
+ Expression exp = pop();
+ Node declaredIdentifier = pop();
+ push(new ForIn(declaredIdentifier, exp, body));
+ }
+
+ handleAssertStatement(Token assertKeyword, Token semicolonToken) {
+ Expression exp = pop();
+ Expression call = new CallFunction(new Identifier("assert"), [exp]);
+ push(new ExpressionStatement(call));
+ }
+
+ endLabeledStatement(int labelCount) {
+ Statement statement = pop();
+ for (int i=0; i<labelCount; i++) {
+ String label = pop(asName);
+ statement = new LabeledStatement(label, statement);
+ }
+ push(statement);
+ }
+
+ endFunctionDeclaration(Token end) {
+ Statement body = pop();
+ Parameters parameters = pop();
+ String name = pop(asName);
+ TypeAnnotation returnType = popTypeAnnotation();
+ push(new FunctionStatement(name, parameters, body, returnType));
+ }
+
+ endFunctionBody(int count, Token begin, Token end) {
+ push(new Block(popList(count, <Statement>[])));
+ }
+}
+
+class DefaultValue {
+ final Expression expression;
+ DefaultValue(this.expression);
+}
+
+/// Compares ASTs for structural equality.
+void checkDeepEqual(x, y) {
+ if (x is List && y is List) {
+ if (x.length != y.length)
+ return;
+ for (var i=0; i<x.length; i++) {
+ checkDeepEqual(x[i], y[i]);
+ }
+ }
+ else if (x is Node && y is Node) {
+ if (x.runtimeType != y.runtimeType)
+ throw new Error();
+ InstanceMirror xm = reflect(x);
+ InstanceMirror ym = reflect(y);
+ for (Symbol name in xm.type.instanceMembers.keys) {
+ if (reflectClass(Object).declarations.containsKey(name)) {
+ continue; // do not check things from Object, such as hashCode
+ }
+ MethodMirror mm = xm.type.instanceMembers[name];
+ if (mm.isGetter) {
+ var xv = xm.getField(name).reflectee;
+ var yv = ym.getField(name).reflectee;
+ checkDeepEqual(xv,yv);
+ }
+ }
+ }
+ else if (x is PrimitiveConstant && y is PrimitiveConstant) {
+ checkDeepEqual(x.value, y.value);
+ }
+ else if (x is DartString && y is DartString) {
+ if (x.slowToString() != y.slowToString()) {
+ throw new Error();
+ }
+ }
+ else {
+ if (x != y) {
+ throw new Error();
+ }
+ }
+}
+
+Expression parseExpression(String code) {
+ SourceFile file = new StringSourceFile('', code);
+ Scanner scan = new Scanner(file);
+ Token tok = scan.tokenize();
+ AstBuilder builder = new AstBuilder();
+ Parser parser = new Parser(builder);
+ tok = parser.parseExpression(tok);
+ if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) {
+ throw "Parse error in $code";
+ }
+ return builder.pop();
+}
+Statement parseStatement(String code) {
+ SourceFile file = new StringSourceFile('', code);
+ Scanner scan = new Scanner(file);
+ Token tok = scan.tokenize();
+ AstBuilder builder = new AstBuilder();
+ Parser parser = new Parser(builder);
+ tok = parser.parseStatement(tok);
+ if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) {
+ throw "Parse error in $code";
+ }
+ return builder.pop();
+}
+
+String unparseExpression(Expression exp) {
+ StringBuffer buf = new StringBuffer();
+ new Unparser(buf).writeExpression(exp);
+ return buf.toString();
+}
+String unparseStatement(Statement stmt) {
+ StringBuffer buf = new StringBuffer();
+ new Unparser(buf).writeStatement(stmt);
+ return buf.toString();
+}
+
+/// Parses [code], unparses the resulting AST, then parses the unparsed text.
+/// The ASTs from the first and second parse are then compared for structural
+/// equality. Alternatively, if [expected] is not an empty string, the second
+/// parse must match the AST of parsing [expected].
+void checkFn(String code, String expected, Function parse, Function unparse) {
+ String unparsed = "";
+ try {
+ var firstParse = parse(code);
+ unparsed = unparse(firstParse);
+ var secondParse = parse(unparsed);
+ var baseline = expected == "" ? firstParse : parse(expected);
+ checkDeepEqual(baseline, secondParse);
Kevin Millikin (Google) 2014/04/24 13:27:25 I guess this should be Expect.isTrue(checkDeepEqua
+ } catch (e, stack) {
+ print("INPUT: ${code}");
+ print("OUTPUT: ${unparsed}");
+ print("ERROR: $e");
+ if (e is! String)
+ print(stack);
+ print("-----------");
+ }
+}
+
+void checkExpression(String code, [String expected=""]) {
+ checkFn(code, expected, parseExpression, unparseExpression);
+}
+void checkStatement(String code, [String expected=""]) {
+ checkFn(code, expected, parseStatement, unparseStatement);
+}
+
+void debugTokens(String code) {
+ SourceFile file = new StringSourceFile('', code);
+ Scanner scan = new Scanner(file);
+ Token tok = scan.tokenize();
+ while (tok.next != tok) {
+ print(tok.toString());
+ tok = tok.next;
+ }
+}
+
+void main() {
+ // To check if these tests are effective, one should manually alter
+ // something in [Unparser] and see if a test fails.
+
+ checkExpression(" a + b + c");
+ checkExpression("(a + b) + c");
+ checkExpression(" a + (b + c)");
+
+ checkExpression(" a + b - c");
+ checkExpression("(a + b) - c");
+ checkExpression(" a + (b - c)");
+
+ checkExpression(" a - b + c");
+ checkExpression("(a - b) + c");
+ checkExpression(" a - (b + c)");
+
+ checkExpression(" a * b + c");
+ checkExpression("(a * b) + c");
+ checkExpression(" a * (b + c)");
+
+ checkExpression(" a + b * c");
+ checkExpression("(a + b) * c");
+ checkExpression(" a + (b * c)");
+
+ checkExpression(" a * b * c");
+ checkExpression("(a * b) * c");
+ checkExpression(" a * (b * c)");
+
+ checkExpression("a is T");
+ checkExpression("a is! T");
+ checkExpression("!(a is T)");
+
+ checkExpression("a is T.x");
+ checkExpression("a is! T.x");
+ checkExpression("!(a is T.x)");
+ checkExpression("!(a is T).x");
+
+ checkExpression("a as T.x");
+ checkExpression("(a as T).x");
+
+ checkExpression("a == b");
+ checkExpression("a != b");
+ checkExpression("!(a == b)", "a != b");
+
+ checkExpression("a && b ? c : d");
+ checkExpression("(a && b) ? c : d");
+ checkExpression("a && (b ? c : d)");
+
+ checkExpression("a || b ? c : d");
+ checkExpression("(a || b) ? c : d");
+ checkExpression("a || (b ? c : d)");
+
+ checkExpression(" a ? b : c && d");
+ checkExpression(" a ? b : (c && d)");
+ checkExpression("(a ? b : c) && d");
+
+ checkExpression(" a ? b : c = d");
+ checkExpression(" a ? b : (c = d)");
+
+ checkExpression("(a == b) == c");
+ checkExpression("a == (b == c)");
+
+ checkExpression(" a < b == c");
+ checkExpression("(a < b) == c");
+ checkExpression(" a < (b == c)");
+
+ checkExpression(" a == b < c");
+ checkExpression("(a == b) < c");
+ checkExpression(" a == (b < c)");
+
+ checkExpression("x.f()");
+ checkExpression("(x.f)()");
+
+ checkExpression("x.f()()");
+ checkExpression("(x.f)()()");
+
+ checkExpression("x.f().g()");
+ checkExpression("(x.f)().g()");
+
+ checkExpression("x.f()");
+ checkExpression("x.f(1 + 2)");
+ checkExpression("x.f(1 + 2, 3 + 4)");
+ checkExpression("x.f(1 + 2, foo:3 + 4)");
+ checkExpression("x.f(1 + 2, foo:3 + 4, bar: 5)");
+ checkExpression("x.f(foo:3 + 4)");
+ checkExpression("x.f(foo:3 + 4, bar: 5)");
+
+ checkExpression("x.f.g.h");
+ checkExpression("(x.f).g.h");
+ checkExpression("(x.f.g).h");
+
+ checkExpression(" a = b + c");
+ checkExpression(" a = (b + c)");
+ checkExpression("(a = b) + c");
+
+ checkExpression("a + (b = c)");
+
+ checkExpression("dx * dx + dy * dy < r * r",
+ "((dx * dx) + (dy * dy)) < (r * r)");
+ checkExpression("mid = left + right << 1",
+ "mid = ((left + right) << 1)");
+ checkExpression("a + b % c * -d ^ e - f ~/ x & ++y / z++ | w > a ? b : c");
+ checkExpression("a + b % c * -d ^ (e - f) ~/ x & ++y / z++ | w > a ? b : c");
+
+ checkExpression("'foo'");
+ checkExpression("'foo' 'bar'", "'foobar'");
+
+ checkExpression("{}.length");
+ checkExpression("{x: 1+2}.length");
+ checkExpression("<String,int>{}.length");
+ checkExpression("<String,int>{x: 1+2}.length");
+
+ checkExpression("[].length");
+ checkExpression("[1+2].length");
+ checkExpression("<num>[].length");
+ checkExpression("<num>[1+2].length");
+
+ checkExpression("x + -y");
+ checkExpression("x + --y");
+ checkExpression("x++ + y");
+ checkExpression("x + ++y");
+ checkExpression("x-- - y");
+ checkExpression("x-- - -y");
+ checkExpression("x - --y");
+
+ checkExpression("x && !y");
+ checkExpression("!x && y");
+ checkExpression("!(x && y)");
+
+ checkExpression(" super + 1 * 2");
+ checkExpression("(super + 1) * 2");
+ checkExpression(" super + (1 * 2)");
+ checkExpression("x + -super");
+ checkExpression("x-- - -super");
+ checkExpression("x - -super");
+ checkExpression("x && !super");
+
+ checkExpression("super.f(1, 2) + 3");
+ checkExpression("super.f + 3");
+
+ checkExpression(r"'foo\nbar'");
+ checkExpression(r"'foo\r\nbar'");
+ checkExpression(r"'foo\rbar'");
+ checkExpression(r"'foo\'bar'");
+ checkExpression(r"""'foo"bar'""");
+ checkExpression(r"r'foo\nbar'");
+ checkExpression("''");
+ checkExpression("r''");
+
+ var sq = "'";
+ var dq = '"';
+ checkExpression("'$dq$dq' \"$sq$sq\"");
+ checkExpression("'$dq$dq$dq$dq' \"$sq$sq$sq$sq\"");
+ checkExpression(r"'\$\$\$\$\$\$\$\$\$'");
+ checkExpression("'$dq$dq$dq' '\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n' \"$sq$sq$sq\"");
+ checkExpression("'$dq$dq$dq' '\\r\\r\\r\\r\\r\\r\\r\\r\\r\\r' \"$sq$sq$sq\"");
+ checkExpression("'$dq$dq$dq' '\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n' \"$sq$sq$sq\"");
+
+ checkExpression(r"'$foo'");
+ checkExpression(r"'${foo}x'");
+ checkExpression(r"'${foo}x\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'");
+ checkExpression(r"'abc' '${foo}' r'\\\\\\\'");
+
+ checkExpression(r"'${$x}'");
+ checkExpression(r"'${$x}y'");
+
+ checkStatement("var x, y, z;");
+ checkStatement("final x, y, z;");
+ checkStatement("dynamic x, y, z;");
+ checkStatement("String x, y, z;");
+ checkStatement("List<int> x, y, z;");
+ checkStatement("final dynamic x, y, z;");
+ checkStatement("final String x, y, z;");
+ checkStatement("final List<int> x, y, z;");
+
+ checkStatement("var x = y, z;");
+ checkStatement("var x, y = z;");
+ checkStatement("var x = y = z;");
+
+ // Note: We sometimes have to pass an expected string to account for
+ // block flattening which does not preserve structural AST equality
+ checkStatement("if (x) if (y) foo(); else bar(); ");
+ checkStatement("if (x) { if (y) foo(); } else bar(); ");
+ checkStatement("if (x) { if (y) foo(); else bar(); }",
+ "if (x) if (y) foo(); else bar(); ");
+
+ checkStatement("if (x) while (y) if (z) foo(); else bar(); ");
+ checkStatement("if (x) while (y) { if (z) foo(); } else bar(); ");
+ checkStatement("if (x) while (y) { if (z) foo(); else bar(); }",
+ "if (x) while (y) if (z) foo(); else bar(); ");
+
+ checkStatement("{var x = 1; {var x = 2;} return x;}");
+ checkStatement("{var x = 1; {x = 2;} return x;}",
+ "{var x = 1; x = 2; return x;}");
+
+ checkStatement("if (x) {var x = 1;}");
+
+ checkStatement("({'foo': 1}).bar();");
+ checkStatement("({'foo': 1}).length;");
+ checkStatement("({'foo': 1}).length + 1;");
+ checkStatement("({'foo': 1})['foo'].toString();");
+ checkStatement("({'foo': 1})['foo'] = 3;");
+ checkStatement("({'foo': 1}['foo']());");
+ checkStatement("({'foo': 1}['foo'])();");
+ checkStatement("({'foo': 1})['foo'].x++;");
+ checkStatement("({'foo': 1}) is Map;");
+ checkStatement("({'foo': 1}) as Map;");
+ checkStatement("({'foo': 1}) is util.Map;");
+ checkStatement("({'foo': 1}) + 1;");
+
+ checkStatement("[1].bar();");
+ checkStatement("1.bar();");
+ checkStatement("'foo'.bar();");
+
+ checkStatement("do while(x); while (y);");
+ checkStatement("{do; while(x); while (y);}");
+
+}
+

Powered by Google App Engine
This is Rietveld 408576698