| // 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. |
| |
| part of js_ast; |
| |
| class TemplateManager { |
| Map<String, Template> expressionTemplates = {}; |
| Map<String, Template> statementTemplates = {}; |
| |
| TemplateManager(); |
| |
| Template lookupExpressionTemplate(String source) { |
| return expressionTemplates[source]; |
| } |
| |
| Template defineExpressionTemplate(String source, Node ast) { |
| Template template = |
| Template(source, ast, isExpression: true, forceCopy: false); |
| expressionTemplates[source] = template; |
| return template; |
| } |
| |
| Template lookupStatementTemplate(String source) { |
| return statementTemplates[source]; |
| } |
| |
| Template defineStatementTemplate(String source, Node ast) { |
| Template template = |
| Template(source, ast, isExpression: false, forceCopy: false); |
| statementTemplates[source] = template; |
| return template; |
| } |
| } |
| |
| /// A Template is created with JavaScript AST containing placeholders (interface |
| /// InterpolatedNode). The [instantiate] method creates an AST that looks like |
| /// the original with the placeholders replaced by the arguments to |
| /// [instantiate]. |
| class Template { |
| final String source; |
| final bool isExpression; |
| final bool forceCopy; |
| final Node ast; |
| |
| Instantiator instantiator; |
| |
| int positionalArgumentCount = -1; |
| |
| // Null, unless there are named holes. |
| List<String> holeNames; |
| bool get isPositional => holeNames == null; |
| |
| Template(this.source, this.ast, |
| {this.isExpression = true, this.forceCopy = false}) { |
| assert(this.isExpression ? ast is Expression : ast is Statement); |
| _compile(); |
| } |
| |
| Template.withExpressionResult(this.ast) |
| : source = null, |
| isExpression = true, |
| forceCopy = false { |
| assert(ast is Expression); |
| assert(_checkNoPlaceholders()); |
| positionalArgumentCount = 0; |
| instantiator = (arguments) => ast; |
| } |
| |
| Template.withStatementResult(this.ast) |
| : source = null, |
| isExpression = false, |
| forceCopy = false { |
| assert(ast is Statement); |
| assert(_checkNoPlaceholders()); |
| positionalArgumentCount = 0; |
| instantiator = (arguments) => ast; |
| } |
| |
| bool _checkNoPlaceholders() { |
| InstantiatorGeneratorVisitor generator = |
| InstantiatorGeneratorVisitor(false); |
| generator.compile(ast); |
| return generator.analysis.count == 0; |
| } |
| |
| void _compile() { |
| InstantiatorGeneratorVisitor generator = |
| InstantiatorGeneratorVisitor(forceCopy); |
| instantiator = generator.compile(ast); |
| positionalArgumentCount = generator.analysis.count; |
| Set<String> names = generator.analysis.holeNames; |
| holeNames = names.toList(growable: false); |
| } |
| |
| /// Instantiates the template with the given [arguments]. |
| /// |
| /// This method fills in the holes with the given arguments. The [arguments] |
| /// must be either a [List] or a [Map]. |
| Node instantiate(var arguments) { |
| if (arguments is List) { |
| if (arguments.length != positionalArgumentCount) { |
| throw 'Wrong number of template arguments, given ${arguments.length}, ' |
| 'expected $positionalArgumentCount' |
| ', source: "$source"'; |
| } |
| return instantiator(arguments); |
| } |
| assert(arguments is Map); |
| if (holeNames.length < arguments.length) { |
| // This search is in O(n), but we only do it in case of an error, and the |
| // number of holes should be quite limited. |
| String unusedNames = |
| arguments.keys.where((name) => !holeNames.contains(name)).join(", "); |
| throw "Template arguments has unused mappings: $unusedNames"; |
| } |
| if (!holeNames.every((String name) => arguments.containsKey(name))) { |
| String notFound = |
| holeNames.where((name) => !arguments.containsKey(name)).join(", "); |
| throw "Template arguments is missing mappings for: $notFound"; |
| } |
| return instantiator(arguments); |
| } |
| } |
| |
| /// An Instantiator is a Function that generates a JS AST tree or List of |
| /// trees. [arguments] is a List for positional templates, or Map for named |
| /// templates. |
| typedef Instantiator = /*Node|Iterable<Node>*/ Function(dynamic arguments); |
| |
| /// InstantiatorGeneratorVisitor compiles a template. This class compiles a tree |
| /// containing [InterpolatedNode]s into a function that will create a copy of the |
| /// tree with the interpolated nodes substituted with provided values. |
| class InstantiatorGeneratorVisitor implements NodeVisitor<Instantiator> { |
| final bool forceCopy; |
| |
| InterpolatedNodeAnalysis analysis = InterpolatedNodeAnalysis(); |
| |
| /// The entire tree is cloned if [forceCopy] is true. |
| InstantiatorGeneratorVisitor(this.forceCopy); |
| |
| Instantiator compile(Node node) { |
| analysis.visit(node); |
| Instantiator result = visit(node); |
| return result; |
| } |
| |
| static error(String message) { |
| throw message; |
| } |
| |
| static Instantiator same(Node node) => (arguments) => node; |
| static Node makeNull(arguments) => null; |
| |
| Instantiator visit(Node node) { |
| if (forceCopy || analysis.containsInterpolatedNodes(node)) { |
| return node.accept(this); |
| } |
| return same(node); |
| } |
| |
| Instantiator visitNullable(Node node) { |
| if (node == null) return makeNull; |
| return visit(node); |
| } |
| |
| Instantiator visitSplayable(Node node) { |
| // TODO(sra): Process immediate [InterpolatedNode]s, permitting splaying. |
| return visit(node); |
| } |
| |
| Instantiator visitNode(Node node) { |
| throw 'Unimplemented InstantiatorGeneratorVisitor for $node'; |
| } |
| |
| static RegExp identifierRE = RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$'); |
| |
| static Expression convertStringToVariableUse(String value) { |
| assert(identifierRE.hasMatch(value)); |
| return VariableUse(value); |
| } |
| |
| static Expression convertStringToVariableDeclaration(String value) { |
| assert(identifierRE.hasMatch(value)); |
| return VariableDeclaration(value); |
| } |
| |
| Instantiator visitInterpolatedExpression(InterpolatedExpression node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is Expression) return value; |
| if (value is String) return convertStringToVariableUse(value); |
| throw error( |
| 'Interpolated value #$nameOrPosition is not an Expression: $value'); |
| }; |
| } |
| |
| Instantiator visitInterpolatedDeclaration(InterpolatedDeclaration node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is Declaration) return value; |
| if (value is String) return convertStringToVariableDeclaration(value); |
| throw error( |
| 'Interpolated value #$nameOrPosition is not a declaration: $value'); |
| }; |
| } |
| |
| Instantiator visitSplayableExpression(Node node) { |
| if (node is InterpolatedExpression) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| Expression toExpression(item) { |
| if (item is Expression) return item; |
| if (item is String) return convertStringToVariableUse(item); |
| throw error('Interpolated value #$nameOrPosition is not ' |
| 'an Expression or List of Expressions: $value'); |
| } |
| |
| if (value is Iterable) return value.map(toExpression); |
| return toExpression(value); |
| }; |
| } |
| return visit(node); |
| } |
| |
| Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is Literal || value is DeferredExpression) return value; |
| error('Interpolated value #$nameOrPosition is not a Literal: $value'); |
| }; |
| } |
| |
| Instantiator visitInterpolatedParameter(InterpolatedParameter node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| |
| Parameter toParameter(item) { |
| if (item is Parameter) return item; |
| if (item is String) return Parameter(item); |
| throw error('Interpolated value #$nameOrPosition is not a Parameter or' |
| ' List of Parameters: $value'); |
| } |
| |
| if (value is Iterable) return value.map(toParameter); |
| return toParameter(value); |
| }; |
| } |
| |
| Instantiator visitInterpolatedSelector(InterpolatedSelector node) { |
| // A selector is an expression, as in `a[selector]`. |
| // A String argument converted into a LiteralString, so `a.#` with argument |
| // 'foo' generates `a["foo"]` which prints as `a.foo`. |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is Expression) return value; |
| if (value is String) return LiteralString(value); |
| throw error( |
| 'Interpolated value #$nameOrPosition is not a selector: $value'); |
| }; |
| } |
| |
| Instantiator visitInterpolatedStatement(InterpolatedStatement node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is Node) return value.toStatement(); |
| throw error( |
| 'Interpolated value #$nameOrPosition is not a Statement: $value'); |
| }; |
| } |
| |
| Instantiator visitSplayableStatement(Node node) { |
| if (node is InterpolatedStatement) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| Statement toStatement(item) { |
| if (item is Statement) return item; |
| if (item is Expression) return item.toStatement(); |
| throw error('Interpolated value #$nameOrPosition is not ' |
| 'a Statement or List of Statements: $value'); |
| } |
| |
| if (value is Iterable) return value.map(toStatement); |
| return toStatement(value); |
| }; |
| } |
| return visit(node); |
| } |
| |
| Instantiator visitProgram(Program node) { |
| List<Instantiator> instantiators = |
| node.body.map(visitSplayableStatement).toList(); |
| return (arguments) { |
| List<Statement> statements = []; |
| void add(node) { |
| if (node is EmptyStatement) return; |
| if (node is Iterable) { |
| statements.addAll(node); |
| } else { |
| statements.add(node.toStatement()); |
| } |
| } |
| |
| for (Instantiator instantiator in instantiators) { |
| add(instantiator(arguments)); |
| } |
| return Program(statements); |
| }; |
| } |
| |
| Instantiator visitBlock(Block node) { |
| List<Instantiator> instantiators = |
| node.statements.map(visitSplayableStatement).toList(); |
| return (arguments) { |
| List<Statement> statements = []; |
| void add(node) { |
| if (node is EmptyStatement) return; |
| if (node is Iterable) { |
| statements.addAll(node); |
| } else if (node is Block) { |
| statements.addAll(node.statements); |
| } else { |
| statements.add(node.toStatement()); |
| } |
| } |
| |
| for (Instantiator instantiator in instantiators) { |
| add(instantiator(arguments)); |
| } |
| return Block(statements); |
| }; |
| } |
| |
| Instantiator visitExpressionStatement(ExpressionStatement node) { |
| Instantiator buildExpression = visit(node.expression); |
| return (arguments) { |
| return buildExpression(arguments).toStatement(); |
| }; |
| } |
| |
| Instantiator visitEmptyStatement(EmptyStatement node) => |
| (arguments) => EmptyStatement(); |
| |
| Instantiator visitIf(If node) { |
| if (node.condition is InterpolatedExpression) { |
| return visitIfConditionalCompilation(node); |
| } else { |
| return visitIfNormal(node); |
| } |
| } |
| |
| Instantiator visitIfConditionalCompilation(If node) { |
| // Special version of visitInterpolatedExpression that permits bools. |
| compileCondition(InterpolatedExpression node) { |
| var nameOrPosition = node.nameOrPosition; |
| return (arguments) { |
| var value = arguments[nameOrPosition]; |
| if (value is bool) return value; |
| if (value is Expression) return value; |
| if (value is String) return convertStringToVariableUse(value); |
| throw error('Interpolated value #$nameOrPosition ' |
| 'is not an Expression: $value'); |
| }; |
| } |
| |
| var makeCondition = compileCondition(node.condition); |
| Instantiator makeThen = visit(node.then); |
| Instantiator makeOtherwise = visit(node.otherwise); |
| return (arguments) { |
| var condition = makeCondition(arguments); |
| if (condition is bool) { |
| if (condition == true) { |
| return makeThen(arguments); |
| } else { |
| return makeOtherwise(arguments); |
| } |
| } |
| return If(condition, makeThen(arguments), makeOtherwise(arguments)); |
| }; |
| } |
| |
| Instantiator visitIfNormal(If node) { |
| Instantiator makeCondition = visit(node.condition); |
| Instantiator makeThen = visit(node.then); |
| Instantiator makeOtherwise = visit(node.otherwise); |
| return (arguments) { |
| return If(makeCondition(arguments), makeThen(arguments), |
| makeOtherwise(arguments)); |
| }; |
| } |
| |
| Instantiator visitFor(For node) { |
| Instantiator makeInit = visitNullable(node.init); |
| Instantiator makeCondition = visitNullable(node.condition); |
| Instantiator makeUpdate = visitNullable(node.update); |
| Instantiator makeBody = visit(node.body); |
| return (arguments) { |
| return For(makeInit(arguments), makeCondition(arguments), |
| makeUpdate(arguments), makeBody(arguments)); |
| }; |
| } |
| |
| Instantiator visitForIn(ForIn node) { |
| Instantiator makeLeftHandSide = visit(node.leftHandSide); |
| Instantiator makeObject = visit(node.object); |
| Instantiator makeBody = visit(node.body); |
| return (arguments) { |
| return ForIn(makeLeftHandSide(arguments), makeObject(arguments), |
| makeBody(arguments)); |
| }; |
| } |
| |
| TODO(String name) { |
| throw UnimplementedError('$this.$name'); |
| } |
| |
| Instantiator visitWhile(While node) { |
| Instantiator makeCondition = visit(node.condition); |
| Instantiator makeBody = visit(node.body); |
| return (arguments) { |
| return While(makeCondition(arguments), makeBody(arguments)); |
| }; |
| } |
| |
| Instantiator visitDo(Do node) { |
| Instantiator makeBody = visit(node.body); |
| Instantiator makeCondition = visit(node.condition); |
| return (arguments) { |
| return Do(makeBody(arguments), makeCondition(arguments)); |
| }; |
| } |
| |
| Instantiator visitContinue(Continue node) => |
| (arguments) => Continue(node.targetLabel); |
| |
| Instantiator visitBreak(Break node) => (arguments) => Break(node.targetLabel); |
| |
| Instantiator visitReturn(Return node) { |
| Instantiator makeExpression = visitNullable(node.value); |
| return (arguments) => Return(makeExpression(arguments)); |
| } |
| |
| Instantiator visitDartYield(DartYield node) { |
| Instantiator makeExpression = visit(node.expression); |
| return (arguments) => DartYield(makeExpression(arguments), node.hasStar); |
| } |
| |
| Instantiator visitThrow(Throw node) { |
| Instantiator makeExpression = visit(node.expression); |
| return (arguments) => Throw(makeExpression(arguments)); |
| } |
| |
| Instantiator visitTry(Try node) { |
| Instantiator makeBody = visit(node.body); |
| Instantiator makeCatch = visitNullable(node.catchPart); |
| Instantiator makeFinally = visitNullable(node.finallyPart); |
| return (arguments) => |
| Try(makeBody(arguments), makeCatch(arguments), makeFinally(arguments)); |
| } |
| |
| Instantiator visitCatch(Catch node) { |
| Instantiator makeDeclaration = visit(node.declaration); |
| Instantiator makeBody = visit(node.body); |
| return (arguments) => |
| Catch(makeDeclaration(arguments), makeBody(arguments)); |
| } |
| |
| Instantiator visitSwitch(Switch node) { |
| Instantiator makeKey = visit(node.key); |
| Iterable<Instantiator> makeCases = node.cases.map(visit); |
| return (arguments) { |
| return Switch( |
| makeKey(arguments), |
| makeCases |
| .map<SwitchClause>((Instantiator makeCase) => makeCase(arguments)) |
| .toList()); |
| }; |
| } |
| |
| Instantiator visitCase(Case node) { |
| Instantiator makeExpression = visit(node.expression); |
| Instantiator makeBody = visit(node.body); |
| return (arguments) { |
| return Case(makeExpression(arguments), makeBody(arguments)); |
| }; |
| } |
| |
| Instantiator visitDefault(Default node) { |
| Instantiator makeBody = visit(node.body); |
| return (arguments) { |
| return Default(makeBody(arguments)); |
| }; |
| } |
| |
| Instantiator visitFunctionDeclaration(FunctionDeclaration node) { |
| Instantiator makeName = visit(node.name); |
| Instantiator makeFunction = visit(node.function); |
| return (arguments) => |
| FunctionDeclaration(makeName(arguments), makeFunction(arguments)); |
| } |
| |
| Instantiator visitLabeledStatement(LabeledStatement node) { |
| Instantiator makeBody = visit(node.body); |
| return (arguments) => LabeledStatement(node.label, makeBody(arguments)); |
| } |
| |
| Instantiator visitLiteralStatement(LiteralStatement node) => |
| TODO('visitLiteralStatement'); |
| Instantiator visitLiteralExpression(LiteralExpression node) => |
| TODO('visitLiteralExpression'); |
| |
| Instantiator visitVariableDeclarationList(VariableDeclarationList node) { |
| List<Instantiator> declarationMakers = |
| node.declarations.map(visit).toList(); |
| return (arguments) { |
| List<VariableInitialization> declarations = []; |
| for (Instantiator instantiator in declarationMakers) { |
| var result = instantiator(arguments); |
| declarations.add(result); |
| } |
| return VariableDeclarationList(declarations); |
| }; |
| } |
| |
| Instantiator visitAssignment(Assignment node) { |
| Instantiator makeLeftHandSide = visit(node.leftHandSide); |
| String op = node.op; |
| Instantiator makeValue = visitNullable(node.value); |
| return (arguments) { |
| return Assignment.compound( |
| makeLeftHandSide(arguments), op, makeValue(arguments)); |
| }; |
| } |
| |
| Instantiator visitVariableInitialization(VariableInitialization node) { |
| Instantiator makeDeclaration = visit(node.declaration); |
| Instantiator makeValue = visitNullable(node.value); |
| return (arguments) { |
| return VariableInitialization( |
| makeDeclaration(arguments), makeValue(arguments)); |
| }; |
| } |
| |
| Instantiator visitConditional(Conditional cond) { |
| Instantiator makeCondition = visit(cond.condition); |
| Instantiator makeThen = visit(cond.then); |
| Instantiator makeOtherwise = visit(cond.otherwise); |
| return (arguments) => Conditional(makeCondition(arguments), |
| makeThen(arguments), makeOtherwise(arguments)); |
| } |
| |
| Instantiator visitNew(New node) => |
| handleCallOrNew(node, (target, arguments) => New(target, arguments)); |
| |
| Instantiator visitCall(Call node) => |
| handleCallOrNew(node, (target, arguments) => Call(target, arguments)); |
| |
| Instantiator handleCallOrNew(Call node, finish(target, arguments)) { |
| Instantiator makeTarget = visit(node.target); |
| Iterable<Instantiator> argumentMakers = |
| node.arguments.map(visitSplayableExpression).toList(); |
| |
| // TODO(sra): Avoid copying call arguments if no interpolation or forced |
| // copying. |
| return (arguments) { |
| Node target = makeTarget(arguments); |
| List<Expression> callArguments = []; |
| for (Instantiator instantiator in argumentMakers) { |
| var result = instantiator(arguments); |
| if (result is Iterable) { |
| callArguments.addAll(result); |
| } else { |
| callArguments.add(result); |
| } |
| } |
| return finish(target, callArguments.toList(growable: false)); |
| }; |
| } |
| |
| Instantiator visitBinary(Binary node) { |
| Instantiator makeLeft = visit(node.left); |
| Instantiator makeRight = visit(node.right); |
| String op = node.op; |
| return (arguments) => Binary(op, makeLeft(arguments), makeRight(arguments)); |
| } |
| |
| Instantiator visitPrefix(Prefix node) { |
| Instantiator makeOperand = visit(node.argument); |
| String op = node.op; |
| return (arguments) => Prefix(op, makeOperand(arguments)); |
| } |
| |
| Instantiator visitPostfix(Postfix node) { |
| Instantiator makeOperand = visit(node.argument); |
| String op = node.op; |
| return (arguments) => Postfix(op, makeOperand(arguments)); |
| } |
| |
| Instantiator visitVariableUse(VariableUse node) => |
| (arguments) => VariableUse(node.name); |
| |
| Instantiator visitThis(This node) => (arguments) => This(); |
| |
| Instantiator visitVariableDeclaration(VariableDeclaration node) => |
| (arguments) => VariableDeclaration(node.name); |
| |
| Instantiator visitParameter(Parameter node) => |
| (arguments) => Parameter(node.name); |
| |
| Instantiator visitAccess(PropertyAccess node) { |
| Instantiator makeReceiver = visit(node.receiver); |
| Instantiator makeSelector = visit(node.selector); |
| return (arguments) => |
| PropertyAccess(makeReceiver(arguments), makeSelector(arguments)); |
| } |
| |
| Instantiator visitNamedFunction(NamedFunction node) { |
| Instantiator makeDeclaration = visit(node.name); |
| Instantiator makeFunction = visit(node.function); |
| return (arguments) => |
| NamedFunction(makeDeclaration(arguments), makeFunction(arguments)); |
| } |
| |
| Instantiator visitFun(Fun node) { |
| List<Instantiator> paramMakers = node.params.map(visitSplayable).toList(); |
| Instantiator makeBody = visit(node.body); |
| // TODO(sra): Avoid copying params if no interpolation or forced copying. |
| return (arguments) { |
| List<Parameter> params = []; |
| for (Instantiator instantiator in paramMakers) { |
| var result = instantiator(arguments); |
| if (result is Iterable) { |
| params.addAll(result); |
| } else { |
| params.add(result); |
| } |
| } |
| Statement body = makeBody(arguments); |
| return Fun(params, body); |
| }; |
| } |
| |
| Instantiator visitArrowFunction(ArrowFunction node) { |
| List<Instantiator> paramMakers = node.params.map(visitSplayable).toList(); |
| Instantiator makeBody = visit(node.body); |
| // TODO(sra): Avoid copying params if no interpolation or forced copying. |
| return (arguments) { |
| List<Parameter> params = []; |
| for (Instantiator instantiator in paramMakers) { |
| var result = instantiator(arguments); |
| if (result is Iterable) { |
| params.addAll(result); |
| } else { |
| params.add(result); |
| } |
| } |
| // Either a Block or Expression. |
| Node body = makeBody(arguments); |
| return ArrowFunction(params, body); |
| }; |
| } |
| |
| Instantiator visitDeferredExpression(DeferredExpression node) => same(node); |
| |
| Instantiator visitDeferredStatement(DeferredStatement node) => same(node); |
| |
| Instantiator visitDeferredNumber(DeferredNumber node) => same(node); |
| |
| Instantiator visitDeferredString(DeferredString node) => (arguments) => node; |
| |
| Instantiator visitLiteralBool(LiteralBool node) => |
| (arguments) => LiteralBool(node.value); |
| |
| Instantiator visitLiteralString(LiteralString node) => |
| (arguments) => LiteralString(node.value); |
| |
| Instantiator visitLiteralNumber(LiteralNumber node) => |
| (arguments) => LiteralNumber(node.value); |
| |
| Instantiator visitLiteralNull(LiteralNull node) => |
| (arguments) => LiteralNull(); |
| |
| Instantiator visitStringConcatenation(StringConcatenation node) { |
| List<Instantiator> partMakers = |
| node.parts.map(visit).toList(growable: false); |
| return (arguments) { |
| List<Literal> parts = partMakers |
| .map((Instantiator instantiator) => instantiator(arguments)) |
| .toList(growable: false); |
| return StringConcatenation(parts); |
| }; |
| } |
| |
| Instantiator visitName(Name node) => same(node); |
| |
| Instantiator visitParentheses(Parentheses node) { |
| Instantiator makeEnclosed = visit(node.enclosed); |
| return (arguments) { |
| Expression enclosed = makeEnclosed(arguments); |
| return Parentheses(enclosed); |
| }; |
| } |
| |
| Instantiator visitArrayInitializer(ArrayInitializer node) { |
| // TODO(sra): Implement splicing? |
| List<Instantiator> elementMakers = |
| node.elements.map(visit).toList(growable: false); |
| return (arguments) { |
| List<Expression> elements = elementMakers |
| .map<Expression>( |
| (Instantiator instantiator) => instantiator(arguments)) |
| .toList(growable: false); |
| return ArrayInitializer(elements); |
| }; |
| } |
| |
| Instantiator visitArrayHole(ArrayHole node) { |
| return (arguments) => ArrayHole(); |
| } |
| |
| Instantiator visitObjectInitializer(ObjectInitializer node) { |
| List<Instantiator> propertyMakers = |
| node.properties.map(visitSplayable).toList(); |
| bool isOneLiner = node.isOneLiner; |
| return (arguments) { |
| List<Property> properties = []; |
| for (Instantiator instantiator in propertyMakers) { |
| var result = instantiator(arguments); |
| if (result is Iterable) { |
| properties.addAll(result); |
| } else { |
| properties.add(result); |
| } |
| } |
| return ObjectInitializer(properties, isOneLiner: isOneLiner); |
| }; |
| } |
| |
| Instantiator visitProperty(Property node) { |
| Instantiator makeName = visit(node.name); |
| Instantiator makeValue = visit(node.value); |
| return (arguments) { |
| return Property(makeName(arguments), makeValue(arguments)); |
| }; |
| } |
| |
| Instantiator visitMethodDefinition(MethodDefinition node) { |
| Instantiator makeName = visit(node.name); |
| Instantiator makeFunction = visit(node.function); |
| return (arguments) { |
| return MethodDefinition(makeName(arguments), makeFunction(arguments)); |
| }; |
| } |
| |
| Instantiator visitRegExpLiteral(RegExpLiteral node) => |
| (arguments) => RegExpLiteral(node.pattern); |
| |
| Instantiator visitComment(Comment node) => TODO('visitComment'); |
| |
| Instantiator visitAwait(Await node) { |
| Instantiator makeExpression = visit(node.expression); |
| return (arguments) { |
| return Await(makeExpression(arguments)); |
| }; |
| } |
| } |
| |
| /// InterpolatedNodeAnalysis determines which AST trees contain |
| /// [InterpolatedNode]s, and the names of the named interpolated nodes. |
| class InterpolatedNodeAnalysis extends BaseVisitorVoid { |
| final Set<Node> containsInterpolatedNode = {}; |
| final Set<String> holeNames = {}; |
| int count = 0; |
| |
| InterpolatedNodeAnalysis(); |
| |
| bool containsInterpolatedNodes(Node node) => |
| containsInterpolatedNode.contains(node); |
| |
| void visit(Node node) { |
| node.accept(this); |
| } |
| |
| void visitNode(Node node) { |
| int before = count; |
| node.visitChildren(this); |
| if (count != before) containsInterpolatedNode.add(node); |
| } |
| |
| visitInterpolatedNode(InterpolatedNode node) { |
| containsInterpolatedNode.add(node); |
| if (node.isNamed) holeNames.add(node.nameOrPosition); |
| ++count; |
| } |
| } |