| // Copyright (c) 2024, 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 'arguments.dart'; | |
| import 'elements.dart'; | |
| import 'proto.dart'; | |
| import 'record_fields.dart'; | |
| import 'references.dart'; | |
| import 'string_literal_parts.dart'; | |
| import 'type_annotations.dart'; | |
| import 'util.dart'; | |
| /// Superclass for all expression nodes. | |
| sealed class Expression { | |
| /// Returns the [Expression] corresponding to this [Expression] in which all | |
| /// [UnresolvedIdentifier]s have been resolved within their scope. | |
| /// | |
| /// If this didn't create a new [Expression], `null` is returned. | |
| Expression? resolve(); | |
| } | |
| class InvalidExpression extends Expression { | |
| @override | |
| String toString() => 'InvalidExpression()'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class StaticGet extends Expression { | |
| final FieldReference reference; | |
| StaticGet(this.reference); | |
| @override | |
| String toString() => 'StaticGet($reference)'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class FunctionTearOff extends Expression { | |
| final FunctionReference reference; | |
| FunctionTearOff(this.reference); | |
| @override | |
| String toString() => 'FunctionTearOff($reference)'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class ConstructorTearOff extends Expression { | |
| final TypeAnnotation type; | |
| final ConstructorReference reference; | |
| ConstructorTearOff(this.type, this.reference); | |
| @override | |
| String toString() => 'ConstructorTearOff($type,$reference)'; | |
| @override | |
| Expression? resolve() { | |
| TypeAnnotation? newType = type.resolve(); | |
| return newType == null ? null : new ConstructorTearOff(newType, reference); | |
| } | |
| } | |
| class ConstructorInvocation extends Expression { | |
| final TypeAnnotation type; | |
| final Reference constructor; | |
| final List<Argument> arguments; | |
| ConstructorInvocation(this.type, this.constructor, this.arguments); | |
| @override | |
| String toString() => 'ConstructorInvocation($type,$constructor,$arguments)'; | |
| @override | |
| Expression? resolve() { | |
| TypeAnnotation? newType = type.resolve(); | |
| List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | |
| return newType == null && newArguments == null | |
| ? null | |
| : new ConstructorInvocation( | |
| newType ?? type, | |
| constructor, | |
| newArguments ?? arguments, | |
| ); | |
| } | |
| } | |
| class IntegerLiteral extends Expression { | |
| final String? text; | |
| final int? value; | |
| IntegerLiteral.fromText(String this.text, [this.value]); | |
| IntegerLiteral.fromValue(int this.value) : text = null; | |
| @override | |
| String toString() => 'IntegerLiteral(${value ?? text})'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class DoubleLiteral extends Expression { | |
| final String text; | |
| final double value; | |
| DoubleLiteral(this.text, this.value); | |
| @override | |
| String toString() => 'DoubleLiteral($text)'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class BooleanLiteral extends Expression { | |
| final bool value; | |
| BooleanLiteral(this.value); | |
| @override | |
| String toString() => 'BooleanLiteral($value)'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class NullLiteral extends Expression { | |
| NullLiteral(); | |
| @override | |
| String toString() => 'NullLiteral()'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class SymbolLiteral extends Expression { | |
| final List<String> parts; | |
| SymbolLiteral(this.parts); | |
| @override | |
| String toString() => 'SymbolLiteral($parts)'; | |
| @override | |
| Expression? resolve() => null; | |
| } | |
| class StringLiteral extends Expression { | |
| final List<StringLiteralPart> parts; | |
| StringLiteral(this.parts); | |
| @override | |
| String toString() => 'StringLiteral($parts)'; | |
| @override | |
| Expression? resolve() { | |
| List<StringLiteralPart>? newParts = parts.resolve((p) => p.resolve()); | |
| return newParts == null ? null : new StringLiteral(newParts); | |
| } | |
| } | |
| class AdjacentStringLiterals extends Expression { | |
| final List<Expression> expressions; | |
| AdjacentStringLiterals(this.expressions); | |
| @override | |
| String toString() => 'AdjacentStringLiterals($expressions)'; | |
| @override | |
| Expression? resolve() { | |
| List<Expression>? newExpressions = expressions.resolve((e) => e.resolve()); | |
| return newExpressions == null | |
| ? null | |
| : new AdjacentStringLiterals(newExpressions); | |
| } | |
| } | |
| class ImplicitInvocation extends Expression { | |
| final Expression receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Argument> arguments; | |
| ImplicitInvocation(this.receiver, this.typeArguments, this.arguments); | |
| @override | |
| String toString() => | |
| 'ImplicitInvocation($receiver,$typeArguments,$arguments)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newReceiver = receiver.resolve(); | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | |
| return newReceiver == null && | |
| newTypeArguments == null && | |
| newArguments == null | |
| ? null | |
| : new ImplicitInvocation( | |
| newReceiver ?? receiver, | |
| newTypeArguments ?? typeArguments, | |
| newArguments ?? arguments, | |
| ); | |
| } | |
| } | |
| class StaticInvocation extends Expression { | |
| final FunctionReference function; | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Argument> arguments; | |
| StaticInvocation(this.function, this.typeArguments, this.arguments); | |
| @override | |
| String toString() => 'StaticInvocation($function,$typeArguments,$arguments)'; | |
| @override | |
| Expression? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | |
| return newTypeArguments == null && newArguments == null | |
| ? null | |
| : new StaticInvocation( | |
| function, | |
| newTypeArguments ?? typeArguments, | |
| newArguments ?? arguments, | |
| ); | |
| } | |
| } | |
| class Instantiation extends Expression { | |
| final Expression receiver; | |
| final List<TypeAnnotation> typeArguments; | |
| Instantiation(this.receiver, this.typeArguments); | |
| @override | |
| String toString() => 'Instantiation($receiver,$typeArguments)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newReceiver = receiver.resolve(); | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| return newReceiver == null && newTypeArguments == null | |
| ? null | |
| : new Instantiation( | |
| newReceiver ?? receiver, | |
| newTypeArguments ?? typeArguments, | |
| ); | |
| } | |
| } | |
| class MethodInvocation extends Expression { | |
| final Expression receiver; | |
| final String name; | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Argument> arguments; | |
| MethodInvocation( | |
| this.receiver, | |
| this.name, | |
| this.typeArguments, | |
| this.arguments, | |
| ); | |
| @override | |
| String toString() => | |
| 'MethodInvocation($receiver,$name,$typeArguments,$arguments)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newReceiver = receiver.resolve(); | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| List<Argument>? newArguments = arguments.resolve((a) => a.resolve()); | |
| return newReceiver == null && | |
| newTypeArguments == null && | |
| newArguments == null | |
| ? null | |
| : new MethodInvocation( | |
| newReceiver ?? receiver, | |
| name, | |
| newTypeArguments ?? typeArguments, | |
| newArguments ?? arguments, | |
| ); | |
| } | |
| } | |
| class PropertyGet extends Expression { | |
| final Expression receiver; | |
| final String name; | |
| PropertyGet(this.receiver, this.name); | |
| @override | |
| String toString() => 'PropertyGet($receiver,$name)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newReceiver = receiver.resolve(); | |
| return newReceiver == null ? null : new PropertyGet(newReceiver, name); | |
| } | |
| } | |
| class NullAwarePropertyGet extends Expression { | |
| final Expression receiver; | |
| final String name; | |
| NullAwarePropertyGet(this.receiver, this.name); | |
| @override | |
| String toString() => 'NullAwarePropertyGet($receiver,$name)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newReceiver = receiver.resolve(); | |
| return newReceiver == null | |
| ? null | |
| : new NullAwarePropertyGet(newReceiver, name); | |
| } | |
| } | |
| class TypeLiteral extends Expression { | |
| final TypeAnnotation typeAnnotation; | |
| TypeLiteral(this.typeAnnotation); | |
| @override | |
| String toString() => 'TypeLiteral($typeAnnotation)'; | |
| @override | |
| Expression? resolve() { | |
| TypeAnnotation? newTypeAnnotation = typeAnnotation.resolve(); | |
| return newTypeAnnotation == null | |
| ? null | |
| : new TypeLiteral(newTypeAnnotation); | |
| } | |
| } | |
| class ParenthesizedExpression extends Expression { | |
| final Expression expression; | |
| ParenthesizedExpression(this.expression); | |
| @override | |
| String toString() => 'ParenthesizedExpression($expression)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newExpression = expression.resolve(); | |
| return newExpression == null | |
| ? null | |
| : new ParenthesizedExpression(newExpression); | |
| } | |
| } | |
| class ConditionalExpression extends Expression { | |
| final Expression condition; | |
| final Expression then; | |
| final Expression otherwise; | |
| ConditionalExpression(this.condition, this.then, this.otherwise); | |
| @override | |
| String toString() => 'ConditionalExpression($condition,$then,$otherwise)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newCondition = condition.resolve(); | |
| Expression? newThen = then.resolve(); | |
| Expression? newOtherwise = otherwise.resolve(); | |
| return newCondition == null && newThen == null && newOtherwise == null | |
| ? null | |
| : new ConditionalExpression( | |
| newCondition ?? condition, | |
| newThen ?? then, | |
| newOtherwise ?? otherwise, | |
| ); | |
| } | |
| } | |
| class ListLiteral extends Expression { | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Element> elements; | |
| ListLiteral(this.typeArguments, this.elements); | |
| @override | |
| String toString() => 'ListLiteral($typeArguments,$elements)'; | |
| @override | |
| Expression? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| List<Element>? newElements = elements.resolve((e) => e.resolve()); | |
| return newTypeArguments == null && newElements == null | |
| ? null | |
| : new ListLiteral( | |
| newTypeArguments ?? typeArguments, | |
| newElements ?? elements, | |
| ); | |
| } | |
| } | |
| class SetOrMapLiteral extends Expression { | |
| final List<TypeAnnotation> typeArguments; | |
| final List<Element> elements; | |
| SetOrMapLiteral(this.typeArguments, this.elements); | |
| @override | |
| String toString() => 'SetOrMapLiteral($typeArguments,$elements)'; | |
| @override | |
| Expression? resolve() { | |
| List<TypeAnnotation>? newTypeArguments = typeArguments.resolve( | |
| (t) => t.resolve(), | |
| ); | |
| List<Element>? newElements = elements.resolve((e) => e.resolve()); | |
| return newTypeArguments == null && newElements == null | |
| ? null | |
| : new SetOrMapLiteral( | |
| newTypeArguments ?? typeArguments, | |
| newElements ?? elements, | |
| ); | |
| } | |
| } | |
| class RecordLiteral extends Expression { | |
| final List<RecordField> fields; | |
| RecordLiteral(this.fields); | |
| @override | |
| String toString() => 'RecordLiteral($fields)'; | |
| @override | |
| Expression? resolve() { | |
| List<RecordField>? newFields = fields.resolve((e) => e.resolve()); | |
| return newFields == null ? null : new RecordLiteral(newFields); | |
| } | |
| } | |
| class IfNull extends Expression { | |
| final Expression left; | |
| final Expression right; | |
| IfNull(this.left, this.right); | |
| @override | |
| String toString() => 'IfNull($left,$right)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newLeft = left.resolve(); | |
| Expression? newRight = right.resolve(); | |
| return newLeft == null && newRight == null | |
| ? null | |
| : new IfNull(newLeft ?? left, newRight ?? right); | |
| } | |
| } | |
| enum LogicalOperator { | |
| and('&&'), | |
| or('||'); | |
| final String text; | |
| const LogicalOperator(this.text); | |
| } | |
| class LogicalExpression extends Expression { | |
| final Expression left; | |
| final LogicalOperator operator; | |
| final Expression right; | |
| LogicalExpression(this.left, this.operator, this.right); | |
| @override | |
| String toString() => 'LogicalExpression($left,$operator,$right)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newLeft = left.resolve(); | |
| Expression? newRight = right.resolve(); | |
| return newLeft == null && newRight == null | |
| ? null | |
| : new LogicalExpression(newLeft ?? left, operator, newRight ?? right); | |
| } | |
| } | |
| class EqualityExpression extends Expression { | |
| final Expression left; | |
| final Expression right; | |
| final bool isNotEquals; | |
| EqualityExpression(this.left, this.right, {required this.isNotEquals}); | |
| @override | |
| String toString() => | |
| 'EqualityExpression($left,$right,isNotEquals=$isNotEquals)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newLeft = left.resolve(); | |
| Expression? newRight = right.resolve(); | |
| return newLeft == null && newRight == null | |
| ? null | |
| : new EqualityExpression( | |
| newLeft ?? left, | |
| newRight ?? right, | |
| isNotEquals: isNotEquals, | |
| ); | |
| } | |
| } | |
| enum BinaryOperator { | |
| greaterThan('>'), | |
| greaterThanOrEqual('>='), | |
| lessThan('<'), | |
| lessThanOrEqual('<='), | |
| shiftLeft('<<'), | |
| signedShiftRight('>>'), | |
| unsignedShiftRight('>>>'), | |
| plus('+'), | |
| minus('-'), | |
| times('*'), | |
| divide('/'), | |
| integerDivide('~/'), | |
| modulo('%'), | |
| bitwiseOr('|'), | |
| bitwiseAnd('&'), | |
| bitwiseXor('^'); | |
| final String text; | |
| const BinaryOperator(this.text); | |
| } | |
| class BinaryExpression extends Expression { | |
| final Expression left; | |
| final BinaryOperator operator; | |
| final Expression right; | |
| BinaryExpression(this.left, this.operator, this.right); | |
| @override | |
| String toString() => 'BinaryExpression($left,$operator,$right)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newLeft = left.resolve(); | |
| Expression? newRight = right.resolve(); | |
| return newLeft == null && newRight == null | |
| ? null | |
| : new BinaryExpression(newLeft ?? left, operator, newRight ?? right); | |
| } | |
| } | |
| enum UnaryOperator { | |
| minus('-'), | |
| bang('!'), | |
| tilde('~'); | |
| final String text; | |
| const UnaryOperator(this.text); | |
| } | |
| class UnaryExpression extends Expression { | |
| final UnaryOperator operator; | |
| final Expression expression; | |
| UnaryExpression(this.operator, this.expression); | |
| @override | |
| String toString() => 'UnaryExpression($operator,$expression)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newExpression = expression.resolve(); | |
| return newExpression == null | |
| ? null | |
| : new UnaryExpression(operator, newExpression); | |
| } | |
| } | |
| class IsTest extends Expression { | |
| final Expression expression; | |
| final TypeAnnotation type; | |
| final bool isNot; | |
| IsTest(this.expression, this.type, {required this.isNot}); | |
| @override | |
| String toString() => 'IsTest($expression,$type,isNot=$isNot)'; | |
| @override | |
| Expression? resolve() { | |
| TypeAnnotation? newType = type.resolve(); | |
| Expression? newExpression = expression.resolve(); | |
| return newType == null && newExpression == null | |
| ? null | |
| : new IsTest( | |
| newExpression ?? expression, | |
| newType ?? type, | |
| isNot: isNot, | |
| ); | |
| } | |
| } | |
| class AsExpression extends Expression { | |
| final Expression expression; | |
| final TypeAnnotation type; | |
| AsExpression(this.expression, this.type); | |
| @override | |
| String toString() => 'AsExpression($expression,$type)'; | |
| @override | |
| Expression? resolve() { | |
| TypeAnnotation? newType = type.resolve(); | |
| Expression? newExpression = expression.resolve(); | |
| return newType == null && newExpression == null | |
| ? null | |
| : new AsExpression(newExpression ?? expression, newType ?? type); | |
| } | |
| } | |
| class NullCheck extends Expression { | |
| final Expression expression; | |
| NullCheck(this.expression); | |
| @override | |
| String toString() => 'NullCheck($expression)'; | |
| @override | |
| Expression? resolve() { | |
| Expression? newExpression = expression.resolve(); | |
| return newExpression == null ? null : new NullCheck(newExpression); | |
| } | |
| } | |
| class UnresolvedExpression extends Expression { | |
| final Unresolved unresolved; | |
| UnresolvedExpression(this.unresolved); | |
| @override | |
| String toString() => 'UnresolvedExpression($unresolved)'; | |
| @override | |
| Expression? resolve() { | |
| return unresolved.resolveAsExpression(); | |
| } | |
| } |