Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit e2c28f2

Browse files
author
Harry Terkelsen
authored
Add assignment expressions. (#13)
Add assignment expressions. Assignment expressions can be either normal or null-aware (using ??=). This commit also does some cleanup regarding Tokens, moving all Token literals to a shared file.
1 parent 1fafa99 commit e2c28f2

14 files changed

+205
-97
lines changed

lib/code_builder.dart

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ library code_builder;
3030

3131
import 'package:analyzer/analyzer.dart';
3232
import 'package:analyzer/dart/ast/token.dart';
33-
import 'package:analyzer/src/dart/ast/token.dart';
3433
import 'package:dart_style/dart_style.dart';
3534
import 'package:meta/meta.dart';
3635

3736
import 'src/analyzer_patch.dart';
37+
import 'src/tokens.dart';
3838

3939
part 'src/builders/annotation_builder.dart';
4040
part 'src/builders/class_builder.dart';
@@ -56,13 +56,10 @@ final DartFormatter _dartfmt = new DartFormatter();
5656
@visibleForTesting
5757
String dartfmt(String source) => _dartfmt.format(source);
5858

59-
Identifier _stringIdentifier(String s) {
60-
return new SimpleIdentifier(new StringToken(TokenType.STRING, s, 0));
61-
}
59+
SimpleIdentifier _stringIdentifier(String s) =>
60+
new SimpleIdentifier(stringToken(s));
6261

63-
Literal _stringLiteral(String s) {
64-
return new SimpleStringLiteral(new StringToken(TokenType.STRING, s, 0), s);
65-
}
62+
Literal _stringLiteral(String s) => new SimpleStringLiteral(stringToken(s), s);
6663

6764
/// Base class for building and emitting a Dart language [AstNode].
6865
abstract class CodeBuilder<A extends AstNode> {

lib/src/builders/class_builder.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ part of code_builder;
66

77
/// Builds a [ClassDeclaration] AST.
88
class ClassBuilder implements CodeBuilder<ClassDeclaration> {
9-
static Token _abstract = new KeywordToken(Keyword.ABSTRACT, 0);
10-
static Token _extends = new KeywordToken(Keyword.EXTENDS, 0);
11-
static Token _implements = new KeywordToken(Keyword.IMPLEMENTS, 0);
12-
static Token _with = new KeywordToken(Keyword.WITH, 0);
13-
149
final String _name;
1510
final bool _isAbstract;
1611
final TypeBuilder _extend;
@@ -85,18 +80,18 @@ class ClassBuilder implements CodeBuilder<ClassDeclaration> {
8580
ClassDeclaration toAst([Scope scope = const Scope.identity()]) {
8681
var astNode = _emptyClassDeclaration()..name = _stringIdentifier(_name);
8782
if (_isAbstract) {
88-
astNode.abstractKeyword = _abstract;
83+
astNode.abstractKeyword = $abstract;
8984
}
9085
if (_extend != null) {
91-
astNode.extendsClause = new ExtendsClause(_extends, _extend.toAst(scope));
86+
astNode.extendsClause = new ExtendsClause($extends, _extend.toAst(scope));
9287
}
9388
if (_implement.isNotEmpty) {
94-
astNode.implementsClause = new ImplementsClause(_implements,
89+
astNode.implementsClause = new ImplementsClause($implements,
9590
_implement.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
9691
}
9792
if (_mixin.isNotEmpty) {
9893
astNode.withClause = new WithClause(
99-
_with, _mixin.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
94+
$with, _mixin.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
10095
}
10196
astNode
10297
..metadata.addAll(_metadata.map/*<Annotation>*/((a) => a.toAst(scope)));

lib/src/builders/constructor_builder.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ part of code_builder;
88
///
99
/// Similar to [MethodBuilder] but with constructor-only features.
1010
class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
11-
static final Token _const = new KeywordToken(Keyword.CONST, 0);
12-
static final Token _this = new KeywordToken(Keyword.THIS, 0);
13-
1411
final bool _isConstant;
1512
final String _name;
1613
final List<ParameterBuilder> _parameters = <ParameterBuilder>[];
@@ -43,7 +40,7 @@ class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
4340
null,
4441
null,
4542
null,
46-
_isConstant ? _const : null,
43+
_isConstant ? $const : null,
4744
null,
4845
null,
4946
_name != null ? _stringIdentifier(_name) : null,
@@ -53,7 +50,7 @@ class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
5350
null,
5451
null,
5552
null,
56-
new EmptyFunctionBody(MethodBuilder._semicolon),
53+
new EmptyFunctionBody($semicolon),
5754
);
5855
return astNode;
5956
}

lib/src/builders/expression_builder.dart

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,8 @@ const literalNull = const _LiteralNull();
1414
/// Represents an expression value of `true`.
1515
const literalTrue = const LiteralBool(true);
1616

17-
// Returns wrapped as a [ExpressionFunctionBody] AST.
18-
final Token _closeP = new Token(TokenType.CLOSE_PAREN, 0);
19-
20-
// Returns wrapped as a [FunctionExpression] AST.
21-
final Token _openP = new Token(TokenType.OPEN_PAREN, 0);
22-
23-
final Token _semicolon = new Token(TokenType.SEMICOLON, 0);
24-
2517
// TODO(matanl): Make this part of the public API. See annotation_builder.dart.
18+
// Returns wrapped as a [ExpressionFunctionBody] AST.
2619
ExpressionFunctionBody _asFunctionBody(
2720
CodeBuilder<Expression> expression,
2821
Scope scope,
@@ -31,22 +24,23 @@ ExpressionFunctionBody _asFunctionBody(
3124
null,
3225
null,
3326
expression.toAst(scope),
34-
_semicolon,
27+
$semicolon,
3528
);
3629
}
3730

31+
// Returns wrapped as a [FunctionExpression] AST.
3832
FunctionExpression _asFunctionExpression(
3933
CodeBuilder<Expression> expression,
4034
Scope scope,
4135
) {
4236
return new FunctionExpression(
4337
null,
4438
new FormalParameterList(
45-
_openP,
39+
$openParen,
4640
const [],
4741
null,
4842
null,
49-
_closeP,
43+
$closeParen,
5044
),
5145
_asFunctionBody(expression, scope),
5246
);
@@ -108,6 +102,15 @@ abstract class ExpressionBuilder implements CodeBuilder<Expression> {
108102
);
109103
}
110104

105+
/// Assign [left] to [right].
106+
///
107+
/// If [nullAware] is true, the assignment uses the `??=` operator.
108+
factory ExpressionBuilder.assignment(
109+
String left, CodeBuilder<Expression> right,
110+
{bool nullAware: false}) {
111+
return new _AssignmentExpression(left, right, nullAware: nullAware);
112+
}
113+
111114
const ExpressionBuilder._();
112115

113116
/// Return a new [ExpressionBuilder] invoking the result of this expression.
@@ -136,10 +139,8 @@ abstract class ExpressionBuilder implements CodeBuilder<Expression> {
136139

137140
/// Creates a new literal `bool` value.
138141
class LiteralBool extends _LiteralExpression<BooleanLiteral> {
139-
static final BooleanLiteral _true =
140-
new BooleanLiteral(new KeywordToken(Keyword.TRUE, 0), true);
141-
static final BooleanLiteral _false =
142-
new BooleanLiteral(new KeywordToken(Keyword.FALSE, 0), false);
142+
static final BooleanLiteral _true = new BooleanLiteral($true, true);
143+
static final BooleanLiteral _false = new BooleanLiteral($false, false);
143144

144145
final bool _value;
145146

@@ -159,7 +160,7 @@ class LiteralInt extends _LiteralExpression<IntegerLiteral> {
159160

160161
@override
161162
IntegerLiteral toAst([_]) => new IntegerLiteral(
162-
new StringToken(TokenType.INT, '$_value', 0),
163+
intToken(_value),
163164
_value,
164165
);
165166
}
@@ -173,18 +174,12 @@ class LiteralString extends _LiteralExpression<StringLiteral> {
173174

174175
@override
175176
StringLiteral toAst([_]) => new SimpleStringLiteral(
176-
new StringToken(
177-
TokenType.STRING,
178-
"'$_value'",
179-
0,
180-
),
177+
stringToken("'$_value'"),
181178
_value,
182179
);
183180
}
184181

185182
class _InvokeExpression extends ExpressionBuilder {
186-
static final Token _colon = new Token(TokenType.COLON, 0);
187-
188183
final String _importFrom;
189184
final ExpressionBuilder _target;
190185
final String _name;
@@ -231,18 +226,18 @@ class _InvokeExpression extends ExpressionBuilder {
231226
// TODO(matanl): Move to TypeBuilder.newInstance.
232227
if (_type != null) {
233228
return new InstanceCreationExpression(
234-
new KeywordToken(Keyword.NEW, 0),
229+
$new,
235230
new ConstructorName(
236231
_type.toAst(scope),
237-
_name != null ? new Token(TokenType.PERIOD, 0) : null,
232+
_name != null ? $period : null,
238233
_name != null ? _stringIdentifier(_name) : null,
239234
),
240235
_getArgumentList(scope),
241236
);
242237
}
243238
return new MethodInvocation(
244239
_target?.toAst(scope),
245-
_target != null ? new Token(TokenType.PERIOD, 0) : null,
240+
_target != null ? $period : null,
246241
_stringIdentifier(_name),
247242
null,
248243
_getArgumentList(scope),
@@ -254,21 +249,46 @@ class _InvokeExpression extends ExpressionBuilder {
254249

255250
ArgumentList _getArgumentList(Scope scope) {
256251
return new ArgumentList(
257-
new Token(TokenType.OPEN_CURLY_BRACKET, 0),
252+
$openCurly,
258253
_positionalArguments.map/*<Expression*/((p) => p.toAst(scope)).toList()
259254
..addAll(_namedArguments.keys
260255
.map/*<Expression>*/((name) => new NamedExpression(
261256
new Label(
262257
_stringIdentifier(name),
263-
_colon,
258+
$colon,
264259
),
265260
_namedArguments[name].toAst(scope),
266261
))),
267-
new Token(TokenType.CLOSE_CURLY_BRACKET, 0),
262+
$closeCurly,
268263
);
269264
}
270265
}
271266

267+
class _AssignmentExpression extends ExpressionBuilder {
268+
final String left;
269+
final CodeBuilder<Expression> right;
270+
final bool nullAware;
271+
272+
_AssignmentExpression(this.left, this.right, {this.nullAware: false})
273+
: super._();
274+
275+
@override
276+
ExpressionBuilder invokeSelf(String name,
277+
{Iterable<CodeBuilder<Expression>> positional: const [],
278+
Map<String, CodeBuilder<Expression>> named: const {}}) {
279+
return _invokeSelfImpl(this, name, positional: positional, named: named);
280+
}
281+
282+
@override
283+
Expression toAst([Scope scope = const Scope.identity()]) {
284+
return new AssignmentExpression(_stringIdentifier(left),
285+
nullAware ? $nullAwareEquals : $equals, right.toAst(scope));
286+
}
287+
288+
@override
289+
StatementBuilder toStatement() => new _ExpressionStatementBuilder(this);
290+
}
291+
272292
abstract class _LiteralExpression<A extends Literal>
273293
implements ExpressionBuilder, CodeBuilder<A> {
274294
const _LiteralExpression();
@@ -296,7 +316,7 @@ abstract class _LiteralExpression<A extends Literal>
296316
}
297317

298318
class _LiteralNull extends _LiteralExpression<NullLiteral> {
299-
static NullLiteral _null = new NullLiteral(new KeywordToken(Keyword.NULL, 0));
319+
static final NullLiteral _null = new NullLiteral($null);
300320

301321
const _LiteralNull();
302322

lib/src/builders/field_builder.dart

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ part of code_builder;
1010
/// AST (for class members) via [toFieldAst] or a variable declaration (used
1111
/// both at the top-level and within methods) via [toVariablesAst].
1212
class FieldBuilder implements CodeBuilder<Declaration> {
13-
static Token _equals = new Token(TokenType.EQ, 0);
14-
static Token _semicolon = new Token(TokenType.SEMICOLON, 0);
15-
static Token _static = new KeywordToken(Keyword.STATIC, 0);
16-
static Token _final = new KeywordToken(Keyword.FINAL, 0);
17-
static Token _const = new KeywordToken(Keyword.CONST, 0);
18-
static Token _var = new KeywordToken(Keyword.VAR, 0);
19-
2013
final bool _isConst;
2114
final bool _isFinal;
2215
final bool _isStatic;
@@ -84,7 +77,7 @@ class FieldBuilder implements CodeBuilder<Declaration> {
8477
new FieldDeclaration(
8578
null,
8679
null,
87-
_isStatic ? _static : null,
80+
_isStatic ? $static : null,
8881
toVariablesAst(scope),
8982
null,
9083
);
@@ -100,19 +93,19 @@ class FieldBuilder implements CodeBuilder<Declaration> {
10093
[
10194
new VariableDeclaration(
10295
_stringIdentifier(_name),
103-
_initialize != null ? _equals : null,
96+
_initialize != null ? $equals : null,
10497
_initialize?.toAst(scope),
10598
)
10699
],
107100
);
108101

109102
Token _getVariableKeyword() {
110103
if (_isFinal) {
111-
return _final;
104+
return $final;
112105
}
113106
if (_isConst) {
114-
return _const;
107+
return $const;
115108
}
116-
return _type == null ? _var : null;
109+
return _type == null ? $var : null;
117110
}
118111
}

lib/src/builders/file_builder.dart

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,6 @@ class ImportBuilder implements CodeBuilder<ImportDirective> {
9292

9393
/// Builds a standalone Dart library [CompilationUnit] AST.
9494
class LibraryBuilder extends FileBuilder {
95-
static final Token _library = new KeywordToken(Keyword.LIBRARY, 0);
96-
9795
final String _name;
9896
final Scope _scope;
9997

@@ -128,7 +126,7 @@ class LibraryBuilder extends FileBuilder {
128126
new LibraryDirective(
129127
null,
130128
null,
131-
_library,
129+
$library,
132130
new LibraryIdentifier([_stringIdentifier(_name)]),
133131
null,
134132
),
@@ -142,9 +140,6 @@ class LibraryBuilder extends FileBuilder {
142140

143141
/// Builds a `part of` [CompilationUnit] AST for an existing Dart library.
144142
class PartBuilder extends FileBuilder {
145-
static final Token _part = new KeywordToken(Keyword.PART, 0);
146-
static final Token _of = new StringToken(TokenType.KEYWORD, 'of', 0);
147-
148143
final String _name;
149144

150145
/// Create a new `part of` source file.
@@ -158,8 +153,8 @@ class PartBuilder extends FileBuilder {
158153
originalAst.directives.add(new PartOfDirective(
159154
null,
160155
null,
161-
_part,
162-
_of,
156+
$part,
157+
$of,
163158
new LibraryIdentifier([_stringIdentifier(_name)]),
164159
null,
165160
));

0 commit comments

Comments
 (0)