OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 interface Visitor<R> { | |
6 R visitBlock(Block node); | |
7 R visitBreakStatement(BreakStatement node); | |
8 R visitCatchBlock(CatchBlock node); | |
9 R visitClassNode(ClassNode node); | |
10 R visitConditional(Conditional node); | |
11 R visitContinueStatement(ContinueStatement node); | |
12 R visitDoWhile(DoWhile node); | |
13 R visitEmptyStatement(EmptyStatement node); | |
14 R visitExpressionStatement(ExpressionStatement node); | |
15 R visitFor(For node); | |
16 R visitForInStatement(ForInStatement node); | |
17 R visitFunctionDeclaration(FunctionDeclaration node); | |
18 R visitFunctionExpression(FunctionExpression node); | |
19 R visitIdentifier(Identifier node); | |
20 R visitIf(If node); | |
21 R visitLabeledStatement(LabeledStatement node); | |
22 R visitLiteralBool(LiteralBool node); | |
23 R visitLiteralDouble(LiteralDouble node); | |
24 R visitLiteralInt(LiteralInt node); | |
25 R visitLiteralList(LiteralList node); | |
26 R visitLiteralMap(LiteralMap node); | |
27 R visitLiteralMapEntry(LiteralMapEntry node); | |
28 R visitLiteralNull(LiteralNull node); | |
29 R visitLiteralString(LiteralString node); | |
30 R visitStringJuxtaposition(StringJuxtaposition node); | |
31 R visitModifiers(Modifiers node); | |
32 R visitNamedArgument(NamedArgument node); | |
33 R visitNewExpression(NewExpression node); | |
34 R visitNodeList(NodeList node); | |
35 R visitOperator(Operator node); | |
36 R visitParenthesizedExpression(ParenthesizedExpression node); | |
37 R visitReturn(Return node); | |
38 R visitScriptTag(ScriptTag node); | |
39 R visitSend(Send node); | |
40 R visitSendSet(SendSet node); | |
41 R visitStringInterpolation(StringInterpolation node); | |
42 R visitStringInterpolationPart(StringInterpolationPart node); | |
43 R visitSwitchCase(SwitchCase node); | |
44 R visitSwitchStatement(SwitchStatement node); | |
45 R visitThrow(Throw node); | |
46 R visitTryStatement(TryStatement node); | |
47 R visitTypeAnnotation(TypeAnnotation node); | |
48 R visitTypedef(Typedef node); | |
49 R visitTypeVariable(TypeVariable node); | |
50 R visitVariableDefinitions(VariableDefinitions node); | |
51 R visitWhile(While node); | |
52 } | |
53 | |
54 Token firstBeginToken(Node first, Node second) { | |
55 if (first !== null) return first.getBeginToken(); | |
56 if (second !== null) return second.getBeginToken(); | |
57 return null; | |
58 } | |
59 | |
60 class NodeAssertionFailure implements Exception { | |
61 final Node node; | |
62 final String message; | |
63 NodeAssertionFailure(this.node, this.message); | |
64 } | |
65 | |
66 /** | |
67 * A node in a syntax tree. | |
68 * | |
69 * The abstract part of "abstract syntax tree" is invalidated when | |
70 * supporting tools such as code formatting. These tools need concrete | |
71 * syntax such as parentheses and no constant folding. | |
72 * | |
73 * We support these tools by storing additional references back to the | |
74 * token stream. These references are stored in fields ending with | |
75 * "Token". | |
76 */ | |
77 class Node implements Hashable { | |
78 final int _hashCode; | |
79 static int _HASH_COUNTER = 0; | |
80 | |
81 Node() : _hashCode = ++_HASH_COUNTER; | |
82 | |
83 hashCode() => _hashCode; | |
84 | |
85 abstract accept(Visitor visitor); | |
86 | |
87 abstract visitChildren(Visitor visitor); | |
88 | |
89 toString() => unparse(false); | |
90 | |
91 toDebugString() => unparse(true); | |
92 | |
93 String getObjectDescription() => super.toString(); | |
94 | |
95 String unparse(bool printDebugInfo) { | |
96 Unparser unparser = new Unparser(printDebugInfo); | |
97 try { | |
98 return unparser.unparse(this); | |
99 } catch (var e, var trace) { | |
100 print(trace); | |
101 return '<<unparse error: ${getObjectDescription()}: ${unparser.sb}>>'; | |
102 } | |
103 } | |
104 | |
105 abstract Token getBeginToken(); | |
106 | |
107 abstract Token getEndToken(); | |
108 | |
109 Block asBlock() => null; | |
110 BreakStatement asBreakStatement() => null; | |
111 CatchBlock asCatchBlock() => null; | |
112 ClassNode asClassNode() => null; | |
113 Conditional asConditional() => null; | |
114 ContinueStatement asContinueStatement() => null; | |
115 DoWhile asDoWhile() => null; | |
116 EmptyStatement asEmptyStatement() => null; | |
117 Expression asExpression() => null; | |
118 ExpressionStatement asExpressionStatement() => null; | |
119 For asFor() => null; | |
120 ForInStatement asForInStatement() => null; | |
121 FunctionDeclaration asFunctionDeclaration() => null; | |
122 FunctionExpression asFunctionExpression() => null; | |
123 Identifier asIdentifier() => null; | |
124 If asIf() => null; | |
125 LabeledStatement asLabeledStatement() => null; | |
126 LiteralBool asLiteralBool() => null; | |
127 LiteralDouble asLiteralDouble() => null; | |
128 LiteralInt asLiteralInt() => null; | |
129 LiteralList asLiteralList() => null; | |
130 LiteralMap asLiteralMap() => null; | |
131 LiteralMapEntry asLiteralMapEntry() => null; | |
132 LiteralNull asLiteralNull() => null; | |
133 LiteralString asLiteralString() => null; | |
134 Modifiers asModifiers() => null; | |
135 NamedArgument asNamedArgument() => null; | |
136 NodeList asNodeList() => null; | |
137 Operator asOperator() => null; | |
138 ParenthesizedExpression asParenthesizedExpression() => null; | |
139 Return asReturn() => null; | |
140 ScriptTag asScriptTag() => null; | |
141 Send asSend() => null; | |
142 SendSet asSendSet() => null; | |
143 Statement asStatement() => null; | |
144 StringInterpolation asStringInterpolation() => null; | |
145 StringInterpolationPart asStringInterpolationPart() => null; | |
146 StringJuxtaposition asStringJuxtaposition() => null; | |
147 SwitchCase asSwitchCase() => null; | |
148 SwitchStatement asSwitchStatement() => null; | |
149 Throw asThrow() => null; | |
150 TryStatement asTryStatement() => null; | |
151 TypeAnnotation asTypeAnnotation() => null; | |
152 Typedef asTypedef() => null; | |
153 TypeVariable asTypeVariable() => null; | |
154 VariableDefinitions asVariableDefinitions() => null; | |
155 While asWhile() => null; | |
156 | |
157 bool isValidBreakTarget() => false; | |
158 bool isValidContinueTarget() => false; | |
159 } | |
160 | |
161 class ClassNode extends Node { | |
162 final Identifier name; | |
163 final TypeAnnotation superclass; | |
164 final NodeList interfaces; | |
165 final NodeList typeParameters; | |
166 | |
167 // TODO(ahe, karlklose): the default keyword is not recorded. | |
168 final TypeAnnotation defaultClause; | |
169 | |
170 final Token beginToken; | |
171 final Token extendsKeyword; | |
172 final Token endToken; | |
173 | |
174 ClassNode(this.name, this.typeParameters, this.superclass, this.interfaces, | |
175 this.defaultClause, this.beginToken, this.extendsKeyword, | |
176 this.endToken); | |
177 | |
178 ClassNode asClassNode() => this; | |
179 | |
180 accept(Visitor visitor) => visitor.visitClassNode(this); | |
181 | |
182 visitChildren(Visitor visitor) { | |
183 if (name !== null) name.accept(visitor); | |
184 if (superclass !== null) superclass.accept(visitor); | |
185 if (interfaces !== null) interfaces.accept(visitor); | |
186 } | |
187 | |
188 bool get isInterface() => beginToken.stringValue === 'interface'; | |
189 | |
190 bool get isClass() => !isInterface; | |
191 | |
192 Token getBeginToken() => beginToken; | |
193 | |
194 Token getEndToken() => endToken; | |
195 } | |
196 | |
197 class Expression extends Node { | |
198 Expression(); | |
199 | |
200 Expression asExpression() => this; | |
201 | |
202 // TODO(ahe): make class abstract instead of adding an abstract method. | |
203 abstract accept(Visitor visitor); | |
204 } | |
205 | |
206 class Statement extends Node { | |
207 Statement(); | |
208 | |
209 Statement asStatement() => this; | |
210 | |
211 // TODO(ahe): make class abstract instead of adding an abstract method. | |
212 abstract accept(Visitor visitor); | |
213 | |
214 bool isValidBreakTarget() => true; | |
215 } | |
216 | |
217 /** | |
218 * A message send aka method invocation. In Dart, most operations can | |
219 * (and should) be considered as message sends. Getters and setters | |
220 * are just methods with a special syntax. Consequently, we model | |
221 * property access, assignment, operators, and method calls with this | |
222 * one node. | |
223 */ | |
224 class Send extends Expression { | |
225 final Node receiver; | |
226 final Node selector; | |
227 final NodeList argumentsNode; | |
228 Link<Node> get arguments() => argumentsNode.nodes; | |
229 | |
230 Send([this.receiver, this.selector, this.argumentsNode]); | |
231 Send.postfix(this.receiver, this.selector, [Node argument = null]) | |
232 : argumentsNode = (argument === null) | |
233 ? new Postfix() | |
234 : new Postfix.singleton(argument); | |
235 Send.prefix(this.receiver, this.selector, [Node argument = null]) | |
236 : argumentsNode = (argument === null) | |
237 ? new Prefix() | |
238 : new Prefix.singleton(argument); | |
239 | |
240 Send asSend() => this; | |
241 | |
242 accept(Visitor visitor) => visitor.visitSend(this); | |
243 | |
244 visitChildren(Visitor visitor) { | |
245 if (receiver !== null) receiver.accept(visitor); | |
246 if (selector !== null) selector.accept(visitor); | |
247 if (argumentsNode !== null) argumentsNode.accept(visitor); | |
248 } | |
249 | |
250 int argumentCount() => argumentsNode.length(); | |
251 | |
252 bool get isSuperCall() { | |
253 return receiver !== null && | |
254 receiver.asIdentifier() !== null && | |
255 receiver.asIdentifier().isSuper(); | |
256 } | |
257 bool get isOperator() => selector is Operator; | |
258 bool get isPropertyAccess() => argumentsNode === null; | |
259 bool get isFunctionObjectInvocation() => selector === null; | |
260 bool get isPrefix() => argumentsNode is Prefix; | |
261 bool get isPostfix() => argumentsNode is Postfix; | |
262 bool get isIndex() => | |
263 isOperator && selector.asOperator().source.stringValue === '[]'; | |
264 | |
265 Token getBeginToken() { | |
266 if (isPrefix && !isIndex) return selector.getBeginToken(); | |
267 return firstBeginToken(receiver, selector); | |
268 } | |
269 | |
270 Token getEndToken() { | |
271 if (isPrefix) { | |
272 if (receiver !== null) return receiver.getEndToken(); | |
273 if (selector !== null) return selector.getEndToken(); | |
274 return null; | |
275 } | |
276 if (!isPostfix && argumentsNode !== null) { | |
277 return argumentsNode.getEndToken(); | |
278 } | |
279 if (selector !== null) return selector.getEndToken(); | |
280 return receiver.getBeginToken(); | |
281 } | |
282 | |
283 Send copyWithReceiver(Node newReceiver) { | |
284 assert(receiver === null); | |
285 return new Send(newReceiver, selector, argumentsNode); | |
286 } | |
287 } | |
288 | |
289 class Postfix extends NodeList { | |
290 Postfix() : super(nodes: const EmptyLink<Node>()); | |
291 Postfix.singleton(Node argument) : super.singleton(argument); | |
292 } | |
293 | |
294 class Prefix extends NodeList { | |
295 Prefix() : super(nodes: const EmptyLink<Node>()); | |
296 Prefix.singleton(Node argument) : super.singleton(argument); | |
297 } | |
298 | |
299 class SendSet extends Send { | |
300 final Operator assignmentOperator; | |
301 SendSet(receiver, selector, this.assignmentOperator, argumentsNode) | |
302 : super(receiver, selector, argumentsNode); | |
303 SendSet.postfix(receiver, | |
304 selector, | |
305 this.assignmentOperator, | |
306 [Node argument = null]) | |
307 : super.postfix(receiver, selector, argument); | |
308 SendSet.prefix(receiver, | |
309 selector, | |
310 this.assignmentOperator, | |
311 [Node argument = null]) | |
312 : super.prefix(receiver, selector, argument); | |
313 | |
314 SendSet asSendSet() => this; | |
315 | |
316 accept(Visitor visitor) => visitor.visitSendSet(this); | |
317 | |
318 visitChildren(Visitor visitor) { | |
319 super.visitChildren(visitor); | |
320 if (assignmentOperator !== null) assignmentOperator.accept(visitor); | |
321 } | |
322 | |
323 Send copyWithReceiver(Node newReceiver) { | |
324 assert(receiver === null); | |
325 return new SendSet(newReceiver, selector, assignmentOperator, | |
326 argumentsNode); | |
327 } | |
328 } | |
329 | |
330 class NewExpression extends Expression { | |
331 /** The token NEW or CONST */ | |
332 final Token newToken; | |
333 | |
334 // Note: we expect that send.receiver is null. | |
335 final Send send; | |
336 | |
337 NewExpression([this.newToken, this.send]); | |
338 | |
339 accept(Visitor visitor) => visitor.visitNewExpression(this); | |
340 | |
341 visitChildren(Visitor visitor) { | |
342 if (send !== null) send.accept(visitor); | |
343 } | |
344 | |
345 bool isConst() => newToken.stringValue === 'const'; | |
346 | |
347 Token getBeginToken() => newToken; | |
348 | |
349 Token getEndToken() => send.getEndToken(); | |
350 } | |
351 | |
352 class NodeList extends Node implements Iterable<Node> { | |
353 final Link<Node> nodes; | |
354 final Token beginToken; | |
355 final Token endToken; | |
356 final SourceString delimiter; | |
357 bool isEmpty() => nodes.isEmpty(); | |
358 | |
359 NodeList([this.beginToken, this.nodes, this.endToken, this.delimiter]); | |
360 | |
361 Iterator<Node> iterator() => nodes.iterator(); | |
362 | |
363 NodeList.singleton(Node node) : this(null, new Link<Node>(node)); | |
364 NodeList.empty() : this(null, const EmptyLink<Node>()); | |
365 | |
366 NodeList asNodeList() => this; | |
367 | |
368 int length() { | |
369 int result = 0; | |
370 for (Link<Node> cursor = nodes; !cursor.isEmpty(); cursor = cursor.tail) { | |
371 result++; | |
372 } | |
373 return result; | |
374 } | |
375 | |
376 accept(Visitor visitor) => visitor.visitNodeList(this); | |
377 | |
378 visitChildren(Visitor visitor) { | |
379 if (nodes === null) return; | |
380 for (Link<Node> link = nodes; !link.isEmpty(); link = link.tail) { | |
381 if (link.head !== null) link.head.accept(visitor); | |
382 } | |
383 } | |
384 | |
385 Token getBeginToken() { | |
386 if (beginToken !== null) return beginToken; | |
387 if (nodes !== null) { | |
388 for (Link<Node> link = nodes; !link.isEmpty(); link = link.tail) { | |
389 if (link.head.getBeginToken() !== null) { | |
390 return link.head.getBeginToken(); | |
391 } | |
392 if (link.head.getEndToken() !== null) { | |
393 return link.head.getEndToken(); | |
394 } | |
395 } | |
396 } | |
397 return endToken; | |
398 } | |
399 | |
400 Token getEndToken() { | |
401 if (endToken !== null) return endToken; | |
402 if (nodes !== null) { | |
403 Link<Node> link = nodes; | |
404 if (link.isEmpty()) return beginToken; | |
405 while (!link.tail.isEmpty()) link = link.tail; | |
406 if (link.head.getEndToken() !== null) return link.head.getEndToken(); | |
407 if (link.head.getBeginToken() !== null) return link.head.getBeginToken(); | |
408 } | |
409 return beginToken; | |
410 } | |
411 } | |
412 | |
413 class Block extends Statement { | |
414 final NodeList statements; | |
415 | |
416 Block(this.statements); | |
417 | |
418 Block asBlock() => this; | |
419 | |
420 accept(Visitor visitor) => visitor.visitBlock(this); | |
421 | |
422 visitChildren(Visitor visitor) { | |
423 if (statements !== null) statements.accept(visitor); | |
424 } | |
425 | |
426 Token getBeginToken() => statements.getBeginToken(); | |
427 | |
428 Token getEndToken() => statements.getEndToken(); | |
429 } | |
430 | |
431 class If extends Statement { | |
432 final ParenthesizedExpression condition; | |
433 final Statement thenPart; | |
434 final Statement elsePart; | |
435 | |
436 final Token ifToken; | |
437 final Token elseToken; | |
438 | |
439 If(this.condition, this.thenPart, this.elsePart, | |
440 this.ifToken, this.elseToken); | |
441 | |
442 If asIf() => this; | |
443 | |
444 bool get hasElsePart() => elsePart !== null; | |
445 | |
446 void validate() { | |
447 // TODO(ahe): Check that condition has size one. | |
448 } | |
449 | |
450 accept(Visitor visitor) => visitor.visitIf(this); | |
451 | |
452 visitChildren(Visitor visitor) { | |
453 if (condition !== null) condition.accept(visitor); | |
454 if (thenPart !== null) thenPart.accept(visitor); | |
455 if (elsePart !== null) elsePart.accept(visitor); | |
456 } | |
457 | |
458 Token getBeginToken() => ifToken; | |
459 | |
460 Token getEndToken() { | |
461 if (elsePart === null) return thenPart.getEndToken(); | |
462 return elsePart.getEndToken(); | |
463 } | |
464 } | |
465 | |
466 class Conditional extends Expression { | |
467 final Expression condition; | |
468 final Expression thenExpression; | |
469 final Expression elseExpression; | |
470 | |
471 final Token questionToken; | |
472 final Token colonToken; | |
473 | |
474 Conditional(this.condition, this.thenExpression, | |
475 this.elseExpression, this.questionToken, this.colonToken); | |
476 | |
477 Conditional asConditional() => this; | |
478 | |
479 accept(Visitor visitor) => visitor.visitConditional(this); | |
480 | |
481 visitChildren(Visitor visitor) { | |
482 condition.accept(visitor); | |
483 thenExpression.accept(visitor); | |
484 elseExpression.accept(visitor); | |
485 } | |
486 | |
487 Token getBeginToken() => condition.getBeginToken(); | |
488 | |
489 Token getEndToken() => elseExpression.getEndToken(); | |
490 } | |
491 | |
492 class For extends Loop { | |
493 /** Either a variable declaration or an expression. */ | |
494 final Node initializer; | |
495 /** Either an expression statement or an empty statement. */ | |
496 final Statement conditionStatement; | |
497 final NodeList update; | |
498 | |
499 final Token forToken; | |
500 | |
501 For(this.initializer, this.conditionStatement, this.update, body, | |
502 this.forToken) : super(body); | |
503 | |
504 For asFor() => this; | |
505 | |
506 Expression get condition() { | |
507 if (conditionStatement is ExpressionStatement) { | |
508 return conditionStatement.asExpressionStatement().expression; | |
509 } else { | |
510 return null; | |
511 } | |
512 } | |
513 | |
514 accept(Visitor visitor) => visitor.visitFor(this); | |
515 | |
516 visitChildren(Visitor visitor) { | |
517 if (initializer !== null) initializer.accept(visitor); | |
518 if (conditionStatement !== null) conditionStatement.accept(visitor); | |
519 if (update !== null) update.accept(visitor); | |
520 if (body !== null) body.accept(visitor); | |
521 } | |
522 | |
523 Token getBeginToken() => forToken; | |
524 | |
525 Token getEndToken() { | |
526 return body.getEndToken(); | |
527 } | |
528 } | |
529 | |
530 class FunctionDeclaration extends Statement { | |
531 final FunctionExpression function; | |
532 | |
533 FunctionDeclaration(this.function); | |
534 | |
535 FunctionDeclaration asFunctionDeclaration() => this; | |
536 | |
537 accept(Visitor visitor) => visitor.visitFunctionDeclaration(this); | |
538 | |
539 visitChildren(Visitor visitor) => function.accept(visitor); | |
540 | |
541 Token getBeginToken() => function.getBeginToken(); | |
542 Token getEndToken() => function.getEndToken(); | |
543 } | |
544 | |
545 class FunctionExpression extends Expression { | |
546 final Node name; | |
547 | |
548 /** | |
549 * List of VariableDefinitions or NodeList. | |
550 * | |
551 * A NodeList can only occur at the end and holds named parameters. | |
552 */ | |
553 final NodeList parameters; | |
554 | |
555 final Statement body; | |
556 final TypeAnnotation returnType; | |
557 final Modifiers modifiers; | |
558 final NodeList initializers; | |
559 | |
560 final Token getOrSet; | |
561 | |
562 FunctionExpression(this.name, this.parameters, this.body, this.returnType, | |
563 this.modifiers, this.initializers, this.getOrSet); | |
564 | |
565 FunctionExpression asFunctionExpression() => this; | |
566 | |
567 accept(Visitor visitor) => visitor.visitFunctionExpression(this); | |
568 | |
569 visitChildren(Visitor visitor) { | |
570 if (modifiers !== null) modifiers.accept(visitor); | |
571 if (returnType !== null) returnType.accept(visitor); | |
572 if (name !== null) name.accept(visitor); | |
573 if (parameters !== null) parameters.accept(visitor); | |
574 if (initializers !== null) initializers.accept(visitor); | |
575 if (body !== null) body.accept(visitor); | |
576 } | |
577 | |
578 bool hasBody() { | |
579 // TODO(karlklose,ahe): refactor AST nodes (issue 1713). | |
580 if (body.asReturn() !== null) return true; | |
581 NodeList statements = body.asBlock().statements; | |
582 return (!statements.nodes.isEmpty() || | |
583 statements.getBeginToken().kind !== $SEMICOLON); | |
584 } | |
585 | |
586 Token getBeginToken() { | |
587 Token token = firstBeginToken(modifiers, returnType); | |
588 if (token !== null) return token; | |
589 if (getOrSet !== null) return getOrSet; | |
590 return firstBeginToken(name, parameters); | |
591 } | |
592 | |
593 Token getEndToken() { | |
594 Token token = (body === null) ? null : body.getEndToken(); | |
595 token = (token === null) ? parameters.getEndToken() : token; | |
596 return (token === null) ? name.getEndToken() : token; | |
597 } | |
598 } | |
599 | |
600 typedef void DecodeErrorHandler(Token token, var error); | |
601 | |
602 class Literal<T> extends Expression { | |
603 final Token token; | |
604 final DecodeErrorHandler handler; | |
605 | |
606 Literal(Token this.token, DecodeErrorHandler this.handler); | |
607 | |
608 abstract T get value(); | |
609 | |
610 visitChildren(Visitor visitor) {} | |
611 | |
612 Token getBeginToken() => token; | |
613 | |
614 Token getEndToken() => token; | |
615 } | |
616 | |
617 class LiteralInt extends Literal<int> { | |
618 LiteralInt(Token token, DecodeErrorHandler handler) : super(token, handler); | |
619 | |
620 LiteralInt asLiteralInt() => this; | |
621 | |
622 int get value() { | |
623 try { | |
624 Token token = this.token; | |
625 if (token.kind === PLUS_TOKEN) token = token.next; | |
626 return Math.parseInt(token.value.slowToString()); | |
627 } catch (BadNumberFormatException ex) { | |
628 (this.handler)(token, ex); | |
629 } | |
630 } | |
631 | |
632 accept(Visitor visitor) => visitor.visitLiteralInt(this); | |
633 } | |
634 | |
635 class LiteralDouble extends Literal<double> { | |
636 LiteralDouble(Token token, DecodeErrorHandler handler) | |
637 : super(token, handler); | |
638 | |
639 LiteralDouble asLiteralDouble() => this; | |
640 | |
641 double get value() { | |
642 try { | |
643 Token token = this.token; | |
644 if (token.kind === PLUS_TOKEN) token = token.next; | |
645 return Math.parseDouble(token.value.slowToString()); | |
646 } catch (BadNumberFormatException ex) { | |
647 (this.handler)(token, ex); | |
648 } | |
649 } | |
650 | |
651 accept(Visitor visitor) => visitor.visitLiteralDouble(this); | |
652 } | |
653 | |
654 class LiteralBool extends Literal<bool> { | |
655 LiteralBool(Token token, DecodeErrorHandler handler) : super(token, handler); | |
656 | |
657 LiteralBool asLiteralBool() => this; | |
658 | |
659 bool get value() { | |
660 switch (token.value) { | |
661 case Keyword.TRUE: return true; | |
662 case Keyword.FALSE: return false; | |
663 default: | |
664 (this.handler)(token, "not a bool ${token.value}"); | |
665 } | |
666 } | |
667 | |
668 accept(Visitor visitor) => visitor.visitLiteralBool(this); | |
669 } | |
670 | |
671 | |
672 class StringQuoting { | |
673 static final StringQuoting SINGLELINE_DQ = | |
674 const StringQuoting($DQ, raw: false, leftQuoteLength: 1); | |
675 static final StringQuoting RAW_SINGLELINE_DQ = | |
676 const StringQuoting($DQ, raw: true, leftQuoteLength: 1); | |
677 static final StringQuoting MULTILINE_DQ = | |
678 const StringQuoting($DQ, raw: false, leftQuoteLength: 3); | |
679 static final StringQuoting RAW_MULTILINE_DQ = | |
680 const StringQuoting($DQ, raw: true, leftQuoteLength: 3); | |
681 static final StringQuoting MULTILINE_NL_DQ = | |
682 const StringQuoting($DQ, raw: false, leftQuoteLength: 4); | |
683 static final StringQuoting RAW_MULTILINE_NL_DQ = | |
684 const StringQuoting($DQ, raw: true, leftQuoteLength: 4); | |
685 static final StringQuoting MULTILINE_NL2_DQ = | |
686 const StringQuoting($DQ, raw: false, leftQuoteLength: 5); | |
687 static final StringQuoting RAW_MULTILINE_NL2_DQ = | |
688 const StringQuoting($DQ, raw: true, leftQuoteLength: 5); | |
689 static final StringQuoting SINGLELINE_SQ = | |
690 const StringQuoting($SQ, raw: false, leftQuoteLength: 1); | |
691 static final StringQuoting RAW_SINGLELINE_SQ = | |
692 const StringQuoting($SQ, raw: true, leftQuoteLength: 1); | |
693 static final StringQuoting MULTILINE_SQ = | |
694 const StringQuoting($SQ, raw: false, leftQuoteLength: 3); | |
695 static final StringQuoting RAW_MULTILINE_SQ = | |
696 const StringQuoting($SQ, raw: true, leftQuoteLength: 3); | |
697 static final StringQuoting MULTILINE_NL_SQ = | |
698 const StringQuoting($SQ, raw: false, leftQuoteLength: 4); | |
699 static final StringQuoting RAW_MULTILINE_NL_SQ = | |
700 const StringQuoting($SQ, raw: true, leftQuoteLength: 4); | |
701 static final StringQuoting MULTILINE_NL2_SQ = | |
702 const StringQuoting($SQ, raw: false, leftQuoteLength: 5); | |
703 static final StringQuoting RAW_MULTILINE_NL2_SQ = | |
704 const StringQuoting($SQ, raw: true, leftQuoteLength: 5); | |
705 | |
706 | |
707 static final List<StringQuoting> mapping = const <StringQuoting>[ | |
708 SINGLELINE_DQ, | |
709 RAW_SINGLELINE_DQ, | |
710 MULTILINE_DQ, | |
711 RAW_MULTILINE_DQ, | |
712 MULTILINE_NL_DQ, | |
713 RAW_MULTILINE_NL_DQ, | |
714 MULTILINE_NL2_DQ, | |
715 RAW_MULTILINE_NL2_DQ, | |
716 SINGLELINE_SQ, | |
717 RAW_SINGLELINE_SQ, | |
718 MULTILINE_SQ, | |
719 RAW_MULTILINE_SQ, | |
720 MULTILINE_NL_SQ, | |
721 RAW_MULTILINE_NL_SQ, | |
722 MULTILINE_NL2_SQ, | |
723 RAW_MULTILINE_NL2_SQ | |
724 ]; | |
725 final bool raw; | |
726 final int leftQuoteCharCount; | |
727 final int quote; | |
728 const StringQuoting(this.quote, [bool raw, int leftQuoteLength]) | |
729 : this.raw = raw, this.leftQuoteCharCount = leftQuoteLength; | |
730 String get quoteChar() => quote === $DQ ? '"' : "'"; | |
731 | |
732 int get leftQuoteLength() => (raw ? 1 : 0) + leftQuoteCharCount; | |
733 int get rightQuoteLength() => (leftQuoteCharCount > 2) ? 3 : 1; | |
734 static StringQuoting getQuoting(int quote, bool raw, int quoteLength) { | |
735 int index = quoteLength - 1; | |
736 if (quoteLength > 2) index -= 1; | |
737 return mapping[(raw ? 1 : 0) + index * 2 + (quote === $SQ ? 8 : 0)]; | |
738 } | |
739 } | |
740 | |
741 /** | |
742 * Superclass for classes representing string literals. | |
743 */ | |
744 class StringNode extends Expression { | |
745 abstract DartString get dartString(); | |
746 abstract bool get isInterpolation(); | |
747 } | |
748 | |
749 class LiteralString extends StringNode { | |
750 final Token token; | |
751 /** Non-null on validated string literals. */ | |
752 final DartString dartString; | |
753 | |
754 LiteralString(this.token, this.dartString); | |
755 | |
756 LiteralString asLiteralString() => this; | |
757 | |
758 void visitChildren(Visitor visitor) {} | |
759 | |
760 bool get isInterpolation() => false; | |
761 bool isValidated() => dartString !== null; | |
762 | |
763 Token getBeginToken() => token; | |
764 Token getEndToken() => token; | |
765 | |
766 accept(Visitor visitor) => visitor.visitLiteralString(this); | |
767 } | |
768 | |
769 class LiteralNull extends Literal<SourceString> { | |
770 LiteralNull(Token token) : super(token, null); | |
771 | |
772 LiteralNull asLiteralNull() => this; | |
773 | |
774 SourceString get value() => null; | |
775 | |
776 accept(Visitor visitor) => visitor.visitLiteralNull(this); | |
777 } | |
778 | |
779 class LiteralList extends Expression { | |
780 final TypeAnnotation type; | |
781 final NodeList elements; | |
782 | |
783 final Token constKeyword; | |
784 | |
785 LiteralList(this.type, this.elements, this.constKeyword); | |
786 | |
787 bool isConst() => constKeyword !== null; | |
788 | |
789 LiteralList asLiteralList() => this; | |
790 accept(Visitor visitor) => visitor.visitLiteralList(this); | |
791 | |
792 visitChildren(Visitor visitor) { | |
793 if (type !== null) type.accept(visitor); | |
794 elements.accept(visitor); | |
795 } | |
796 | |
797 Token getBeginToken() { | |
798 if (constKeyword !== null) return constKeyword; | |
799 return firstBeginToken(type, elements); | |
800 } | |
801 | |
802 Token getEndToken() => elements.getEndToken(); | |
803 } | |
804 | |
805 class Identifier extends Expression { | |
806 final Token token; | |
807 | |
808 SourceString get source() => token.value; | |
809 | |
810 Identifier(Token this.token); | |
811 | |
812 bool isThis() => source.stringValue === 'this'; | |
813 | |
814 bool isSuper() => source.stringValue === 'super'; | |
815 | |
816 Identifier asIdentifier() => this; | |
817 | |
818 accept(Visitor visitor) => visitor.visitIdentifier(this); | |
819 | |
820 visitChildren(Visitor visitor) {} | |
821 | |
822 Token getBeginToken() => token; | |
823 | |
824 Token getEndToken() => token; | |
825 } | |
826 | |
827 class Operator extends Identifier { | |
828 Operator(Token token) : super(token); | |
829 | |
830 Operator asOperator() => this; | |
831 | |
832 accept(Visitor visitor) => visitor.visitOperator(this); | |
833 } | |
834 | |
835 class Return extends Statement { | |
836 final Expression expression; | |
837 final Token beginToken; | |
838 final Token endToken; | |
839 | |
840 Return(this.beginToken, this.endToken, this.expression); | |
841 | |
842 Return asReturn() => this; | |
843 | |
844 bool get hasExpression() => expression !== null; | |
845 | |
846 accept(Visitor visitor) => visitor.visitReturn(this); | |
847 | |
848 visitChildren(Visitor visitor) { | |
849 if (expression !== null) expression.accept(visitor); | |
850 } | |
851 | |
852 Token getBeginToken() => beginToken; | |
853 | |
854 Token getEndToken() { | |
855 if (endToken === null) return expression.getEndToken(); | |
856 return endToken; | |
857 } | |
858 } | |
859 | |
860 class ExpressionStatement extends Statement { | |
861 final Expression expression; | |
862 final Token endToken; | |
863 | |
864 ExpressionStatement(this.expression, this.endToken); | |
865 | |
866 ExpressionStatement asExpressionStatement() => this; | |
867 | |
868 accept(Visitor visitor) => visitor.visitExpressionStatement(this); | |
869 | |
870 visitChildren(Visitor visitor) { | |
871 if (expression !== null) expression.accept(visitor); | |
872 } | |
873 | |
874 Token getBeginToken() => expression.getBeginToken(); | |
875 | |
876 Token getEndToken() => endToken; | |
877 } | |
878 | |
879 class Throw extends Statement { | |
880 final Expression expression; | |
881 | |
882 final Token throwToken; | |
883 final Token endToken; | |
884 | |
885 Throw(this.expression, this.throwToken, this.endToken); | |
886 | |
887 Throw asThrow() => this; | |
888 | |
889 accept(Visitor visitor) => visitor.visitThrow(this); | |
890 | |
891 visitChildren(Visitor visitor) { | |
892 if (expression !== null) expression.accept(visitor); | |
893 } | |
894 | |
895 Token getBeginToken() => throwToken; | |
896 | |
897 Token getEndToken() => endToken; | |
898 } | |
899 | |
900 class TypeAnnotation extends Node { | |
901 final Expression typeName; | |
902 final NodeList typeArguments; | |
903 | |
904 TypeAnnotation(Expression this.typeName, NodeList this.typeArguments); | |
905 | |
906 TypeAnnotation asTypeAnnotation() => this; | |
907 | |
908 accept(Visitor visitor) => visitor.visitTypeAnnotation(this); | |
909 | |
910 visitChildren(Visitor visitor) { | |
911 typeName.accept(visitor); | |
912 if (typeArguments !== null) typeArguments.accept(visitor); | |
913 } | |
914 | |
915 Token getBeginToken() => typeName.getBeginToken(); | |
916 | |
917 Token getEndToken() => typeName.getEndToken(); | |
918 } | |
919 | |
920 class TypeVariable extends Node { | |
921 final Identifier name; | |
922 final TypeAnnotation bound; | |
923 TypeVariable(Identifier this.name, TypeAnnotation this.bound); | |
924 | |
925 accept(Visitor visitor) => visitor.visitTypeVariable(this); | |
926 | |
927 visitChildren(Visitor visitor) { | |
928 name.accept(visitor); | |
929 if (bound !== null) { | |
930 bound.accept(visitor); | |
931 } | |
932 } | |
933 | |
934 TypeVariable asTypeVariable() => this; | |
935 | |
936 Token getBeginToken() => name.getBeginToken(); | |
937 | |
938 Token getEndToken() { | |
939 return (bound !== null) ? bound.getEndToken() : name.getEndToken(); | |
940 } | |
941 } | |
942 | |
943 class VariableDefinitions extends Statement { | |
944 final Token endToken; | |
945 final TypeAnnotation type; | |
946 final Modifiers modifiers; | |
947 final NodeList definitions; | |
948 VariableDefinitions(this.type, this.modifiers, this.definitions, | |
949 this.endToken); | |
950 | |
951 VariableDefinitions asVariableDefinitions() => this; | |
952 | |
953 accept(Visitor visitor) => visitor.visitVariableDefinitions(this); | |
954 | |
955 visitChildren(Visitor visitor) { | |
956 if (type !== null) type.accept(visitor); | |
957 if (definitions !== null) definitions.accept(visitor); | |
958 } | |
959 | |
960 Token getBeginToken() { | |
961 return firstBeginToken(type, definitions); | |
962 } | |
963 | |
964 Token getEndToken() => endToken; | |
965 } | |
966 | |
967 class Loop extends Statement { | |
968 abstract Expression get condition(); | |
969 final Statement body; | |
970 | |
971 Loop(this.body); | |
972 | |
973 bool isValidContinueTarget() => true; | |
974 } | |
975 | |
976 class DoWhile extends Loop { | |
977 final Token doKeyword; | |
978 final Token whileKeyword; | |
979 final Token endToken; | |
980 | |
981 final Expression condition; | |
982 | |
983 DoWhile(Statement body, Expression this.condition, | |
984 Token this.doKeyword, Token this.whileKeyword, Token this.endToken) | |
985 : super(body); | |
986 | |
987 DoWhile asDoWhile() => this; | |
988 | |
989 accept(Visitor visitor) => visitor.visitDoWhile(this); | |
990 | |
991 visitChildren(Visitor visitor) { | |
992 if (condition !== null) condition.accept(visitor); | |
993 if (body !== null) body.accept(visitor); | |
994 } | |
995 | |
996 Token getBeginToken() => doKeyword; | |
997 | |
998 Token getEndToken() => endToken; | |
999 } | |
1000 | |
1001 class While extends Loop { | |
1002 final Token whileKeyword; | |
1003 final Expression condition; | |
1004 | |
1005 While(Expression this.condition, Statement body, | |
1006 Token this.whileKeyword) : super(body); | |
1007 | |
1008 While asWhile() => this; | |
1009 | |
1010 accept(Visitor visitor) => visitor.visitWhile(this); | |
1011 | |
1012 visitChildren(Visitor visitor) { | |
1013 if (condition !== null) condition.accept(visitor); | |
1014 if (body !== null) body.accept(visitor); | |
1015 } | |
1016 | |
1017 Token getBeginToken() => whileKeyword; | |
1018 | |
1019 Token getEndToken() => body.getEndToken(); | |
1020 } | |
1021 | |
1022 class ParenthesizedExpression extends Expression { | |
1023 final Expression expression; | |
1024 final BeginGroupToken beginToken; | |
1025 | |
1026 ParenthesizedExpression(Expression this.expression, | |
1027 BeginGroupToken this.beginToken); | |
1028 | |
1029 ParenthesizedExpression asParenthesizedExpression() => this; | |
1030 | |
1031 accept(Visitor visitor) => visitor.visitParenthesizedExpression(this); | |
1032 | |
1033 visitChildren(Visitor visitor) { | |
1034 if (expression !== null) expression.accept(visitor); | |
1035 } | |
1036 | |
1037 Token getBeginToken() => beginToken; | |
1038 | |
1039 Token getEndToken() => beginToken.endGroup; | |
1040 } | |
1041 | |
1042 /** Representation of modifiers such as static, abstract, final, etc. */ | |
1043 class Modifiers extends Node { | |
1044 /* TODO(ahe): The following should be validated relating to modifiers: | |
1045 * 1. The nodes must come in a certain order. | |
1046 * 2. The keywords "var" and "final" may not be used at the same time. | |
1047 * 3. The type of an element must be null if isVar() is true. | |
1048 */ | |
1049 | |
1050 final NodeList nodes; | |
1051 /** Bit pattern to easy check what modifiers are present. */ | |
1052 final int flags; | |
1053 | |
1054 static final int FLAG_STATIC = 1; | |
1055 static final int FLAG_ABSTRACT = FLAG_STATIC << 1; | |
1056 static final int FLAG_FINAL = FLAG_ABSTRACT << 1; | |
1057 static final int FLAG_VAR = FLAG_FINAL << 1; | |
1058 static final int FLAG_CONST = FLAG_VAR << 1; | |
1059 static final int FLAG_FACTORY = FLAG_CONST << 1; | |
1060 | |
1061 Modifiers(NodeList nodes) | |
1062 : this.nodes = nodes, flags = computeFlags(nodes.nodes); | |
1063 | |
1064 Modifiers.empty() : this(new NodeList.empty()); | |
1065 | |
1066 static int computeFlags(Link<Node> nodes) { | |
1067 int flags = 0; | |
1068 for (; !nodes.isEmpty(); nodes = nodes.tail) { | |
1069 String value = nodes.head.asIdentifier().source.stringValue; | |
1070 if (value === 'static') flags += FLAG_STATIC; | |
1071 else if (value === 'abstract') flags += FLAG_ABSTRACT; | |
1072 else if (value === 'final') flags += FLAG_FINAL; | |
1073 else if (value === 'var') flags += FLAG_VAR; | |
1074 else if (value === 'const') flags += FLAG_CONST; | |
1075 else if (value === 'factory') flags += FLAG_FACTORY; | |
1076 else throw 'internal error: ${nodes.head}'; | |
1077 } | |
1078 return flags; | |
1079 } | |
1080 | |
1081 Modifiers asModifiers() => this; | |
1082 Token getBeginToken() => nodes.getBeginToken(); | |
1083 Token getEndToken() => nodes.getEndToken(); | |
1084 accept(Visitor visitor) => visitor.visitModifiers(this); | |
1085 visitChildren(Visitor visitor) => nodes.accept(visitor); | |
1086 | |
1087 bool isStatic() => (flags & FLAG_STATIC) != 0; | |
1088 bool isAbstract() => (flags & FLAG_ABSTRACT) != 0; | |
1089 bool isFinal() => (flags & FLAG_FINAL) != 0; | |
1090 bool isVar() => (flags & FLAG_VAR) != 0; | |
1091 bool isConst() => (flags & FLAG_CONST) != 0; | |
1092 bool isFactory() => (flags & FLAG_FACTORY) != 0; | |
1093 } | |
1094 | |
1095 class StringInterpolation extends StringNode { | |
1096 final LiteralString string; | |
1097 final NodeList parts; | |
1098 | |
1099 StringInterpolation(this.string, this.parts); | |
1100 | |
1101 StringInterpolation asStringInterpolation() => this; | |
1102 | |
1103 DartString get dartString() => null; | |
1104 bool get isInterpolation() => true; | |
1105 | |
1106 accept(Visitor visitor) => visitor.visitStringInterpolation(this); | |
1107 | |
1108 visitChildren(Visitor visitor) { | |
1109 string.accept(visitor); | |
1110 parts.accept(visitor); | |
1111 } | |
1112 | |
1113 Token getBeginToken() => string.getBeginToken(); | |
1114 Token getEndToken() => parts.getEndToken(); | |
1115 } | |
1116 | |
1117 class StringInterpolationPart extends Node { | |
1118 final Expression expression; | |
1119 final LiteralString string; | |
1120 | |
1121 StringInterpolationPart(this.expression, this.string); | |
1122 | |
1123 StringInterpolationPart asStringInterpolationPart() => this; | |
1124 | |
1125 accept(Visitor visitor) => visitor.visitStringInterpolationPart(this); | |
1126 | |
1127 visitChildren(Visitor visitor) { | |
1128 expression.accept(visitor); | |
1129 string.accept(visitor); | |
1130 } | |
1131 | |
1132 Token getBeginToken() => expression.getBeginToken(); | |
1133 | |
1134 Token getEndToken() => string.getEndToken(); | |
1135 } | |
1136 | |
1137 /** | |
1138 * A class representing juxtaposed string literals. | |
1139 * The string literals can be both plain literals and string interpolations. | |
1140 */ | |
1141 class StringJuxtaposition extends StringNode { | |
1142 final Expression first; | |
1143 final Expression second; | |
1144 | |
1145 /** | |
1146 * Caches the check for whether this juxtaposition contains a string | |
1147 * interpolation | |
1148 */ | |
1149 bool isInterpolationCache = null; | |
1150 | |
1151 /** | |
1152 * Caches a Dart string representation of the entire juxtaposition's | |
1153 * content. Only juxtapositions that don't (transitively) contains | |
1154 * interpolations have a static representation. | |
1155 */ | |
1156 DartString dartStringCache = null; | |
1157 | |
1158 StringJuxtaposition(this.first, this.second); | |
1159 | |
1160 StringJuxtaposition asStringJuxtaposition() => this; | |
1161 | |
1162 bool get isInterpolation() { | |
1163 if (isInterpolationCache === null) { | |
1164 isInterpolationCache = (first.accept(const IsInterpolationVisitor()) || | |
1165 second.accept(const IsInterpolationVisitor())); | |
1166 } | |
1167 return isInterpolationCache; | |
1168 } | |
1169 | |
1170 /** | |
1171 * Retrieve a single DartString that represents this entire juxtaposition | |
1172 * of string literals. | |
1173 * Should only be called if [isInterpolation] returns false. | |
1174 */ | |
1175 DartString get dartString() { | |
1176 if (isInterpolation) { | |
1177 throw new NodeAssertionFailure(this, | |
1178 "Getting dartString on interpolation;"); | |
1179 } | |
1180 if (dartStringCache === null) { | |
1181 DartString firstString = first.accept(const GetDartStringVisitor()); | |
1182 DartString secondString = second.accept(const GetDartStringVisitor()); | |
1183 if (firstString === null || secondString === null) { | |
1184 return null; | |
1185 } | |
1186 dartStringCache = new DartString.concat(firstString, secondString); | |
1187 } | |
1188 return dartStringCache; | |
1189 } | |
1190 | |
1191 accept(Visitor visitor) => visitor.visitStringJuxtaposition(this); | |
1192 | |
1193 void visitChildren(Visitor visitor) { | |
1194 first.accept(visitor); | |
1195 second.accept(visitor); | |
1196 } | |
1197 | |
1198 Token getBeginToken() => first.getBeginToken(); | |
1199 | |
1200 Token getEndToken() => second.getEndToken(); | |
1201 } | |
1202 | |
1203 class EmptyStatement extends Statement { | |
1204 final Token semicolonToken; | |
1205 | |
1206 EmptyStatement(this.semicolonToken); | |
1207 | |
1208 EmptyStatement asEmptyStatement() => this; | |
1209 | |
1210 accept(Visitor visitor) => visitor.visitEmptyStatement(this); | |
1211 | |
1212 visitChildren(Visitor visitor) {} | |
1213 | |
1214 Token getBeginToken() => semicolonToken; | |
1215 | |
1216 Token getEndToken() => semicolonToken; | |
1217 } | |
1218 | |
1219 class LiteralMap extends Expression { | |
1220 final NodeList typeArguments; | |
1221 final NodeList entries; | |
1222 | |
1223 LiteralMap(this.typeArguments, this.entries); | |
1224 | |
1225 bool isConst() => false; // TODO(ahe): Store constness. | |
1226 | |
1227 LiteralMap asLiteralMap() => this; | |
1228 | |
1229 accept(Visitor visitor) => visitor.visitLiteralMap(this); | |
1230 | |
1231 visitChildren(Visitor visitor) { | |
1232 if (typeArguments != null) typeArguments.accept(visitor); | |
1233 entries.accept(visitor); | |
1234 } | |
1235 | |
1236 Token getBeginToken() => firstBeginToken(typeArguments, entries); | |
1237 | |
1238 Token getEndToken() => entries.getEndToken(); | |
1239 } | |
1240 | |
1241 class LiteralMapEntry extends Node { | |
1242 final Expression key; | |
1243 final Expression value; | |
1244 | |
1245 final Token colonToken; | |
1246 | |
1247 LiteralMapEntry(this.key, this.colonToken, this.value); | |
1248 | |
1249 LiteralMapEntry asLiteralMapEntry() => this; | |
1250 | |
1251 accept(Visitor visitor) => visitor.visitLiteralMapEntry(this); | |
1252 | |
1253 visitChildren(Visitor visitor) { | |
1254 key.accept(visitor); | |
1255 value.accept(visitor); | |
1256 } | |
1257 | |
1258 Token getBeginToken() => key.getBeginToken(); | |
1259 | |
1260 Token getEndToken() => value.getEndToken(); | |
1261 } | |
1262 | |
1263 class NamedArgument extends Expression { | |
1264 final Identifier name; | |
1265 final Expression expression; | |
1266 | |
1267 final Token colonToken; | |
1268 | |
1269 NamedArgument(this.name, this.colonToken, this.expression); | |
1270 | |
1271 NamedArgument asNamedArgument() => this; | |
1272 | |
1273 accept(Visitor visitor) => visitor.visitNamedArgument(this); | |
1274 | |
1275 visitChildren(Visitor visitor) { | |
1276 name.accept(visitor); | |
1277 expression.accept(visitor); | |
1278 } | |
1279 | |
1280 Token getBeginToken() => name.getBeginToken(); | |
1281 | |
1282 Token getEndToken() => expression.getEndToken(); | |
1283 } | |
1284 | |
1285 class SwitchStatement extends Statement { | |
1286 final ParenthesizedExpression parenthesizedExpression; | |
1287 final NodeList cases; | |
1288 | |
1289 final Token switchKeyword; | |
1290 | |
1291 SwitchStatement(this.parenthesizedExpression, this.cases, | |
1292 this.switchKeyword); | |
1293 | |
1294 SwitchStatement asSwitchStatement() => this; | |
1295 | |
1296 Expression get expression() => parenthesizedExpression.expression; | |
1297 | |
1298 accept(Visitor visitor) => visitor.visitSwitchStatement(this); | |
1299 | |
1300 visitChildren(Visitor visitor) { | |
1301 parenthesizedExpression.accept(visitor); | |
1302 cases.accept(visitor); | |
1303 } | |
1304 | |
1305 Token getBeginToken() => switchKeyword; | |
1306 | |
1307 Token getEndToken() => cases.getEndToken(); | |
1308 } | |
1309 | |
1310 class SwitchCase extends Node { | |
1311 // Represents the grammar: | |
1312 // label? ('case' expression ':')* ('default' ':')? statement* | |
1313 // Each expression is collected in [expressions]. | |
1314 // The 'case' keywords can be obtained using [caseKeywords()]. | |
1315 // Any actual switch case must have at least one 'case' or 'default' | |
1316 // clause. | |
1317 final Identifier label; | |
1318 final NodeList expressions; | |
1319 final Token defaultKeyword; | |
1320 final NodeList statements; | |
1321 | |
1322 final Token startToken; | |
1323 | |
1324 SwitchCase(this.label, this.expressions, this.defaultKeyword, | |
1325 this.statements, this.startToken); | |
1326 | |
1327 SwitchCase asSwitchCase() => this; | |
1328 | |
1329 bool get isDefaultCase() => defaultKeyword !== null; | |
1330 | |
1331 accept(Visitor visitor) => visitor.visitSwitchCase(this); | |
1332 | |
1333 visitChildren(Visitor visitor) { | |
1334 if (label !== null) label.accept(visitor); | |
1335 expressions.accept(visitor); | |
1336 statements.accept(visitor); | |
1337 } | |
1338 | |
1339 Token getBeginToken() { | |
1340 return startToken; | |
1341 } | |
1342 | |
1343 Token getEndToken() { | |
1344 if (statements.nodes.isEmpty()) { | |
1345 // All cases must have at least one expression or be the default. | |
1346 if (defaultKeyword !== null) { | |
1347 // The colon after 'default'. | |
1348 return defaultKeyword.next; | |
1349 } | |
1350 // The colon after the expression. | |
1351 return expressions.getEndToken().next; | |
1352 } else { | |
1353 return statements.getEndToken(); | |
1354 } | |
1355 } | |
1356 | |
1357 Link<Token> caseKeywords() { | |
1358 Token token = startToken; | |
1359 if (label !== null) { | |
1360 // Skip past the label: <Identifier> ':'. | |
1361 token = token.next.next; | |
1362 } | |
1363 Link<Token> recursiveGetCases(Token token, Link<Expression> expressions) { | |
1364 if (token.stringValue === 'case') { | |
1365 Token colon = expressions.head.getEndToken().next; | |
1366 return new Link<Token>(token, | |
1367 recursiveGetCases(colon.next, expressions.tail)); | |
1368 } | |
1369 return const EmptyLink<Token>(); | |
1370 } | |
1371 return recursiveGetCases(token, expressions); | |
1372 } | |
1373 } | |
1374 | |
1375 class GotoStatement extends Statement { | |
1376 final Identifier target; | |
1377 final Token keywordToken; | |
1378 final Token semicolonToken; | |
1379 | |
1380 GotoStatement(this.target, this.keywordToken, this.semicolonToken); | |
1381 | |
1382 visitChildren(Visitor visitor) { | |
1383 if (target !== null) target.accept(visitor); | |
1384 } | |
1385 | |
1386 Token getBeginToken() => keywordToken; | |
1387 | |
1388 Token getEndToken() => semicolonToken; | |
1389 | |
1390 // TODO(ahe): make class abstract instead of adding an abstract method. | |
1391 abstract accept(Visitor visitor); | |
1392 } | |
1393 | |
1394 class BreakStatement extends GotoStatement { | |
1395 BreakStatement(Identifier target, Token keywordToken, Token semicolonToken) | |
1396 : super(target, keywordToken, semicolonToken); | |
1397 | |
1398 BreakStatement asBreakStatement() => this; | |
1399 | |
1400 accept(Visitor visitor) => visitor.visitBreakStatement(this); | |
1401 } | |
1402 | |
1403 class ContinueStatement extends GotoStatement { | |
1404 ContinueStatement(Identifier target, Token keywordToken, Token semicolonToken) | |
1405 : super(target, keywordToken, semicolonToken); | |
1406 | |
1407 ContinueStatement asContinueStatement() => this; | |
1408 | |
1409 accept(Visitor visitor) => visitor.visitContinueStatement(this); | |
1410 } | |
1411 | |
1412 class ForInStatement extends Loop { | |
1413 final Node declaredIdentifier; | |
1414 final Expression expression; | |
1415 | |
1416 final Token forToken; | |
1417 final Token inToken; | |
1418 | |
1419 ForInStatement(this.declaredIdentifier, this.expression, | |
1420 Statement body, this.forToken, this.inToken) : super(body); | |
1421 | |
1422 Expression get condition() => null; | |
1423 | |
1424 ForInStatement asForInStatement() => this; | |
1425 | |
1426 accept(Visitor visitor) => visitor.visitForInStatement(this); | |
1427 | |
1428 visitChildren(Visitor visitor) { | |
1429 declaredIdentifier.accept(visitor); | |
1430 expression.accept(visitor); | |
1431 body.accept(visitor); | |
1432 } | |
1433 | |
1434 Token getBeginToken() => forToken; | |
1435 | |
1436 Token getEndToken() => body.getEndToken(); | |
1437 } | |
1438 | |
1439 class LabeledStatement extends Statement { | |
1440 final Identifier label; | |
1441 final Token colonToken; | |
1442 final Statement statement; | |
1443 | |
1444 LabeledStatement(this.label, this.colonToken, this.statement); | |
1445 | |
1446 LabeledStatement asLabeledStatement() => this; | |
1447 | |
1448 accept(Visitor visitor) => visitor.visitLabeledStatement(this); | |
1449 | |
1450 visitChildren(Visitor visitor) { | |
1451 label.accept(visitor); | |
1452 statement.accept(visitor); | |
1453 } | |
1454 | |
1455 Token getBeginToken() => label.getBeginToken(); | |
1456 | |
1457 Token getEndToken() => statement.getEndToken(); | |
1458 | |
1459 bool isValidContinueTarget() => statement.isValidContinueTarget(); | |
1460 | |
1461 Node getBody() { | |
1462 if (statement is! LabeledStatement) return statement; | |
1463 LabeledStatement labeled = statement; | |
1464 return labeled.getBody(); | |
1465 } | |
1466 } | |
1467 | |
1468 class ScriptTag extends Node { | |
1469 final Identifier tag; | |
1470 final StringNode argument; | |
1471 final Identifier prefixIdentifier; | |
1472 final StringNode prefix; | |
1473 | |
1474 final Token beginToken; | |
1475 final Token endToken; | |
1476 | |
1477 ScriptTag(this.tag, this.argument, this.prefixIdentifier, this.prefix, | |
1478 this.beginToken, this.endToken); | |
1479 | |
1480 bool isImport() => tag.source == const SourceString("import"); | |
1481 bool isSource() => tag.source == const SourceString("source"); | |
1482 bool isLibrary() => tag.source == const SourceString("library"); | |
1483 bool isResource() => tag.source == const SourceString("resource"); | |
1484 | |
1485 ScriptTag asScriptTag() => this; | |
1486 | |
1487 accept(Visitor visitor) => visitor.visitScriptTag(this); | |
1488 | |
1489 visitChildren(Visitor visitor) { | |
1490 tag.accept(visitor); | |
1491 argument.accept(visitor); | |
1492 if (prefixIdentifier !== null) prefixIdentifier.accept(visitor); | |
1493 if (prefix !== null) prefix.accept(visitor); | |
1494 } | |
1495 | |
1496 Token getBeginToken() => beginToken; | |
1497 | |
1498 Token getEndToken() => endToken; | |
1499 } | |
1500 | |
1501 class Typedef extends Node { | |
1502 final TypeAnnotation returnType; | |
1503 final Identifier name; | |
1504 final NodeList typeParameters; | |
1505 final NodeList formals; | |
1506 | |
1507 final Token typedefKeyword; | |
1508 final Token endToken; | |
1509 | |
1510 Typedef(this.returnType, this.name, this.typeParameters, this.formals, | |
1511 this.typedefKeyword, this.endToken); | |
1512 | |
1513 Typedef asTypedef() => this; | |
1514 | |
1515 accept(Visitor visitor) => visitor.visitTypedef(this); | |
1516 | |
1517 visitChildren(Visitor visitor) { | |
1518 if (returnType !== null) returnType.accept(visitor); | |
1519 name.accept(visitor); | |
1520 if (typeParameters !== null) typeParameters.accept(visitor); | |
1521 formals.accept(visitor); | |
1522 } | |
1523 | |
1524 Token getBeginToken() => typedefKeyword; | |
1525 | |
1526 Token getEndToken() => endToken; | |
1527 } | |
1528 | |
1529 class TryStatement extends Statement { | |
1530 final Block tryBlock; | |
1531 final NodeList catchBlocks; | |
1532 final Block finallyBlock; | |
1533 | |
1534 final Token tryKeyword; | |
1535 final Token finallyKeyword; | |
1536 | |
1537 TryStatement(this.tryBlock, this.catchBlocks, this.finallyBlock, | |
1538 this.tryKeyword, this.finallyKeyword); | |
1539 | |
1540 TryStatement asTryStatement() => this; | |
1541 | |
1542 accept(Visitor visitor) => visitor.visitTryStatement(this); | |
1543 | |
1544 visitChildren(Visitor visitor) { | |
1545 tryBlock.accept(visitor); | |
1546 catchBlocks.accept(visitor); | |
1547 if (finallyBlock !== null) finallyBlock.accept(visitor); | |
1548 } | |
1549 | |
1550 Token getBeginToken() => tryKeyword; | |
1551 | |
1552 Token getEndToken() { | |
1553 if (finallyBlock !== null) return finallyBlock.getEndToken(); | |
1554 if (!catchBlocks.isEmpty()) return catchBlocks.getEndToken(); | |
1555 return tryBlock.getEndToken(); | |
1556 } | |
1557 } | |
1558 | |
1559 class CatchBlock extends Node { | |
1560 final NodeList formals; | |
1561 final Block block; | |
1562 | |
1563 final catchKeyword; | |
1564 | |
1565 CatchBlock(this.formals, this.block, this.catchKeyword); | |
1566 | |
1567 CatchBlock asCatchBlock() => this; | |
1568 | |
1569 accept(Visitor visitor) => visitor.visitCatchBlock(this); | |
1570 | |
1571 Node get exception() { | |
1572 if (formals.nodes.isEmpty()) return null; | |
1573 VariableDefinitions declarations = formals.nodes.head; | |
1574 return declarations.definitions.nodes.head; | |
1575 } | |
1576 | |
1577 Node get trace() { | |
1578 if (formals.nodes.isEmpty()) return null; | |
1579 Link<Node> declarations = formals.nodes.tail; | |
1580 if (declarations.isEmpty()) return null; | |
1581 VariableDefinitions head = declarations.head; | |
1582 return head.definitions.nodes.head; | |
1583 } | |
1584 | |
1585 visitChildren(Visitor visitor) { | |
1586 formals.accept(visitor); | |
1587 block.accept(visitor); | |
1588 } | |
1589 | |
1590 Token getBeginToken() => catchKeyword; | |
1591 | |
1592 Token getEndToken() => block.getEndToken(); | |
1593 } | |
1594 | |
1595 class Initializers { | |
1596 static bool isSuperConstructorCall(Send node) { | |
1597 return (node.receiver === null && | |
1598 node.selector.asIdentifier() !== null && | |
1599 node.selector.asIdentifier().isSuper()) || | |
1600 (node.receiver !== null && | |
1601 node.receiver.asIdentifier() !== null && | |
1602 node.receiver.asIdentifier().isSuper() && | |
1603 node.selector.asIdentifier() !== null); | |
1604 } | |
1605 | |
1606 static bool isConstructorRedirect(Send node) { | |
1607 return (node.receiver === null && | |
1608 node.selector.asIdentifier() !== null && | |
1609 node.selector.asIdentifier().isThis()) || | |
1610 (node.receiver !== null && | |
1611 node.receiver.asIdentifier() !== null && | |
1612 node.receiver.asIdentifier().isThis() && | |
1613 node.selector.asIdentifier() !== null); | |
1614 } | |
1615 } | |
1616 | |
1617 class GetDartStringVisitor extends AbstractVisitor<DartString> { | |
1618 const GetDartStringVisitor(); | |
1619 DartString visitNode(Node node) => null; | |
1620 DartString visitStringJuxtaposition(StringJuxtaposition node) | |
1621 => node.dartString; | |
1622 DartString visitLiteralString(LiteralString node) => node.dartString; | |
1623 } | |
1624 | |
1625 class IsInterpolationVisitor extends AbstractVisitor<bool> { | |
1626 const IsInterpolationVisitor(); | |
1627 bool visitNode(Node node) => false; | |
1628 bool visitStringInterpolation(StringInterpolation node) => true; | |
1629 bool visitStringJuxtaposition(StringJuxtaposition node) | |
1630 => node.isInterpolation; | |
1631 } | |
1632 | |
1633 /** | |
1634 * If the given node is a send set, it visits its initializer (first | |
1635 * argument). | |
1636 * | |
1637 * TODO(ahe): This method is controversial, the team needs to discuss | |
1638 * if top-level methods are acceptable and what naming conventions to | |
1639 * use. | |
1640 */ | |
1641 initializerDo(Node node, f(Node node)) { | |
1642 SendSet send = node.asSendSet(); | |
1643 if (send !== null) return f(send.arguments.head); | |
1644 } | |
OLD | NEW |