| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart_tree_printer; |
| |
| import '../constants/values.dart' as values; |
| import '../dart_types.dart' as types; |
| import '../dart2jslib.dart' as dart2js; |
| import '../elements/elements.dart' as elements; |
| import '../tree/tree.dart' as tree; |
| import '../scanner/scannerlib.dart'; |
| import '../util/util.dart'; |
| import 'backend_ast_nodes.dart'; |
| import 'backend_ast_emitter.dart' show TypeGenerator; |
| |
| /// Translates the backend AST to Dart frontend AST. |
| tree.Node emit(dart2js.TreeElementMapping treeElements, |
| RootNode root) { |
| return new TreePrinter(treeElements).makeDefinition(root); |
| } |
| |
| /// If true, the unparser will insert a coment in front of every function |
| /// it emits. This helps indicate which functions were translated by the new |
| /// backend. |
| bool INSERT_NEW_BACKEND_COMMENT = |
| const bool.fromEnvironment("INSERT_NEW_BACKEND_COMMENT"); |
| |
| /// Converts backend ASTs to frontend ASTs. |
| class TreePrinter { |
| dart2js.TreeElementMapping treeElements; |
| |
| TreePrinter([this.treeElements]); |
| |
| tree.Node makeDefinition(RootNode node) { |
| if (node is FieldDefinition) { |
| tree.Node definition; |
| if (node.initializer == null) { |
| definition = makeIdentifier(node.element.name); |
| } else { |
| definition = new tree.SendSet( |
| null, |
| makeIdentifier(node.element.name), |
| new tree.Operator(assignmentToken("=")), |
| singleton(makeExpression(node.initializer))); |
| } |
| setElement(definition, node.element, node); |
| return new tree.VariableDefinitions( |
| null, // TODO(sigurdm): Type |
| makeVarModifiers(useVar: true, |
| isFinal: node.element.isFinal, |
| isStatic: node.element.isStatic, |
| isConst: node.element.isConst), |
| makeList(null, [definition], close: semicolon)); |
| } else if (node is FunctionExpression) { |
| return makeExpression(node); |
| } else { |
| assert(false); |
| return null; |
| } |
| } |
| |
| void setElement(tree.Node node, elements.Element element, source) { |
| if (treeElements != null) { |
| if (element == null) { |
| throw "Missing element from ${source}"; |
| } |
| treeElements[node] = element; |
| } |
| } |
| |
| void setType(tree.Node node, types.DartType type, source) { |
| if (treeElements != null) { |
| if (type == null) { |
| throw "Missing type from ${source}"; |
| } |
| treeElements.setType(node, type); |
| } |
| } |
| |
| // Group tokens: () [] {} <> |
| static BeginGroupToken makeGroup(PrecedenceInfo open, PrecedenceInfo close) { |
| BeginGroupToken openTok = new BeginGroupToken(open, -1); |
| openTok.endGroup = new SymbolToken(close, -1); |
| return openTok; |
| } |
| |
| final BeginGroupToken openParen = makeGroup(OPEN_PAREN_INFO, |
| CLOSE_PAREN_INFO); |
| final BeginGroupToken openBrace = makeGroup(OPEN_CURLY_BRACKET_INFO, |
| CLOSE_CURLY_BRACKET_INFO); |
| final BeginGroupToken openBracket = makeGroup(OPEN_SQUARE_BRACKET_INFO, |
| CLOSE_SQUARE_BRACKET_INFO); |
| final BeginGroupToken lt = makeGroup(LT_INFO, GT_INFO); |
| |
| Token get closeParen => openParen.endGroup; |
| Token get closeBrace => openBrace.endGroup; |
| Token get closeBracket => openBracket.endGroup; |
| Token get gt => lt.endGroup; |
| |
| // Symbol tokens |
| final Token semicolon = new SymbolToken(SEMICOLON_INFO, -1); |
| final Token indexToken = new SymbolToken(INDEX_INFO, -1); // "[]" |
| final Token question = new SymbolToken(QUESTION_INFO, -1); |
| final Token colon = new SymbolToken(COLON_INFO, -1); |
| final Token hash = new SymbolToken(HASH_INFO, -1); |
| final Token bang = new SymbolToken(BANG_INFO, -1); |
| final Token eq = new SymbolToken(EQ_INFO, -1); |
| |
| // Keyword tokens |
| static Token makeIdToken(String text) { |
| return new StringToken.fromString(IDENTIFIER_INFO, text, -1); |
| } |
| final Token newToken = makeIdToken('new'); |
| final Token constToken = makeIdToken('const'); |
| final Token throwToken = makeIdToken('throw'); |
| final Token rethrowToken = makeIdToken('rethrow'); |
| final Token breakToken = makeIdToken('break'); |
| final Token continueToken = makeIdToken('continue'); |
| final Token doToken = makeIdToken('do'); |
| final Token whileToken = makeIdToken('while'); |
| final Token ifToken = makeIdToken('if'); |
| final Token elseToken = makeIdToken('else'); |
| final Token awaitToken = makeIdToken('await'); |
| final Token forToken = makeIdToken('for'); |
| final Token inToken = makeIdToken('in'); |
| final Token returnToken = makeIdToken('return'); |
| final Token switchToken = makeIdToken('switch'); |
| final Token caseToken = makeIdToken('case'); |
| final Token defaultToken = makeIdToken('default'); |
| final Token tryToken = makeIdToken('try'); |
| final Token catchToken = makeIdToken('catch'); |
| final Token onToken = makeIdToken('on'); |
| final Token finallyToken = makeIdToken('finally'); |
| final Token getToken = makeIdToken('get'); |
| final Token setToken = makeIdToken('set'); |
| final Token classToken = makeIdToken('class'); |
| final Token extendsToken = makeIdToken('extends'); |
| final Token withToken = makeIdToken('with'); |
| final Token implementsToken = makeIdToken('implements'); |
| final Token typedefToken = makeIdToken('typedef'); |
| final Token enumToken = makeIdToken('enum'); |
| |
| static tree.Identifier makeIdentifier(String name) { |
| return new tree.Identifier( |
| new StringToken.fromString(IDENTIFIER_INFO, name, -1)); |
| } |
| |
| static tree.Operator makeOperator(String name) { |
| return new tree.Operator( |
| new StringToken.fromString(IDENTIFIER_INFO, name, -1)); |
| } |
| |
| // Utilities for creating NodeLists |
| Link<tree.Node> makeLink(Iterable<tree.Node> nodes) { |
| LinkBuilder builder = new LinkBuilder(); |
| for (tree.Node node in nodes) { |
| builder.addLast(node); |
| } |
| return builder.toLink(); |
| } |
| |
| tree.NodeList blankList() { |
| return new tree.NodeList(null, makeLink([]), null, ''); |
| } |
| tree.NodeList singleton(tree.Node node) { |
| return new tree.NodeList(null, makeLink([node]), null, ''); |
| } |
| tree.NodeList makeList(String delimiter, |
| Iterable<tree.Node> nodes, |
| { Token open, |
| Token close }) { |
| return new tree.NodeList(open, makeLink(nodes), close, delimiter); |
| } |
| tree.NodeList parenList(String delimiter, Iterable<tree.Node> nodes) { |
| return makeList(delimiter, nodes, open: openParen, close: closeParen); |
| } |
| tree.NodeList bracketList(String delimiter, Iterable<tree.Node> nodes) { |
| return makeList(delimiter, nodes, open: openBracket, close: closeBracket); |
| } |
| tree.NodeList braceList(String delimiter, Iterable<tree.Node> nodes) { |
| return makeList(delimiter, nodes, open: openBrace, close: closeBrace); |
| } |
| tree.NodeList argList(Iterable<tree.Node> nodes) { |
| return parenList(',', nodes); |
| } |
| tree.NodeList typeArgList(Iterable<tree.Node> nodes) { |
| return makeList(',', nodes, open: lt, close: gt); |
| } |
| |
| /// Converts a qualified name into nested Sends. |
| tree.Node makeName(String name) { |
| if (name == null) { |
| return null; |
| } |
| List<String> names = name.split('.').toList(growable:false); |
| tree.Node node = makeIdentifier(names[0]); |
| for (int i = 1; i < names.length; i++) { |
| node = new tree.Send(node, makeIdentifier(names[i])); |
| } |
| return node; |
| } |
| |
| static Token assignmentToken(String operatorName) { |
| switch (operatorName) { |
| case '=': return new SymbolToken(EQ_INFO, -1); |
| case '+=': return new SymbolToken(PLUS_EQ_INFO, -1); |
| case '-=': return new SymbolToken(MINUS_EQ_INFO, -1); |
| case '*=': return new SymbolToken(STAR_EQ_INFO, -1); |
| case '/=': return new SymbolToken(SLASH_EQ_INFO, -1); |
| case '~/=': return new SymbolToken(TILDE_SLASH_EQ_INFO, -1); |
| case '%=': return new SymbolToken(PERCENT_EQ_INFO, -1); |
| case '&=': return new SymbolToken(AMPERSAND_EQ_INFO, -1); |
| case '^=': return new SymbolToken(CARET_EQ_INFO, -1); |
| case '|=': return new SymbolToken(BAR_EQ_INFO, -1); |
| case '>>=': return new SymbolToken(GT_GT_EQ_INFO, -1); |
| case '<<=': return new SymbolToken(LT_LT_EQ_INFO, -1); |
| default: |
| throw "Unrecognized assignment operator: $operatorName"; |
| } |
| } |
| |
| static Token binopToken(String operatorName) { |
| switch (operatorName) { |
| case '+': return new SymbolToken(PLUS_INFO, -1); |
| case '-': return new SymbolToken(MINUS_INFO, -1); |
| case '*': return new SymbolToken(STAR_INFO, -1); |
| case '/': return new SymbolToken(SLASH_INFO, -1); |
| case '~/': return new SymbolToken(TILDE_SLASH_INFO, -1); |
| case '%': return new SymbolToken(PERCENT_INFO, -1); |
| case '&': return new SymbolToken(AMPERSAND_INFO, -1); |
| case '^': return new SymbolToken(CARET_INFO, -1); |
| case '|': return new SymbolToken(BAR_INFO, -1); |
| case '>>': return new SymbolToken(GT_GT_INFO, -1); |
| case '<<': return new SymbolToken(LT_LT_INFO, -1); |
| case '==': return new SymbolToken(EQ_EQ_INFO, -1); |
| case '!=': return new SymbolToken(BANG_EQ_INFO, -1); |
| case '>': return new SymbolToken(GT_INFO, -1); |
| case '>=': return new SymbolToken(GT_EQ_INFO, -1); |
| case '<': return new SymbolToken(LT_INFO, -1); |
| case '<=': return new SymbolToken(LT_EQ_INFO, -1); |
| case '&&': return new SymbolToken(AMPERSAND_AMPERSAND_INFO, -1); |
| case '||': return new SymbolToken(BAR_BAR_INFO, -1); |
| default: |
| throw "Unrecognized binary operator: $operatorName"; |
| } |
| } |
| |
| static Token incrementToken(String operatorName) { |
| switch (operatorName) { |
| case '++': return new SymbolToken(PLUS_PLUS_INFO, -1); |
| case '--': return new SymbolToken(MINUS_MINUS_INFO, -1); |
| default: |
| throw "Unrecognized increment operator: $operatorName"; |
| } |
| } |
| |
| static Token typeOpToken(String operatorName) { |
| switch (operatorName) { // "is!" is not an operator in the frontend AST. |
| case 'is': return new SymbolToken(IS_INFO, -1); |
| case 'as': return new SymbolToken(AS_INFO, -1); |
| default: |
| throw 'Unrecognized type operator: $operatorName'; |
| } |
| } |
| |
| Token unopToken(String operatorName) { |
| switch (operatorName) { |
| case '-': return new SymbolToken(MINUS_INFO, -1); |
| case '~': return new SymbolToken(TILDE_INFO, -1); |
| case '!': return bang; |
| default: |
| throw "Unrecognized unary operator: $operatorName"; |
| } |
| } |
| |
| tree.Node makeStaticReceiver(elements.Element element) { |
| if (treeElements == null) return null; |
| if (element.isStatic) { |
| elements.ClassElement enclosingClass = element.enclosingClass; |
| tree.Send send = new tree.Send( |
| null, |
| makeIdentifier(enclosingClass.name)); |
| treeElements[send] = enclosingClass; |
| return send; |
| } else { |
| return null; |
| } |
| } |
| |
| tree.Node makeArgument(Argument arg) { |
| if (arg is Expression) { |
| return makeExpression(arg); |
| } else if (arg is NamedArgument) { |
| return new tree.NamedArgument( |
| makeIdentifier(arg.name), |
| colon, |
| makeExpression(arg.expression)); |
| } else { |
| throw "Unrecognized argument type: ${arg}"; |
| } |
| } |
| |
| tree.Node makeExpression(Expression exp) { |
| return makeExp(exp, EXPRESSION); |
| } |
| |
| /// Converts [exp] to a [tree.Node] that unparses to an expression with |
| /// a precedence level of at least [minPrecedence]. The expression will be |
| /// wrapped in a parenthesis if necessary. |
| tree.Node makeExp(Receiver exp, int minPrecedence, {bool beginStmt: false}) { |
| tree.Node result; |
| int precedence; |
| bool needParen = false; |
| if (exp is SuperReceiver) { |
| precedence = CALLEE; |
| result = makeIdentifier('super'); |
| } else if (exp is Assignment) { |
| Expression left = exp.left; |
| tree.Node receiver; |
| tree.Node selector; |
| tree.NodeList arguments; |
| elements.Element element; |
| if (left is Identifier) { |
| receiver = makeStaticReceiver(left.element); |
| selector = makeIdentifier(left.name); |
| arguments = singleton(makeExpression(exp.right)); |
| element = left.element; |
| } else if (left is FieldExpression) { |
| receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); |
| selector = makeIdentifier(left.fieldName); |
| arguments = singleton(makeExpression(exp.right)); |
| } else if (left is IndexExpression) { |
| receiver = makeExp(left.object, PRIMARY, beginStmt: beginStmt); |
| selector = new tree.Operator(indexToken); |
| arguments = bracketList(',', |
| [makeExpression(left.index), makeExpression(exp.right)]); |
| } else { |
| throw "Unexpected left-hand side of assignment: ${left}"; |
| } |
| tree.Operator op = new tree.Operator(assignmentToken(exp.operator)); |
| result = new tree.SendSet(receiver, selector, op, arguments); |
| if (left is Identifier) { |
| setElement(result, element, exp); |
| } |
| precedence = EXPRESSION; |
| } else if (exp is FieldInitializer) { |
| precedence = EXPRESSION; |
| tree.Node receiver = makeIdentifier('this'); |
| tree.Node selector = makeIdentifier(exp.element.name); |
| tree.Operator op = new tree.Operator(assignmentToken("=")); |
| // We pass CALLEE to ensure we write eg.: |
| // class B { var x; B() : x = (() {return a;}) {}} |
| // Not the invalid: |
| // class B { var x; B() : x = () {return a;} {}} |
| result = new tree.SendSet(receiver, selector, op, |
| singleton(makeExp(exp.body, CALLEE))); |
| setElement(result, exp.element, exp); |
| } else if (exp is SuperInitializer) { |
| precedence = EXPRESSION; |
| tree.Node receiver = makeIdentifier('super'); |
| tree.NodeList arguments = |
| argList(exp.arguments.map(makeArgument).toList()); |
| if (exp.target.name == "") { |
| result = new tree.Send(null, receiver, arguments); |
| } else { |
| result = new tree.Send(receiver, |
| makeIdentifier(exp.target.name), |
| arguments); |
| } |
| setElement(result, exp.target, exp); |
| } else if (exp is BinaryOperator) { |
| precedence = BINARY_PRECEDENCE[exp.operator]; |
| int deltaLeft = isAssociativeBinaryOperator(precedence) ? 0 : 1; |
| result = new tree.Send( |
| makeExp(exp.left, precedence + deltaLeft, beginStmt: beginStmt), |
| new tree.Operator(binopToken(exp.operator)), |
| singleton(makeExp(exp.right, precedence + 1))); |
| } else if (exp is CallFunction) { |
| precedence = CALLEE; |
| tree.Node selector; |
| Expression callee = exp.callee; |
| elements.Element element; |
| tree.Node receiver; |
| if (callee is Identifier) { |
| receiver = makeStaticReceiver(callee.element); |
| selector = makeIdentifier(callee.name); |
| element = callee.element; |
| } else { |
| selector = makeExp(callee, CALLEE, beginStmt: beginStmt); |
| } |
| result = new tree.Send( |
| receiver, |
| selector, |
| argList(exp.arguments.map(makeArgument))); |
| if (callee is Identifier) { |
| setElement(result, element, exp); |
| } |
| } else if (exp is CallMethod) { |
| precedence = CALLEE; |
| // TODO(sra): Elide receiver when This, but only if not in a scope that |
| // shadows the method (e.g. constructor body). |
| tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); |
| result = new tree.Send( |
| receiver, |
| makeIdentifier(exp.methodName), |
| argList(exp.arguments.map(makeArgument))); |
| } else if (exp is CallNew) { |
| precedence = CALLEE; |
| tree.Node selector = makeName(exp.type.name); |
| if (exp.type.typeArguments.length > 0) { |
| selector = new tree.TypeAnnotation( |
| selector, |
| typeArgList(exp.type.typeArguments.map(makeType))); |
| setType(selector, exp.dartType, exp); |
| } |
| if (exp.constructorName != null) { |
| selector = new tree.Send( |
| selector, |
| makeIdentifier(exp.constructorName)); |
| } |
| tree.Send send = new tree.Send( |
| null, |
| selector, |
| argList(exp.arguments.map(makeArgument))); |
| result = new tree.NewExpression( |
| exp.isConst ? constToken : newToken, |
| send); |
| setType(result, exp.dartType, exp); |
| setElement(send, exp.constructor, exp); |
| } else if (exp is CallStatic) { |
| precedence = CALLEE; |
| result = new tree.Send( |
| makeStaticReceiver(exp.element), |
| makeIdentifier(exp.methodName), |
| argList(exp.arguments.map(makeArgument))); |
| setElement(result, exp.element, exp); |
| } else if (exp is Conditional) { |
| precedence = CONDITIONAL; |
| result = new tree.Conditional( |
| makeExp(exp.condition, LOGICAL_OR, beginStmt: beginStmt), |
| makeExp(exp.thenExpression, EXPRESSION), |
| makeExp(exp.elseExpression, EXPRESSION), |
| question, |
| colon); |
| } else if (exp is FieldExpression) { |
| precedence = PRIMARY; |
| // TODO(sra): Elide receiver when This, but only if not in a scope that |
| // shadows the method (e.g. constructor body). |
| tree.Node receiver = makeExp(exp.object, PRIMARY, beginStmt: beginStmt); |
| result = new tree.Send(receiver, makeIdentifier(exp.fieldName)); |
| } else if (exp is ConstructorDefinition) { |
| precedence = EXPRESSION; |
| tree.NodeList parameters = makeParameters(exp.parameters); |
| tree.NodeList initializers = |
| exp.initializers == null || exp.initializers.isEmpty |
| ? null |
| : makeList(",", exp.initializers.map(makeExpression).toList()); |
| tree.Node body = exp.isConst || exp.body == null |
| ? new tree.EmptyStatement(semicolon) |
| : makeFunctionBody(exp.body); |
| result = new tree.FunctionExpression(constructorName(exp), |
| parameters, |
| body, |
| null, // return type |
| makeFunctionModifiers(exp), |
| initializers, |
| null, // get/set |
| null); // async modifier |
| setElement(result, exp.element, exp); |
| } else if (exp is FunctionExpression) { |
| precedence = PRIMARY; |
| if (beginStmt && exp.name != null) { |
| needParen = true; // Do not mistake for function declaration. |
| } |
| Token getOrSet = exp.isGetter |
| ? getToken |
| : exp.isSetter |
| ? setToken |
| : null; |
| tree.NodeList parameters = exp.isGetter |
| ? makeList("", []) |
| : makeParameters(exp.parameters); |
| tree.Node body = makeFunctionBody(exp.body); |
| result = new tree.FunctionExpression( |
| functionName(exp), |
| parameters, |
| body, |
| exp.returnType == null || exp.element.isConstructor |
| ? null |
| : makeType(exp.returnType), |
| makeFunctionModifiers(exp), |
| null, // initializers |
| getOrSet, // get/set |
| null); // async modifier |
| elements.Element element = exp.element; |
| if (element != null) setElement(result, element, exp); |
| } else if (exp is Identifier) { |
| precedence = CALLEE; |
| result = new tree.Send( |
| makeStaticReceiver(exp.element), |
| makeIdentifier(exp.name)); |
| setElement(result, exp.element, exp); |
| } else if (exp is Increment) { |
| Expression lvalue = exp.expression; |
| tree.Node receiver; |
| tree.Node selector; |
| tree.Node argument; |
| bool innerBeginStmt = beginStmt && !exp.isPrefix; |
| if (lvalue is Identifier) { |
| receiver = makeStaticReceiver(lvalue.element); |
| selector = makeIdentifier(lvalue.name); |
| } else if (lvalue is FieldExpression) { |
| receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); |
| selector = makeIdentifier(lvalue.fieldName); |
| } else if (lvalue is IndexExpression) { |
| receiver = makeExp(lvalue.object, PRIMARY, beginStmt: innerBeginStmt); |
| selector = new tree.Operator(indexToken); |
| argument = makeExpression(lvalue.index); |
| } else { |
| throw "Unrecognized left-hand side: ${lvalue}"; |
| } |
| tree.Operator op = new tree.Operator(incrementToken(exp.operator)); |
| if (exp.isPrefix) { |
| precedence = UNARY; |
| result = new tree.SendSet.prefix(receiver, selector, op, argument); |
| } else { |
| precedence = POSTFIX_INCREMENT; |
| result = new tree.SendSet.postfix(receiver, selector, op, argument); |
| } |
| if (lvalue is Identifier) { |
| setElement(result, lvalue.element, exp); |
| } |
| } else if (exp is IndexExpression) { |
| precedence = CALLEE; |
| result = new tree.Send( |
| makeExp(exp.object, PRIMARY, beginStmt: beginStmt), |
| new tree.Operator(indexToken), |
| bracketList(',', [makeExpression(exp.index)])); |
| } else if (exp is Literal) { |
| precedence = CALLEE; |
| values.PrimitiveConstantValue value = exp.value; |
| Token tok = new StringToken.fromString( |
| STRING_INFO, '${value.primitiveValue}', -1); |
| if (value.isString) { |
| result = unparseStringLiteral(exp); |
| } else if (value.isInt) { |
| result = new tree.LiteralInt(tok, null); |
| } else if (value.isDouble) { |
| if (value.primitiveValue == double.INFINITY) { |
| precedence = MULTIPLICATIVE; |
| tok = new StringToken.fromString(STRING_INFO, '1/0.0', -1); |
| } else if (value.primitiveValue == double.NEGATIVE_INFINITY) { |
| precedence = MULTIPLICATIVE; |
| tok = new StringToken.fromString(STRING_INFO, '-1/0.0', -1); |
| } else if (value.primitiveValue.isNaN) { |
| precedence = MULTIPLICATIVE; |
| tok = new StringToken.fromString(STRING_INFO, '0/0.0', -1); |
| } |
| result = new tree.LiteralDouble(tok, null); |
| } else if (value.isBool) { |
| result = new tree.LiteralBool(tok, null); |
| } else if (value.isNull) { |
| result = new tree.LiteralNull(tok); |
| } else { |
| throw "Unrecognized constant: ${value}"; |
| } |
| } else if (exp is LiteralList) { |
| precedence = PRIMARY; |
| tree.NodeList typeArgs = null; |
| if (exp.typeArgument != null) { |
| typeArgs = typeArgList([makeType(exp.typeArgument)]); |
| } |
| result = new tree.LiteralList( |
| typeArgs, |
| bracketList(',', exp.values.map(makeExpression)), |
| exp.isConst ? constToken : null); |
| } else if (exp is LiteralMap) { |
| precedence = PRIMARY; |
| if (beginStmt) { |
| // The opening brace can be confused with a block statement. |
| needParen = true; |
| } |
| tree.NodeList typeArgs = null; |
| if (exp.typeArguments != null && exp.typeArguments.length > 0) { |
| typeArgs = typeArgList(exp.typeArguments.map(makeType)); |
| } |
| result = new tree.LiteralMap( |
| typeArgs, |
| braceList(',', exp.entries.map(makeLiteralMapEntry)), |
| exp.isConst ? constToken : null); |
| } else if (exp is LiteralSymbol) { |
| precedence = PRIMARY; |
| result = new tree.LiteralSymbol( |
| hash, |
| makeList('.', exp.id.split('.').map(makeIdentifier))); |
| } else if (exp is LiteralType) { |
| precedence = TYPE_LITERAL; |
| elements.Element optionalElement = exp.type.element; |
| result = new tree.Send( |
| optionalElement == null ? null : makeStaticReceiver(optionalElement), |
| makeIdentifier(exp.name)); |
| treeElements.setType(result, exp.type); |
| if (optionalElement != null) { // dynamic does not have an element |
| setElement(result, optionalElement, exp); |
| } |
| } else if (exp is ReifyTypeVar) { |
| precedence = PRIMARY; |
| result = new tree.Send( |
| null, |
| makeIdentifier(exp.name)); |
| setElement(result, exp.element, exp); |
| setType(result, exp.element.type, exp); |
| } else if (exp is StringConcat) { |
| precedence = PRIMARY; |
| result = unparseStringLiteral(exp); |
| } else if (exp is This) { |
| precedence = CALLEE; |
| result = makeIdentifier('this'); |
| } else if (exp is Throw) { |
| precedence = EXPRESSION; // ??? |
| result = new tree.Throw( |
| makeExpression(exp.expression), |
| throwToken, |
| throwToken); // endToken not used by unparser |
| } else if (exp is TypeOperator) { |
| precedence = RELATIONAL; |
| tree.Operator operator; |
| tree.Node rightOperand = makeType(exp.type); |
| if (exp.operator == 'is!') { |
| operator = new tree.Operator(typeOpToken('is')); |
| rightOperand = new tree.Send( |
| rightOperand, |
| new tree.Operator(bang), |
| blankList()); |
| } else { |
| operator = new tree.Operator(typeOpToken(exp.operator)); |
| } |
| result = new tree.Send( |
| makeExp(exp.expression, BITWISE_OR, beginStmt: beginStmt), |
| operator, |
| singleton(rightOperand)); |
| } else if (exp is UnaryOperator) { |
| precedence = UNARY; |
| result = new tree.Send.prefix( |
| makeExp(exp.operand, UNARY), |
| new tree.Operator(unopToken(exp.operatorName))); |
| } else { |
| throw "Unknown expression type: ${exp}"; |
| } |
| |
| needParen = needParen || precedence < minPrecedence; |
| if (needParen) { |
| result = parenthesize(result); |
| } |
| return result; |
| } |
| |
| /// Creates a LiteralString with [verbatim] as the value. |
| /// No (un)quoting or (un)escaping will be performed by this method. |
| /// The [DartString] inside the literal will be set to null because the |
| /// code emitter does not use it. |
| tree.LiteralString makeVerbatimStringLiteral(String verbatim) { |
| Token tok = new StringToken.fromString(STRING_INFO, verbatim, -1); |
| return new tree.LiteralString(tok, null); |
| } |
| |
| tree.LiteralMapEntry makeLiteralMapEntry(LiteralMapEntry en) { |
| return new tree.LiteralMapEntry( |
| makeExpression(en.key), |
| colon, |
| makeExpression(en.value)); |
| } |
| |
| /// A comment token to be inserted when [INSERT_NEW_BACKEND_COMMENT] is true. |
| final SymbolToken newBackendComment = new SymbolToken( |
| const PrecedenceInfo('/* new backend */ ', 0, OPEN_CURLY_BRACKET_TOKEN), |
| -1); |
| |
| tree.Node makeFunctionBody(Statement stmt) { |
| if (INSERT_NEW_BACKEND_COMMENT) { |
| return new tree.Block(makeList('', [makeBlock(stmt)], |
| open: newBackendComment)); |
| } else { |
| return makeBlock(stmt); |
| } |
| } |
| |
| /// Produces a statement in a context where only blocks are allowed. |
| tree.Node makeBlock(Statement stmt) { |
| if (stmt is Block || stmt is EmptyStatement) { |
| return makeStatement(stmt); |
| } else { |
| return new tree.Block(braceList('', [makeStatement(stmt)])); |
| } |
| } |
| |
| /// Adds the given statement to a block. If the statement itself is a block |
| /// it will be flattened (if this does not change lexical scoping). |
| void addBlockMember(Statement stmt, List<tree.Node> accumulator) { |
| if (stmt is Block && !stmt.statements.any(Unparser.definesVariable)) { |
| for (Statement innerStmt in stmt.statements) { |
| addBlockMember(innerStmt, accumulator); |
| } |
| } else if (stmt is EmptyStatement) { |
| // No need to include empty statements inside blocks |
| } else { |
| accumulator.add(makeStatement(stmt)); |
| } |
| } |
| |
| /// True if [stmt] is equivalent to an empty statement. |
| bool isEmptyStatement(Statement stmt) { |
| return stmt is EmptyStatement || |
| (stmt is Block && stmt.statements.every(isEmptyStatement)); |
| } |
| |
| tree.Node makeStatement(Statement stmt, {bool shortIf: true}) { |
| if (stmt is Block) { |
| List<tree.Node> body = <tree.Node>[]; |
| for (Statement innerStmt in stmt.statements) { |
| addBlockMember(innerStmt, body); |
| } |
| return new tree.Block(braceList('', body)); |
| } else if (stmt is Break) { |
| return new tree.BreakStatement( |
| stmt.label == null ? null : makeIdentifier(stmt.label), |
| breakToken, |
| semicolon); |
| } else if (stmt is Continue) { |
| return new tree.ContinueStatement( |
| stmt.label == null ? null : makeIdentifier(stmt.label), |
| continueToken, |
| semicolon); |
| } else if (stmt is DoWhile) { |
| return new tree.DoWhile( |
| makeStatement(stmt.body, shortIf: shortIf), |
| parenthesize(makeExpression(stmt.condition)), |
| doToken, |
| whileToken, |
| semicolon); |
| } else if (stmt is EmptyStatement) { |
| return new tree.EmptyStatement(semicolon); |
| } else if (stmt is ExpressionStatement) { |
| return new tree.ExpressionStatement( |
| makeExp(stmt.expression, EXPRESSION, beginStmt: true), |
| semicolon); |
| } else if (stmt is For) { |
| tree.Node initializer; |
| if (stmt.initializer is VariableDeclarations) { |
| initializer = makeVariableDeclarations(stmt.initializer); |
| } else if (stmt.initializer is Expression) { |
| initializer = makeExpression(stmt.initializer); |
| } else { |
| initializer = null; |
| } |
| tree.Node condition; |
| if (stmt.condition != null) { |
| condition = new tree.ExpressionStatement( |
| makeExpression(stmt.condition), |
| semicolon); |
| } else { |
| condition = new tree.EmptyStatement(semicolon); |
| } |
| return new tree.For( |
| initializer, |
| condition, |
| makeList(',', stmt.updates.map(makeExpression)), |
| makeStatement(stmt.body, shortIf: shortIf), |
| forToken); |
| } else if (stmt is ForIn) { |
| tree.Node left; |
| if (stmt.leftHandValue is Identifier) { |
| left = makeExpression(stmt.leftHandValue); |
| } else { |
| left = makeVariableDeclarations(stmt.leftHandValue); |
| } |
| return new tree.SyncForIn( |
| left, |
| makeExpression(stmt.expression), |
| makeStatement(stmt.body, shortIf: shortIf), |
| forToken, |
| inToken); |
| } else if (stmt is FunctionDeclaration) { |
| tree.FunctionExpression function = new tree.FunctionExpression( |
| stmt.name != null ? makeIdentifier(stmt.name) : null, |
| makeParameters(stmt.parameters), |
| makeFunctionBody(stmt.body), |
| stmt.returnType != null ? makeType(stmt.returnType) : null, |
| makeEmptyModifiers(), |
| null, // initializers |
| null, // get/set |
| null); // async modifier |
| setElement(function, stmt.function.element, stmt); |
| return new tree.FunctionDeclaration(function); |
| } else if (stmt is If) { |
| if (stmt.elseStatement == null || isEmptyStatement(stmt.elseStatement)) { |
| tree.Node node = new tree.If( |
| parenthesize(makeExpression(stmt.condition)), |
| makeStatement(stmt.thenStatement), |
| null, // else statement |
| ifToken, |
| null); // else token |
| if (shortIf) |
| return node; |
| else |
| return new tree.Block(braceList('', [node])); |
| } else { |
| return new tree.If( |
| parenthesize(makeExpression(stmt.condition)), |
| makeStatement(stmt.thenStatement, shortIf: false), |
| makeStatement(stmt.elseStatement, shortIf: shortIf), |
| ifToken, |
| elseToken); // else token |
| } |
| } else if (stmt is LabeledStatement) { |
| List<tree.Label> labels = []; |
| Statement inner = stmt; |
| while (inner is LabeledStatement) { |
| LabeledStatement lbl = inner as LabeledStatement; |
| labels.add(new tree.Label(makeIdentifier(lbl.label), colon)); |
| inner = lbl.statement; |
| } |
| return new tree.LabeledStatement( |
| makeList('', labels), |
| makeStatement(inner, shortIf: shortIf)); |
| } else if (stmt is Rethrow) { |
| return new tree.Rethrow(rethrowToken, semicolon); |
| } else if (stmt is Return) { |
| return new tree.Return( |
| returnToken, |
| semicolon, |
| stmt.expression == null ? null : makeExpression(stmt.expression)); |
| } else if (stmt is Switch) { |
| return new tree.SwitchStatement( |
| parenthesize(makeExpression(stmt.expression)), |
| braceList('', stmt.cases.map(makeSwitchCase)), |
| switchToken); |
| } else if (stmt is Try) { |
| return new tree.TryStatement( |
| makeBlock(stmt.tryBlock), |
| makeList(null, stmt.catchBlocks.map(makeCatchBlock)), |
| stmt.finallyBlock == null ? null : makeBlock(stmt.finallyBlock), |
| tryToken, |
| stmt.finallyBlock == null ? null : finallyToken); |
| } else if (stmt is VariableDeclarations) { |
| return makeVariableDeclarations(stmt, useVar: true, endToken: semicolon); |
| } else if (stmt is While) { |
| return new tree.While( |
| parenthesize(makeExpression(stmt.condition)), |
| makeStatement(stmt.body, shortIf: shortIf), |
| whileToken); |
| } else { |
| throw "Unrecognized statement: ${stmt}"; |
| } |
| } |
| |
| tree.Node makeVariableDeclaration(VariableDeclaration vd) { |
| tree.Node id = makeIdentifier(vd.name); |
| setElement(id, vd.element, vd); |
| if (vd.initializer == null) { |
| return id; |
| } |
| tree.Node send = new tree.SendSet( |
| null, |
| id, |
| new tree.Operator(eq), |
| singleton(makeExpression(vd.initializer))); |
| setElement(send, vd.element, vd); |
| return send; |
| } |
| |
| /// If [useVar] is true, the variable definition will use `var` as modifier |
| /// if no other modifiers are present. |
| /// [endToken] will be used to terminate the declaration list. |
| tree.Node makeVariableDeclarations(VariableDeclarations decl, |
| { bool useVar: false, |
| Token endToken: null }) { |
| return new tree.VariableDefinitions( |
| decl.type == null ? null : makeType(decl.type), |
| makeVarModifiers(isConst: decl.isConst, |
| isFinal: decl.isFinal, |
| useVar: useVar && decl.type == null), |
| makeList(',', |
| decl.declarations.map(makeVariableDeclaration), |
| close: endToken)); |
| } |
| |
| tree.CatchBlock makeCatchBlock(CatchBlock block) { |
| List<tree.VariableDefinitions> formals = []; |
| if (block.exceptionVar != null) { |
| tree.Node exceptionName = makeIdentifier(block.exceptionVar.name); |
| setElement(exceptionName, block.exceptionVar.element, block.exceptionVar); |
| formals.add(new tree.VariableDefinitions( |
| null, |
| makeEmptyModifiers(), |
| singleton(exceptionName))); |
| } |
| if (block.stackVar != null) { |
| tree.Node stackTraceName = makeIdentifier(block.stackVar.name); |
| setElement(stackTraceName, block.stackVar.element, block.stackVar); |
| formals.add(new tree.VariableDefinitions( |
| null, |
| makeEmptyModifiers(), |
| singleton(stackTraceName))); |
| } |
| return new tree.CatchBlock( |
| block.onType == null ? null : makeType(block.onType), |
| block.exceptionVar == null ? null : argList(formals), |
| makeBlock(block.body), |
| block.onType == null ? null : onToken, |
| block.exceptionVar == null ? null : catchToken); |
| } |
| |
| tree.SwitchCase makeSwitchCase(SwitchCase caze) { |
| if (caze.isDefaultCase) { |
| return new tree.SwitchCase( |
| blankList(), |
| defaultToken, |
| makeList('', caze.statements.map(makeStatement)), |
| null); // startToken unused by unparser |
| } else { |
| return new tree.SwitchCase( |
| makeList('', caze.expressions.map(makeCaseMatch)), |
| null, // defaultKeyword, |
| makeList('', caze.statements.map(makeStatement)), |
| null); // startToken unused by unparser |
| } |
| } |
| |
| tree.CaseMatch makeCaseMatch(Expression exp) { |
| return new tree.CaseMatch(caseToken, makeExpression(exp), colon); |
| } |
| |
| tree.TypeAnnotation makeType(TypeAnnotation type) { |
| tree.NodeList typeArgs; |
| if (type.typeArguments.length > 0) { |
| typeArgs = typeArgList(type.typeArguments.map(makeType)); |
| } else { |
| typeArgs = null; |
| } |
| tree.TypeAnnotation result = |
| new tree.TypeAnnotation(makeIdentifier(type.name), typeArgs); |
| setType(result, type.dartType, type); |
| return result; |
| } |
| |
| tree.NodeList makeParameters(Parameters params) { |
| List<tree.Node> nodes = |
| params.requiredParameters.map(makeParameter).toList(); |
| if (params.hasOptionalParameters) { |
| Token assign = params.hasNamedParameters ? colon : eq; |
| Token open = params.hasNamedParameters ? openBrace : openBracket; |
| Token close = params.hasNamedParameters ? closeBrace : closeBracket; |
| Iterable<tree.Node> opt = |
| params.optionalParameters.map((p) => makeParameter(p,assign)); |
| nodes.add(new tree.NodeList(open, makeLink(opt), close, ',')); |
| } |
| return argList(nodes); |
| } |
| |
| /// [assignOperator] is used for writing the default value. |
| tree.Node makeParameter(Parameter param, [Token assignOperator]) { |
| if (param.isFunction) { |
| tree.Node definition = new tree.FunctionExpression( |
| makeIdentifier(param.name), |
| makeParameters(param.parameters), |
| null, // body |
| param.type == null ? null : makeType(param.type), |
| makeEmptyModifiers(), // TODO: Function parameter modifiers? |
| null, // initializers |
| null, // get/set |
| null); // async modifier |
| if (param.element != null) { |
| setElement(definition, param.element, param); |
| } |
| if (param.defaultValue != null) { |
| definition = new tree.SendSet( |
| null, |
| definition, |
| new tree.Operator(assignOperator), |
| singleton(makeExpression(param.defaultValue))); |
| } |
| return new tree.VariableDefinitions( |
| null, |
| makeEmptyModifiers(), |
| singleton(definition)); |
| } else { |
| tree.Node definition; |
| if (param.defaultValue != null) { |
| definition = new tree.SendSet( |
| null, |
| makeIdentifier(param.name), |
| new tree.Operator(assignOperator), |
| singleton(makeExpression(param.defaultValue))); |
| } else { |
| definition = makeIdentifier(param.name); |
| } |
| if (param.element != null) { |
| setElement(definition, param.element, param); |
| } |
| return new tree.VariableDefinitions( |
| param.type == null ? null : makeType(param.type), |
| makeEmptyModifiers(), // TODO: Parameter modifiers? |
| singleton(definition)); |
| } |
| } |
| |
| tree.Modifiers makeEmptyModifiers() { |
| return new tree.Modifiers(blankList()); |
| } |
| |
| tree.Modifiers makeModifiers({bool isExternal: false, |
| bool isStatic: false, |
| bool isAbstract: false, |
| bool isFactory: false, |
| bool isConst: false, |
| bool isFinal: false, |
| bool isVar: false}) { |
| List<tree.Node> nodes = []; |
| if (isExternal) { |
| nodes.add(makeIdentifier('external')); |
| } |
| if (isStatic) { |
| nodes.add(makeIdentifier('static')); |
| } |
| if (isAbstract) { |
| nodes.add(makeIdentifier('abstract')); |
| } |
| if (isFactory) { |
| nodes.add(makeIdentifier('factory')); |
| } |
| if (isConst) { |
| nodes.add(makeIdentifier('const')); |
| } |
| if (isFinal) { |
| nodes.add(makeIdentifier('final')); |
| } |
| if (isVar) { |
| nodes.add(makeIdentifier('var')); |
| } |
| return new tree.Modifiers(makeList(' ', nodes)); |
| } |
| |
| tree.Modifiers makeVarModifiers({bool isConst: false, |
| bool isFinal: false, |
| bool useVar: false, |
| bool isStatic: false}) { |
| return makeModifiers(isStatic: isStatic, |
| isConst: isConst, |
| isFinal: isFinal, |
| isVar: useVar && !(isConst || isFinal)); |
| } |
| |
| tree.Modifiers makeFunctionModifiers(FunctionExpression exp) { |
| if (exp.element == null) return makeEmptyModifiers(); |
| return makeModifiers(isExternal: exp.element.isExternal, |
| isStatic: exp.element.isStatic, |
| isFactory: exp.element.isFactoryConstructor, |
| isConst: exp.element.isConst); |
| } |
| |
| tree.Node makeNodeForClassElement(elements.ClassElement cls) { |
| if (cls.isMixinApplication) { |
| return makeNamedMixinApplication(cls); |
| } else if (cls.isEnumClass) { |
| return makeEnum(cls); |
| } else { |
| return makeClassNode(cls); |
| } |
| } |
| |
| tree.Typedef makeTypedef(elements.TypedefElement typdef) { |
| types.FunctionType functionType = typdef.alias; |
| final tree.TypeAnnotation returnType = |
| makeType(TypeGenerator.createType(functionType.returnType)); |
| |
| final tree.Identifier name = makeIdentifier(typdef.name); |
| final tree.NodeList typeParameters = |
| makeTypeParameters(typdef.typeVariables); |
| final tree.NodeList formals = |
| makeParameters(TypeGenerator.createParametersFromType(functionType)); |
| |
| final Token typedefKeyword = typedefToken; |
| final Token endToken = semicolon; |
| |
| return new tree.Typedef(returnType, name, typeParameters, formals, |
| typedefKeyword, endToken); |
| } |
| |
| /// Create a [tree.NodeList] containing the type variable declarations in |
| /// [typeVaraiables. |
| tree.NodeList makeTypeParameters(List<types.DartType> typeVariables) { |
| if (typeVariables.isEmpty) { |
| return new tree.NodeList.empty(); |
| } else { |
| List<tree.Node> typeVariableList = <tree.Node>[]; |
| for (types.TypeVariableType typeVariable in typeVariables) { |
| tree.Node id = makeIdentifier(typeVariable.name); |
| treeElements[id] = typeVariable.element; |
| tree.Node bound; |
| if (!typeVariable.element.bound.isObject) { |
| bound = |
| makeType(TypeGenerator.createType(typeVariable.element.bound)); |
| } |
| tree.TypeVariable node = new tree.TypeVariable(id, bound); |
| treeElements.setType(node, typeVariable); |
| typeVariableList.add(node); |
| } |
| return makeList(',', typeVariableList, open: lt, close: gt); |
| } |
| } |
| |
| /// Create a [tree.NodeList] containing the declared interfaces. |
| /// |
| /// [interfaces] is from [elements.ClassElement] in reverse declaration order |
| /// and it contains mixins. To produce a list of the declared interfaces only, |
| /// interfaces in [mixinTypes] are omitted. |
| /// |
| /// [forNamedMixinApplication] is because the structure of the [tree.NodeList] |
| /// differs between [tree.NamedMixinApplication] and [tree.ClassNode]. |
| // TODO(johnniwinther): Normalize interfaces on[tree.NamedMixinApplication] |
| // and [tree.ClassNode]. |
| tree.NodeList makeInterfaces(Link<types.DartType> interfaces, |
| Set<types.DartType> mixinTypes, |
| {bool forNamedMixinApplication: false}) { |
| Link<tree.Node> typeAnnotations = const Link<tree.Node>(); |
| for (Link<types.DartType> link = interfaces; |
| !link.isEmpty; |
| link = link.tail) { |
| types.DartType interface = link.head; |
| if (!mixinTypes.contains(interface)) { |
| typeAnnotations = typeAnnotations.prepend( |
| makeType(TypeGenerator.createType(interface))); |
| } |
| } |
| if (typeAnnotations.isEmpty) { |
| return forNamedMixinApplication ? null : new tree.NodeList.empty(); |
| } else { |
| return new tree.NodeList( |
| forNamedMixinApplication ? null : implementsToken, |
| typeAnnotations, null, ','); |
| } |
| } |
| |
| /// Creates a [tree.NamedMixinApplication] node for [cls]. |
| // TODO(johnniwinther): Unify creation of mixin lists between |
| // [NamedMixinApplicationElement] and [ClassElement]. |
| tree.NamedMixinApplication makeNamedMixinApplication( |
| elements.MixinApplicationElement cls) { |
| |
| assert(dart2js.invariant(cls, !cls.isUnnamedMixinApplication, |
| message: "Cannot create ClassNode for unnamed mixin application " |
| "$cls.")); |
| tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); |
| tree.Identifier name = makeIdentifier(cls.name); |
| tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); |
| |
| Set<types.DartType> mixinTypes = new Set<types.DartType>(); |
| Link<tree.Node> mixins = const Link<tree.Node>(); |
| |
| void addMixin(types.DartType mixinType) { |
| mixinTypes.add(mixinType); |
| mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); |
| } |
| |
| addMixin(cls.mixinType); |
| |
| tree.Node superclass; |
| types.InterfaceType supertype = cls.supertype; |
| while (supertype.element.isUnnamedMixinApplication) { |
| elements.MixinApplicationElement mixinApplication = supertype.element; |
| addMixin(cls.asInstanceOf(mixinApplication.mixin)); |
| supertype = mixinApplication.supertype; |
| } |
| superclass = |
| makeType(TypeGenerator.createType(cls.asInstanceOf(supertype.element))); |
| tree.Node supernode = new tree.MixinApplication( |
| superclass, new tree.NodeList(null, mixins, null, ',')); |
| |
| tree.NodeList interfaces = makeInterfaces( |
| cls.interfaces, mixinTypes, forNamedMixinApplication: true); |
| |
| return new tree.NamedMixinApplication( |
| name, typeParameters, modifiers, supernode, |
| interfaces, classToken, semicolon); |
| } |
| |
| tree.Enum makeEnum(elements.EnumClassElement cls) { |
| return new tree.Enum( |
| enumToken, |
| makeIdentifier(cls.name), |
| makeList(',', cls.enumValues.map((e) => makeIdentifier(e.name)), |
| open: openBrace, close: closeBrace)); |
| } |
| |
| /// Creates a [tree.ClassNode] node for [cls]. |
| tree.ClassNode makeClassNode(elements.ClassElement cls) { |
| assert(dart2js.invariant(cls, !cls.isUnnamedMixinApplication, |
| message: "Cannot create ClassNode for unnamed mixin application " |
| "$cls.")); |
| tree.Modifiers modifiers = makeModifiers(isAbstract: cls.isAbstract); |
| tree.Identifier name = makeIdentifier(cls.name); |
| tree.NodeList typeParameters = makeTypeParameters(cls.typeVariables); |
| tree.Node supernode; |
| types.InterfaceType supertype = cls.supertype; |
| Set<types.DartType> mixinTypes = new Set<types.DartType>(); |
| Link<tree.Node> mixins = const Link<tree.Node>(); |
| |
| void addMixin(types.DartType mixinType) { |
| mixinTypes.add(mixinType); |
| mixins = mixins.prepend(makeType(TypeGenerator.createType(mixinType))); |
| } |
| |
| if (supertype != null) { |
| tree.Node superclass; |
| if (supertype.element.isUnnamedMixinApplication) { |
| while (supertype.element.isUnnamedMixinApplication) { |
| elements.MixinApplicationElement mixinApplication = supertype.element; |
| addMixin(cls.asInstanceOf(mixinApplication.mixin)); |
| supertype = mixinApplication.supertype; |
| } |
| tree.Node superclass = makeType( |
| TypeGenerator.createType(cls.asInstanceOf(supertype.element))); |
| supernode = new tree.MixinApplication( |
| superclass, new tree.NodeList(null, mixins, null, ',')); |
| } else if (!supertype.isObject) { |
| supernode = makeType(TypeGenerator.createType(supertype)); |
| } |
| } |
| tree.NodeList interfaces = makeInterfaces( |
| cls.interfaces, mixinTypes); |
| |
| Token extendsKeyword = supernode != null ? extendsToken : null; |
| return new tree.ClassNode( |
| modifiers, name, typeParameters, supernode, |
| interfaces, openBrace, extendsKeyword, |
| null, // No body. |
| closeBrace); |
| } |
| |
| tree.Node constructorName(ConstructorDefinition exp) { |
| String name = exp.name; |
| tree.Identifier className = makeIdentifier(exp.element.enclosingClass.name); |
| tree.Node result = name == "" |
| ? className |
| : new tree.Send(className, makeIdentifier(name)); |
| setElement(result, exp.element, exp); |
| return result; |
| } |
| |
| tree.Node functionName(FunctionExpression exp) { |
| String name = exp.name; |
| if (name == null) return null; |
| if (isUserDefinableOperator(name)) { |
| return makeOperator("operator$name"); |
| } else if (name == "unary-") { |
| return makeOperator("operator-"); |
| } |
| return makeIdentifier(name); |
| } |
| |
| tree.Node parenthesize(tree.Node node) { |
| return new tree.ParenthesizedExpression(node, openParen); |
| } |
| |
| tree.Node unparseStringLiteral(Expression exp) { |
| StringLiteralOutput output = Unparser.analyzeStringLiteral(exp); |
| List parts = output.parts; |
| tree.Node printStringChunk(StringChunk chunk) { |
| bool raw = chunk.quoting.raw; |
| int quoteCode = chunk.quoting.quote; |
| |
| List<tree.StringInterpolationPart> literalParts = []; |
| tree.LiteralString firstLiteral; |
| tree.Node currentInterpolation; |
| |
| // sb contains the current unfinished LiteralString |
| StringBuffer sb = new StringBuffer(); |
| if (raw) { |
| sb.write('r'); |
| } |
| for (int i = 0; i < chunk.quoting.leftQuoteCharCount; i++) { |
| sb.write(chunk.quoting.quoteChar); |
| } |
| |
| // Print every character and string interpolation |
| int startIndex = chunk.previous != null ? chunk.previous.endIndex : 0; |
| for (int i = startIndex; i < chunk.endIndex; i++) { |
| var part = parts[i]; |
| if (part is Expression) { |
| // Finish the previous string interpolation, if there is one. |
| tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); |
| if (currentInterpolation != null) { |
| literalParts.add(new tree.StringInterpolationPart( |
| currentInterpolation, |
| lit)); |
| } else { |
| firstLiteral = lit; |
| } |
| sb.clear(); |
| currentInterpolation = makeExpression(part); |
| } else { |
| int char = part; |
| sb.write(Unparser.getEscapedCharacter(char, quoteCode, raw)); |
| } |
| } |
| |
| // Print ending quotes |
| for (int i = 0; i < chunk.quoting.rightQuoteLength; i++) { |
| sb.write(chunk.quoting.quoteChar); |
| } |
| |
| // Finish the previous string interpolation, if there is one. |
| // Then wrap everything in a StringInterpolation, if relevant. |
| tree.LiteralString lit = makeVerbatimStringLiteral(sb.toString()); |
| tree.Node node; |
| if (firstLiteral == null) { |
| node = lit; |
| } else { |
| literalParts.add(new tree.StringInterpolationPart( |
| currentInterpolation, |
| lit)); |
| node = new tree.StringInterpolation( |
| firstLiteral, |
| makeList('', literalParts)); |
| } |
| |
| // Juxtapose with the previous string chunks, if any. |
| if (chunk.previous != null) { |
| return new tree.StringJuxtaposition( |
| printStringChunk(chunk.previous), |
| node); |
| } else { |
| return node; |
| } |
| } |
| return printStringChunk(output.chunk); |
| } |
| |
| } |