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

Commit 1aacdfa

Browse files
authored
Boom expression args (#133)
* Initial support for Expression(s). * More in changelog. * Add context everywhere. * All tests passing. * More cleanup. * More expression work. * Improve calls, add property access. * Update README. * Actually update it. * Be nice to users. * Add literalString, literalMap.
1 parent de8f4e9 commit 1aacdfa

19 files changed

+380
-88
lines changed

CHANGELOG.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,31 @@
88
* Added `show|hide` to `Directive`.
99
* Added `Directive.importDeferredAs`.
1010
* Added a new line character after emitting some types (class, method, etc).
11+
* Added `refer` as a short-hand for `new Reference(...)`.
12+
* `Reference` now implements `Expression`.
1113

12-
* `SpecVisitor<T>`'s methods all have an optional `[T context]` parameter now.
13-
* This makes it much easier to avoid allocating extra `StringBuffer`s.
14-
* Removed `SimpleSpecVisitor` (it was unused).
15-
* Removed `implements Reference` from `Method` and `Field`; not a lot of value.
16-
* `equalsDart` removes insignificant white space before comparing results.
17-
18-
* In process of adding classes/methods for writing bodies of `Code` fluently:
14+
* Added many classes/methods for writing bodies of `Code` fluently:
1915
* `Expression`
2016
* `LiteralExpression`
2117
* `literal`
2218
* `literalNull`
2319
* `literalBool`
2420
* `literalTrue`
2521
* `literalFalse`
26-
* `literalList`
22+
* `literalNum`
23+
* `literalString`
24+
* `literalList` and `literalConstList`
25+
* `literalMap` and `literalConstMap`
2726
* `const Code(staticString)`
2827
* `const Code.scope((allocate) => '')`
2928

29+
* Removed `SimpleSpecVisitor` (it was unused).
30+
* Removed `implements Reference` from `Method` and `Field`; not a lot of value.
31+
32+
* `SpecVisitor<T>`'s methods all have an optional `[T context]` parameter now.
33+
* This makes it much easier to avoid allocating extra `StringBuffer`s.
34+
* `equalsDart` removes insignificant white space before comparing results.
35+
3036
## 2.0.0-alpha+1
3137

3238
* Removed `Reference.localScope`. Just use `Reference(symbol)` now.

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ import 'package:dart_style/dart_style.dart';
4040
void main() {
4141
final animal = new Class((b) => b
4242
..name = 'Animal'
43-
..extend = const Reference('Organism').toType()
43+
..extend = refer('Organism').toType()
4444
..methods.add(new Method.returnsVoid((b) => b
4545
..name = 'eat'
4646
..lambda = true
47-
..body = new Code((b) => b..code = 'print(\'Yum\')'))));
47+
..body = const Code('print(\'Yum\')'))));
4848
final emitter = const DartEmitter();
4949
print(new DartFormatter().format('${animal.accept(emitter)}'));
5050
}
@@ -70,11 +70,11 @@ void main() {
7070
new Method((b) => b
7171
..body = new Code((b) => b.code = '')
7272
..name = 'doThing'
73-
..returns = const Reference('Thing', 'package:a/a.dart')),
73+
..returns = refer('Thing', 'package:a/a.dart')),
7474
new Method((b) => b
7575
..body = new Code((b) => b..code = '')
7676
..name = 'doOther'
77-
..returns = const Reference('Other', 'package:b/b.dart')),
77+
..returns = refer('Other', 'package:b/b.dart')),
7878
]));
7979
final emitter = new DartEmitter(new Allocator.simplePrefixing());
8080
print(new DartFormatter().format('${library.accept(emitter)}'));

example/animal.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'package:dart_style/dart_style.dart';
88
void main() {
99
final animal = new Class((b) => b
1010
..name = 'Animal'
11-
..extend = const Reference('Organism')
11+
..extend = refer('Organism')
1212
..methods.add(new Method.returnsVoid((b) => b
1313
..name = 'eat'
1414
..lambda = true

example/scope.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ void main() {
1010
new Method((b) => b
1111
..body = const Code('')
1212
..name = 'doThing'
13-
..returns = const Reference('Thing', 'package:a/a.dart')),
13+
..returns = refer('Thing', 'package:a/a.dart')),
1414
new Method((b) => b
1515
..body = const Code('')
1616
..name = 'doOther'
17-
..returns = const Reference('Other', 'package:b/b.dart')),
17+
..returns = refer('Other', 'package:b/b.dart')),
1818
]));
1919
final emitter = new DartEmitter(new Allocator.simplePrefixing());
2020
print(new DartFormatter().format('${library.accept(emitter)}'));

