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

Commit a9d0ce5

Browse files
authored
Add Block + BlockBuilder, some utilities. (#134)
* 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. * Add blocks of code (statements).
1 parent 1aacdfa commit a9d0ce5

File tree

9 files changed

+228
-8
lines changed

9 files changed

+228
-8
lines changed

lib/code_builder.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ export 'src/emitter.dart' show DartEmitter;
88
export 'src/matchers.dart' show equalsDart;
99
export 'src/specs/annotation.dart' show Annotation, AnnotationBuilder;
1010
export 'src/specs/class.dart' show Class, ClassBuilder;
11-
export 'src/specs/code.dart' show Code, StaticCode, ScopedCode;
11+
export 'src/specs/code.dart'
12+
show Block, BlockBuilder, Code, StaticCode, ScopedCode;
1213
export 'src/specs/constructor.dart' show Constructor, ConstructorBuilder;
1314
export 'src/specs/directive.dart'
1415
show Directive, DirectiveType, DirectiveBuilder;
1516
export 'src/specs/expression.dart'
1617
show
18+
AsCodeExpression,
1719
BinaryExpression,
1820
CodeExpression,
1921
Expression,

lib/src/emitter.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import 'visitors.dart';
2525
///
2626
/// If [elements] is at least 2 elements, inserts [separator] delimiting them.
2727
@visibleForTesting
28-
void visitAll<T>(
28+
StringSink visitAll<T>(
2929
Iterable<T> elements,
3030
StringSink output,
3131
void visit(T element), [
@@ -36,14 +36,15 @@ void visitAll<T>(
3636
//
3737
// ... which would allocate more StringBuffer(s) for a one-time use.
3838
if (elements.isEmpty) {
39-
return;
39+
return output;
4040
}
4141
final iterator = elements.iterator..moveNext();
4242
visit(iterator.current);
4343
while (iterator.moveNext()) {
4444
output.write(separator);
4545
visit(iterator.current);
4646
}
47+
return output;
4748
}
4849

4950
class DartEmitter extends Object

lib/src/matchers.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ String _dartfmt(String source) {
1919
} catch (_) {
2020
// Ignored on purpose, probably not exactly valid Dart code.
2121
} finally {
22-
source = source.replaceAll(' ', ' ').replaceAll('\n', '').trim();
22+
source = collapseWhitespace(source);
2323
}
2424
return source;
2525
}

lib/src/specs/code.dart

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44

55
library code_builder.src.specs.code;
66

7+
import 'package:built_value/built_value.dart';
8+
import 'package:built_collection/built_collection.dart';
79
import 'package:meta/meta.dart';
810

911
import '../allocator.dart';
1012
import '../base.dart';
13+
import '../emitter.dart';
1114
import '../visitors.dart';
1215

16+
import 'expression.dart';
1317
import 'reference.dart';
1418

19+
part 'code.g.dart';
20+
1521
/// Returns a scoped symbol to [Reference], with an import prefix if needed.
1622
///
1723
/// This is short-hand for [Allocator.allocate] in most implementations.
@@ -41,10 +47,40 @@ abstract class Code implements Spec {
4147
R accept<R>(covariant CodeVisitor<R> visitor, [R context]);
4248
}
4349

50+
/// Represents blocks of statements of Dart code.
51+
abstract class Block implements Built<Block, BlockBuilder>, Spec {
52+
factory Block([void updates(BlockBuilder b)]) = _$Block;
53+
54+
Block._();
55+
56+
@override
57+
R accept<R>(covariant CodeVisitor<R> visitor, [R context]) {
58+
return visitor.visitBlock(this, context);
59+
}
60+
61+
BuiltList<Code> get statements;
62+
}
63+
64+
abstract class BlockBuilder implements Builder<Block, BlockBuilder> {
65+
factory BlockBuilder() = _$BlockBuilder;
66+
67+
BlockBuilder._();
68+
69+
/// Adds an [expression] to [statements].
70+
///
71+
/// **NOTE**: Not all expressions are _useful_ statements.
72+
void addExpression(Expression expression) {
73+
statements.add(expression.asStatement());
74+
}
75+
76+
ListBuilder<Code> statements = new ListBuilder<Code>();
77+
}
78+
4479
/// Knowledge of different types of blocks of code in Dart.
4580
///
4681
/// **INTERNAL ONLY**.
4782
abstract class CodeVisitor<T> implements SpecVisitor<T> {
83+
T visitBlock(Block code, [T context]);
4884
T visitStaticCode(StaticCode code, [T context]);
4985
T visitScopedCode(ScopedCode code, [T context]);
5086
}
@@ -54,6 +90,14 @@ abstract class CodeEmitter implements CodeVisitor<StringSink> {
5490
@protected
5591
Allocator get allocator;
5692

93+
@override
94+
visitBlock(Block block, [StringSink output]) {
95+
output ??= new StringBuffer();
96+
return visitAll<Code>(block.statements, output, (statement) {
97+
statement.accept(this, output);
98+
}, '\n');
99+
}
100+
57101
@override
58102
visitStaticCode(StaticCode code, [StringSink output]) {
59103
output ??= new StringBuffer();
@@ -77,6 +121,9 @@ class StaticCode implements Code {
77121
R accept<R>(CodeVisitor<R> visitor, [R context]) {
78122
return visitor.visitStaticCode(this, context);
79123
}
124+
125+
@override
126+
String toString() => code;
80127
}
81128

82129
/// Represents a [code] block that may require scoping.
@@ -89,4 +136,7 @@ class ScopedCode implements Code {
89136
R accept<R>(CodeVisitor<R> visitor, [R context]) {
90137
return visitor.visitScopedCode(this, context);
91138
}
139+
140+
@override
141+
String toString() => code((ref) => ref.symbol);
92142
}

lib/src/specs/code.g.dart

Lines changed: 92 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/specs/expression.dart

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ abstract class Expression implements Spec {
2727
R accept<R>(covariant ExpressionVisitor<R> visitor, [R context]);
2828

2929
/// Returns the expression as a valid [Code] block.
30-
Code asCode() => new _AsExpressionCode(this);
30+
///
31+
/// Also see [asStatement].
32+
Code asCode() => new AsCodeExpression(this, false);
33+
34+
/// Returns the expression asa valid [Code] block with a trailing `;`.
35+
Code asStatement() => new AsCodeExpression(this, true);
3136

3237
/// Returns the result of [this] `&&` [other].
3338
Expression and(Expression other) {
@@ -86,21 +91,30 @@ abstract class Expression implements Spec {
8691
}
8792

8893
/// Represents a [code] block that wraps an [Expression].
89-
class _AsExpressionCode implements Code {
94+
class AsCodeExpression implements Code {
9095
final Expression code;
9196

92-
const _AsExpressionCode(this.code);
97+
/// Whether this code should be considered a _statement_.
98+
final bool isStatement;
99+
100+
@visibleForTesting
101+
const AsCodeExpression(this.code, [this.isStatement = false]);
93102

94103
@override
95104
R accept<R>(CodeVisitor<R> visitor, [R context]) {
96-
return code.accept(visitor as ExpressionVisitor<R>, context);
105+
return (visitor as ExpressionVisitor<R>)
106+
.visitAsCodeExpression(this, context);
97107
}
108+
109+
@override
110+
String toString() => code.toString();
98111
}
99112

100113
/// Knowledge of different types of expressions in Dart.
101114
///
102115
/// **INTERNAL ONLY**.
103116
abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
117+
T visitAsCodeExpression(AsCodeExpression code, [T context]);
104118
T visitBinaryExpression(BinaryExpression expression, [T context]);
105119
T visitCodeExpression(CodeExpression expression, [T context]);
106120
T visitInvokeExpression(InvokeExpression expression, [T context]);
@@ -113,6 +127,16 @@ abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
113127
///
114128
/// **INTERNAL ONLY**.
115129
abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
130+
@override
131+
visitAsCodeExpression(AsCodeExpression expression, [StringSink output]) {
132+
output ??= new StringBuffer();
133+
expression.code.accept(this, output);
134+
if (expression.isStatement) {
135+
output.write(';');
136+
}
137+
return output;
138+
}
139+
116140
@override
117141
visitBinaryExpression(BinaryExpression expression, [StringSink output]) {
118142
output ??= new StringBuffer();

lib/src/specs/expression/invoke.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class InvokeExpression extends Expression {
4141
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
4242
return visitor.visitInvokeExpression(this, context);
4343
}
44+
45+
@override
46+
String toString() =>
47+
'${type ?? ''} $target($positionalArguments, $namedArguments)';
4448
}
4549

4650
enum InvokeExpressionType {

lib/src/specs/expression/literal.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class LiteralExpression extends Expression {
9595
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
9696
return visitor.visitLiteralExpression(this, context);
9797
}
98+
99+
@override
100+
String toString() => literal;
98101
}
99102

100103
class LiteralListExpression extends Expression {
@@ -108,6 +111,9 @@ class LiteralListExpression extends Expression {
108111
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
109112
return visitor.visitLiteralListExpression(this, context);
110113
}
114+
115+
@override
116+
String toString() => '[${values.map(literal).join(', ')}]';
111117
}
112118

113119
class LiteralMapExpression extends Expression {
@@ -127,4 +133,7 @@ class LiteralMapExpression extends Expression {
127133
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
128134
return visitor.visitLiteralMapExpression(this, context);
129135
}
136+
137+
@override
138+
String toString() => '{$values}';
130139
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2017, 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+
import 'package:code_builder/code_builder.dart';
6+
import 'package:test/test.dart';
7+
8+
void main() {
9+
test('should emit a block of code', () {
10+
expect(
11+
new Block((b) => b.statements.addAll([
12+
const Code('if (foo) {'),
13+
const Code(' print(true);'),
14+
const Code('}'),
15+
])),
16+
equalsDart(r'''
17+
if (foo) {
18+
print(true);
19+
}
20+
'''),
21+
);
22+
});
23+
24+
test('should emit a block of code including expressions', () {
25+
expect(
26+
new Block((b) => b.statements.addAll([
27+
const Code('if (foo) {'),
28+
refer('print')([literalTrue]).asStatement(),
29+
const Code('}'),
30+
])),
31+
equalsDart(r'''
32+
if (foo) {
33+
print(true);
34+
}
35+
'''),
36+
);
37+
});
38+
}

0 commit comments

Comments
 (0)