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

Commit cde2a56

Browse files
authored
Add support for library scoping (#10)
* Add a simple identifier Scope class and a test. * Address comments. * Add some support for scoping (methods, types, parameters) * Add some support to LibraryBuilder and a test. * Pubspec +1
1 parent 84366d0 commit cde2a56

File tree

12 files changed

+310
-96
lines changed

12 files changed

+310
-96
lines changed

lib/code_builder.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@ abstract class _AbstractCodeBuilder<A extends AstNode> extends CodeBuilder<A> {
7373
/// **NOTE**: This currently (as of 0.2.0) has no effect. It is planned that
7474
/// the [FileBuilder] will be able to act as a scope 'resolver' and subtly
7575
/// rewrite the AST tree to use prefixing if required (or requested).
76-
abstract class RequiresImport<A extends AstNode> implements CodeBuilder<A> {
77-
/// Imports that are required for this AST to work.
78-
List<String> get requiredImports;
76+
abstract class ScopeAware<A extends AstNode> implements CodeBuilder<A> {
77+
@override
78+
A toAst() => toScopedAst(const Scope.identity());
7979

8080
/// Creates a copy-safe [AstNode] representing the current builder state.
8181
///
8282
/// Uses [scope] to output an AST re-written to use appropriate prefixes.
83-
A toScopedAst(FileBuilder scope) => throw new UnimplementedError();
83+
A toScopedAst(Scope scope) => throw new UnimplementedError();
8484
}
8585

8686
// Creates a defensive copy of an AST node.

lib/src/builders/annotation_builder.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ part of code_builder;
1616
/// void destroyTheWorld() { ... }
1717
///
1818
/// To create a `@DoNotUse('Blows up')` use [AnnotationBuilder.invoke].
19-
abstract class AnnotationBuilder implements RequiresImport<Annotation> {
19+
abstract class AnnotationBuilder implements ScopeAware<Annotation> {
2020
/// Create a new annotated `const` [constructor] invocation.
2121
///
2222
/// May optionally specify an [importFrom] to auto-prefix the annotation _if_
@@ -46,7 +46,7 @@ abstract class AnnotationBuilder implements RequiresImport<Annotation> {
4646
[String importFrom]) = _ReferenceAnnotationBuilder;
4747
}
4848

49-
class _ConstructorAnnotationBuilder extends RequiresImport<Annotation>
49+
class _ConstructorAnnotationBuilder extends ScopeAware<Annotation>
5050
implements AnnotationBuilder {
5151
final String _constructor;
5252
final ExpressionBuilder _expression;

lib/src/builders/file_builder.dart

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ abstract class FileBuilder extends _AbstractCodeBuilder<CompilationUnit> {
2828
class LibraryBuilder extends FileBuilder {
2929
static final Token _library = new KeywordToken(Keyword.LIBRARY, 0);
3030

31+
final Scope _scope;
32+
3133
/// Create a new standalone Dart library, optionally with a [name].
3234
factory LibraryBuilder([String name]) {
3335
var astNode = _emptyCompilationUnit();
@@ -39,22 +41,49 @@ class LibraryBuilder extends FileBuilder {
3941
new LibraryIdentifier([_stringId(name)]),
4042
null,));
4143
}
42-
return new LibraryBuilder._(astNode);
44+
return new LibraryBuilder._(astNode, new Scope.dedupe());
4345
}
4446

4547
/// Create a new standalone Dart library, optionally with a [name].
4648
///
47-
/// As references are added in the library that implements [RequiresImport]
49+
/// As references are added in the library that implements [ScopeAware]
4850
/// they are re-written to avoid collisions and the imports are automatically
4951
/// included at the top with optional prefixes.
50-
factory LibraryBuilder.autoScope({String name}) => new LibraryBuilder(name);
52+
factory LibraryBuilder.scope({String name, Scope scope}) {
53+
var astNode = _emptyCompilationUnit();
54+
if (name != null) {
55+
astNode.directives.add(new LibraryDirective(
56+
null,
57+
null,
58+
_library,
59+
new LibraryIdentifier([_stringId(name)]),
60+
null,));
61+
}
62+
return new LibraryBuilder._(astNode, scope ?? new Scope());
63+
}
5164

52-
LibraryBuilder._(CompilationUnit astNode) : super._(astNode);
65+
LibraryBuilder._(CompilationUnit astNode, this._scope) : super._(astNode);
66+
67+
@override
68+
void addDeclaration(CodeBuilder<Declaration> declaration) {
69+
if (declaration is ScopeAware<Declaration>) {
70+
_astNode.declarations.add(declaration.toScopedAst(_scope));
71+
} else {
72+
super.addDeclaration(declaration);
73+
}
74+
}
5375

5476
/// Adds [directive]'s resulting AST to the source.
5577
void addDirective(CodeBuilder<Directive> directive) {
5678
_astNode.directives.add(directive.toAst());
5779
}
80+
81+
@override
82+
CompilationUnit toAst() {
83+
var originalAst = super.toAst();
84+
originalAst.directives..addAll(_scope.getImports().map((i) => i.toAst()));
85+
return originalAst;
86+
}
5887
}
5988

6089
/// Builds a `part of` [CompilationUnit] AST for an existing Dart library.

lib/src/builders/method_builder.dart

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ part of code_builder;
1111
/// the top level and within other methods) via [toFunctionAst].
1212
///
1313
/// To return nothing (`void`), use [MethodBuilder.returnVoid].
14-
class MethodBuilder implements CodeBuilder<Declaration> {
14+
class MethodBuilder implements
15+
CodeBuilder<Declaration>,
16+
ScopeAware<Declaration> {
1517
static Token _abstract = new KeywordToken(Keyword.ABSTRACT, 0);
1618
static Token _semicolon = new Token(TokenType.SEMICOLON, 0);
1719
static Token _static = new KeywordToken(Keyword.STATIC, 0);
@@ -41,6 +43,9 @@ class MethodBuilder implements CodeBuilder<Declaration> {
4143

4244
MethodBuilder._(this._name, this._returnType);
4345

46+
@override
47+
List<String> get requiredImports => null;
48+
4449
/// Lazily adds [annotation].
4550
///
4651
/// When the method is emitted as an AST, [AnnotationBuilder.toAst] is used.
@@ -76,14 +81,14 @@ class MethodBuilder implements CodeBuilder<Declaration> {
7681
/// [toMethodAst].
7782
@override
7883
@visibleForTesting
79-
Declaration toAst() => toFunctionAst();
84+
Declaration toAst() => toScopedAst(const Scope.identity());
8085

8186
/// Returns a copy-safe [FunctionDeclaration] AST representing current state.
82-
FunctionDeclaration toFunctionAst() {
87+
FunctionDeclaration toFunctionAst({Scope scope: const Scope.identity()}) {
8388
var functionAst = _emptyFunction()
8489
..metadata.addAll(_annotations.map/*<Annotation>*/((a) => a.toAst()))
8590
..name = _stringId(_name)
86-
..returnType = _returnType?.toAst();
91+
..returnType = _returnType?.toScopedAst(scope);
8792
if (_returnExpression != null) {
8893
functionAst.functionExpression = _returnExpression.toFunctionExpression();
8994
} else {
@@ -95,7 +100,7 @@ class MethodBuilder implements CodeBuilder<Declaration> {
95100
}
96101
if (_parameters.isNotEmpty) {
97102
functionAst.functionExpression.parameters.parameters
98-
.addAll(_parameters.map/*<FormalParameter>*/((p) => p.toAst()));
103+
.addAll(_parameters.map/*<FormalParameter>*/((p) => p.toScopedAst(scope)));
99104
}
100105
return functionAst;
101106
}
@@ -104,11 +109,12 @@ class MethodBuilder implements CodeBuilder<Declaration> {
104109
MethodDeclaration toMethodAst({
105110
bool static: false,
106111
bool canBeAbstract: false,
112+
Scope scope: const Scope.identity(),
107113
}) {
108114
var methodAst = _emptyMethod()
109115
..metadata.addAll(_annotations.map/*<Annotation>*/((a) => a.toAst()))
110116
..name = _stringId(_name)
111-
..returnType = _returnType?.toAst();
117+
..returnType = _returnType?.toScopedAst(scope);
112118
FunctionBody methodBody = _returnExpression?.toFunctionBody();
113119
if (static) {
114120
methodAst.modifierKeyword = _static;
@@ -123,12 +129,15 @@ class MethodBuilder implements CodeBuilder<Declaration> {
123129
}
124130
if (_parameters.isNotEmpty) {
125131
methodAst.parameters.parameters
126-
.addAll(_parameters.map/*<FormalParameter>*/((p) => p.toAst()));
132+
.addAll(_parameters.map/*<FormalParameter>*/((p) => p.toScopedAst(scope)));
127133
}
128134
methodAst.body = methodBody;
129135
return methodAst;
130136
}
131137

138+
@override
139+
Declaration toScopedAst(Scope scope) => toFunctionAst(scope: scope);
140+
132141
@override
133142
String toString() => 'MethodBuilder ${toAst().toSource()}';
134143

@@ -156,7 +165,7 @@ class MethodBuilder implements CodeBuilder<Declaration> {
156165
_blockBody(),
157166
),
158167
);
159-
168+
// TODO: implement requiredImports
160169
static MethodDeclaration _emptyMethod() => new MethodDeclaration(
161170
null,
162171
null,

0 commit comments

Comments
 (0)