blob: 5e086e2c74953ed0894017ac13dc29a947687ba6 [file] [log] [blame]
// Copyright (c) 2016, 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.
library code_builder.src.builders.expression;
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:code_builder/dart/core.dart';
import 'package:code_builder/src/builders/method.dart';
import 'package:code_builder/src/builders/parameter.dart';
import 'package:code_builder/src/builders/reference.dart';
import 'package:code_builder/src/builders/shared.dart';
import 'package:code_builder/src/builders/expression/raw.dart';
import 'package:code_builder/src/builders/statement.dart';
import 'package:code_builder/src/builders/statement/if.dart';
import 'package:code_builder/src/builders/statement/while.dart';
import 'package:code_builder/src/builders/type.dart';
import 'package:code_builder/src/tokens.dart';
part 'expression/assert.dart';
part 'expression/assign.dart';
part 'expression/await.dart';
part 'expression/cascade.dart';
part 'expression/invocation.dart';
part 'expression/negate.dart';
part 'expression/operators.dart';
part 'expression/return.dart';
part 'expression/yield.dart';
final _false =
astFactory.booleanLiteral(new KeywordToken(Keyword.FALSE, 0), true);
final _null = astFactory.nullLiteral(new KeywordToken(Keyword.NULL, 0));
final _true =
astFactory.booleanLiteral(new KeywordToken(Keyword.TRUE, 0), true);
/// A reference to `super`.
ExpressionBuilder get superRef => new _SuperExpression();
/// Returns a pre-defined literal expression of [value].
///
/// Only primitive values are allowed.
ExpressionBuilder literal(value) {
if (value is List) {
return list(value);
}
if (value is Map) {
return map(value);
}
return new _LiteralExpression(_literal(value));
}
/// Returns a literal `List` expression from [values].
///
/// Optionally specify [asConst] or with a generic [type].
ExpressionBuilder list(
Iterable values, {
bool asConst: false,
TypeBuilder type,
}) =>
new _TypedListExpression(values, asConst: asConst, type: type);
/// Returns a literal `Map` expression from [values].
///
/// Optionally specify [asConst] or with a generic [keyType] or [valueType].
ExpressionBuilder map(
Map values, {
bool asConst: false,
TypeBuilder keyType,
TypeBuilder valueType,
}) =>
new _TypedMapExpression(values,
asConst: asConst, keyType: keyType, valueType: valueType);
Literal _literal(value) {
if (value == null) {
return _null;
} else if (value is bool) {
return value ? _true : _false;
} else if (value is String) {
return astFactory.simpleStringLiteral(stringToken("'$value'"), value);
} else if (value is int) {
return astFactory.integerLiteral(stringToken('$value'), value);
} else if (value is double) {
return astFactory.doubleLiteral(stringToken('$value'), value);
}
throw new ArgumentError.value(value, 'Unsupported');
}
/// Implements much of [ExpressionBuilder].
abstract class AbstractExpressionMixin implements ExpressionBuilder {
@override
ExpressionBuilder operator *(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$multiply,
);
}
@override
ExpressionBuilder operator +(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$plus,
);
}
@override
ExpressionBuilder operator -(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$minus,
);
}
@override
ExpressionBuilder operator /(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$divide,
);
}
@override
ExpressionBuilder operator >(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$gt,
);
}
@override
ExpressionBuilder operator <(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$lt,
);
}
@override
ExpressionBuilder and(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$and,
);
}
@override
StatementBuilder asAssert() => new _AsAssert(this);
@override
StatementBuilder asAssign(
String variable, {
ExpressionBuilder target,
bool nullAware: false,
}) =>
new _AsAssign(this, variable, nullAware, target);
@override
ExpressionBuilder asAwait() => new _AsAwait(this);
@override
StatementBuilder asConst(String variable, [TypeBuilder type]) {
return new _AsAssignNew(this, variable, type, $const);
}
@override
StatementBuilder asFinal(String variable, [TypeBuilder type]) {
return new _AsAssignNew(this, variable, type, $final);
}
@override
IfStatementBuilder asIf() => new IfStatementBuilder(this);
@override
StatementBuilder asReturn() => new _AsReturn(this);
@override
StatementBuilder asStatement() => new _AsStatement(this);
@override
StatementBuilder asVar(String variable, [TypeBuilder type]) {
return new _AsAssignNew(this, variable, type, $var);
}
@override
StatementBuilder asYield() => new _AsYield(this, false);
@override
StatementBuilder asYieldStar() => new _AsYield(this, true);
@override
WhileStatementBuilder asWhile({bool asDo: false}) {
return new WhileStatementBuilder(asDo, this);
}
@override
Statement buildStatement([Scope scope]) {
return asStatement().buildStatement(scope);
}
@override
InvocationBuilder call(
Iterable<ExpressionBuilder> positionalArguments, [
Map<String, ExpressionBuilder> namedArguments = const {},
]) {
final invocation = new InvocationBuilder._(this);
positionalArguments.forEach(invocation.addPositionalArgument);
namedArguments.forEach(invocation.addNamedArgument);
return invocation;
}
@override
ExpressionBuilder cascade(
Iterable<ExpressionBuilder> create(ExpressionBuilder self),
) {
// Sorry for the huge hack. Need to think more clearly about this in future.
return new _CascadeExpression(this, create(reference('.')));
}
@override
ExpressionBuilder decrement() => new _DecrementExpression(this);
@override
ExpressionBuilder equals(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$equalsEquals,
);
}
@override
ExpressionBuilder increment([bool prefix = false]) {
return new _IncrementExpression(this, prefix);
}
@override
ExpressionBuilder identical(ExpressionBuilder other) {
return lib$core.identical.call(<ExpressionBuilder>[
this,
other,
]);
}
@override
InvocationBuilder invoke(
String method,
Iterable<ExpressionBuilder> positionalArguments, [
Map<String, ExpressionBuilder> namedArguments = const {},
]) {
final invocation = new InvocationBuilder._on(this, method);
positionalArguments.forEach(invocation.addPositionalArgument);
namedArguments.forEach(invocation.addNamedArgument);
return invocation;
}
@override
ExpressionBuilder negate() => new _NegateExpression(this);
@override
ExpressionBuilder negative() => new _NegativeExpression(this);
@override
ExpressionBuilder notEquals(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$notEquals,
);
}
@override
ExpressionBuilder or(ExpressionBuilder other) {
return new _AsBinaryExpression(
this,
other,
$or,
);
}
@override
ExpressionBuilder parentheses() => new _ParenthesesExpression(this);
@override
ExpressionBuilder property(String name) => new _MemberExpression(this, name);
}
/// Builds an [Expression] AST when [buildExpression] is invoked.
abstract class ExpressionBuilder
implements AstBuilder, StatementBuilder, ValidParameterMember {
/// Create an expression builder that parses and emits a [raw] expression.
factory ExpressionBuilder.raw(String raw(Scope scope)) = RawExpressionBuilder;
/// Returns as an [ExpressionBuilder] multiplying by [other].
ExpressionBuilder operator *(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] adding [other].
ExpressionBuilder operator +(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] subtracting [other].
ExpressionBuilder operator -(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] dividing by [other].
ExpressionBuilder operator /(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] `<` by [other].
ExpressionBuilder operator <(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] `>` by [other].
ExpressionBuilder operator >(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] `&&` [other].
ExpressionBuilder and(ExpressionBuilder other);
/// Return as a [StatementBuilder] that `assert`s this expression.
StatementBuilder asAssert();
/// Returns as a [StatementBuilder] that assigns to an existing [variable].
///
/// If [target] is specified, determined to be `{target}.{variable}`.
StatementBuilder asAssign(
String variable, {
ExpressionBuilder target,
bool nullAware,
});
/// Returns as an expression `await`-ing this one.
ExpressionBuilder asAwait() => new _AsAwait(this);
/// Returns as a [StatementBuilder] that assigns to a new `const` [variable].
StatementBuilder asConst(String variable, [TypeBuilder type]);
/// Returns as a [StatementBuilder] that assigns to a new `final` [variable].
StatementBuilder asFinal(String variable, [TypeBuilder type]);
/// Returns as a [StatementBuilder] that builds an `if` statement.
IfStatementBuilder asIf();
/// Returns as a [StatementBuilder] that `return`s this expression.
StatementBuilder asReturn();
/// Returns _explicitly_ as a [StatementBuilder].
///
/// **NOTE**: [ExpressionBuilder] is _already_ usable as a [StatementBuilder]
/// directly; this API exists in order force [buildAst] to return a
/// [Statement] AST instead of an expression.
StatementBuilder asStatement();
/// Returns as a [StatementBuilder] that assigns to a new `var` [variable].
///
/// If [type] is supplied, the resulting statement is `{type} {variable} =`.
StatementBuilder asVar(String variable, [TypeBuilder type]);
/// Returns as a [StatementBuilder] yielding this one.
StatementBuilder asYield();
/// Returns as a [StatementBuilder] yielding this one.
StatementBuilder asYieldStar();
/// Returns as a [WhileStatementBuilder] with this as the condition.
WhileStatementBuilder asWhile({bool asDo: false});
/// Returns an [Expression] AST representing the builder.
Expression buildExpression([Scope scope]);
/// Returns as an [InvocationBuilder] with arguments added.
InvocationBuilder call(
Iterable<ExpressionBuilder> positionalArguments, [
Map<String, ExpressionBuilder> namedArguments,
]);
/// Return as an [ExpressionBuilder] with `..` appended.
ExpressionBuilder cascade(
Iterable<ExpressionBuilder> create(ExpressionBuilder self),
);
/// Returns as an [ExpressionBuilder] decrementing this expression.
ExpressionBuilder decrement();
/// Returns as an [ExpressionBuilder] comparing using `==` against [other].
ExpressionBuilder equals(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] comparing using `identical`.
ExpressionBuilder identical(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] incrementing this expression.
ExpressionBuilder increment([bool prefix = false]);
/// Returns as an [InvocationBuilder] on [method] of this expression.
InvocationBuilder invoke(
String method,
Iterable<ExpressionBuilder> positionalArguments, [
Map<String, ExpressionBuilder> namedArguments,
]);
/// Returns as an [ExpressionBuilder] negating using the `!` operator.
ExpressionBuilder negate();
/// Returns as an [ExpressionBuilder] negating the value,
ExpressionBuilder negative();
/// Returns as an [ExpressionBuilder] comparing using `!=` against [other].
ExpressionBuilder notEquals(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] `||` [other].
ExpressionBuilder or(ExpressionBuilder other);
/// Returns as an [ExpressionBuilder] wrapped in parentheses.
ExpressionBuilder parentheses();
/// Returns {{this}}.{{name}}.
ExpressionBuilder property(String name);
}
/// An [AstBuilder] that can add [ExpressionBuilder].
abstract class HasExpressions implements AstBuilder {
final List<ExpressionBuilder> _expressions = <ExpressionBuilder>[];
/// Adds [expression] to the builder.
void addExpression(ExpressionBuilder expression) {
_expressions.add(expression);
}
/// Adds [expressions] to the builder.
void addExpressions(Iterable<ExpressionBuilder> expressions) {
_expressions.addAll(expressions);
}
}
/// Implements [HasExpressions].
abstract class HasExpressionsMixin extends HasExpressions {
/// Returns a [List] of all built [Expression]s.
List<Expression> buildExpressions([Scope scope]) => _expressions
.map/*<Expression>*/((e) => e.buildExpression(scope))
.toList();
/// Clones all expressions to [clone].
void cloneExpressionsTo(HasExpressions clone) {
clone.addExpressions(_expressions);
}
}
class _AsStatement extends TopLevelMixin implements StatementBuilder {
final ExpressionBuilder _expression;
_AsStatement(this._expression);
@override
Statement buildAst([Scope scope]) => buildStatement(scope);
@override
Statement buildStatement([Scope scope]) {
return astFactory.expressionStatement(
_expression.buildExpression(scope),
$semicolon,
);
}
}
class _MemberExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
final String _name;
final ExpressionBuilder _target;
_MemberExpression(this._target, this._name);
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([Scope scope]) {
return astFactory.propertyAccess(
_target.buildExpression(scope),
$period,
stringIdentifier(_name),
);
}
}
class _LiteralExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
final Literal _literal;
_LiteralExpression(this._literal);
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([_]) => _literal;
}
class _ParenthesesExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
final ExpressionBuilder _expression;
_ParenthesesExpression(this._expression);
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([Scope scope]) {
return astFactory.parenthesizedExpression(
$openParen,
_expression.buildExpression(scope),
$closeParen,
);
}
}
ExpressionBuilder _expressionify(v) {
if (v == null || v is bool || v is String || v is int || v is double) {
return new _LiteralExpression(_literal(v));
}
if (v is ExpressionBuilder) {
return v;
}
throw new ArgumentError('Could not expressionify $v');
}
class _SuperExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([_]) => astFactory.superExpression($super);
}
class _TypedListExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
static List<ExpressionBuilder> _toExpression(Iterable values) {
return values.map(_expressionify).toList();
}
final bool _asConst;
final TypeBuilder _type;
final List<ExpressionBuilder> _values;
_TypedListExpression(Iterable values, {bool asConst, TypeBuilder type})
: _values = _toExpression(values),
_asConst = asConst,
_type = type;
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([Scope scope]) {
return astFactory.listLiteral(
_asConst ? $const : null,
_type != null
? astFactory.typeArgumentList(
$openBracket,
[_type.buildType(scope)],
$closeBracket,
)
: null,
$openBracket,
_values.map((v) => v.buildExpression(scope)).toList(),
$closeBracket,
);
}
}
class _TypedMapExpression extends Object
with AbstractExpressionMixin, TopLevelMixin {
static Map<ExpressionBuilder, ExpressionBuilder> _toExpression(Map values) {
return new Map<ExpressionBuilder, ExpressionBuilder>.fromIterable(
values.keys,
key: (k) => _expressionify(k),
value: (k) => _expressionify(values[k]),
);
}
final bool _asConst;
final TypeBuilder _keyType;
final TypeBuilder _valueType;
final Map<ExpressionBuilder, ExpressionBuilder> _values;
_TypedMapExpression(Map values,
{bool asConst, TypeBuilder keyType, TypeBuilder valueType})
: _values = _toExpression(values),
_asConst = asConst,
_keyType = keyType != null
? keyType
: valueType != null ? lib$core.$dynamic : null,
_valueType = valueType != null
? valueType
: keyType != null ? lib$core.$dynamic : null;
@override
AstNode buildAst([Scope scope]) => buildExpression(scope);
@override
Expression buildExpression([Scope scope]) {
return astFactory.mapLiteral(
_asConst ? $const : null,
_keyType != null
? astFactory.typeArgumentList(
$openBracket,
[
_keyType.buildType(scope),
_valueType.buildType(scope),
],
$closeBracket,
)
: null,
$openBracket,
_values.keys.map((k) {
return astFactory.mapLiteralEntry(
k.buildExpression(scope),
$colon,
_values[k].buildExpression(scope),
);
}).toList(),
$closeBracket,
);
}
}