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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/js/template.dart

Issue 237583014: JS templates (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: mostly switched to templates Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of js;
6
7 class TemplateManager {
8 Map<String, Template> expressionTemplates = new Map<String, Template>();
9 Map<String, Template> statementTemplates = new Map<String, Template>();
10
11 TemplateManager();
12
13 Template findExpressionTemplate(String source) {
14 var prev = expressionTemplates.length;
15 Template template = expressionTemplates.putIfAbsent(source,
16 () => new Template.expression(source));
17 /*
18 if (expressionTemplates.length != prev) {
19 var n = ((expressionTemplates.length == prev) ? '-' : '${expressionTemplat es.length}')
20 .padLeft(6);
21 print('E $n $source');
22 }
23 */
24 return template;
25 }
26
27 Template findStatementTemplate(String source) {
28 var prev = statementTemplates.length;
29 Template template = statementTemplates.putIfAbsent(source,
30 () => new Template.statement(source));
31 /*
32 if (statementTemplates.length != prev) {
33 var n = ((statementTemplates.length == prev) ? '-' : '${statementTemplates .length}')
kevmoo 2014/05/12 04:19:33 long line
34 .padLeft(6);
35 print('S $n $source');
36 }
37 */
38 return template;
39 }
40 }
41
42 /**
43 */
44 class Template {
45 final String source;
46 final bool isExpression;
47 /*final*/ bool forceCopy;
48
49 Node ast;
50 Instantiator instantiator;
51
52 int positionalArgumentCount;
53 // TODO(sra): Named arguments.
54
55 Template.expression(this.source, {this.forceCopy: false})
56 : isExpression = true {
57 ast = new MiniJsParser(source).expression();
58 _compile();
59 }
60
61 Template.expressionAst(Node codeAst)
62 : source = null, isExpression = true, forceCopy = false {
63 ast = codeAst;
64 _compile();
65 }
66
67 Template.statement(this.source, {this.forceCopy: false})
68 : isExpression = false {
69 ast = new MiniJsParser(source).statement();
70 _compile();
71 }
72
73 void _compile() {
74 InstantiatorGeneratorVisitor generator =
75 new InstantiatorGeneratorVisitor(forceCopy);
76 instantiator = generator.compile(ast);
77 positionalArgumentCount = generator.analysis.count;
78 }
79
80 Node instantiate(arguments) {
81 if (arguments is List) {
82 if (arguments.length != positionalArgumentCount) {
83 throw 'Wrong number of template arguments, given ${arguments.length}, '
84 'expected $positionalArgumentCount';
85 }
86 return instantiator(arguments);
87 }
88 // TODO(sra): Named arguments.
89 throw 'Expected List';
90 }
91 }
92
93 /**
94 * An Instantiator is a Function that generates a JS AST tree or List of
95 * trees. [arguments] is a List for positional templates, or (TODO) Map for
96 * named templates.
97 */
98 typedef Node Instantiator(var arguments);
99
100
101 /**
102 * InstantiatorGeneratorVisitor compiles a tree containing [InterpolatedNode]s
103 * into a function that will create a copy of the tree with the interpolated
104 * nodes substituted with provided values.
105 */
106 class InstantiatorGeneratorVisitor implements NodeVisitor<Instantiator> {
107
108 final bool forceCopy;
109
110 InterpolatedNodeAnalysis analysis = new InterpolatedNodeAnalysis();
111
112 /**
113 * The entire tree is cloned if [forceCopy] is true.
114 */
115 InstantiatorGeneratorVisitor(this.forceCopy);
116
117 Instantiator compile(Node node) {
118 analysis.visit(node);
119 Instantiator result = visit(node);
120 return result;
121 }
122
123 static error(String message) {
124 throw message;
125 }
126
127 static Instantiator same(Node node) => (arguments) => node;
128 static Node makeNull(arguments) => null;
129
130 Instantiator visit(Node node) {
131 if (forceCopy || analysis.containsInterpolatedNodes(node)) {
132 return node.accept(this);
133 }
134 return same(node);
135 }
136
137 Instantiator visitNullable(Node node) {
138 if (node == null) return makeNull;
139 return visit(node);
140 }
141
142 Instantiator visitSplayable(Node node) {
143 // TODO(sra): Process immediate [InterpolatedNode]s, permitting splaying.
144 return visit(node);
145 }
146
147 Instantiator visitNode(Node node) {
148 throw 'Unimplemented InstantiatorGeneratorVisitor for $node';
149 }
150
151 static RegExp identiferRE = new RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$');
152
153 Instantiator visitInterpolatedExpression(InterpolatedExpression node) {
154 int position = node.name;
155 return (arguments) {
156 var value = arguments[position];
157 if (value is Expression) return value;
158 if (value is String) {
159 if (!identiferRE.hasMatch(value)) error("Not identifier: '$value'");
160 assert(identiferRE.hasMatch(value));
161 return new VariableUse(value);
162 }
163 error('Interpolated value #$position is not an Expression: $value');
164 };
165 }
166
167 Instantiator visitSplayableExpression(Node node) {
168 if (node is InterpolatedExpression) {
169 int position = node.name;
170 return (arguments) {
171 var value = arguments[position];
172 Expression toExpression(item) {
173 if (item is Expression) return item;
174 if (item is String) {
175 assert(identiferRE.hasMatch(item));
176 return new VariableUse(item);
177 }
178 error('Interpolated value #$position is not '
179 'an Expression or List of Expressions: $value');
180 }
181 if (value is Iterable) return value.map(toExpression);
182 return toExpression(value);
183 };
184 }
185 return visit(node);
186 }
187
188 Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) {
189 int position = node.name;
190 return (arguments) {
191 var value = arguments[position];
192 if (value is Literal) return value;
193 error('Interpolated value #$position is not a Literal: '
194 '$value (${value.runtimeType})');
195 };
196 }
197
198 Instantiator visitInterpolatedParameter(InterpolatedParameter node) {
199 int position = node.name;
200 return (arguments) {
201 var value = arguments[position];
202
203 Parameter toParameter(item) {
204 if (item is Parameter) return item;
205 if (item is String) return new Parameter(item);
206 error('Interpolated value #$position is not a Parameter or '
207 'List of Parameters: $value (a ${value.runtimeType})');
208 }
209 if (value is Iterable) return value.map(toParameter);
210 return toParameter(value);
211 };
212 }
213
214 Instantiator visitInterpolatedSelector(InterpolatedSelector node) {
215 // A selector is an expression, as in `a[selector]`.
216 // A String argument converted into a LiteralString, so `a.#` with argument
217 // 'foo' generates `a["foo"]` which prints as `a.foo`.
218 int position = node.name;
219 return (arguments) {
220 var value = arguments[position];
221 if (value is Expression) return value;
222 if (value is String) return new LiteralString('"$value"');
223 error('Interpolated value #$position is not a selector: $value');
224 };
225 }
226
227 Instantiator visitInterpolatedStatement(InterpolatedStatement node) {
228 int position = node.name;
229 return (arguments) {
230 var value = arguments[position];
231 if (value is Node) return value.toStatement();
232 error('Interpolated value #$position is not a Statement: $value');
233 };
234 }
235
236 Instantiator visitSplayableStatement(Node node) {
237 if (node is InterpolatedStatement) {
238 int position = node.name;
239 return (arguments) {
240 var value = arguments[position];
241 Statement toStatement(item) {
242 if (item is Statement) return item;
243 if (item is Expression) return item.toStatement();;
244 error('Interpolated value #$position is not '
245 'a Statement or List of Statements: $value');
246 }
247 if (value is Iterable) return value.map(toStatement);
248 return toStatement(value);
249 };
250 }
251 return visit(node);
252 }
253
254 Instantiator visitProgram(Program node) {
255 List instantiators = node.body.map(visitSplayableStatement).toList();
256 return (arguments) {
257 List<Statement> statements = <Statement>[];
258 void add(node) {
259 if (node is EmptyStatement) return;
260 if (node is Iterable) {
261 statements.addAll(node);
262 } else {
263 statements.add(node.toStatement());
264 }
265 }
266 for (Instantiator instantiator in instantiators) {
267 add(instantiator(arguments));
268 }
269 return new Program(statements);
270 };
271 }
272
273 Instantiator visitBlock(Block node) {
274 List instantiators = node.statements.map(visitSplayableStatement).toList();
275 return (arguments) {
276 List<Statement> statements = <Statement>[];
277 void add(node) {
278 if (node is EmptyStatement) return;
279 if (node is Iterable) {
280 statements.addAll(node);
281 } else if (node is Block) {
282 statements.addAll(node.statements);
283 } else {
284 statements.add(node.toStatement());
285 }
286 }
287 for (Instantiator instantiator in instantiators) {
288 add(instantiator(arguments));
289 }
290 return new Block(statements);
291 };
292 }
293
294 Instantiator visitExpressionStatement(ExpressionStatement node) {
295 Instantiator buildExpression = visit(node.expression);
296 return (arguments) {
297 return buildExpression(arguments).toStatement();
298 };
299 }
300
301 Instantiator visitEmptyStatement(EmptyStatement node) =>
302 (arguments) => new EmptyStatement();
303
304 Instantiator visitIf(If node) {
305 if (node.condition is InterpolatedExpression) {
306 return visitIfConditionalCompilation(node);
307 } else {
308 return visitIfNormal(node);
309 }
310 }
311
312 Instantiator visitIfConditionalCompilation(If node) {
313 // Special version of visitInterpolatedExpression that permits bools.
314 compileCondition(InterpolatedExpression node) {
315 int position = node.name;
316 return (arguments) {
317 var value = arguments[position];
318 if (value is bool) return value;
319 if (value is Expression) return value;
320 error('Interpolated value #$position is not an Expression: $value');
321 };
322 }
323 var makeCondition = compileCondition(node.condition);
324 Instantiator makeThen = visit(node.then);
325 Instantiator makeOtherwise = visit(node.otherwise);
326 return (arguments) {
327 var condition = makeCondition(arguments);
328 if (condition is bool) {
329 if (condition == true) {
330 return makeThen(arguments);
331 } else {
332 return makeOtherwise(arguments);
333 }
334 }
335 return new If(
336 condition,
337 makeThen(arguments),
338 makeOtherwise(arguments));
339 };
340 }
341
342 Instantiator visitIfNormal(If node) {
343 Instantiator makeCondition = visit(node.condition);
344 Instantiator makeThen = visit(node.then);
345 Instantiator makeOtherwise = visit(node.otherwise);
346 return (arguments) {
347 return new If(
348 makeCondition(arguments),
349 makeThen(arguments),
350 makeOtherwise(arguments));
351 };
352 }
353
354 Instantiator visitFor(For node) {
355 Instantiator makeInit = visitNullable(node.init);
356 Instantiator makeCondition = visitNullable(node.condition);
357 Instantiator makeUpdate = visitNullable(node.update);
358 Instantiator makeBody = visit(node.body);
359 return (arguments) {
360 return new For(
361 makeInit(arguments), makeCondition(arguments), makeUpdate(arguments),
362 makeBody(arguments));
363 };
364 }
365
366 Instantiator visitForIn(ForIn node) {
367 Instantiator makeLeftHandSide = visit(node.leftHandSide);
368 Instantiator makeObject = visit(node.object);
369 Instantiator makeBody = visit(node.body);
370 return (arguments) {
371 return new ForIn(
372 makeLeftHandSide(arguments),
373 makeObject(arguments),
374 makeBody(arguments));
375 };
376 }
377
378 TODO(String name) {
379 throw new UnimplementedError('${this.runtimeType}.$name');
380 }
381
382 Instantiator visitWhile(While node) => TODO('visitWhile');
383 Instantiator visitDo(Do node) => TODO('visitDo');
384
385 Instantiator visitContinue(Continue node) =>
386 (arguments) => new Continue(node.targetLabel);
387
388 Instantiator visitBreak(Break node) =>
389 (arguments) => new Break(node.targetLabel);
390
391 Instantiator visitReturn(Return node) {
392 Instantiator makeExpression = visitNullable(node.value);
393 return (arguments) => new Return(makeExpression(arguments));
394 }
395
396 Instantiator visitThrow(Throw node) {
397 Instantiator makeExpression = visit(node.expression);
398 return (arguments) => new Throw(makeExpression(arguments));
399 }
400
401
402 Instantiator visitTry(Try node) {
403 Instantiator makeBody = visit(node.body);
404 Instantiator makeCatch = visitNullable(node.catchPart);
405 Instantiator makeFinally = visitNullable(node.finallyPart);
406 return (arguments) => new Try(
407 makeBody(arguments), makeCatch(arguments), makeFinally(arguments));
408 }
409
410 Instantiator visitCatch(Catch node) {
411 Instantiator makeDeclaration = visit(node.declaration);
412 Instantiator makeBody = visit(node.body);
413 return (arguments) => new Catch(
414 makeDeclaration(arguments), makeBody(arguments));
415 }
416
417 Instantiator visitSwitch(Switch node) => TODO('visitSwitch');
418 Instantiator visitCase(Case node) => TODO('visitCase');
419 Instantiator visitDefault(Default node) => TODO('visitDefault');
420
421 Instantiator visitFunctionDeclaration(FunctionDeclaration node) {
422 Instantiator makeName = visit(node.name);
423 Instantiator makeFunction = visit(node.function);
424 return (arguments) =>
425 new FunctionDeclaration(makeName(arguments), makeFunction(arguments));
426 }
427
428 Instantiator visitLabeledStatement(LabeledStatement node) =>
429 TODO('visitLabeledStatement');
430 Instantiator visitLiteralStatement(LiteralStatement node) =>
431 TODO('visitLiteralStatement');
432 Instantiator visitBlob(Blob node) =>
433 TODO('visitBlob');
434 Instantiator visitLiteralExpression(LiteralExpression node) =>
435 TODO('visitLiteralExpression');
436
437 Instantiator visitVariableDeclarationList(VariableDeclarationList node) {
438 List<Instantiator> declarationMakers = node.declarations.map(visit).toList() ;
439 return (arguments) {
440 List<VariableInitialization> declarations = <VariableInitialization>[];
441 for (Instantiator instantiator in declarationMakers) {
442 var result = instantiator(arguments);
443 declarations.add(result);
444 }
445 return new VariableDeclarationList(declarations);
446 };
447 }
448
449 Instantiator visitSequence(Sequence node) => TODO('visitSequence');
450
451 Instantiator visitAssignment(Assignment node) {
452 Instantiator makeLeftHandSide = visit(node.leftHandSide);
453 Instantiator makeCompoundTarget = visitNullable(node.compoundTarget);
454 Instantiator makeValue = visitNullable(node.value);
455 return (arguments) {
456 return new Assignment._internal(
457 makeLeftHandSide(arguments),
458 makeCompoundTarget(arguments),
459 makeValue(arguments));
460 };
461 }
462
463 Instantiator visitVariableInitialization(VariableInitialization node) {
464 Instantiator makeDeclaration = visit(node.declaration);
465 Instantiator makeValue = visitNullable(node.value);
466 return (arguments) {
467 return new VariableInitialization(
468 makeDeclaration(arguments), makeValue(arguments));
469 };
470 }
471
472 Instantiator visitConditional(Conditional cond) {
473 Instantiator makeCondition = visit(cond.condition);
474 Instantiator makeThen = visit(cond.then);
475 Instantiator makeOtherwise = visit(cond.otherwise);
476 return (arguments) => new Conditional(
477 makeCondition(arguments),
478 makeThen(arguments),
479 makeOtherwise(arguments));
480 }
481
482 Instantiator visitNew(New node) =>
483 handleCallOrNew(node, (target, arguments) => new New(target, arguments));
484
485 Instantiator visitCall(Call node) =>
486 handleCallOrNew(node, (target, arguments) => new Call(target, arguments));
487
488 Instantiator handleCallOrNew(Call node, finish(target, arguments)) {
489 Instantiator makeTarget = visit(node.target);
490 Iterable<Instantiator> argumentMakers =
491 node.arguments.map(visitSplayableExpression).toList();
492
493 return (arguments) {
494 Node target = makeTarget(arguments);
495 List<Expression> callArguments = <Expression>[];
496 for (Instantiator instantiator in argumentMakers) {
497 var result = instantiator(arguments);
498 if (result is Iterable) {
499 callArguments.addAll(result);
500 } else {
501 callArguments.add(result);
502 }
503 }
504 return finish(target, callArguments);
505 };
506 }
507
508 Instantiator visitBinary(Binary node) {
509 Instantiator makeLeft = visit(node.left);
510 Instantiator makeRight = visit(node.right);
511 String op = node.op;
512 return (arguments) =>
513 new Binary(op, makeLeft(arguments), makeRight(arguments));
514 }
515
516 Instantiator visitPrefix(Prefix node) {
517 Instantiator makeOperand = visit(node.argument);
518 String op = node.op;
519 return (arguments) => new Prefix(op, makeOperand(arguments));
520 }
521
522 Instantiator visitPostfix(Postfix node) {
523 Instantiator makeOperand = visit(node.argument);
524 String op = node.op;
525 return (arguments) => new Postfix(op, makeOperand(arguments));
526 }
527
528 Instantiator visitVariableUse(VariableUse node) =>
529 (arguments) => new VariableUse(node.name);
530
531 Instantiator visitThis(This node) => (arguments) => new This();
532
533 Instantiator visitVariableDeclaration(VariableDeclaration node) =>
534 (arguments) => new VariableDeclaration(node.name);
535
536 Instantiator visitParameter(Parameter node) =>
537 (arguments) => new Parameter(node.name);
538
539 Instantiator visitAccess(PropertyAccess node) {
540 Instantiator makeReceiver = visit(node.receiver);
541 Instantiator makeSelector = visit(node.selector);
542 return (arguments) =>
543 new PropertyAccess(makeReceiver(arguments), makeSelector(arguments));
544 }
545
546 Instantiator visitNamedFunction(NamedFunction node) {
547 Instantiator makeDeclaration = visit(node.name);
548 Instantiator makeFunction = visit(node.function);
549 return (arguments) =>
550 new NamedFunction(makeDeclaration(arguments), makeFunction(arguments));
551 }
552
553 Instantiator visitFun(Fun node) {
554 List<Instantiator> paramMakers = node.params.map(visitSplayable).toList();
555 Instantiator makeBody = visit(node.body);
556 // TODO(sra): Avoid copying params if no interpolation or forced copying.
557 return (arguments) {
558 List<Parameter> params = <Parameter>[];
559 for (Instantiator instantiator in paramMakers) {
560 var result = instantiator(arguments);
561 if (result is Iterable) {
562 params.addAll(result);
563 } else {
564 params.add(result);
565 }
566 }
567 Statement body = makeBody(arguments);
568 return new Fun(params, body);
569 };
570 }
571
572 Instantiator visitLiteralBool(LiteralBool node) =>
573 (arguments) => new LiteralBool(node.value);
574
575 Instantiator visitLiteralString(LiteralString node) =>
576 (arguments) => new LiteralString(node.value);
577
578 Instantiator visitLiteralNumber(LiteralNumber node) =>
579 (arguments) => new LiteralNumber(node.value);
580
581 Instantiator visitLiteralNull(LiteralNull node) =>
582 (arguments) => new LiteralNull();
583
584 Instantiator visitArrayInitializer(ArrayInitializer node) {
585 // Assume array has no missing elements.
586 // TODO(sra): Splicing?
587 List<Instantiator> elementMakers = node.elements
588 .map((ArrayElement element) => visit(element.value))
589 .toList();
590 return (arguments) {
591 List<ArrayElement> elements = <ArrayElement>[];
592 void add(Expression value) {
593 elements.add(new ArrayElement(elements.length, value));
594 }
595 for (Instantiator instantiator in elementMakers) {
596 var result = instantiator(arguments);
597 add(result);
598 }
599 return new ArrayInitializer(elements.length, elements);
600 };
601 }
602
603 Instantiator visitArrayElement(ArrayElement node) {
604 throw 'Should not get here'; // Handled in visitArrayInitializer.
605 }
606
607 Instantiator visitObjectInitializer(ObjectInitializer node) {
608 List<Instantiator> propertyMakers =
609 node.properties.map(visitSplayable).toList();
610 bool isOneLiner = node.isOneLiner;
611 return (arguments) {
612 List<Property> properties = <Property>[];
613 for (Instantiator instantiator in propertyMakers) {
614 var result = instantiator(arguments);
615 if (result is Iterable) {
616 properties.addAll(result);
617 } else {
618 properties.add(result);
619 }
620 }
621 return new ObjectInitializer(properties, isOneLiner: isOneLiner);
622 };
623 }
624
625 Instantiator visitProperty(Property node) {
626 Instantiator makeName = visit(node.name);
627 Instantiator makeValue = visit(node.value);
628 return (arguments) {
629 return new Property(makeName(arguments), makeValue(arguments));
630 };
631 }
632
633 Instantiator visitRegExpLiteral(RegExpLiteral node) =>
634 (arguments) => new RegExpLiteral(node.pattern);
635
636 Instantiator visitComment(Comment node) => TODO('visitComment');
637 }
638
639 /**
640 * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST.
641 */
642 class InterpolatedNodeAnalysis extends BaseVisitor {
643 final Set<Node> containsInterpolatedNode = new Set<Node>();
644 final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[];
645 int count = 0;
646
647 InterpolatedNodeAnalysis();
648
649 bool containsInterpolatedNodes(Node node) =>
650 containsInterpolatedNode.contains(node);
651
652 void visit(Node node) {
653 node.accept(this);
654 }
655
656 void visitNode(Node node) {
657 int before = count;
658 node.visitChildren(this);
659 if (count != before) containsInterpolatedNode.add(node);
660 return null;
661 }
662
663 visitInterpolatedNode(InterpolatedNode node) {
664 interpolatedNodes.add(node);
665 containsInterpolatedNode.add(node);
666 ++count;
667 }
668 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698