lib/code_builder.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ export 'src/specs/expression.dart'
2828
literalBool,
2929
literalList,
3030
literalConstList,
31+
literalMap,
32+
literalConstMap,
33+
literalString,
3134
literalTrue,
3235
literalFalse;
3336
export 'src/specs/field.dart' show Field, FieldBuilder, FieldModifier;
@@ -40,5 +43,5 @@ export 'src/specs/method.dart'
4043
MethodType,
4144
Parameter,
4245
ParameterBuilder;
43-
export 'src/specs/reference.dart' show Reference;
46+
export 'src/specs/reference.dart' show refer, Reference;
4447
export 'src/specs/type_reference.dart' show TypeReference, TypeReferenceBuilder;

lib/src/emitter.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:meta/meta.dart';
6+
57
import 'allocator.dart';
68
import 'base.dart';
79
import 'specs/annotation.dart';
@@ -17,6 +19,33 @@ import 'specs/reference.dart';
1719
import 'specs/type_reference.dart';
1820
import 'visitors.dart';
1921

22+
/// Helper method improving on [StringSink.writeAll].
23+
///
24+
/// For every `Spec` in [elements], executing [visit].
25+
///
26+
/// If [elements] is at least 2 elements, inserts [separator] delimiting them.
27+
@visibleForTesting
28+
void visitAll<T>(
29+
Iterable<T> elements,
30+
StringSink output,
31+
void visit(T element), [
32+
String separator = ', ',
33+
]) {
34+
// Basically, this whole method is an improvement on
35+
// output.writeAll(specs.map((s) => s.accept(visitor));
36+
//
37+
// ... which would allocate more StringBuffer(s) for a one-time use.
38+
if (elements.isEmpty) {
39+
return;
40+
}
41+
final iterator = elements.iterator..moveNext();
42+
visit(iterator.current);
43+
while (iterator.moveNext()) {
44+
output.write(separator);
45+
visit(iterator.current);
46+
}
47+
}
48+
2049
class DartEmitter extends Object
2150
with CodeEmitter, ExpressionEmitter
2251
implements SpecVisitor<StringSink> {

lib/src/specs/code.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import 'package:meta/meta.dart';
88

99
import '../allocator.dart';
1010
import '../base.dart';
11-
import '../specs/reference.dart';
1211
import '../visitors.dart';
1312

13+
import 'reference.dart';
14+
1415
/// Returns a scoped symbol to [Reference], with an import prefix if needed.
1516
///
1617
/// This is short-hand for [Allocator.allocate] in most implementations.

lib/src/specs/expression.dart

Lines changed: 116 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ library code_builder.src.specs.expression;
77
import 'package:meta/meta.dart';
88

99
import '../base.dart';
10+
import '../emitter.dart';
1011
import '../visitors.dart';
1112
import 'code.dart';
1213
import 'reference.dart';
@@ -25,31 +26,77 @@ abstract class Expression implements Spec {
2526
@override
2627
R accept<R>(covariant ExpressionVisitor<R> visitor, [R context]);
2728

29+
/// Returns the expression as a valid [Code] block.
30+
Code asCode() => new _AsExpressionCode(this);
31+
2832
/// Returns the result of [this] `&&` [other].
2933
Expression and(Expression other) {
3034
return new BinaryExpression._(toExpression(), other, '&&');
3135
}
3236

3337
/// Call this expression as a method.
34-
Expression call() {
35-
return new InvokeExpression._(this);
38+
Expression call(
39+
List<Expression> positionalArguments, [
40+
Map<String, Expression> namedArguments = const {},
41+
]) {
42+
return new InvokeExpression._(
43+
this,
44+
positionalArguments,
45+
namedArguments,
46+
);
47+
}
48+
49+
/// Returns an expression accessing `.<name>` on this expression.
50+
Expression property(String name) {
51+
return new BinaryExpression._(
52+
this,
53+
new LiteralExpression._(name),
54+
'.',
55+
false,
56+
);
3657
}
3758

3859
/// Returns a new instance of this expression.
39-
Expression newInstance() {
40-
return new InvokeExpression._new(this);
60+
Expression newInstance(
61+
List<Expression> positionalArguments, [
62+
Map<String, Expression> namedArguments = const {},
63+
]) {
64+
return new InvokeExpression._new(
65+
this,
66+
positionalArguments,
67+
namedArguments,
68+
);
4169
}
4270

4371
/// Returns a const instance of this expression.
44-
Expression constInstance() {
45-
return new InvokeExpression._const(this);
72+
Expression constInstance(
73+
List<Expression> positionalArguments, [
74+
Map<String, Expression> namedArguments = const {},
75+
]) {
76+
return new InvokeExpression._const(
77+
this,
78+
positionalArguments,
79+
namedArguments,
80+
);
4681
}
4782

4883
/// May be overriden to support other types implementing [Expression].
4984
@visibleForOverriding
5085
Expression toExpression() => this;
5186
}
5287

88+
/// Represents a [code] block that wraps an [Expression].
89+
class _AsExpressionCode implements Code {
90+
final Expression code;
91+
92+
const _AsExpressionCode(this.code);
93+
94+
@override
95+
R accept<R>(CodeVisitor<R> visitor, [R context]) {
96+
return code.accept(visitor as ExpressionVisitor<R>, context);
97+
}
98+
}
99+
53100
/// Knowledge of different types of expressions in Dart.
54101
///
55102
/// **INTERNAL ONLY**.
@@ -59,6 +106,7 @@ abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
59106
T visitInvokeExpression(InvokeExpression expression, [T context]);
60107
T visitLiteralExpression(LiteralExpression expression, [T context]);
61108
T visitLiteralListExpression(LiteralListExpression expression, [T context]);
109+
T visitLiteralMapExpression(LiteralMapExpression expression, [T context]);
62110
}
63111

64112
/// Knowledge of how to write valid Dart code from [ExpressionVisitor].
@@ -68,12 +116,16 @@ abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
68116
@override
69117
visitBinaryExpression(BinaryExpression expression, [StringSink output]) {
70118
output ??= new StringBuffer();
71-
return output
72-
..write(expression.left.accept(this))
73-
..write(' ')
74-
..write(expression.operator)
75-
..write(' ')
76-
..write(expression.right.accept(this));
119+
expression.left.accept(this, output);
120+
if (expression.addSpace) {
121+
output.write(' ');
122+
}
123+
output.write(expression.operator);
124+
if (expression.addSpace) {
125+
output.write(' ');
126+
}
127+
expression.right.accept(this, output);
128+
return output;
77129
}
78130

79131
@override
@@ -95,7 +147,19 @@ abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
95147
break;
96148
}
97149
expression.target.accept(this, output);
98-
return output..write('()');
150+
output.write('(');
151+
visitAll<Spec>(expression.positionalArguments, output, (spec) {
152+
spec.accept(this, output);
153+
});
154+
if (expression.positionalArguments.isNotEmpty &&
155+
expression.namedArguments.isNotEmpty) {
156+
output.write(', ');
157+
}
158+
visitAll<String>(expression.namedArguments.keys, output, (name) {
159+
output..write(name)..write(': ');
160+
expression.namedArguments[name].accept(this, output);
161+
});
162+
return output..write(')');
99163
}
100164

101165
@override
@@ -104,6 +168,14 @@ abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
104168
return output..write(expression.literal);
105169
}
106170

