| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of js; | 5 part of js; |
| 6 | 6 |
| 7 class TemplateManager { | 7 class TemplateManager { |
| 8 Map<String, Template> expressionTemplates = new Map<String, Template>(); | 8 Map<String, Template> expressionTemplates = new Map<String, Template>(); |
| 9 Map<String, Template> statementTemplates = new Map<String, Template>(); | 9 Map<String, Template> statementTemplates = new Map<String, Template>(); |
| 10 | 10 |
| (...skipping 29 matching lines...) Expand all Loading... |
| 40 * the original with the placeholders replaced by the arguments to | 40 * the original with the placeholders replaced by the arguments to |
| 41 * [instantiate]. | 41 * [instantiate]. |
| 42 */ | 42 */ |
| 43 class Template { | 43 class Template { |
| 44 final String source; | 44 final String source; |
| 45 final bool isExpression; | 45 final bool isExpression; |
| 46 final bool forceCopy; | 46 final bool forceCopy; |
| 47 final Node ast; | 47 final Node ast; |
| 48 | 48 |
| 49 Instantiator instantiator; | 49 Instantiator instantiator; |
| 50 |
| 50 int positionalArgumentCount = -1; | 51 int positionalArgumentCount = -1; |
| 51 // TODO(sra): Named arguments. | 52 |
| 53 // Null, unless there are named holes. |
| 54 List<String> holeNames; |
| 55 bool get isPositional => holeNames == null; |
| 52 | 56 |
| 53 Template(this.source, this.ast, | 57 Template(this.source, this.ast, |
| 54 {this.isExpression: true, this.forceCopy: false}) { | 58 {this.isExpression: true, this.forceCopy: false}) { |
| 55 _compile(); | 59 _compile(); |
| 56 } | 60 } |
| 57 | 61 |
| 58 Template.withExpressionResult(this.ast) | 62 Template.withExpressionResult(this.ast) |
| 59 : source = null, isExpression = true, forceCopy = false { | 63 : source = null, isExpression = true, forceCopy = false { |
| 60 assert(ast is Expression); | 64 assert(ast is Expression); |
| 61 assert(_checkNoPlaceholders()); | 65 assert(_checkNoPlaceholders()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 76 new InstantiatorGeneratorVisitor(false); | 80 new InstantiatorGeneratorVisitor(false); |
| 77 generator.compile(ast); | 81 generator.compile(ast); |
| 78 return generator.analysis.count == 0; | 82 return generator.analysis.count == 0; |
| 79 } | 83 } |
| 80 | 84 |
| 81 void _compile() { | 85 void _compile() { |
| 82 InstantiatorGeneratorVisitor generator = | 86 InstantiatorGeneratorVisitor generator = |
| 83 new InstantiatorGeneratorVisitor(forceCopy); | 87 new InstantiatorGeneratorVisitor(forceCopy); |
| 84 instantiator = generator.compile(ast); | 88 instantiator = generator.compile(ast); |
| 85 positionalArgumentCount = generator.analysis.count; | 89 positionalArgumentCount = generator.analysis.count; |
| 90 Set<String> names = generator.analysis.holeNames; |
| 91 holeNames = names.isEmpty ? null : names.toList(growable:false); |
| 86 } | 92 } |
| 87 | 93 |
| 88 Node instantiate(List arguments) { | 94 /// Instantiates the template with the given [arguments]. |
| 95 /// |
| 96 /// This method fills in the holes with the given arguments. The [arguments] |
| 97 /// must be either a [List] or a [Map]. |
| 98 Node instantiate(var arguments) { |
| 89 if (arguments is List) { | 99 if (arguments is List) { |
| 90 if (arguments.length != positionalArgumentCount) { | 100 if (arguments.length != positionalArgumentCount) { |
| 91 throw 'Wrong number of template arguments, given ${arguments.length}, ' | 101 throw 'Wrong number of template arguments, given ${arguments.length}, ' |
| 92 'expected $positionalArgumentCount'; | 102 'expected $positionalArgumentCount'; |
| 93 } | 103 } |
| 94 return instantiator(arguments); | 104 return instantiator(arguments); |
| 95 } | 105 } |
| 96 // TODO(sra): Add named placeholders and a Map of arguments. | 106 assert(arguments is Map); |
| 97 throw new UnimplementedError('Template arguments must be a list'); | 107 if (holeNames.length < arguments.length) { |
| 108 // This search is in O(n), but we only do it in case of an error, and the |
| 109 // number of holes should be quite limited. |
| 110 String unusedNames = |
| 111 arguments.keys.where((name) => !holeNames.contains(name)).join(", "); |
| 112 throw "Template arguments has unused mappings: $unusedNames"; |
| 113 } |
| 114 if (!holeNames.every((String name) => arguments.containsKey(name))) { |
| 115 String notFound = |
| 116 holeNames.where((name) => !arguments.containsKey(name)).join(", "); |
| 117 throw "Template arguments is missing mappings for: $notFound"; |
| 118 } |
| 119 return instantiator(arguments); |
| 98 } | 120 } |
| 99 } | 121 } |
| 100 | 122 |
| 101 /** | 123 /** |
| 102 * An Instantiator is a Function that generates a JS AST tree or List of | 124 * An Instantiator is a Function that generates a JS AST tree or List of |
| 103 * trees. [arguments] is a List for positional templates, or (TODO) Map for | 125 * trees. [arguments] is a List for positional templates, or Map for |
| 104 * named templates. | 126 * named templates. |
| 105 */ | 127 */ |
| 106 typedef Node Instantiator(var arguments); | 128 typedef Node Instantiator(var arguments); |
| 107 | 129 |
| 108 | 130 |
| 109 /** | 131 /** |
| 110 * InstantiatorGeneratorVisitor compiles a template. This class compiles a tree | 132 * InstantiatorGeneratorVisitor compiles a template. This class compiles a tree |
| 111 * containing [InterpolatedNode]s into a function that will create a copy of the | 133 * containing [InterpolatedNode]s into a function that will create a copy of the |
| 112 * tree with the interpolated nodes substituted with provided values. | 134 * tree with the interpolated nodes substituted with provided values. |
| 113 */ | 135 */ |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 } | 179 } |
| 158 | 180 |
| 159 static RegExp identiferRE = new RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$'); | 181 static RegExp identiferRE = new RegExp(r'^[A-Za-z_$][A-Za-z_$0-9]*$'); |
| 160 | 182 |
| 161 static Expression convertStringToVariableUse(String value) { | 183 static Expression convertStringToVariableUse(String value) { |
| 162 assert(identiferRE.hasMatch(value)); | 184 assert(identiferRE.hasMatch(value)); |
| 163 return new VariableUse(value); | 185 return new VariableUse(value); |
| 164 } | 186 } |
| 165 | 187 |
| 166 Instantiator visitInterpolatedExpression(InterpolatedExpression node) { | 188 Instantiator visitInterpolatedExpression(InterpolatedExpression node) { |
| 167 int position = node.name; | 189 var nameOrPosition = node.nameOrPosition; |
| 168 return (arguments) { | 190 return (arguments) { |
| 169 var value = arguments[position]; | 191 var value = arguments[nameOrPosition]; |
| 170 if (value is Expression) return value; | 192 if (value is Expression) return value; |
| 171 if (value is String) return convertStringToVariableUse(value);; | 193 if (value is String) return convertStringToVariableUse(value); |
| 172 error('Interpolated value #$position is not an Expression: $value'); | 194 error('Interpolated value #$nameOrPosition is not an Expression: $value'); |
| 173 }; | 195 }; |
| 174 } | 196 } |
| 175 | 197 |
| 176 Instantiator visitSplayableExpression(Node node) { | 198 Instantiator visitSplayableExpression(Node node) { |
| 177 if (node is InterpolatedExpression) { | 199 if (node is InterpolatedExpression) { |
| 178 int position = node.name; | 200 var nameOrPosition = node.nameOrPosition; |
| 179 return (arguments) { | 201 return (arguments) { |
| 180 var value = arguments[position]; | 202 var value = arguments[nameOrPosition]; |
| 181 Expression toExpression(item) { | 203 Expression toExpression(item) { |
| 182 if (item is Expression) return item; | 204 if (item is Expression) return item; |
| 183 if (item is String) return convertStringToVariableUse(item); | 205 if (item is String) return convertStringToVariableUse(item); |
| 184 return error('Interpolated value #$position is not ' | 206 return error('Interpolated value #$nameOrPosition is not ' |
| 185 'an Expression or List of Expressions: $value'); | 207 'an Expression or List of Expressions: $value'); |
| 186 } | 208 } |
| 187 if (value is Iterable) return value.map(toExpression); | 209 if (value is Iterable) return value.map(toExpression); |
| 188 return toExpression(value); | 210 return toExpression(value); |
| 189 }; | 211 }; |
| 190 } | 212 } |
| 191 return visit(node); | 213 return visit(node); |
| 192 } | 214 } |
| 193 | 215 |
| 194 Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) { | 216 Instantiator visitInterpolatedLiteral(InterpolatedLiteral node) { |
| 195 int position = node.name; | 217 var nameOrPosition = node.nameOrPosition; |
| 196 return (arguments) { | 218 return (arguments) { |
| 197 var value = arguments[position]; | 219 var value = arguments[nameOrPosition]; |
| 198 if (value is Literal) return value; | 220 if (value is Literal) return value; |
| 199 error('Interpolated value #$position is not a Literal: $value'); | 221 error('Interpolated value #$nameOrPosition is not a Literal: $value'); |
| 200 }; | 222 }; |
| 201 } | 223 } |
| 202 | 224 |
| 203 Instantiator visitInterpolatedParameter(InterpolatedParameter node) { | 225 Instantiator visitInterpolatedParameter(InterpolatedParameter node) { |
| 204 int position = node.name; | 226 var nameOrPosition = node.nameOrPosition; |
| 205 return (arguments) { | 227 return (arguments) { |
| 206 var value = arguments[position]; | 228 var value = arguments[nameOrPosition]; |
| 207 | 229 |
| 208 Parameter toParameter(item) { | 230 Parameter toParameter(item) { |
| 209 if (item is Parameter) return item; | 231 if (item is Parameter) return item; |
| 210 if (item is String) return new Parameter(item); | 232 if (item is String) return new Parameter(item); |
| 211 return error('Interpolated value #$position is not a Parameter or ' | 233 return error('Interpolated value #$nameOrPosition is not a Parameter or' |
| 212 'List of Parameters: $value'); | 234 ' List of Parameters: $value'); |
| 213 } | 235 } |
| 214 if (value is Iterable) return value.map(toParameter); | 236 if (value is Iterable) return value.map(toParameter); |
| 215 return toParameter(value); | 237 return toParameter(value); |
| 216 }; | 238 }; |
| 217 } | 239 } |
| 218 | 240 |
| 219 Instantiator visitInterpolatedSelector(InterpolatedSelector node) { | 241 Instantiator visitInterpolatedSelector(InterpolatedSelector node) { |
| 220 // A selector is an expression, as in `a[selector]`. | 242 // A selector is an expression, as in `a[selector]`. |
| 221 // A String argument converted into a LiteralString, so `a.#` with argument | 243 // A String argument converted into a LiteralString, so `a.#` with argument |
| 222 // 'foo' generates `a["foo"]` which prints as `a.foo`. | 244 // 'foo' generates `a["foo"]` which prints as `a.foo`. |
| 223 int position = node.name; | 245 var nameOrPosition = node.nameOrPosition; |
| 224 return (arguments) { | 246 return (arguments) { |
| 225 var value = arguments[position]; | 247 var value = arguments[nameOrPosition]; |
| 226 if (value is Expression) return value; | 248 if (value is Expression) return value; |
| 227 if (value is String) return new LiteralString('"$value"'); | 249 if (value is String) return new LiteralString('"$value"'); |
| 228 error('Interpolated value #$position is not a selector: $value'); | 250 error('Interpolated value #$nameOrPosition is not a selector: $value'); |
| 229 }; | 251 }; |
| 230 } | 252 } |
| 231 | 253 |
| 232 Instantiator visitInterpolatedStatement(InterpolatedStatement node) { | 254 Instantiator visitInterpolatedStatement(InterpolatedStatement node) { |
| 233 int position = node.name; | 255 var nameOrPosition = node.nameOrPosition; |
| 234 return (arguments) { | 256 return (arguments) { |
| 235 var value = arguments[position]; | 257 var value = arguments[nameOrPosition]; |
| 236 if (value is Node) return value.toStatement(); | 258 if (value is Node) return value.toStatement(); |
| 237 error('Interpolated value #$position is not a Statement: $value'); | 259 error('Interpolated value #$nameOrPosition is not a Statement: $value'); |
| 238 }; | 260 }; |
| 239 } | 261 } |
| 240 | 262 |
| 241 Instantiator visitSplayableStatement(Node node) { | 263 Instantiator visitSplayableStatement(Node node) { |
| 242 if (node is InterpolatedStatement) { | 264 if (node is InterpolatedStatement) { |
| 243 int position = node.name; | 265 var nameOrPosition = node.nameOrPosition; |
| 244 return (arguments) { | 266 return (arguments) { |
| 245 var value = arguments[position]; | 267 var value = arguments[nameOrPosition]; |
| 246 Statement toStatement(item) { | 268 Statement toStatement(item) { |
| 247 if (item is Statement) return item; | 269 if (item is Statement) return item; |
| 248 if (item is Expression) return item.toStatement();; | 270 if (item is Expression) return item.toStatement();; |
| 249 return error('Interpolated value #$position is not ' | 271 return error('Interpolated value #$nameOrPosition is not ' |
| 250 'a Statement or List of Statements: $value'); | 272 'a Statement or List of Statements: $value'); |
| 251 } | 273 } |
| 252 if (value is Iterable) return value.map(toStatement); | 274 if (value is Iterable) return value.map(toStatement); |
| 253 return toStatement(value); | 275 return toStatement(value); |
| 254 }; | 276 }; |
| 255 } | 277 } |
| 256 return visit(node); | 278 return visit(node); |
| 257 } | 279 } |
| 258 | 280 |
| 259 Instantiator visitProgram(Program node) { | 281 Instantiator visitProgram(Program node) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 310 if (node.condition is InterpolatedExpression) { | 332 if (node.condition is InterpolatedExpression) { |
| 311 return visitIfConditionalCompilation(node); | 333 return visitIfConditionalCompilation(node); |
| 312 } else { | 334 } else { |
| 313 return visitIfNormal(node); | 335 return visitIfNormal(node); |
| 314 } | 336 } |
| 315 } | 337 } |
| 316 | 338 |
| 317 Instantiator visitIfConditionalCompilation(If node) { | 339 Instantiator visitIfConditionalCompilation(If node) { |
| 318 // Special version of visitInterpolatedExpression that permits bools. | 340 // Special version of visitInterpolatedExpression that permits bools. |
| 319 compileCondition(InterpolatedExpression node) { | 341 compileCondition(InterpolatedExpression node) { |
| 320 int position = node.name; | 342 var nameOrPosition = node.nameOrPosition; |
| 321 return (arguments) { | 343 return (arguments) { |
| 322 var value = arguments[position]; | 344 var value = arguments[nameOrPosition]; |
| 323 if (value is bool) return value; | 345 if (value is bool) return value; |
| 324 if (value is Expression) return value; | 346 if (value is Expression) return value; |
| 325 if (value is String) return convertStringToVariableUse(value);; | 347 if (value is String) return convertStringToVariableUse(value);; |
| 326 error('Interpolated value #$position is not an Expression: $value'); | 348 error('Interpolated value #$nameOrPosition ' |
| 349 'is not an Expression: $value'); |
| 327 }; | 350 }; |
| 328 } | 351 } |
| 329 var makeCondition = compileCondition(node.condition); | 352 var makeCondition = compileCondition(node.condition); |
| 330 Instantiator makeThen = visit(node.then); | 353 Instantiator makeThen = visit(node.then); |
| 331 Instantiator makeOtherwise = visit(node.otherwise); | 354 Instantiator makeOtherwise = visit(node.otherwise); |
| 332 return (arguments) { | 355 return (arguments) { |
| 333 var condition = makeCondition(arguments); | 356 var condition = makeCondition(arguments); |
| 334 if (condition is bool) { | 357 if (condition is bool) { |
| 335 if (condition == true) { | 358 if (condition == true) { |
| 336 return makeThen(arguments); | 359 return makeThen(arguments); |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 640 Instantiator visitRegExpLiteral(RegExpLiteral node) => | 663 Instantiator visitRegExpLiteral(RegExpLiteral node) => |
| 641 (arguments) => new RegExpLiteral(node.pattern); | 664 (arguments) => new RegExpLiteral(node.pattern); |
| 642 | 665 |
| 643 Instantiator visitComment(Comment node) => TODO('visitComment'); | 666 Instantiator visitComment(Comment node) => TODO('visitComment'); |
| 644 } | 667 } |
| 645 | 668 |
| 646 /** | 669 /** |
| 647 * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST. | 670 * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST. |
| 648 */ | 671 */ |
| 649 class InterpolatedNodeAnalysis extends BaseVisitor { | 672 class InterpolatedNodeAnalysis extends BaseVisitor { |
| 650 final Set<Node> containsInterpolatedNode = new Set<Node>(); | 673 final Setlet<Node> containsInterpolatedNode = new Setlet<Node>(); |
| 651 final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[]; | 674 final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[]; |
| 675 final Setlet<String> holeNames = new Setlet<String>(); |
| 652 int count = 0; | 676 int count = 0; |
| 653 | 677 |
| 654 InterpolatedNodeAnalysis(); | 678 InterpolatedNodeAnalysis(); |
| 655 | 679 |
| 656 bool containsInterpolatedNodes(Node node) => | 680 bool containsInterpolatedNodes(Node node) => |
| 657 containsInterpolatedNode.contains(node); | 681 containsInterpolatedNode.contains(node); |
| 658 | 682 |
| 659 void visit(Node node) { | 683 void visit(Node node) { |
| 660 node.accept(this); | 684 node.accept(this); |
| 661 } | 685 } |
| 662 | 686 |
| 663 void visitNode(Node node) { | 687 void visitNode(Node node) { |
| 664 int before = count; | 688 int before = count; |
| 665 node.visitChildren(this); | 689 node.visitChildren(this); |
| 666 if (count != before) containsInterpolatedNode.add(node); | 690 if (count != before) containsInterpolatedNode.add(node); |
| 667 return null; | 691 return null; |
| 668 } | 692 } |
| 669 | 693 |
| 670 visitInterpolatedNode(InterpolatedNode node) { | 694 visitInterpolatedNode(InterpolatedNode node) { |
| 671 interpolatedNodes.add(node); | 695 interpolatedNodes.add(node); |
| 672 containsInterpolatedNode.add(node); | 696 containsInterpolatedNode.add(node); |
| 697 if (node.isNamed) holeNames.add(node.nameOrPosition); |
| 673 ++count; | 698 ++count; |
| 674 } | 699 } |
| 675 } | 700 } |
| OLD | NEW |