blob: 929f6216ec95a16f7dc29a173049dfb233724807 [file] [log] [blame]
// 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 = new Map<String, Template>();
Map<String, Template> statementTemplates = new Map<String, Template>();
TemplateManager();
Template lookupExpressionTemplate(String source) {
return expressionTemplates[source];
}
Template defineExpressionTemplate(String source, Node ast) {
Template template =
new 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 =
new 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}) {
_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 =
new InstantiatorGeneratorVisitor(false);
generator.compile(ast);
return generator.analysis.count == 0;
}
void _compile() {
InstantiatorGeneratorVisitor generator =
new 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:\n$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(var 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 = new 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';
}
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 new Identifier(value);
error('Interpolated value #$nameOrPosition is not an Expression: $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 new Identifier(item);
return 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) 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 toIdentifier(item) {
if (item is Parameter) return item;
if (item is String) return new Identifier(item);
return error('Interpolated value #$nameOrPosition is not an Identifier'
' or List of Identifiers: $value');
}
if (value is Iterable) return value.map(toIdentifier);
return toIdentifier(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 new LiteralString('"$value"');
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();
error('Interpolated value #$nameOrPosition is not a Statement: $value');
};
}
Instantiator visitInterpolatedMethod(InterpolatedMethod node) {
var nameOrPosition = node.nameOrPosition;
return (arguments) {
var value = arguments[nameOrPosition];
Method toMethod(item) {
if (item is Method) return item;
return error('Interpolated value #$nameOrPosition is not a Method '
'or List of Methods: $value');
}
if (value is Iterable) return value.map(toMethod);
return toMethod(value);
};
}
Instantiator visitInterpolatedIdentifier(InterpolatedIdentifier node) {
var nameOrPosition = node.nameOrPosition;
return (arguments) {
var item = arguments[nameOrPosition];
if (item is Identifier) return item;
if (item is String) return new Identifier(item);
return error('Interpolated value #$nameOrPosition is not a '
'Identifier or String: $item');
};
}
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();
;
return 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 instantiators = node.body.map(visitSplayableStatement).toList();
return (arguments) {
List<Statement> statements = <Statement>[];
void add(node) {
if (node is EmptyStatement) return;
if (node is Iterable) {
for (var n in node) statements.add(n);
} else {
statements.add(node.toStatement());
}
}
for (Instantiator instantiator in instantiators) {
add(instantiator(arguments));
}
return new Program(statements);
};
}
Instantiator visitBlock(Block node) {
List instantiators = node.statements.map(visitSplayableStatement).toList();
return (arguments) {
List<Statement> statements = <Statement>[];
void add(node) {
if (node is EmptyStatement) return;
if (node is Iterable) {
for (var n in node) statements.add(n);
} else if (node is Block) {
statements.addAll(node.statements);
} else {
statements.add(node.toStatement());
}
}
for (Instantiator instantiator in instantiators) {
add(instantiator(arguments));
}
return new Block(statements);
};
}
Instantiator visitExpressionStatement(ExpressionStatement node) {
Instantiator buildExpression = visit(node.expression);
return (arguments) {
return buildExpression(arguments).toStatement();
};
}
Instantiator visitEmptyStatement(EmptyStatement node) =>
(arguments) => new 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 new Identifier(value);
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 new 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 new 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 new 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 new ForIn(makeLeftHandSide(arguments), makeObject(arguments),
makeBody(arguments));
};
}
Instantiator visitForOf(ForOf node) {
Instantiator makeLeftHandSide = visit(node.leftHandSide);
Instantiator makeObject = visit(node.iterable);
Instantiator makeBody = visit(node.body);
return (arguments) {
return new ForOf(makeLeftHandSide(arguments), makeObject(arguments),
makeBody(arguments));
};
}
TODO(String name) {
throw new UnimplementedError('$this.$name');
}
Instantiator visitWhile(While node) {
Instantiator makeCondition = visit(node.condition);
Instantiator makeBody = visit(node.body);
return (arguments) {
return new While(makeCondition(arguments), makeBody(arguments));
};
}
Instantiator visitDo(Do node) {
Instantiator makeBody = visit(node.body);
Instantiator makeCondition = visit(node.condition);
return (arguments) {
return new Do(makeBody(arguments), makeCondition(arguments));
};
}
Instantiator visitContinue(Continue node) =>
(arguments) => new Continue(node.targetLabel);
Instantiator visitBreak(Break node) =>
(arguments) => new Break(node.targetLabel);
Instantiator visitReturn(Return node) {
Instantiator makeExpression = visitNullable(node.value);
return (arguments) => new Return(makeExpression(arguments));
}
Instantiator visitDartYield(DartYield node) {
Instantiator makeExpression = visit(node.expression);
return (arguments) =>
new DartYield(makeExpression(arguments), node.hasStar);
}
Instantiator visitThrow(Throw node) {
Instantiator makeExpression = visit(node.expression);
return (arguments) => new Throw(makeExpression(arguments));
}
Instantiator visitTry(Try node) {
Instantiator makeBody = visit(node.body);
Instantiator makeCatch = visitNullable(node.catchPart);
Instantiator makeFinally = visitNullable(node.finallyPart);
return (arguments) => new Try(
makeBody(arguments), makeCatch(arguments), makeFinally(arguments));
}
Instantiator visitCatch(Catch node) {
Instantiator makeDeclaration = visit(node.declaration);
Instantiator makeBody = visit(node.body);
return (arguments) =>
new Catch(makeDeclaration(arguments), makeBody(arguments));
}
Instantiator visitSwitch(Switch node) {
Instantiator makeKey = visit(node.key);
Iterable<Instantiator> makeCases = node.cases.map(visit);
return (arguments) {
return new Switch(
makeKey(arguments),
makeCases
.map((makeCase) => makeCase(arguments) as SwitchClause)
.toList());
};
}
Instantiator visitCase(Case node) {
Instantiator makeExpression = visit(node.expression);
Instantiator makeBody = visit(node.body);
return (arguments) {
return new Case(makeExpression(arguments), makeBody(arguments));
};
}
Instantiator visitDefault(Default node) {
Instantiator makeBody = visit(node.body);
return (arguments) {
return new Default(makeBody(arguments));
};
}
Instantiator visitFunctionDeclaration(FunctionDeclaration node) {
Instantiator makeName = visit(node.name);
Instantiator makeFunction = visit(node.function);
return (arguments) =>
new FunctionDeclaration(makeName(arguments), makeFunction(arguments));
}
Instantiator visitLabeledStatement(LabeledStatement node) {
Instantiator makeBody = visit(node.body);
return (arguments) => new 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 = <VariableInitialization>[];
for (Instantiator instantiator in declarationMakers) {
var result = instantiator(arguments);
declarations.add(result);
}
return new VariableDeclarationList(node.keyword, declarations);
};
}
Instantiator visitAssignment(Assignment node) {
Instantiator makeLeftHandSide = visit(node.leftHandSide);
String op = node.op;
Instantiator makeValue = visitNullable(node.value);
return (arguments) {
return new Assignment.compound(
makeLeftHandSide(arguments), op, makeValue(arguments));
};
}
Instantiator visitVariableInitialization(VariableInitialization node) {
Instantiator makeDeclaration = visit(node.declaration);
Instantiator makeValue = visitNullable(node.value);
return (arguments) {
return new 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) => new Conditional(makeCondition(arguments),
makeThen(arguments), makeOtherwise(arguments));
}
Instantiator visitNew(New node) =>
handleCallOrNew(node, (target, arguments) =>
new New(target, arguments as List<Expression>));
Instantiator visitCall(Call node) =>
handleCallOrNew(node, (target, arguments) =>
new Call(target, arguments as List<Expression>));
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 = <Expression>[];
for (Instantiator instantiator in argumentMakers) {
var result = instantiator(arguments);
if (result is Iterable) {
for (var r in result) callArguments.add(r);
} 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) =>
new Binary(op, makeLeft(arguments), makeRight(arguments));
}
Instantiator visitPrefix(Prefix node) {
Instantiator makeOperand = visit(node.argument);
String op = node.op;
return (arguments) => new Prefix(op, makeOperand(arguments));
}
Instantiator visitPostfix(Postfix node) {
Instantiator makeOperand = visit(node.argument);
String op = node.op;
return (arguments) => new Postfix(op, makeOperand(arguments));
}
Instantiator visitThis(This node) => (arguments) => new This();
Instantiator visitSuper(Super node) => (arguments) => new Super();
Instantiator visitIdentifier(Identifier node) =>
(arguments) => new Identifier(node.name);
Instantiator visitSpread(Spread node) =>
(args) => new Spread(visit(node.argument)(args));
Instantiator visitYield(Yield node) =>
(args) => new Yield(node.value != null ? visit(node.value)(args) : null,
star: node.star);
Instantiator visitRestParameter(RestParameter node) =>
(args) => new RestParameter(visit(node.parameter)(args));
Instantiator visitAccess(PropertyAccess node) {
Instantiator makeReceiver = visit(node.receiver);
Instantiator makeSelector = visit(node.selector);
return (arguments) =>
new PropertyAccess(makeReceiver(arguments), makeSelector(arguments));
}
Instantiator visitNamedFunction(NamedFunction node) {
Instantiator makeDeclaration = visit(node.name);
Instantiator makeFunction = visit(node.function);
return (arguments) =>
new NamedFunction(makeDeclaration(arguments), makeFunction(arguments));
}
Instantiator visitFunctionExpression(FunctionExpression 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 = <Parameter>[];
for (Instantiator instantiator in paramMakers) {
var result = instantiator(arguments);
if (result is Iterable) {
for (var r in result) params.add(r);
} else {
params.add(result);
}
}
var body = makeBody(arguments);
if (node is ArrowFun) {
return new ArrowFun(params, body);
} else if (node is Fun) {
return new Fun(params, body);
} else {
throw "Unknown FunctionExpression type ${node.runtimeType}: $node";
}
};
}
Instantiator visitFun(Fun node) => visitFunctionExpression(node);
Instantiator visitArrowFun(ArrowFun node) => visitFunctionExpression(node);
Instantiator visitLiteralBool(LiteralBool node) =>
(arguments) => new LiteralBool(node.value);
Instantiator visitLiteralString(LiteralString node) =>
(arguments) => new LiteralString(node.value);
Instantiator visitLiteralNumber(LiteralNumber node) =>
(arguments) => new LiteralNumber(node.value);
Instantiator visitLiteralNull(LiteralNull node) =>
(arguments) => new LiteralNull();
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((instantiator) => instantiator(arguments) as Expression)
.toList(growable: false);
return new ArrayInitializer(elements);
};
}
Instantiator visitArrayHole(ArrayHole node) {
return (arguments) => new ArrayHole();
}
Instantiator visitObjectInitializer(ObjectInitializer node) {
List<Instantiator> propertyMakers =
node.properties.map(visitSplayable).toList();
return (arguments) {
List<Property> properties = <Property>[];
for (Instantiator instantiator in propertyMakers) {
var result = instantiator(arguments);
if (result is Iterable) {
for (var r in result) properties.add(r);
} else {
properties.add(result);
}
}
return new ObjectInitializer(properties);
};
}
Instantiator visitProperty(Property node) {
Instantiator makeName = visit(node.name);
Instantiator makeValue = visit(node.value);
return (arguments) {
return new Property(makeName(arguments), makeValue(arguments));
};
}
Instantiator visitRegExpLiteral(RegExpLiteral node) =>
(arguments) => new RegExpLiteral(node.pattern);
Instantiator visitTemplateString(TemplateString node) {
Iterable makeElements =
node.elements.map((e) => e is String ? e : visit(e));
return (arguments) {
return new TemplateString(makeElements
.map((m) => m is String ? m : m(arguments))
.toList(growable: false));
};
}
Instantiator visitTaggedTemplate(TaggedTemplate node) {
Instantiator makeTag = visit(node.tag);
Instantiator makeTemplate = visit(node.template);
return (arguments) {
return new TaggedTemplate(makeTag(arguments), makeTemplate(arguments));
};
}
Instantiator visitClassDeclaration(ClassDeclaration node) {
Instantiator makeClass = visit(node.classExpr);
return (arguments) {
return new ClassDeclaration(makeClass(arguments));
};
}
Instantiator visitClassExpression(ClassExpression node) {
List<Instantiator> makeMethods =
node.methods.map(visitSplayableExpression).toList(growable: true);
Instantiator makeName = visit(node.name);
Instantiator makeHeritage = visit(node.heritage);
return (arguments) {
var methods = <Method>[];
for (Instantiator instantiator in makeMethods) {
var result = instantiator(arguments);
if (result is Iterable) {
for (var r in result) methods.add(r);
} else {
methods.add(result);
}
}
return new ClassExpression(
makeName(arguments), makeHeritage(arguments), methods);
};
}
Instantiator visitMethod(Method node) {
Instantiator makeName = visit(node.name);
Instantiator makeFunction = visit(node.function);
return (arguments) {
return new Method(makeName(arguments), makeFunction(arguments),
isGetter: node.isGetter,
isSetter: node.isSetter,
isStatic: node.isStatic);
};
}
Instantiator visitComment(Comment node) =>
(arguments) => new Comment(node.comment);
Instantiator visitCommentExpression(CommentExpression node) {
Instantiator makeExpr = visit(node.expression);
return (arguments) {
return new CommentExpression(node.comment, makeExpr(arguments));
};
}
Instantiator visitAwait(Await node) {
Instantiator makeExpression = visit(node.expression);
return (arguments) {
return new Await(makeExpression(arguments));
};
}
// Note: these are not supported yet in the interpolation grammar.
Instantiator visitModule(Module node) => throw new UnimplementedError();
Instantiator visitNameSpecifier(NameSpecifier node) =>
throw new UnimplementedError();
Instantiator visitImportDeclaration(ImportDeclaration node) =>
throw new UnimplementedError();
Instantiator visitExportDeclaration(ExportDeclaration node) =>
throw new UnimplementedError();
Instantiator visitExportClause(ExportClause node) =>
throw new UnimplementedError();
Instantiator visitAnyTypeRef(AnyTypeRef node) =>
throw new UnimplementedError();
Instantiator visitUnknownTypeRef(UnknownTypeRef node) =>
throw new UnimplementedError();
Instantiator visitArrayTypeRef(ArrayTypeRef node) =>
throw new UnimplementedError();
Instantiator visitFunctionTypeRef(FunctionTypeRef node) =>
throw new UnimplementedError();
Instantiator visitGenericTypeRef(GenericTypeRef node) =>
throw new UnimplementedError();
Instantiator visitQualifiedTypeRef(QualifiedTypeRef node) =>
throw new UnimplementedError();
Instantiator visitOptionalTypeRef(OptionalTypeRef node) =>
throw new UnimplementedError();
Instantiator visitRecordTypeRef(RecordTypeRef node) =>
throw new UnimplementedError();
Instantiator visitUnionTypeRef(UnionTypeRef node) =>
throw new UnimplementedError();
@override
Instantiator visitDestructuredVariable(DestructuredVariable node) {
Instantiator makeName = visit(node.name);
Instantiator makeStructure = visit(node.structure);
Instantiator makeDefaultValue = visit(node.defaultValue);
return (arguments) {
return new DestructuredVariable(
name: makeName(arguments),
structure: makeStructure(arguments),
defaultValue: makeDefaultValue(arguments));
};
}
@override
Instantiator visitArrayBindingPattern(ArrayBindingPattern node) {
List<Instantiator> makeVars = node.variables.map(this.visit).toList();
return (arguments) {
return new ArrayBindingPattern(
makeVars.map((m) => m(arguments) as DestructuredVariable).toList());
};
}
@override
Instantiator visitObjectBindingPattern(ObjectBindingPattern node) {
List<Instantiator> makeVars = node.variables.map(this.visit).toList();
return (arguments) {
return new ObjectBindingPattern(
makeVars.map((m) => m(arguments) as DestructuredVariable).toList());
};
}
@override
Instantiator visitSimpleBindingPattern(SimpleBindingPattern node) =>
(arguments) => new SimpleBindingPattern(new Identifier(node.name.name));
}
/**
* InterpolatedNodeAnalysis determines which AST trees contain
* [InterpolatedNode]s, and the names of the named interpolated nodes.
*/
class InterpolatedNodeAnalysis extends BaseVisitor {
final Set<Node> containsInterpolatedNode = new Set<Node>();
final Set<String> holeNames = new Set<String>();
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);
return null;
}
visitInterpolatedNode(InterpolatedNode node) {
containsInterpolatedNode.add(node);
if (node.isNamed) holeNames.add(node.nameOrPosition);
++count;
}
}