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

Side by Side Diff: pkg/compiler/lib/src/dart_backend/backend_ast_nodes.dart

Issue 2213673002: Delete dart_backend from compiler. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 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
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 library backend_ast_nodes;
6
7 import '../constants/values.dart' as values;
8 import '../dart_types.dart' as types;
9 import '../elements/elements.dart' as elements;
10 import '../tree/tree.dart' as tree;
11 import '../util/characters.dart' as characters;
12
13 /// The following nodes correspond to [tree.Send] expressions:
14 /// [FieldExpression], [IndexExpression], [Assignment], [Increment],
15 /// [CallFunction], [CallMethod], [CallNew], [CallStatic], [UnaryOperator],
16 /// [BinaryOperator], and [TypeOperator].
17 abstract class Node {}
18
19 /// Receiver is an [Expression] or the [SuperReceiver].
20 abstract class Receiver extends Node {}
21
22 /// Argument is an [Expression] or a [NamedArgument].
23 abstract class Argument extends Node {}
24
25 abstract class Expression extends Node implements Receiver, Argument {
26 bool get assignable => false;
27 }
28
29 abstract class RootNode extends Node {
30 elements.Element get element;
31 }
32
33 class FieldDefinition extends RootNode {
34 final elements.Element element;
35 final Expression initializer;
36 FieldDefinition(this.element, this.initializer);
37 }
38
39 abstract class Statement extends Node {}
40
41 /// Used as receiver in expressions that dispatch to the super class.
42 /// For instance, an expression such as `super.f()` is represented
43 /// by a [CallMethod] node with [SuperReceiver] as its receiver.
44 class SuperReceiver extends Receiver {
45 static final SuperReceiver _instance = new SuperReceiver._create();
46
47 factory SuperReceiver() => _instance;
48 SuperReceiver._create();
49 }
50
51 /// Named arguments may occur in the argument list of
52 /// [CallFunction], [CallMethod], [CallNew], and [CallStatic].
53 class NamedArgument extends Argument {
54 final String name;
55 final Expression expression;
56
57 NamedArgument(this.name, this.expression);
58 }
59
60 class TypeAnnotation extends Node {
61 final String name;
62 final List<TypeAnnotation> typeArguments;
63
64 types.DartType dartType;
65
66 TypeAnnotation(this.name, [this.typeArguments = const <TypeAnnotation>[]]);
67 }
68
69 // STATEMENTS
70
71 class Block extends Statement {
72 final List<Statement> statements;
73
74 Block(this.statements);
75 }
76
77 class Break extends Statement {
78 final String label;
79
80 Break([this.label]);
81 }
82
83 class Continue extends Statement {
84 final String label;
85
86 Continue([this.label]);
87 }
88
89 class EmptyStatement extends Statement {
90 static final EmptyStatement _instance = new EmptyStatement._create();
91
92 factory EmptyStatement() => _instance;
93 EmptyStatement._create();
94 }
95
96 class ExpressionStatement extends Statement {
97 final Expression expression;
98
99 ExpressionStatement(this.expression);
100 }
101
102 class For extends Statement {
103 final Node initializer;
104 final Expression condition;
105 final List<Expression> updates;
106 final Statement body;
107
108 /// Initializer must be [VariableDeclarations] or [Expression] or null.
109 For(this.initializer, this.condition, this.updates, this.body) {
110 assert(initializer == null ||
111 initializer is VariableDeclarations ||
112 initializer is Expression);
113 }
114 }
115
116 class ForIn extends Statement {
117 final Node leftHandValue;
118 final Expression expression;
119 final Statement body;
120
121 /// [leftHandValue] must be [Identifier] or [VariableDeclarations] with
122 /// exactly one definition, and that variable definition must have no
123 /// initializer.
124 ForIn(Node leftHandValue, this.expression, this.body)
125 : this.leftHandValue = leftHandValue {
126 assert(leftHandValue is Identifier ||
127 (leftHandValue is VariableDeclarations &&
128 leftHandValue.declarations.length == 1 &&
129 leftHandValue.declarations[0].initializer == null));
130 }
131 }
132
133 class While extends Statement {
134 final Expression condition;
135 final Statement body;
136
137 While(this.condition, this.body);
138 }
139
140 class DoWhile extends Statement {
141 final Statement body;
142 final Expression condition;
143
144 DoWhile(this.body, this.condition);
145 }
146
147 class If extends Statement {
148 final Expression condition;
149 final Statement thenStatement;
150 final Statement elseStatement;
151
152 If(this.condition, this.thenStatement, [this.elseStatement]);
153 }
154
155 class LabeledStatement extends Statement {
156 final String label;
157 final Statement statement;
158
159 LabeledStatement(this.label, this.statement);
160 }
161
162 class Rethrow extends Statement {}
163
164 class Return extends Statement {
165 final Expression expression;
166
167 Return([this.expression]);
168 }
169
170 class Switch extends Statement {
171 final Expression expression;
172 final List<SwitchCase> cases;
173
174 Switch(this.expression, this.cases);
175 }
176
177 /// A sequence of case clauses followed by a sequence of statements.
178 /// Represents the default case if [expressions] is null.
179 ///
180 /// NOTE:
181 /// Control will never fall through to the following SwitchCase, even if
182 /// the list of statements is empty. An empty list of statements will be
183 /// unparsed to a semicolon to guarantee this behaviour.
184 class SwitchCase extends Node {
185 final List<Expression> expressions;
186 final List<Statement> statements;
187
188 SwitchCase(this.expressions, this.statements);
189 SwitchCase.defaultCase(this.statements) : expressions = null;
190
191 bool get isDefaultCase => expressions == null;
192 }
193
194 /// A try statement. The try, catch and finally blocks will automatically
195 /// be printed inside a block statement if necessary.
196 class Try extends Statement {
197 final Statement tryBlock;
198 final List<CatchBlock> catchBlocks;
199 final Statement finallyBlock;
200
201 Try(this.tryBlock, this.catchBlocks, [this.finallyBlock]) {
202 assert(catchBlocks.length > 0 || finallyBlock != null);
203 }
204 }
205
206 class CatchBlock extends Node {
207 final TypeAnnotation onType;
208 final VariableDeclaration exceptionVar;
209 final VariableDeclaration stackVar;
210 final Statement body;
211
212 /// At least onType or exceptionVar must be given.
213 /// stackVar may only be given if exceptionVar is also given.
214 CatchBlock(this.body, {this.onType, this.exceptionVar, this.stackVar}) {
215 // Must specify at least a type or an exception binding.
216 assert(onType != null || exceptionVar != null);
217
218 // We cannot bind the stack trace without binding the exception too.
219 assert(stackVar == null || exceptionVar != null);
220 }
221 }
222
223 class VariableDeclarations extends Statement {
224 final TypeAnnotation type;
225 final bool isFinal;
226 final bool isConst;
227 final List<VariableDeclaration> declarations;
228
229 VariableDeclarations(this.declarations,
230 {this.type, this.isFinal: false, this.isConst: false}) {
231 // Cannot be both final and const.
232 assert(!isFinal || !isConst);
233 }
234 }
235
236 class VariableDeclaration extends Node {
237 final String name;
238 final Expression initializer;
239
240 elements.Element element;
241
242 VariableDeclaration(this.name, [this.initializer]);
243 }
244
245 class FunctionDeclaration extends Statement {
246 final FunctionExpression function;
247
248 TypeAnnotation get returnType => function.returnType;
249 Parameters get parameters => function.parameters;
250 String get name => function.name;
251 Statement get body => function.body;
252
253 FunctionDeclaration(this.function);
254 }
255
256 class Parameters extends Node {
257 final List<Parameter> requiredParameters;
258 final List<Parameter> optionalParameters;
259 final bool hasNamedParameters;
260
261 Parameters(this.requiredParameters,
262 [this.optionalParameters, this.hasNamedParameters = false]);
263
264 Parameters.named(this.requiredParameters, this.optionalParameters)
265 : hasNamedParameters = true;
266
267 Parameters.positional(this.requiredParameters, this.optionalParameters)
268 : hasNamedParameters = false;
269
270 bool get hasOptionalParameters =>
271 optionalParameters != null && optionalParameters.length > 0;
272 }
273
274 class Parameter extends Node {
275 final String name;
276
277 /// Type of parameter, or return type of function parameter.
278 final TypeAnnotation type;
279
280 Expression defaultValue;
281
282 /// Parameters to function parameter. Null for non-function parameters.
283 final Parameters parameters;
284
285 elements.FormalElement element;
286
287 Parameter(this.name, {this.type, this.defaultValue}) : parameters = null;
288
289 Parameter.function(this.name, TypeAnnotation returnType, this.parameters,
290 [this.defaultValue])
291 : type = returnType {
292 assert(parameters != null);
293 }
294
295 /// True if this is a function parameter.
296 bool get isFunction => parameters != null;
297 }
298
299 // EXPRESSIONS
300
301 abstract class Initializer extends Expression {}
302
303 class FieldInitializer extends Initializer {
304 final elements.FieldElement element;
305 final Expression body;
306
307 FieldInitializer(this.element, this.body);
308 }
309
310 class SuperInitializer extends Initializer {
311 final elements.ConstructorElement target;
312 final List<Argument> arguments;
313
314 SuperInitializer(this.target, this.arguments);
315 }
316
317 class FunctionExpression extends Expression implements RootNode {
318 final TypeAnnotation returnType;
319 String name;
320 final Parameters parameters;
321 final Statement body;
322 final bool isGetter;
323 final bool isSetter;
324
325 elements.FunctionElement element;
326
327 FunctionExpression(this.parameters, this.body,
328 {this.name,
329 this.returnType,
330 this.isGetter: false,
331 this.isSetter: false}) {
332 // Function must have a name if it has a return type
333 assert(returnType == null || name != null);
334 }
335 }
336
337 class ConstructorDefinition extends FunctionExpression {
338 final List<Initializer> initializers;
339 final bool isConst;
340
341 ConstructorDefinition(Parameters parameters, Statement body,
342 this.initializers, String name, this.isConst)
343 : super(parameters, body, name: name);
344 }
345
346 class Conditional extends Expression {
347 final Expression condition;
348 final Expression thenExpression;
349 final Expression elseExpression;
350
351 Conditional(this.condition, this.thenExpression, this.elseExpression);
352 }
353
354 /// An identifier expression.
355 /// The unparser does not concern itself with scoping rules, and it is the
356 /// responsibility of the AST creator to ensure that the identifier resolves
357 /// to the proper definition.
358 /// For the time being, this class is also used to reference static fields and
359 /// top-level variables that are qualified with a class and/or library name,
360 /// assuming the [element] is set. This is likely to change when the old backend
361 /// is replaced.
362 class Identifier extends Expression {
363 final String name;
364
365 elements.Element element;
366
367 Identifier(this.name);
368
369 bool get assignable => true;
370 }
371
372 class Literal extends Expression {
373 final values.PrimitiveConstantValue value;
374
375 Literal(this.value);
376 }
377
378 class LiteralList extends Expression {
379 final bool isConst;
380 final TypeAnnotation typeArgument;
381 final List<Expression> values;
382
383 LiteralList(this.values, {this.typeArgument, this.isConst: false});
384 }
385
386 class LiteralMap extends Expression {
387 final bool isConst;
388 final List<TypeAnnotation> typeArguments;
389 final List<LiteralMapEntry> entries;
390
391 LiteralMap(this.entries, {this.typeArguments, this.isConst: false}) {
392 assert(this.typeArguments == null ||
393 this.typeArguments.length == 0 ||
394 this.typeArguments.length == 2);
395 }
396 }
397
398 class LiteralMapEntry extends Node {
399 final Expression key;
400 final Expression value;
401
402 LiteralMapEntry(this.key, this.value);
403 }
404
405 class LiteralSymbol extends Expression {
406 final String id;
407
408 /// [id] should not include the # symbol
409 LiteralSymbol(this.id);
410 }
411
412 /// A type literal. This is distinct from [Identifier] since the unparser
413 /// needs to this distinguish a static invocation from a method invocation
414 /// on a type literal.
415 class LiteralType extends Expression {
416 final String name;
417
418 types.DartType type;
419
420 LiteralType(this.name);
421 }
422
423 /// Reference to a type variable.
424 /// This is distinct from [Identifier] since the unparser needs to this
425 /// distinguish a function invocation `T()` from a type variable invocation
426 /// `(T)()` (the latter is invalid, but must be generated anyway).
427 class ReifyTypeVar extends Expression {
428 final String name;
429
430 elements.TypeVariableElement element;
431
432 ReifyTypeVar(this.name);
433 }
434
435 /// StringConcat is used in place of string interpolation and juxtaposition.
436 /// Semantically, each subexpression is evaluated and converted to a string
437 /// by `toString()`. These string are then concatenated and returned.
438 /// StringConcat unparses to a string literal, possibly with interpolations.
439 /// The unparser will flatten nested StringConcats.
440 /// A StringConcat node may have any number of children, including zero and one.
441 class StringConcat extends Expression {
442 final List<Expression> expressions;
443
444 StringConcat(this.expressions);
445 }
446
447 /// Expression of form `e.f`.
448 class FieldExpression extends Expression {
449 final Receiver object;
450 final String fieldName;
451
452 FieldExpression(this.object, this.fieldName);
453
454 bool get assignable => true;
455 }
456
457 /// Expression of form `e1[e2]`.
458 class IndexExpression extends Expression {
459 final Receiver object;
460 final Expression index;
461
462 IndexExpression(this.object, this.index);
463
464 bool get assignable => true;
465 }
466
467 /// Expression of form `e(..)`
468 /// Note that if [callee] is a [FieldExpression] this will translate into
469 /// `(e.f)(..)` and not `e.f(..)`. Use a [CallMethod] to generate
470 /// the latter type of expression.
471 class CallFunction extends Expression {
472 final Expression callee;
473 final List<Argument> arguments;
474
475 CallFunction(this.callee, this.arguments);
476 }
477
478 /// Expression of form `e.f(..)`.
479 class CallMethod extends Expression {
480 final Receiver object;
481 final String methodName;
482 final List<Argument> arguments;
483
484 CallMethod(this.object, this.methodName, this.arguments);
485 }
486
487 /// Expression of form `new T(..)`, `new T.f(..)`, `const T(..)`,
488 /// or `const T.f(..)`.
489 class CallNew extends Expression {
490 final bool isConst;
491 final TypeAnnotation type;
492 final String constructorName;
493 final List<Argument> arguments;
494
495 elements.FunctionElement constructor;
496 types.DartType dartType;
497
498 CallNew(this.type, this.arguments,
499 {this.constructorName, this.isConst: false});
500 }
501
502 /// Expression of form `T.f(..)`.
503 class CallStatic extends Expression {
504 final String className;
505 final String methodName;
506 final List<Argument> arguments;
507
508 elements.Element element;
509
510 CallStatic(this.className, this.methodName, this.arguments);
511 }
512
513 /// Expression of form `!e` or `-e` or `~e`.
514 class UnaryOperator extends Expression {
515 final String operatorName;
516 final Receiver operand;
517
518 UnaryOperator(this.operatorName, this.operand) {
519 assert(isUnaryOperator(operatorName));
520 }
521 }
522
523 /// Expression of form `e1 + e2`, `e1 - e2`, etc.
524 /// This node also represents application of the logical operators && and ||.
525 class BinaryOperator extends Expression {
526 final Receiver left;
527 final String operator;
528 final Expression right;
529
530 BinaryOperator(this.left, this.operator, this.right) {
531 assert(isBinaryOperator(operator));
532 }
533 }
534
535 /// Expression of form `e is T` or `e is! T` or `e as T`.
536 class TypeOperator extends Expression {
537 final Expression expression;
538 final String operator;
539 final TypeAnnotation type;
540
541 TypeOperator(this.expression, this.operator, this.type) {
542 assert(operator == 'is' || operator == 'as' || operator == 'is!');
543 }
544 }
545
546 class Increment extends Expression {
547 final Expression expression;
548 final String operator;
549 final bool isPrefix;
550
551 Increment(this.expression, this.operator, this.isPrefix) {
552 assert(operator == '++' || operator == '--');
553 assert(expression.assignable);
554 }
555
556 Increment.prefix(Expression expression, String operator)
557 : this(expression, operator, true);
558
559 Increment.postfix(Expression expression, String operator)
560 : this(expression, operator, false);
561 }
562
563 class Assignment extends Expression {
564 static final _operators = new Set.from([
565 '=',
566 '|=',
567 '^=',
568 '&=',
569 '<<=',
570 '>>=',
571 '+=',
572 '-=',
573 '*=',
574 '/=',
575 '%=',
576 '~/='
577 ]);
578
579 final Expression left;
580 final String operator;
581 final Expression right;
582
583 Assignment(this.left, this.operator, this.right) {
584 assert(_operators.contains(operator));
585 assert(left.assignable);
586 }
587 }
588
589 class Throw extends Expression {
590 final Expression expression;
591
592 Throw(this.expression);
593 }
594
595 class This extends Expression {
596 static final This _instance = new This._create();
597
598 factory This() => _instance;
599 This._create();
600 }
601
602 // UNPARSER
603
604 bool isUnaryOperator(String op) {
605 return op == '!' || op == '-' || op == '~';
606 }
607
608 bool isBinaryOperator(String op) {
609 return BINARY_PRECEDENCE.containsKey(op);
610 }
611
612 /// True if the given operator can be converted to a compound assignment.
613 bool isCompoundableOperator(String op) {
614 switch (BINARY_PRECEDENCE[op]) {
615 case BITWISE_OR:
616 case BITWISE_XOR:
617 case BITWISE_AND:
618 case SHIFT:
619 case ADDITIVE:
620 case MULTIPLICATIVE:
621 return true;
622 default:
623 return false;
624 }
625 }
626
627 // Precedence levels
628 const int EXPRESSION = 1;
629 const int CONDITIONAL = 2;
630 const int LOGICAL_OR = 3;
631 const int LOGICAL_AND = 4;
632 const int EQUALITY = 6;
633 const int RELATIONAL = 7;
634 const int BITWISE_OR = 8;
635 const int BITWISE_XOR = 9;
636 const int BITWISE_AND = 10;
637 const int SHIFT = 11;
638 const int ADDITIVE = 12;
639 const int MULTIPLICATIVE = 13;
640 const int UNARY = 14;
641 const int POSTFIX_INCREMENT = 15;
642 const int TYPE_LITERAL = 19;
643 const int PRIMARY = 20;
644
645 /// Precedence level required for the callee in a [FunctionCall].
646 const int CALLEE = 21;
647
648 const Map<String, int> BINARY_PRECEDENCE = const {
649 '&&': LOGICAL_AND,
650 '||': LOGICAL_OR,
651 '==': EQUALITY,
652 '!=': EQUALITY,
653 '>': RELATIONAL,
654 '>=': RELATIONAL,
655 '<': RELATIONAL,
656 '<=': RELATIONAL,
657 '|': BITWISE_OR,
658 '^': BITWISE_XOR,
659 '&': BITWISE_AND,
660 '>>': SHIFT,
661 '<<': SHIFT,
662 '+': ADDITIVE,
663 '-': ADDITIVE,
664 '*': MULTIPLICATIVE,
665 '%': MULTIPLICATIVE,
666 '/': MULTIPLICATIVE,
667 '~/': MULTIPLICATIVE,
668 };
669
670 /// Return true if binary operators with the given precedence level are
671 /// (left) associative. False if they are non-associative.
672 bool isAssociativeBinaryOperator(int precedence) {
673 return precedence != EQUALITY && precedence != RELATIONAL;
674 }
675
676 /// True if [x] is a letter, digit, or underscore.
677 /// Such characters may not follow a shorthand string interpolation.
678 bool isIdentifierPartNoDollar(dynamic x) {
679 if (x is! int) {
680 return false;
681 }
682 return (characters.$0 <= x && x <= characters.$9) ||
683 (characters.$A <= x && x <= characters.$Z) ||
684 (characters.$a <= x && x <= characters.$z) ||
685 (x == characters.$_);
686 }
687
688 /// The unparser will apply the following syntactic rewritings:
689 /// Use short-hand function returns:
690 /// foo(){return E} ==> foo() => E;
691 /// Remove empty else branch:
692 /// if (E) S else ; ==> if (E) S
693 /// Flatten nested blocks:
694 /// {S; {S; S}; S} ==> {S; S; S; S}
695 /// Remove empty statements from block:
696 /// {S; ; S} ==> {S; S}
697 /// Unfold singleton blocks:
698 /// {S} ==> S
699 /// Empty block to empty statement:
700 /// {} ==> ;
701 /// Introduce not-equals operator:
702 /// !(E == E) ==> E != E
703 /// Introduce is-not operator:
704 /// !(E is T) ==> E is!T
705 ///
706 /// The following transformations will NOT be applied here:
707 /// Use implicit this:
708 /// this.foo ==> foo (preconditions too complex for unparser)
709 /// Merge adjacent variable definitions:
710 /// var x; var y ==> var x,y; (hoisting will be done elsewhere)
711 /// Merge adjacent labels:
712 /// foo: bar: S ==> foobar: S (scoping is categorically ignored)
713 ///
714 /// The following transformations might be applied here in the future:
715 /// Use implicit dynamic types:
716 /// dynamic x = E ==> var x = E
717 /// <dynamic>[] ==> []
718 class Unparser {
719 StringSink output;
720
721 Unparser(this.output);
722
723 void write(String s) {
724 output.write(s);
725 }
726
727 /// Outputs each element from [items] separated by [separator].
728 /// The actual printing must be performed by the [callback].
729 void writeEach(String separator, Iterable items, void callback(any)) {
730 bool first = true;
731 for (var x in items) {
732 if (first) {
733 first = false;
734 } else {
735 write(separator);
736 }
737 callback(x);
738 }
739 }
740
741 void writeOperator(String operator) {
742 write(" ");
743 write(operator);
744 write(" ");
745 }
746
747 /// Unfolds singleton blocks and returns the inner statement.
748 /// If an empty block is found, the [EmptyStatement] is returned instead.
749 Statement unfoldBlocks(Statement stmt) {
750 while (stmt is Block && stmt.statements.length == 1) {
751 Statement inner = (stmt as Block).statements[0];
752 if (definesVariable(inner)) {
753 return stmt; // Do not unfold block with lexical scope.
754 }
755 stmt = inner;
756 }
757 if (stmt is Block && stmt.statements.length == 0)
758 return new EmptyStatement();
759 return stmt;
760 }
761
762 void writeArgument(Argument arg) {
763 if (arg is NamedArgument) {
764 write(arg.name);
765 write(':');
766 writeExpression(arg.expression);
767 } else {
768 writeExpression(arg);
769 }
770 }
771
772 /// Prints the expression [e].
773 void writeExpression(Expression e) {
774 writeExp(e, EXPRESSION);
775 }
776
777 /// Prints [e] as an expression with precedence of at least [minPrecedence],
778 /// using parentheses if necessary to raise the precedence level.
779 /// Abusing terminology slightly, the function accepts a [Receiver] which
780 /// may also be the [SuperReceiver] object.
781 void writeExp(Receiver e, int minPrecedence, {beginStmt: false}) {
782 void withPrecedence(int actual, void action()) {
783 if (actual < minPrecedence) {
784 write("(");
785 beginStmt = false;
786 action();
787 write(")");
788 } else {
789 action();
790 }
791 }
792 if (e is SuperReceiver) {
793 write('super');
794 } else if (e is FunctionExpression) {
795 assert(!e.isGetter && !e.isSetter);
796 Statement stmt = unfoldBlocks(e.body);
797 int precedence = stmt is Return ? EXPRESSION : PRIMARY;
798 withPrecedence(precedence, () {
799 // A named function expression at the beginning of a statement
800 // can be mistaken for a function declaration.
801 // (Note: Functions with a return type also have a name)
802 bool needParen = beginStmt && e.name != null;
803 if (needParen) {
804 write('(');
805 }
806 if (e.returnType != null) {
807 writeType(e.returnType);
808 write(' ');
809 }
810 if (e.name != null) {
811 write(e.name);
812 }
813 writeParameters(e.parameters);
814 if (stmt is Return) {
815 write('=> ');
816 writeExp(stmt.expression, EXPRESSION);
817 } else {
818 writeBlock(stmt);
819 }
820 if (needParen) {
821 write(')');
822 }
823 });
824 } else if (e is Conditional) {
825 withPrecedence(CONDITIONAL, () {
826 writeExp(e.condition, LOGICAL_OR, beginStmt: beginStmt);
827 write(' ? ');
828 writeExp(e.thenExpression, EXPRESSION);
829 write(' : ');
830 writeExp(e.elseExpression, EXPRESSION);
831 });
832 } else if (e is Identifier) {
833 write(e.name);
834 } else if (e is Literal) {
835 if (e.value.isString) {
836 writeStringLiteral(e);
837 } else if (e.value.isDouble) {
838 double v = e.value.primitiveValue;
839 if (v == double.INFINITY) {
840 withPrecedence(MULTIPLICATIVE, () {
841 write('1/0.0');
842 });
843 } else if (v == double.NEGATIVE_INFINITY) {
844 withPrecedence(MULTIPLICATIVE, () {
845 write('-1/0.0');
846 });
847 } else if (v.isNaN) {
848 withPrecedence(MULTIPLICATIVE, () {
849 write('0/0.0');
850 });
851 } else {
852 write(v.toString());
853 }
854 } else {
855 write(e.value.toDartText());
856 }
857 } else if (e is LiteralList) {
858 if (e.isConst) {
859 write(' const ');
860 }
861 if (e.typeArgument != null) {
862 write('<');
863 writeType(e.typeArgument);
864 write('>');
865 }
866 write('[');
867 writeEach(',', e.values, writeExpression);
868 write(']');
869 } else if (e is LiteralMap) {
870 // The curly brace can be mistaken for a block statement if we
871 // are at the beginning of a statement.
872 bool needParen = beginStmt;
873 if (e.isConst) {
874 write(' const ');
875 needParen = false;
876 }
877 if (e.typeArguments.length > 0) {
878 write('<');
879 writeEach(',', e.typeArguments, writeType);
880 write('>');
881 needParen = false;
882 }
883 if (needParen) {
884 write('(');
885 }
886 write('{');
887 writeEach(',', e.entries, (LiteralMapEntry en) {
888 writeExp(en.key, EXPRESSION);
889 write(' : ');
890 writeExp(en.value, EXPRESSION);
891 });
892 write('}');
893 if (needParen) {
894 write(')');
895 }
896 } else if (e is LiteralSymbol) {
897 write('#');
898 write(e.id);
899 } else if (e is LiteralType) {
900 withPrecedence(TYPE_LITERAL, () {
901 write(e.name);
902 });
903 } else if (e is ReifyTypeVar) {
904 withPrecedence(PRIMARY, () {
905 write(e.name);
906 });
907 } else if (e is StringConcat) {
908 writeStringLiteral(e);
909 } else if (e is UnaryOperator) {
910 Receiver operand = e.operand;
911 // !(x == y) ==> x != y.
912 if (e.operatorName == '!' &&
913 operand is BinaryOperator &&
914 operand.operator == '==') {
915 withPrecedence(EQUALITY, () {
916 writeExp(operand.left, RELATIONAL);
917 writeOperator('!=');
918 writeExp(operand.right, RELATIONAL);
919 });
920 }
921 // !(x is T) ==> x is!T
922 else if (e.operatorName == '!' &&
923 operand is TypeOperator &&
924 operand.operator == 'is') {
925 withPrecedence(RELATIONAL, () {
926 writeExp(operand.expression, BITWISE_OR, beginStmt: beginStmt);
927 write(' is!');
928 writeType(operand.type);
929 });
930 } else {
931 withPrecedence(UNARY, () {
932 writeOperator(e.operatorName);
933 writeExp(e.operand, UNARY);
934 });
935 }
936 } else if (e is BinaryOperator) {
937 int precedence = BINARY_PRECEDENCE[e.operator];
938 withPrecedence(precedence, () {
939 // All binary operators are left-associative or non-associative.
940 // For each operand, we use either the same precedence level as
941 // the current operator, or one higher.
942 int deltaLeft = isAssociativeBinaryOperator(precedence) ? 0 : 1;
943 writeExp(e.left, precedence + deltaLeft, beginStmt: beginStmt);
944 writeOperator(e.operator);
945 writeExp(e.right, precedence + 1);
946 });
947 } else if (e is TypeOperator) {
948 withPrecedence(RELATIONAL, () {
949 writeExp(e.expression, BITWISE_OR, beginStmt: beginStmt);
950 write(' ');
951 write(e.operator);
952 write(' ');
953 writeType(e.type);
954 });
955 } else if (e is Assignment) {
956 withPrecedence(EXPRESSION, () {
957 writeExp(e.left, PRIMARY, beginStmt: beginStmt);
958 writeOperator(e.operator);
959 writeExp(e.right, EXPRESSION);
960 });
961 } else if (e is FieldExpression) {
962 withPrecedence(PRIMARY, () {
963 writeExp(e.object, PRIMARY, beginStmt: beginStmt);
964 write('.');
965 write(e.fieldName);
966 });
967 } else if (e is IndexExpression) {
968 withPrecedence(CALLEE, () {
969 writeExp(e.object, PRIMARY, beginStmt: beginStmt);
970 write('[');
971 writeExp(e.index, EXPRESSION);
972 write(']');
973 });
974 } else if (e is CallFunction) {
975 withPrecedence(CALLEE, () {
976 writeExp(e.callee, CALLEE, beginStmt: beginStmt);
977 write('(');
978 writeEach(',', e.arguments, writeArgument);
979 write(')');
980 });
981 } else if (e is CallMethod) {
982 withPrecedence(CALLEE, () {
983 writeExp(e.object, PRIMARY, beginStmt: beginStmt);
984 write('.');
985 write(e.methodName);
986 write('(');
987 writeEach(',', e.arguments, writeArgument);
988 write(')');
989 });
990 } else if (e is CallNew) {
991 withPrecedence(CALLEE, () {
992 write(' ');
993 write(e.isConst ? 'const ' : 'new ');
994 writeType(e.type);
995 if (e.constructorName != null) {
996 write('.');
997 write(e.constructorName);
998 }
999 write('(');
1000 writeEach(',', e.arguments, writeArgument);
1001 write(')');
1002 });
1003 } else if (e is CallStatic) {
1004 withPrecedence(CALLEE, () {
1005 write(e.className);
1006 write('.');
1007 write(e.methodName);
1008 write('(');
1009 writeEach(',', e.arguments, writeArgument);
1010 write(')');
1011 });
1012 } else if (e is Increment) {
1013 int precedence = e.isPrefix ? UNARY : POSTFIX_INCREMENT;
1014 withPrecedence(precedence, () {
1015 if (e.isPrefix) {
1016 write(e.operator);
1017 writeExp(e.expression, PRIMARY);
1018 } else {
1019 writeExp(e.expression, PRIMARY, beginStmt: beginStmt);
1020 write(e.operator);
1021 }
1022 });
1023 } else if (e is Throw) {
1024 withPrecedence(EXPRESSION, () {
1025 write('throw ');
1026 writeExp(e.expression, EXPRESSION);
1027 });
1028 } else if (e is This) {
1029 write('this');
1030 } else {
1031 throw "Unexpected expression: $e";
1032 }
1033 }
1034
1035 void writeParameters(Parameters params) {
1036 write('(');
1037 writeEach(',', params.requiredParameters, (Parameter p) {
1038 if (p.type != null) {
1039 writeType(p.type);
1040 write(' ');
1041 }
1042 write(p.name);
1043 if (p.parameters != null) {
1044 writeParameters(p.parameters);
1045 }
1046 });
1047 if (params.hasOptionalParameters) {
1048 if (params.requiredParameters.length > 0) {
1049 write(',');
1050 }
1051 write(params.hasNamedParameters ? '{' : '[');
1052 writeEach(',', params.optionalParameters, (Parameter p) {
1053 if (p.type != null) {
1054 writeType(p.type);
1055 write(' ');
1056 }
1057 write(p.name);
1058 if (p.parameters != null) {
1059 writeParameters(p.parameters);
1060 }
1061 if (p.defaultValue != null) {
1062 write(params.hasNamedParameters ? ':' : '=');
1063 writeExp(p.defaultValue, EXPRESSION);
1064 }
1065 });
1066 write(params.hasNamedParameters ? '}' : ']');
1067 }
1068 write(')');
1069 }
1070
1071 void writeStatement(Statement stmt, {bool shortIf: true}) {
1072 stmt = unfoldBlocks(stmt);
1073 if (stmt is Block) {
1074 write('{');
1075 stmt.statements.forEach(writeBlockMember);
1076 write('}');
1077 } else if (stmt is Break) {
1078 write('break');
1079 if (stmt.label != null) {
1080 write(' ');
1081 write(stmt.label);
1082 }
1083 write(';');
1084 } else if (stmt is Continue) {
1085 write('continue');
1086 if (stmt.label != null) {
1087 write(' ');
1088 write(stmt.label);
1089 }
1090 write(';');
1091 } else if (stmt is EmptyStatement) {
1092 write(';');
1093 } else if (stmt is ExpressionStatement) {
1094 writeExp(stmt.expression, EXPRESSION, beginStmt: true);
1095 write(';');
1096 } else if (stmt is For) {
1097 write('for(');
1098 Node init = stmt.initializer;
1099 if (init is Expression) {
1100 writeExp(init, EXPRESSION);
1101 } else if (init is VariableDeclarations) {
1102 writeVariableDefinitions(init);
1103 }
1104 write(';');
1105 if (stmt.condition != null) {
1106 writeExp(stmt.condition, EXPRESSION);
1107 }
1108 write(';');
1109 writeEach(',', stmt.updates, writeExpression);
1110 write(')');
1111 writeStatement(stmt.body, shortIf: shortIf);
1112 } else if (stmt is ForIn) {
1113 write('for(');
1114 Node lhv = stmt.leftHandValue;
1115 if (lhv is Identifier) {
1116 write(lhv.name);
1117 } else {
1118 writeVariableDefinitions(lhv as VariableDeclarations);
1119 }
1120 write(' in ');
1121 writeExp(stmt.expression, EXPRESSION);
1122 write(')');
1123 writeStatement(stmt.body, shortIf: shortIf);
1124 } else if (stmt is While) {
1125 write('while(');
1126 writeExp(stmt.condition, EXPRESSION);
1127 write(')');
1128 writeStatement(stmt.body, shortIf: shortIf);
1129 } else if (stmt is DoWhile) {
1130 write('do ');
1131 writeStatement(stmt.body);
1132 write('while(');
1133 writeExp(stmt.condition, EXPRESSION);
1134 write(');');
1135 } else if (stmt is If) {
1136 // if (E) S else ; ==> if (E) S
1137 Statement elsePart = unfoldBlocks(stmt.elseStatement);
1138 if (elsePart is EmptyStatement) {
1139 elsePart = null;
1140 }
1141 if (!shortIf && elsePart == null) {
1142 write('{');
1143 }
1144 write('if(');
1145 writeExp(stmt.condition, EXPRESSION);
1146 write(')');
1147 writeStatement(stmt.thenStatement, shortIf: elsePart == null);
1148 if (elsePart != null) {
1149 write('else ');
1150 writeStatement(elsePart, shortIf: shortIf);
1151 }
1152 if (!shortIf && elsePart == null) {
1153 write('}');
1154 }
1155 } else if (stmt is LabeledStatement) {
1156 write(stmt.label);
1157 write(':');
1158 writeStatement(stmt.statement, shortIf: shortIf);
1159 } else if (stmt is Rethrow) {
1160 write('rethrow;');
1161 } else if (stmt is Return) {
1162 write('return');
1163 if (stmt.expression != null) {
1164 write(' ');
1165 writeExp(stmt.expression, EXPRESSION);
1166 }
1167 write(';');
1168 } else if (stmt is Switch) {
1169 write('switch(');
1170 writeExp(stmt.expression, EXPRESSION);
1171 write('){');
1172 for (SwitchCase caze in stmt.cases) {
1173 if (caze.isDefaultCase) {
1174 write('default:');
1175 } else {
1176 for (Expression exp in caze.expressions) {
1177 write('case ');
1178 writeExp(exp, EXPRESSION);
1179 write(':');
1180 }
1181 }
1182 if (caze.statements.isEmpty) {
1183 write(';'); // Prevent fall-through.
1184 } else {
1185 caze.statements.forEach(writeBlockMember);
1186 }
1187 }
1188 write('}');
1189 } else if (stmt is Try) {
1190 write('try');
1191 writeBlock(stmt.tryBlock);
1192 for (CatchBlock block in stmt.catchBlocks) {
1193 if (block.onType != null) {
1194 write('on ');
1195 writeType(block.onType);
1196 }
1197 if (block.exceptionVar != null) {
1198 write('catch(');
1199 write(block.exceptionVar.name);
1200 if (block.stackVar != null) {
1201 write(',');
1202 write(block.stackVar.name);
1203 }
1204 write(')');
1205 }
1206 writeBlock(block.body);
1207 }
1208 if (stmt.finallyBlock != null) {
1209 write('finally');
1210 writeBlock(stmt.finallyBlock);
1211 }
1212 } else if (stmt is VariableDeclarations) {
1213 writeVariableDefinitions(stmt);
1214 write(';');
1215 } else if (stmt is FunctionDeclaration) {
1216 assert(!stmt.function.isGetter && !stmt.function.isSetter);
1217 if (stmt.returnType != null) {
1218 writeType(stmt.returnType);
1219 write(' ');
1220 }
1221 write(stmt.name);
1222 writeParameters(stmt.parameters);
1223 Statement body = unfoldBlocks(stmt.body);
1224 if (body is Return) {
1225 write('=> ');
1226 writeExp(body.expression, EXPRESSION);
1227 write(';');
1228 } else {
1229 writeBlock(body);
1230 }
1231 } else {
1232 throw "Unexpected statement: $stmt";
1233 }
1234 }
1235
1236 /// Writes a variable definition statement without the trailing semicolon
1237 void writeVariableDefinitions(VariableDeclarations vds) {
1238 if (vds.isConst)
1239 write('const ');
1240 else if (vds.isFinal) write('final ');
1241 if (vds.type != null) {
1242 writeType(vds.type);
1243 write(' ');
1244 }
1245 if (!vds.isConst && !vds.isFinal && vds.type == null) {
1246 write('var ');
1247 }
1248 writeEach(',', vds.declarations, (VariableDeclaration vd) {
1249 write(vd.name);
1250 if (vd.initializer != null) {
1251 write('=');
1252 writeExp(vd.initializer, EXPRESSION);
1253 }
1254 });
1255 }
1256
1257 /// True of statements that introduce variables in the scope of their
1258 /// surrounding block. Blocks containing such statements cannot be unfolded.
1259 static bool definesVariable(Statement s) {
1260 return s is VariableDeclarations || s is FunctionDeclaration;
1261 }
1262
1263 /// Writes the given statement in a context where only blocks are allowed.
1264 void writeBlock(Statement stmt) {
1265 if (stmt is Block) {
1266 writeStatement(stmt);
1267 } else {
1268 write('{');
1269 writeBlockMember(stmt);
1270 write('}');
1271 }
1272 }
1273
1274 /// Outputs a statement that is a member of a block statement (or a similar
1275 /// sequence of statements, such as in switch statement).
1276 /// This will flatten blocks and skip empty statement.
1277 void writeBlockMember(Statement stmt) {
1278 if (stmt is Block && !stmt.statements.any(definesVariable)) {
1279 stmt.statements.forEach(writeBlockMember);
1280 } else if (stmt is EmptyStatement) {
1281 // do nothing
1282 } else {
1283 writeStatement(stmt);
1284 }
1285 }
1286
1287 void writeType(TypeAnnotation type) {
1288 write(type.name);
1289 if (type.typeArguments != null && type.typeArguments.length > 0) {
1290 write('<');
1291 writeEach(',', type.typeArguments, writeType);
1292 write('>');
1293 }
1294 }
1295
1296 /// A list of string quotings that the printer may use to quote strings.
1297 // Ignore multiline quotings for now. Would need to make sure that no
1298 // newline (potentially prefixed by whitespace) follows the quoting.
1299 static const _QUOTINGS = const <tree.StringQuoting>[
1300 const tree.StringQuoting(characters.$DQ, raw: false, leftQuoteLength: 1),
1301 const tree.StringQuoting(characters.$DQ, raw: true, leftQuoteLength: 1),
1302 const tree.StringQuoting(characters.$SQ, raw: false, leftQuoteLength: 1),
1303 const tree.StringQuoting(characters.$SQ, raw: true, leftQuoteLength: 1),
1304 ];
1305
1306 static StringLiteralOutput analyzeStringLiteral(Expression node) {
1307 // Flatten the StringConcat tree.
1308 List parts = []; // Expression or int (char node)
1309 void collectParts(Expression e) {
1310 if (e is StringConcat) {
1311 e.expressions.forEach(collectParts);
1312 } else if (e is Literal && e.value.isString) {
1313 for (int char in e.value.primitiveValue) {
1314 parts.add(char);
1315 }
1316 } else {
1317 parts.add(e);
1318 }
1319 }
1320 collectParts(node);
1321
1322 // We use a dynamic algorithm to compute the optimal way of printing
1323 // the string literal.
1324 //
1325 // Using string juxtapositions, it is possible to switch from one quoting
1326 // to another, e.g. the constant "''''" '""""' uses this trick.
1327 //
1328 // As we move through the string from left to right, we maintain a strategy
1329 // for each StringQuoting Q, denoting the best way to print the current
1330 // prefix so that we end with a string literal quoted with Q.
1331 // At every step, each strategy is either:
1332 // 1) Updated to include the cost of printing the next character.
1333 // 2) Abandoned because it is cheaper to use another strategy as prefix,
1334 // and then switching quotation using a juxtaposition.
1335
1336 int getQuoteCost(tree.StringQuoting quot) {
1337 return quot.leftQuoteLength + quot.rightQuoteLength;
1338 }
1339
1340 // Create initial scores for each StringQuoting and index them
1341 // into raw/non-raw and single-quote/double-quote.
1342 List<OpenStringChunk> best = <OpenStringChunk>[];
1343 List<int> raws = <int>[];
1344 List<int> nonRaws = <int>[];
1345 List<int> sqs = <int>[];
1346 List<int> dqs = <int>[];
1347 for (tree.StringQuoting q in _QUOTINGS) {
1348 OpenStringChunk chunk = new OpenStringChunk(null, q, getQuoteCost(q));
1349 int index = best.length;
1350 best.add(chunk);
1351
1352 if (q.raw) {
1353 raws.add(index);
1354 } else {
1355 nonRaws.add(index);
1356 }
1357 if (q.quote == characters.$SQ) {
1358 sqs.add(index);
1359 } else {
1360 dqs.add(index);
1361 }
1362 }
1363
1364 /// Applies additional cost to each track in [penalized], and considers
1365 /// switching from each [penalized] to a [nonPenalized] track.
1366 void penalize(List<int> penalized, List<int> nonPenalized, int endIndex,
1367 num cost(tree.StringQuoting q)) {
1368 for (int j in penalized) {
1369 // Check if another track can benefit from switching from this track.
1370 for (int k in nonPenalized) {
1371 num newCost = best[j].cost +
1372 1 // Whitespace in string juxtaposition
1373 +
1374 getQuoteCost(best[k].quoting);
1375 if (newCost < best[k].cost) {
1376 best[k] = new OpenStringChunk(
1377 best[j].end(endIndex), best[k].quoting, newCost);
1378 }
1379 }
1380 best[j].cost += cost(best[j].quoting);
1381 }
1382 }
1383
1384 // Iterate through the string and update the score for each StringQuoting.
1385 for (int i = 0; i < parts.length; i++) {
1386 var part = parts[i];
1387 if (part is int) {
1388 int char = part;
1389 switch (char) {
1390 case characters.$$:
1391 case characters.$BACKSLASH:
1392 penalize(nonRaws, raws, i, (q) => 1);
1393 break;
1394 case characters.$DQ:
1395 penalize(dqs, sqs, i, (q) => q.raw ? double.INFINITY : 1);
1396 break;
1397 case characters.$SQ:
1398 penalize(sqs, dqs, i, (q) => q.raw ? double.INFINITY : 1);
1399 break;
1400 case characters.$LF:
1401 case characters.$CR:
1402 case characters.$FF:
1403 case characters.$BS:
1404 case characters.$VTAB:
1405 case characters.$TAB:
1406 case characters.$EOF:
1407 penalize(raws, nonRaws, i, (q) => double.INFINITY);
1408 break;
1409 }
1410 } else {
1411 // Penalize raw literals for string interpolation.
1412 penalize(raws, nonRaws, i, (q) => double.INFINITY);
1413
1414 // Splitting a string can sometimes allow us to use a shorthand
1415 // string interpolation that would otherwise be illegal.
1416 // E.g. "...${foo}x..." -> "...$foo" 'x...'
1417 // If are other factors that make splitting advantageous,
1418 // we can gain even more by doing the split here.
1419 if (part is Identifier &&
1420 !part.name.contains(r'$') &&
1421 i + 1 < parts.length &&
1422 isIdentifierPartNoDollar(parts[i + 1])) {
1423 for (int j in nonRaws) {
1424 for (int k = 0; k < best.length; k++) {
1425 num newCost = best[j].cost +
1426 1 // Whitespace in string juxtaposition
1427 -
1428 2 // Save two curly braces
1429 +
1430 getQuoteCost(best[k].quoting);
1431 if (newCost < best[k].cost) {
1432 best[k] = new OpenStringChunk(
1433 best[j].end(i + 1), best[k].quoting, newCost);
1434 }
1435 }
1436 }
1437 }
1438 }
1439 }
1440
1441 // Select the cheapest strategy
1442 OpenStringChunk bestChunk = best[0];
1443 for (OpenStringChunk chunk in best) {
1444 if (chunk.cost < bestChunk.cost) {
1445 bestChunk = chunk;
1446 }
1447 }
1448
1449 return new StringLiteralOutput(parts, bestChunk.end(parts.length));
1450 }
1451
1452 void writeStringLiteral(Expression node) {
1453 StringLiteralOutput output = analyzeStringLiteral(node);
1454 List parts = output.parts;
1455 void printChunk(StringChunk chunk) {
1456 int startIndex;
1457 if (chunk.previous != null) {
1458 printChunk(chunk.previous);
1459 write(' '); // String juxtaposition requires a space between literals.
1460 startIndex = chunk.previous.endIndex;
1461 } else {
1462 startIndex = 0;
1463 }
1464 if (chunk.quoting.raw) {
1465 write('r');
1466 }
1467 write(chunk.quoting.quoteChar);
1468 bool raw = chunk.quoting.raw;
1469 int quoteCode = chunk.quoting.quote;
1470 for (int i = startIndex; i < chunk.endIndex; i++) {
1471 var part = parts[i];
1472 if (part is int) {
1473 int char = part;
1474 write(getEscapedCharacter(char, quoteCode, raw));
1475 } else if (part is Identifier &&
1476 !part.name.contains(r'$') &&
1477 (i == chunk.endIndex - 1 ||
1478 !isIdentifierPartNoDollar(parts[i + 1]))) {
1479 write(r'$');
1480 write(part.name);
1481 } else {
1482 write(r'${');
1483 writeExpression(part);
1484 write('}');
1485 }
1486 }
1487 write(chunk.quoting.quoteChar);
1488 }
1489 printChunk(output.chunk);
1490 }
1491
1492 static String getEscapedCharacter(int char, int quoteCode, bool raw) {
1493 switch (char) {
1494 case characters.$$:
1495 return raw ? r'$' : r'\$';
1496 case characters.$BACKSLASH:
1497 return raw ? r'\' : r'\\';
1498 case characters.$DQ:
1499 return quoteCode == char ? r'\"' : r'"';
1500 case characters.$SQ:
1501 return quoteCode == char ? r"\'" : r"'";
1502 case characters.$LF:
1503 return r'\n';
1504 case characters.$CR:
1505 return r'\r';
1506 case characters.$FF:
1507 return r'\f';
1508 case characters.$BS:
1509 return r'\b';
1510 case characters.$TAB:
1511 return r'\t';
1512 case characters.$VTAB:
1513 return r'\v';
1514 case characters.$EOF:
1515 return r'\x00';
1516 default:
1517 return new String.fromCharCode(char);
1518 }
1519 }
1520 }
1521
1522 /// The contents of a string literal together with a strategy for printing it.
1523 class StringLiteralOutput {
1524 /// Mix of [Expression] and `int`. Each expression is a string interpolation,
1525 /// and each `int` is the character code of a character in a string literal.
1526 final List parts;
1527 final StringChunk chunk;
1528
1529 StringLiteralOutput(this.parts, this.chunk);
1530 }
1531
1532 /// Strategy for printing a prefix of a string literal.
1533 /// A chunk represents the substring going from [:previous.endIndex:] to
1534 /// [endIndex] (or from 0 to [endIndex] if [previous] is null).
1535 class StringChunk {
1536 final StringChunk previous;
1537 final tree.StringQuoting quoting;
1538 final int endIndex;
1539
1540 StringChunk(this.previous, this.quoting, this.endIndex);
1541 }
1542
1543 /// [StringChunk] that has not yet been assigned an [endIndex].
1544 /// It additionally has a [cost] denoting the number of auxilliary characters
1545 /// (quotes, spaces, etc) needed to print the literal using this strategy
1546 class OpenStringChunk {
1547 final StringChunk previous;
1548 final tree.StringQuoting quoting;
1549 num cost;
1550
1551 OpenStringChunk(this.previous, this.quoting, this.cost);
1552
1553 StringChunk end(int endIndex) {
1554 return new StringChunk(previous, quoting, endIndex);
1555 }
1556 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698