171+
void _acceptLiteral(Object literalOrSpec, StringSink output) {
172+
if (literalOrSpec is Spec) {
173+
literalOrSpec.accept(this, output);
174+
return;
175+
}
176+
literal(literalOrSpec).accept(this, output);
177+
}
178+
107179
@override
108180
visitLiteralListExpression(
109181
LiteralListExpression expression, [
@@ -119,18 +191,39 @@ abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
119191
output.write('>');
120192
}
121193
output.write('[');
122-
// ignore: prefer_final_locals
123-
for (var i = 0, l = expression.values.length; i < l; i++) {
124-
final value = expression.values[i];
125-
if (value is Spec) {
126-
value.accept(this, output);
194+
visitAll<Object>(expression.values, output, (value) {
195+
_acceptLiteral(value, output);
196+
});
197+
return output..write(']');
198+
}
199+
200+
@override
201+
visitLiteralMapExpression(
202+
LiteralMapExpression expression, [
203+
StringSink output,
204+
]) {
205+
output ??= new StringBuffer();
206+
if (expression.isConst) {
207+
output.write('const ');
208+
}
209+
if (expression.keyType != null) {
210+
output.write('<');
211+
expression.keyType.accept(this, output);
212+
output.write(', ');
213+
if (expression.valueType == null) {
214+
const Reference('dynamic', 'dart:core').accept(this, output);
127215
} else {
128-
literal(value).accept(this, output);
129-
}
130-
if (i < l - 1) {
131-
output.write(', ');
216+
expression.valueType.accept(this, output);
132217
}
218+
output.write('>');
133219
}
134-
return output..write(']');
220+
output.write('{');
221+
visitAll<Object>(expression.values.keys, output, (key) {
222+
final value = expression.values[key];
223+
_acceptLiteral(key, output);
224+
output.write(': ');
225+
_acceptLiteral(value, output);
226+
});
227+
return output..write('}');
135228
}
136229
}

lib/src/specs/expression/binary.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ class BinaryExpression extends Expression {
99
final Expression left;
1010
final Expression right;
1111
final String operator;
12+
final bool addSpace;
1213

13-
const BinaryExpression._(this.left, this.right, this.operator);
14+
const BinaryExpression._(
15+
this.left,
16+
this.right,
17+
this.operator, [
18+
this.addSpace = true,
19+
]);
1420

1521
@override
1622
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {

0 commit comments

Comments
 (0)