Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Add assignment expressions. (#13)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Harry Terkelsen authored Sep 23, 2016
1 parent 1fafa99 commit e2c28f2
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 97 deletions.
11 changes: 4 additions & 7 deletions lib/code_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ library code_builder;

import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:dart_style/dart_style.dart';
import 'package:meta/meta.dart';

import 'src/analyzer_patch.dart';
import 'src/tokens.dart';

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

Identifier _stringIdentifier(String s) {
return new SimpleIdentifier(new StringToken(TokenType.STRING, s, 0));
}
SimpleIdentifier _stringIdentifier(String s) =>
new SimpleIdentifier(stringToken(s));

Literal _stringLiteral(String s) {
return new SimpleStringLiteral(new StringToken(TokenType.STRING, s, 0), s);
}
Literal _stringLiteral(String s) => new SimpleStringLiteral(stringToken(s), s);

/// Base class for building and emitting a Dart language [AstNode].
abstract class CodeBuilder<A extends AstNode> {
Expand Down
13 changes: 4 additions & 9 deletions lib/src/builders/class_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ part of code_builder;

/// Builds a [ClassDeclaration] AST.
class ClassBuilder implements CodeBuilder<ClassDeclaration> {
static Token _abstract = new KeywordToken(Keyword.ABSTRACT, 0);
static Token _extends = new KeywordToken(Keyword.EXTENDS, 0);
static Token _implements = new KeywordToken(Keyword.IMPLEMENTS, 0);
static Token _with = new KeywordToken(Keyword.WITH, 0);

final String _name;
final bool _isAbstract;
final TypeBuilder _extend;
Expand Down Expand Up @@ -85,18 +80,18 @@ class ClassBuilder implements CodeBuilder<ClassDeclaration> {
ClassDeclaration toAst([Scope scope = const Scope.identity()]) {
var astNode = _emptyClassDeclaration()..name = _stringIdentifier(_name);
if (_isAbstract) {
astNode.abstractKeyword = _abstract;
astNode.abstractKeyword = $abstract;
}
if (_extend != null) {
astNode.extendsClause = new ExtendsClause(_extends, _extend.toAst(scope));
astNode.extendsClause = new ExtendsClause($extends, _extend.toAst(scope));
}
if (_implement.isNotEmpty) {
astNode.implementsClause = new ImplementsClause(_implements,
astNode.implementsClause = new ImplementsClause($implements,
_implement.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
}
if (_mixin.isNotEmpty) {
astNode.withClause = new WithClause(
_with, _mixin.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
$with, _mixin.map/*<TypeName>*/((i) => i.toAst(scope)).toList());
}
astNode
..metadata.addAll(_metadata.map/*<Annotation>*/((a) => a.toAst(scope)));
Expand Down
7 changes: 2 additions & 5 deletions lib/src/builders/constructor_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ part of code_builder;
///
/// Similar to [MethodBuilder] but with constructor-only features.
class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
static final Token _const = new KeywordToken(Keyword.CONST, 0);
static final Token _this = new KeywordToken(Keyword.THIS, 0);

final bool _isConstant;
final String _name;
final List<ParameterBuilder> _parameters = <ParameterBuilder>[];
Expand Down Expand Up @@ -43,7 +40,7 @@ class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
null,
null,
null,
_isConstant ? _const : null,
_isConstant ? $const : null,
null,
null,
_name != null ? _stringIdentifier(_name) : null,
Expand All @@ -53,7 +50,7 @@ class ConstructorBuilder implements CodeBuilder<ConstructorDeclaration> {
null,
null,
null,
new EmptyFunctionBody(MethodBuilder._semicolon),
new EmptyFunctionBody($semicolon),
);
return astNode;
}
Expand Down
80 changes: 50 additions & 30 deletions lib/src/builders/expression_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,8 @@ const literalNull = const _LiteralNull();
/// Represents an expression value of `true`.
const literalTrue = const LiteralBool(true);

// Returns wrapped as a [ExpressionFunctionBody] AST.
final Token _closeP = new Token(TokenType.CLOSE_PAREN, 0);

// Returns wrapped as a [FunctionExpression] AST.
final Token _openP = new Token(TokenType.OPEN_PAREN, 0);

final Token _semicolon = new Token(TokenType.SEMICOLON, 0);

// TODO(matanl): Make this part of the public API. See annotation_builder.dart.
// Returns wrapped as a [ExpressionFunctionBody] AST.
ExpressionFunctionBody _asFunctionBody(
CodeBuilder<Expression> expression,
Scope scope,
Expand All @@ -31,22 +24,23 @@ ExpressionFunctionBody _asFunctionBody(
null,
null,
expression.toAst(scope),
_semicolon,
$semicolon,
);
}

// Returns wrapped as a [FunctionExpression] AST.
FunctionExpression _asFunctionExpression(
CodeBuilder<Expression> expression,
Scope scope,
) {
return new FunctionExpression(
null,
new FormalParameterList(
_openP,
$openParen,
const [],
null,
null,
_closeP,
$closeParen,
),
_asFunctionBody(expression, scope),
);
Expand Down Expand Up @@ -108,6 +102,15 @@ abstract class ExpressionBuilder implements CodeBuilder<Expression> {
);
}

/// Assign [left] to [right].
///
/// If [nullAware] is true, the assignment uses the `??=` operator.
factory ExpressionBuilder.assignment(
String left, CodeBuilder<Expression> right,
{bool nullAware: false}) {
return new _AssignmentExpression(left, right, nullAware: nullAware);
}

const ExpressionBuilder._();

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

/// Creates a new literal `bool` value.
class LiteralBool extends _LiteralExpression<BooleanLiteral> {
static final BooleanLiteral _true =
new BooleanLiteral(new KeywordToken(Keyword.TRUE, 0), true);
static final BooleanLiteral _false =
new BooleanLiteral(new KeywordToken(Keyword.FALSE, 0), false);
static final BooleanLiteral _true = new BooleanLiteral($true, true);
static final BooleanLiteral _false = new BooleanLiteral($false, false);

final bool _value;

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

@override
IntegerLiteral toAst([_]) => new IntegerLiteral(
new StringToken(TokenType.INT, '$_value', 0),
intToken(_value),
_value,
);
}
Expand All @@ -173,18 +174,12 @@ class LiteralString extends _LiteralExpression<StringLiteral> {

@override
StringLiteral toAst([_]) => new SimpleStringLiteral(
new StringToken(
TokenType.STRING,
"'$_value'",
0,
),
stringToken("'$_value'"),
_value,
);
}

class _InvokeExpression extends ExpressionBuilder {
static final Token _colon = new Token(TokenType.COLON, 0);

final String _importFrom;
final ExpressionBuilder _target;
final String _name;
Expand Down Expand Up @@ -231,18 +226,18 @@ class _InvokeExpression extends ExpressionBuilder {
// TODO(matanl): Move to TypeBuilder.newInstance.
if (_type != null) {
return new InstanceCreationExpression(
new KeywordToken(Keyword.NEW, 0),
$new,
new ConstructorName(
_type.toAst(scope),
_name != null ? new Token(TokenType.PERIOD, 0) : null,
_name != null ? $period : null,
_name != null ? _stringIdentifier(_name) : null,
),
_getArgumentList(scope),
);
}
return new MethodInvocation(
_target?.toAst(scope),
_target != null ? new Token(TokenType.PERIOD, 0) : null,
_target != null ? $period : null,
_stringIdentifier(_name),
null,
_getArgumentList(scope),
Expand All @@ -254,21 +249,46 @@ class _InvokeExpression extends ExpressionBuilder {

ArgumentList _getArgumentList(Scope scope) {
return new ArgumentList(
new Token(TokenType.OPEN_CURLY_BRACKET, 0),
$openCurly,
_positionalArguments.map/*<Expression*/((p) => p.toAst(scope)).toList()
..addAll(_namedArguments.keys
.map/*<Expression>*/((name) => new NamedExpression(
new Label(
_stringIdentifier(name),
_colon,
$colon,
),
_namedArguments[name].toAst(scope),
))),
new Token(TokenType.CLOSE_CURLY_BRACKET, 0),
$closeCurly,
);
}
}

class _AssignmentExpression extends ExpressionBuilder {
final String left;
final CodeBuilder<Expression> right;
final bool nullAware;

_AssignmentExpression(this.left, this.right, {this.nullAware: false})
: super._();

@override
ExpressionBuilder invokeSelf(String name,
{Iterable<CodeBuilder<Expression>> positional: const [],
Map<String, CodeBuilder<Expression>> named: const {}}) {
return _invokeSelfImpl(this, name, positional: positional, named: named);
}

@override
Expression toAst([Scope scope = const Scope.identity()]) {
return new AssignmentExpression(_stringIdentifier(left),
nullAware ? $nullAwareEquals : $equals, right.toAst(scope));
}

@override
StatementBuilder toStatement() => new _ExpressionStatementBuilder(this);
}

abstract class _LiteralExpression<A extends Literal>
implements ExpressionBuilder, CodeBuilder<A> {
const _LiteralExpression();
Expand Down Expand Up @@ -296,7 +316,7 @@ abstract class _LiteralExpression<A extends Literal>
}

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

const _LiteralNull();

Expand Down
17 changes: 5 additions & 12 deletions lib/src/builders/field_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@ part of code_builder;
/// AST (for class members) via [toFieldAst] or a variable declaration (used
/// both at the top-level and within methods) via [toVariablesAst].
class FieldBuilder implements CodeBuilder<Declaration> {
static Token _equals = new Token(TokenType.EQ, 0);
static Token _semicolon = new Token(TokenType.SEMICOLON, 0);
static Token _static = new KeywordToken(Keyword.STATIC, 0);
static Token _final = new KeywordToken(Keyword.FINAL, 0);
static Token _const = new KeywordToken(Keyword.CONST, 0);
static Token _var = new KeywordToken(Keyword.VAR, 0);

final bool _isConst;
final bool _isFinal;
final bool _isStatic;
Expand Down Expand Up @@ -84,7 +77,7 @@ class FieldBuilder implements CodeBuilder<Declaration> {
new FieldDeclaration(
null,
null,
_isStatic ? _static : null,
_isStatic ? $static : null,
toVariablesAst(scope),
null,
);
Expand All @@ -100,19 +93,19 @@ class FieldBuilder implements CodeBuilder<Declaration> {
[
new VariableDeclaration(
_stringIdentifier(_name),
_initialize != null ? _equals : null,
_initialize != null ? $equals : null,
_initialize?.toAst(scope),
)
],
);

Token _getVariableKeyword() {
if (_isFinal) {
return _final;
return $final;
}
if (_isConst) {
return _const;
return $const;
}
return _type == null ? _var : null;
return _type == null ? $var : null;
}
}
11 changes: 3 additions & 8 deletions lib/src/builders/file_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ class ImportBuilder implements CodeBuilder<ImportDirective> {

/// Builds a standalone Dart library [CompilationUnit] AST.
class LibraryBuilder extends FileBuilder {
static final Token _library = new KeywordToken(Keyword.LIBRARY, 0);

final String _name;
final Scope _scope;

Expand Down Expand Up @@ -128,7 +126,7 @@ class LibraryBuilder extends FileBuilder {
new LibraryDirective(
null,
null,
_library,
$library,
new LibraryIdentifier([_stringIdentifier(_name)]),
null,
),
Expand All @@ -142,9 +140,6 @@ class LibraryBuilder extends FileBuilder {

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

final String _name;

/// Create a new `part of` source file.
Expand All @@ -158,8 +153,8 @@ class PartBuilder extends FileBuilder {
originalAst.directives.add(new PartOfDirective(
null,
null,
_part,
_of,
$part,
$of,
new LibraryIdentifier([_stringIdentifier(_name)]),
null,
));
Expand Down
Loading

0 comments on commit e2c28f2

Please sign in to comment.