Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/code_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export 'src/emitter.dart' show DartEmitter;
export 'src/matchers.dart' show equalsDart;
export 'src/specs/annotation.dart' show Annotation, AnnotationBuilder;
export 'src/specs/class.dart' show Class, ClassBuilder;
export 'src/specs/code.dart' show Code, StaticCode, ScopedCode;
export 'src/specs/code.dart'
show Block, BlockBuilder, Code, StaticCode, ScopedCode;
export 'src/specs/constructor.dart' show Constructor, ConstructorBuilder;
export 'src/specs/directive.dart'
show Directive, DirectiveType, DirectiveBuilder;
export 'src/specs/expression.dart'
show
AsCodeExpression,
BinaryExpression,
CodeExpression,
Expression,
Expand Down
5 changes: 3 additions & 2 deletions lib/src/emitter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import 'visitors.dart';
///
/// If [elements] is at least 2 elements, inserts [separator] delimiting them.
@visibleForTesting
void visitAll<T>(
StringSink visitAll<T>(
Iterable<T> elements,
StringSink output,
void visit(T element), [
Expand All @@ -36,14 +36,15 @@ void visitAll<T>(
//
// ... which would allocate more StringBuffer(s) for a one-time use.
if (elements.isEmpty) {
return;
return output;
}
final iterator = elements.iterator..moveNext();
visit(iterator.current);
while (iterator.moveNext()) {
output.write(separator);
visit(iterator.current);
}
return output;
}

class DartEmitter extends Object
Expand Down
2 changes: 1 addition & 1 deletion lib/src/matchers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ String _dartfmt(String source) {
} catch (_) {
// Ignored on purpose, probably not exactly valid Dart code.
} finally {
source = source.replaceAll(' ', ' ').replaceAll('\n', '').trim();
source = collapseWhitespace(source);
}
return source;
}
Expand Down
50 changes: 50 additions & 0 deletions lib/src/specs/code.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@

library code_builder.src.specs.code;

import 'package:built_value/built_value.dart';
import 'package:built_collection/built_collection.dart';
import 'package:meta/meta.dart';

import '../allocator.dart';
import '../base.dart';
import '../emitter.dart';
import '../visitors.dart';

import 'expression.dart';
import 'reference.dart';

part 'code.g.dart';

/// Returns a scoped symbol to [Reference], with an import prefix if needed.
///
/// This is short-hand for [Allocator.allocate] in most implementations.
Expand Down Expand Up @@ -41,10 +47,40 @@ abstract class Code implements Spec {
R accept<R>(covariant CodeVisitor<R> visitor, [R context]);
}

/// Represents blocks of statements of Dart code.
abstract class Block implements Built<Block, BlockBuilder>, Spec {
factory Block([void updates(BlockBuilder b)]) = _$Block;

Block._();

@override
R accept<R>(covariant CodeVisitor<R> visitor, [R context]) {
return visitor.visitBlock(this, context);
}

BuiltList<Code> get statements;
}

abstract class BlockBuilder implements Builder<Block, BlockBuilder> {
factory BlockBuilder() = _$BlockBuilder;

BlockBuilder._();

/// Adds an [expression] to [statements].
///
/// **NOTE**: Not all expressions are _useful_ statements.
void addExpression(Expression expression) {
statements.add(expression.asStatement());
}

ListBuilder<Code> statements = new ListBuilder<Code>();
}

/// Knowledge of different types of blocks of code in Dart.
///
/// **INTERNAL ONLY**.
abstract class CodeVisitor<T> implements SpecVisitor<T> {
T visitBlock(Block code, [T context]);
T visitStaticCode(StaticCode code, [T context]);
T visitScopedCode(ScopedCode code, [T context]);
}
Expand All @@ -54,6 +90,14 @@ abstract class CodeEmitter implements CodeVisitor<StringSink> {
@protected
Allocator get allocator;

@override
visitBlock(Block block, [StringSink output]) {
output ??= new StringBuffer();
return visitAll<Code>(block.statements, output, (statement) {
statement.accept(this, output);
}, '\n');
}

@override
visitStaticCode(StaticCode code, [StringSink output]) {
output ??= new StringBuffer();
Expand All @@ -77,6 +121,9 @@ class StaticCode implements Code {
R accept<R>(CodeVisitor<R> visitor, [R context]) {
return visitor.visitStaticCode(this, context);
}

@override
String toString() => code;
}

/// Represents a [code] block that may require scoping.
Expand All @@ -89,4 +136,7 @@ class ScopedCode implements Code {
R accept<R>(CodeVisitor<R> visitor, [R context]) {
return visitor.visitScopedCode(this, context);
}

@override
String toString() => code((ref) => ref.symbol);
}
92 changes: 92 additions & 0 deletions lib/src/specs/code.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 28 additions & 4 deletions lib/src/specs/expression.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ abstract class Expression implements Spec {
R accept<R>(covariant ExpressionVisitor<R> visitor, [R context]);

/// Returns the expression as a valid [Code] block.
Code asCode() => new _AsExpressionCode(this);
///
/// Also see [asStatement].
Code asCode() => new AsCodeExpression(this, false);

/// Returns the expression asa valid [Code] block with a trailing `;`.
Code asStatement() => new AsCodeExpression(this, true);

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

/// Represents a [code] block that wraps an [Expression].
class _AsExpressionCode implements Code {
class AsCodeExpression implements Code {
final Expression code;

const _AsExpressionCode(this.code);
/// Whether this code should be considered a _statement_.
final bool isStatement;

@visibleForTesting
const AsCodeExpression(this.code, [this.isStatement = false]);

@override
R accept<R>(CodeVisitor<R> visitor, [R context]) {
return code.accept(visitor as ExpressionVisitor<R>, context);
return (visitor as ExpressionVisitor<R>)
.visitAsCodeExpression(this, context);
}

@override
String toString() => code.toString();
}

/// Knowledge of different types of expressions in Dart.
///
/// **INTERNAL ONLY**.
abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
T visitAsCodeExpression(AsCodeExpression code, [T context]);
T visitBinaryExpression(BinaryExpression expression, [T context]);
T visitCodeExpression(CodeExpression expression, [T context]);
T visitInvokeExpression(InvokeExpression expression, [T context]);
Expand All @@ -113,6 +127,16 @@ abstract class ExpressionVisitor<T> implements SpecVisitor<T> {
///
/// **INTERNAL ONLY**.
abstract class ExpressionEmitter implements ExpressionVisitor<StringSink> {
@override
visitAsCodeExpression(AsCodeExpression expression, [StringSink output]) {
output ??= new StringBuffer();
expression.code.accept(this, output);
if (expression.isStatement) {
output.write(';');
}
return output;
}

@override
visitBinaryExpression(BinaryExpression expression, [StringSink output]) {
output ??= new StringBuffer();
Expand Down
4 changes: 4 additions & 0 deletions lib/src/specs/expression/invoke.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class InvokeExpression extends Expression {
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
return visitor.visitInvokeExpression(this, context);
}

@override
String toString() =>
'${type ?? ''} $target($positionalArguments, $namedArguments)';
}

enum InvokeExpressionType {
Expand Down
9 changes: 9 additions & 0 deletions lib/src/specs/expression/literal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ class LiteralExpression extends Expression {
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
return visitor.visitLiteralExpression(this, context);
}

@override
String toString() => literal;
}

class LiteralListExpression extends Expression {
Expand All @@ -108,6 +111,9 @@ class LiteralListExpression extends Expression {
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
return visitor.visitLiteralListExpression(this, context);
}

@override
String toString() => '[${values.map(literal).join(', ')}]';
}

class LiteralMapExpression extends Expression {
Expand All @@ -127,4 +133,7 @@ class LiteralMapExpression extends Expression {
R accept<R>(ExpressionVisitor<R> visitor, [R context]) {
return visitor.visitLiteralMapExpression(this, context);
}

@override
String toString() => '{$values}';
}
38 changes: 38 additions & 0 deletions test/specs/code/statement_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:code_builder/code_builder.dart';
import 'package:test/test.dart';

void main() {
test('should emit a block of code', () {
expect(
new Block((b) => b.statements.addAll([
const Code('if (foo) {'),
const Code(' print(true);'),
const Code('}'),
])),
equalsDart(r'''
if (foo) {
print(true);
}
'''),
);
});

test('should emit a block of code including expressions', () {
expect(
new Block((b) => b.statements.addAll([
const Code('if (foo) {'),
refer('print')([literalTrue]).asStatement(),
const Code('}'),
])),
equalsDart(r'''
if (foo) {
print(true);
}
'''),
);
});
}