becd91df17
Change-Id: I410cd1cf63fbf00b868bbb3e060433cad3ac9e6c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/423520 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
803 lines
19 KiB
Dart
803 lines
19 KiB
Dart
// 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:_fe_analyzer_shared/src/messages/codes.dart'
|
|
show
|
|
LocatedMessage,
|
|
Message,
|
|
templateExperimentNotEnabled,
|
|
templateInternalProblemUnsupported;
|
|
import 'package:_fe_analyzer_shared/src/parser/parser.dart';
|
|
import 'package:_fe_analyzer_shared/src/parser/stack_listener.dart';
|
|
import 'package:_fe_analyzer_shared/src/scanner/token.dart';
|
|
import 'package:analyzer/src/dart/analysis/experiments.dart';
|
|
import 'package:pub_semver/pub_semver.dart';
|
|
|
|
/// "Mini AST" representation of a declaration which can accept annotations.
|
|
class AnnotatedNode {
|
|
final Comment? documentationComment;
|
|
|
|
final List<Annotation> metadata;
|
|
|
|
AnnotatedNode(this.documentationComment, List<Annotation>? metadata)
|
|
: metadata = metadata ?? const [];
|
|
}
|
|
|
|
/// "Mini AST" representation of an annotation.
|
|
class Annotation {
|
|
final String name;
|
|
|
|
final String? constructorName;
|
|
|
|
final List<Expression>? arguments;
|
|
|
|
Annotation(this.name, this.constructorName, this.arguments);
|
|
}
|
|
|
|
/// "Mini AST" representation of a class declaration.
|
|
class ClassDeclaration extends CompilationUnitMember {
|
|
final String name;
|
|
|
|
final TypeName? superclass;
|
|
|
|
final List<ClassMember> members;
|
|
|
|
ClassDeclaration(
|
|
super.documentationComment,
|
|
super.metadata,
|
|
this.name,
|
|
this.superclass,
|
|
this.members,
|
|
);
|
|
}
|
|
|
|
/// "Mini AST" representation of a class member.
|
|
class ClassMember extends AnnotatedNode {
|
|
ClassMember(super.documentationComment, super.metadata);
|
|
}
|
|
|
|
/// "Mini AST" representation of a comment.
|
|
class Comment {
|
|
final bool isDocumentation;
|
|
|
|
final List<Token> tokens;
|
|
|
|
factory Comment(Token commentToken) {
|
|
var tokens = <Token>[];
|
|
bool isDocumentation = false;
|
|
|
|
Token? token = commentToken;
|
|
while (token != null) {
|
|
if (token.lexeme.startsWith('/**') || token.lexeme.startsWith('///')) {
|
|
isDocumentation = true;
|
|
}
|
|
tokens.add(token);
|
|
token = token.next;
|
|
}
|
|
return Comment._(isDocumentation, tokens);
|
|
}
|
|
|
|
Comment._(this.isDocumentation, this.tokens);
|
|
}
|
|
|
|
/// "Mini AST" representation of a CompilationUnit.
|
|
class CompilationUnit {
|
|
final declarations = <CompilationUnitMember>[];
|
|
}
|
|
|
|
/// "Mini AST" representation of a top level member of a compilation unit.
|
|
class CompilationUnitMember extends AnnotatedNode {
|
|
CompilationUnitMember(super.documentationComment, super.metadata);
|
|
}
|
|
|
|
/// "Mini AST" representation of a constructor declaration.
|
|
class ConstructorDeclaration extends ClassMember {
|
|
final String name;
|
|
|
|
ConstructorDeclaration(super.documentationComment, super.metadata, this.name);
|
|
}
|
|
|
|
/// "Mini AST" representation of an individual enum constant in an enum
|
|
/// declaration.
|
|
class EnumConstantDeclaration extends AnnotatedNode {
|
|
final String name;
|
|
|
|
EnumConstantDeclaration(
|
|
super.documentationComment,
|
|
super.metadata,
|
|
this.name,
|
|
);
|
|
}
|
|
|
|
/// "Mini AST" representation of an enum declaration.
|
|
class EnumDeclaration extends CompilationUnitMember {
|
|
final String name;
|
|
|
|
final List<EnumConstantDeclaration> constants;
|
|
|
|
EnumDeclaration(
|
|
super.documentationComment,
|
|
super.metadata,
|
|
this.name,
|
|
this.constants,
|
|
);
|
|
}
|
|
|
|
/// "Mini AST" representation of an expression.
|
|
class Expression {
|
|
String toCode() {
|
|
throw UnimplementedError('$runtimeType');
|
|
}
|
|
}
|
|
|
|
/// "Mini AST" representation of an integer literal.
|
|
class IntegerLiteral extends Expression {
|
|
final int value;
|
|
|
|
IntegerLiteral(this.value);
|
|
}
|
|
|
|
/// "Mini AST" representation of a list literal.
|
|
class ListLiteral extends Expression {
|
|
final Token leftBracket;
|
|
final List<Expression> elements;
|
|
final Token rightBracket;
|
|
|
|
ListLiteral(this.leftBracket, this.elements, this.rightBracket);
|
|
|
|
@override
|
|
String toCode() {
|
|
return '[${elements.map((e) => e.toCode()).join(', ')}]';
|
|
}
|
|
}
|
|
|
|
/// "Mini AST" representation of a method declaration.
|
|
class MethodDeclaration extends ClassMember {
|
|
final bool isGetter;
|
|
|
|
final String name;
|
|
|
|
final TypeName? returnType;
|
|
|
|
MethodDeclaration(
|
|
super.documentationComment,
|
|
super.metadata,
|
|
this.isGetter,
|
|
this.name,
|
|
this.returnType,
|
|
);
|
|
}
|
|
|
|
/// Parser listener which generates a "mini AST" representation of the source
|
|
/// code. This representation is just sufficient for summary code generation.
|
|
class MiniAstBuilder extends StackListener {
|
|
bool inMetadata = false;
|
|
|
|
final compilationUnit = CompilationUnit();
|
|
|
|
@override
|
|
bool get isDartLibrary => throw UnimplementedError();
|
|
|
|
@override
|
|
Uri get uri => throw UnimplementedError();
|
|
|
|
@override
|
|
void addProblem(
|
|
Message message,
|
|
int charOffset,
|
|
int length, {
|
|
bool wasHandled = false,
|
|
List<LocatedMessage>? context,
|
|
}) {
|
|
internalProblem(message, charOffset, uri);
|
|
}
|
|
|
|
@override
|
|
void beginMetadata(Token token) {
|
|
inMetadata = true;
|
|
}
|
|
|
|
@override
|
|
void beginMetadataStar(Token token) {
|
|
debugEvent("beginMetadataStar");
|
|
var precedingComments = token.precedingComments;
|
|
if (precedingComments != null) {
|
|
push(Comment(precedingComments));
|
|
} else {
|
|
push(NullValues.Comments);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void endArguments(int count, Token beginToken, Token endToken) {
|
|
debugEvent("Arguments");
|
|
push(popList(count, List<dynamic>.filled(count, null, growable: true)));
|
|
}
|
|
|
|
@override
|
|
void endBinaryExpression(Token token, Token endToken) {
|
|
debugEvent("BinaryExpression");
|
|
|
|
if (identical('.', token.stringValue)) {
|
|
var rightOperand = pop() as String;
|
|
var leftOperand = pop();
|
|
if (leftOperand is String && !leftOperand.contains('.')) {
|
|
push(PrefixedIdentifier(leftOperand, token, rightOperand));
|
|
} else {
|
|
push(UnknownExpression());
|
|
}
|
|
} else {
|
|
pop(); // RHS
|
|
pop(); // LHS
|
|
push(UnknownExpression());
|
|
}
|
|
}
|
|
|
|
@override
|
|
void endClassDeclaration(Token beginToken, Token endToken) {
|
|
debugEvent("ClassDeclaration");
|
|
var members = popTypedList<ClassMember>() ?? [];
|
|
var superclass = pop() as TypeName?;
|
|
pop(); // Type variables
|
|
var name = pop() as String;
|
|
var metadata = popTypedList<Annotation>();
|
|
var comment = pop() as Comment?;
|
|
compilationUnit.declarations.add(
|
|
ClassDeclaration(comment, metadata, name, superclass, members),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void endClassFactoryMethod(
|
|
Token beginToken,
|
|
Token factoryKeyword,
|
|
Token endToken,
|
|
) {
|
|
debugEvent("ClassFactoryMethod");
|
|
pop(); // Body
|
|
pop(); // Type variables
|
|
var name = pop() as String;
|
|
var metadata = popTypedList() as List<Annotation>?;
|
|
var comment = pop() as Comment?;
|
|
push(ConstructorDeclaration(comment, metadata, name));
|
|
}
|
|
|
|
@override
|
|
void endClassMethod(
|
|
Token? getOrSet,
|
|
Token beginToken,
|
|
Token beginParam,
|
|
Token? beginInitializers,
|
|
Token endToken,
|
|
) {
|
|
debugEvent("Method");
|
|
pop(); // Body
|
|
pop(); // Initializers
|
|
pop(); // Formal parameters
|
|
pop(); // Type variables
|
|
var name = pop() as String;
|
|
var returnType = pop() as TypeName?;
|
|
var metadata = popTypedList<Annotation>();
|
|
var comment = pop() as Comment?;
|
|
push(
|
|
MethodDeclaration(
|
|
comment,
|
|
metadata,
|
|
getOrSet?.lexeme == 'get',
|
|
name,
|
|
returnType,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void endClassOrMixinOrExtensionBody(
|
|
DeclarationKind kind,
|
|
int memberCount,
|
|
Token beginToken,
|
|
Token endToken,
|
|
) {
|
|
debugEvent("ClassOrMixinBody");
|
|
push(
|
|
popList(
|
|
memberCount,
|
|
List<ClassMember?>.filled(memberCount, null, growable: true),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void endCombinators(int count) {
|
|
debugEvent("Combinators");
|
|
}
|
|
|
|
@override
|
|
void endConditionalUris(int count) {
|
|
debugEvent("ConditionalUris");
|
|
if (count != 0) {
|
|
internalProblem(
|
|
templateInternalProblemUnsupported.withArguments("Conditional URIs"),
|
|
-1,
|
|
null,
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void endConstructorReference(
|
|
Token start,
|
|
Token? periodBeforeName,
|
|
Token endToken,
|
|
ConstructorReferenceContext constructorReferenceContext,
|
|
) {
|
|
debugEvent("ConstructorReference");
|
|
}
|
|
|
|
@override
|
|
void endEnum(
|
|
Token beginToken,
|
|
Token enumKeyword,
|
|
Token leftBrace,
|
|
int memberCount,
|
|
Token endToken,
|
|
) {
|
|
debugEvent("Enum");
|
|
}
|
|
|
|
@override
|
|
void endFieldInitializer(Token assignment, Token token) {
|
|
debugEvent("FieldInitializer");
|
|
pop(); // Expression
|
|
}
|
|
|
|
@override
|
|
void endFormalParameter(
|
|
Token? thisKeyword,
|
|
Token? superKeyword,
|
|
Token? periodAfterThisOrSuper,
|
|
Token nameToken,
|
|
Token? initializerStart,
|
|
Token? initializerEnd,
|
|
FormalParameterKind kind,
|
|
MemberKind memberKind,
|
|
) {
|
|
debugEvent("FormalParameter");
|
|
pop(); // Name
|
|
pop(); // Type
|
|
pop(); // Metadata
|
|
pop(); // Comment
|
|
}
|
|
|
|
@override
|
|
void endFormalParameters(
|
|
int count,
|
|
Token beginToken,
|
|
Token endToken,
|
|
MemberKind kind,
|
|
) {
|
|
debugEvent("FormalParameters");
|
|
}
|
|
|
|
@override
|
|
void endImport(Token importKeyword, Token? augmentToken, Token? semicolon) {
|
|
debugEvent("Import");
|
|
pop(NullValues.Prefix); // Prefix identifier
|
|
pop(); // URI
|
|
pop(); // Metadata
|
|
pop(); // Comment
|
|
}
|
|
|
|
@override
|
|
void endLibraryName(Token libraryKeyword, Token semicolon, bool hasName) {
|
|
debugEvent("LibraryName");
|
|
if (hasName) {
|
|
pop(); // Library name
|
|
}
|
|
pop(); // Metadata
|
|
pop(); // Comment
|
|
}
|
|
|
|
@override
|
|
void endLiteralString(int interpolationCount, Token endToken) {
|
|
super.endLiteralString(interpolationCount, endToken);
|
|
var value = pop() as String;
|
|
push(StringLiteral(value));
|
|
}
|
|
|
|
@override
|
|
void endMember() {
|
|
debugEvent("Member");
|
|
}
|
|
|
|
@override
|
|
void endMetadata(Token beginToken, Token? periodBeforeName, Token endToken) {
|
|
debugEvent("Metadata");
|
|
inMetadata = false;
|
|
var arguments = pop() as List?;
|
|
var constructorName = popIfNotNull(periodBeforeName) as String?;
|
|
pop(); // Type arguments
|
|
var name = pop() as String;
|
|
push(Annotation(name, constructorName, arguments?.cast<Expression>()));
|
|
}
|
|
|
|
@override
|
|
void endMetadataStar(int count) {
|
|
debugEvent("MetadataStar");
|
|
push(
|
|
popList(count, List<Annotation?>.filled(count, null, growable: true)) ??
|
|
NullValues.Metadata,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void endShow(Token showKeyword) {
|
|
debugEvent("Show");
|
|
pop(); // Shown names
|
|
}
|
|
|
|
@override
|
|
void endTopLevelFields(
|
|
Token? augmentToken,
|
|
Token? externalToken,
|
|
Token? staticToken,
|
|
Token? covariantToken,
|
|
Token? lateToken,
|
|
Token? varFinalOrConst,
|
|
int count,
|
|
Token beginToken,
|
|
Token endToken,
|
|
) {
|
|
// We ignore top level variable declarations; they are present just to make
|
|
// the IDL analyze without warnings.
|
|
debugEvent("TopLevelFields");
|
|
popList(count, List<dynamic>.filled(count, null, growable: true)); // Fields
|
|
pop(); // Type
|
|
pop(); // Metadata
|
|
pop(); // Comment
|
|
}
|
|
|
|
@override
|
|
void endTypeArguments(int count, Token beginToken, Token endToken) {
|
|
debugEvent("TypeArguments");
|
|
push(popList(count, List<TypeName?>.filled(count, null, growable: true)));
|
|
}
|
|
|
|
@override
|
|
void handleAsyncModifier(Token? asyncToken, Token? starToken) {
|
|
debugEvent("AsyncModifier");
|
|
}
|
|
|
|
@override
|
|
void handleClassNoWithClause() {
|
|
debugEvent("NoClassWithClause");
|
|
}
|
|
|
|
@override
|
|
void handleClassWithClause(Token withKeyword) {
|
|
debugEvent("ClassWithClause");
|
|
}
|
|
|
|
@override
|
|
void handleEnumElement(Token beginToken, Token? augmentToken) {
|
|
debugEvent("EnumElement");
|
|
pop(); // Arguments.
|
|
pop(); // Type arguments.
|
|
}
|
|
|
|
@override
|
|
void handleEnumElements(Token endToken, int count) {
|
|
debugEvent("EnumElements");
|
|
var constants = List<EnumConstantDeclaration?>.filled(
|
|
count,
|
|
null,
|
|
growable: true,
|
|
);
|
|
popList(count, constants);
|
|
pop(); // Type variables.
|
|
var name = pop() as String;
|
|
var metadata = popTypedList<Annotation>();
|
|
var comment = pop() as Comment?;
|
|
compilationUnit.declarations.add(
|
|
EnumDeclaration(comment, metadata, name, constants.nonNulls.toList()),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void handleEnumHeader(
|
|
Token? augmentToken,
|
|
Token enumKeyword,
|
|
Token leftBrace,
|
|
) {
|
|
debugEvent("EnumHeader");
|
|
}
|
|
|
|
@override
|
|
void handleEnumNoWithClause() {
|
|
debugEvent("NoEnumWithClause");
|
|
}
|
|
|
|
@override
|
|
void handleFormalParameterWithoutValue(Token token) {
|
|
debugEvent("FormalParameterWithoutValue");
|
|
}
|
|
|
|
@override
|
|
void handleFunctionBodySkipped(Token token, bool isExpressionBody) {
|
|
if (isExpressionBody) pop();
|
|
push(NullValues.FunctionBody);
|
|
}
|
|
|
|
@override
|
|
void handleIdentifier(Token token, IdentifierContext context) {
|
|
if (context == IdentifierContext.enumValueDeclaration) {
|
|
var metadata = popTypedList() as List<Annotation>?;
|
|
var comment = pop() as Comment?;
|
|
push(EnumConstantDeclaration(comment, metadata, token.lexeme));
|
|
} else {
|
|
push(token.lexeme);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void handleIdentifierList(int count) {
|
|
debugEvent("IdentifierList");
|
|
push(popList(count, List<dynamic>.filled(count, null, growable: true)));
|
|
}
|
|
|
|
@override
|
|
void handleImportPrefix(Token? deferredKeyword, Token? asKeyword) {
|
|
debugEvent("ImportPrefix");
|
|
pushIfNull(asKeyword, NullValues.Prefix);
|
|
}
|
|
|
|
@override
|
|
void handleInvalidMember(Token endToken) {
|
|
debugEvent("InvalidMember");
|
|
pop(); // metadata star
|
|
}
|
|
|
|
@override
|
|
void handleInvalidTopLevelDeclaration(Token endToken) {
|
|
debugEvent("InvalidTopLevelDeclaration");
|
|
pop(); // metadata star
|
|
}
|
|
|
|
@override
|
|
void handleInvalidTypeArguments(Token token) {
|
|
debugEvent("InvalidTypeArguments");
|
|
pop(NullValues.TypeArguments);
|
|
}
|
|
|
|
@override
|
|
void handleLiteralInt(Token token) {
|
|
debugEvent("LiteralInt");
|
|
push(IntegerLiteral(int.parse(token.lexeme)));
|
|
}
|
|
|
|
@override
|
|
void handleLiteralList(
|
|
int count,
|
|
Token leftBracket,
|
|
Token? constKeyword,
|
|
Token rightBracket,
|
|
) {
|
|
debugEvent("LiteralList");
|
|
|
|
var elements = List<Object?>.filled(count, null);
|
|
popList(count, elements);
|
|
pop(); // type arguments
|
|
|
|
push(
|
|
ListLiteral(leftBracket, List<Expression>.from(elements), rightBracket),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void handleLiteralNull(Token token) {
|
|
debugEvent("LiteralNull");
|
|
push(UnknownExpression());
|
|
}
|
|
|
|
@override
|
|
void handleNamedArgument(Token colon) {
|
|
var expression = pop() as Expression;
|
|
var name = pop() as String;
|
|
push(NamedExpression(name, colon, expression));
|
|
}
|
|
|
|
@override
|
|
void handleNamedMixinApplicationWithClause(Token withKeyword) {
|
|
debugEvent("NamedMixinApplicationWithClause");
|
|
}
|
|
|
|
@override
|
|
// TODO(jensj): Handle directly.
|
|
void handleNamedRecordField(Token colon) => handleNamedArgument(colon);
|
|
|
|
@override
|
|
void handleNativeClause(Token nativeToken, bool hasName) {
|
|
debugEvent("NativeClause");
|
|
if (hasName) {
|
|
pop(); // Pop the native name which is a StringLiteral.
|
|
}
|
|
}
|
|
|
|
@override
|
|
void handleNativeFunctionBodyIgnored(Token nativeToken, Token semicolon) {
|
|
debugEvent("NativeFunctionBodyIgnored");
|
|
}
|
|
|
|
@override
|
|
void handleNativeFunctionBodySkipped(Token nativeToken, Token semicolon) {
|
|
debugEvent("NativeFunctionBodySkipped");
|
|
push(NullValues.FunctionBody);
|
|
}
|
|
|
|
@override
|
|
void handleNonNullAssertExpression(Token bang) {
|
|
reportNonNullAssertExpressionNotEnabled(bang);
|
|
}
|
|
|
|
@override
|
|
void handleNoTypeNameInConstructorReference(Token token) {
|
|
debugEvent("NoTypeNameInConstructorReference");
|
|
}
|
|
|
|
@override
|
|
void handleQualified(Token period) {
|
|
debugEvent("Qualified");
|
|
var suffix = pop() as String;
|
|
var prefix = pop() as String;
|
|
push('$prefix.$suffix');
|
|
}
|
|
|
|
@override
|
|
void handleRecoverDeclarationHeader(DeclarationHeaderKind kind) {
|
|
pop(); // superclass
|
|
}
|
|
|
|
@override
|
|
void handleRecoverImport(Token? semicolon) {
|
|
debugEvent("RecoverImport");
|
|
pop(NullValues.Prefix); // Prefix identifier
|
|
}
|
|
|
|
@override
|
|
void handleSend(Token beginToken, Token endToken) {
|
|
debugEvent("Send");
|
|
|
|
var arguments = pop();
|
|
pop(); // Type arguments
|
|
if (arguments != null) {
|
|
pop(); // Receiver
|
|
push(UnknownExpression());
|
|
} else {
|
|
// Property get.
|
|
}
|
|
}
|
|
|
|
@override
|
|
void handleType(Token beginToken, Token? questionMark) {
|
|
debugEvent("Type");
|
|
// reportErrorIfNullableType(questionMark);
|
|
var typeArguments = popTypedList<TypeName>();
|
|
var name = pop() as String;
|
|
push(TypeName(name, typeArguments));
|
|
}
|
|
|
|
@override
|
|
internalProblem(Message message, int charOffset, Uri? uri) {
|
|
throw UnsupportedError(message.problemMessage);
|
|
}
|
|
|
|
List? popList(int n, List list) {
|
|
if (n == 0) return null;
|
|
return stack.popList(n, list, null);
|
|
}
|
|
|
|
/// Calls [pop] and creates a list with the appropriate type parameter `T`
|
|
/// from the resulting `List<dynamic>`.
|
|
List<T>? popTypedList<T>() {
|
|
var list = pop() as List?;
|
|
return list != null ? List<T>.from(list) : null;
|
|
}
|
|
|
|
void reportErrorIfNullableType(Token? questionMark) {
|
|
if (questionMark != null) {
|
|
assert(optional('?', questionMark));
|
|
var feature = ExperimentalFeatures.non_nullable;
|
|
handleRecoverableError(
|
|
templateExperimentNotEnabled.withArguments(
|
|
feature.enableString,
|
|
_versionAsString(ExperimentStatus.currentVersion),
|
|
),
|
|
questionMark,
|
|
questionMark,
|
|
);
|
|
}
|
|
}
|
|
|
|
void reportNonNullAssertExpressionNotEnabled(Token bang) {
|
|
var feature = ExperimentalFeatures.non_nullable;
|
|
handleRecoverableError(
|
|
templateExperimentNotEnabled.withArguments(
|
|
feature.enableString,
|
|
_versionAsString(ExperimentStatus.currentVersion),
|
|
),
|
|
bang,
|
|
bang,
|
|
);
|
|
}
|
|
|
|
static String _versionAsString(Version version) {
|
|
return '${version.major}.${version.minor}.${version.patch}';
|
|
}
|
|
}
|
|
|
|
/// Parser intended for use with [MiniAstBuilder].
|
|
class MiniAstParser extends Parser {
|
|
MiniAstParser(MiniAstBuilder super.listener);
|
|
|
|
@override
|
|
Token parseArgumentsOpt(Token token) {
|
|
var listener = this.listener as MiniAstBuilder;
|
|
if (listener.inMetadata) {
|
|
return super.parseArgumentsOpt(token);
|
|
} else {
|
|
return skipArgumentsOpt(token);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) {
|
|
return skipFunctionBody(token, isExpression, allowAbstract);
|
|
}
|
|
|
|
@override
|
|
Token parseInvalidBlock(Token token) => skipBlock(token);
|
|
}
|
|
|
|
/// "Mini AST" representation of a named expression.
|
|
class NamedExpression extends Expression {
|
|
final String name;
|
|
final Token colon;
|
|
final Expression expression;
|
|
|
|
NamedExpression(this.name, this.colon, this.expression);
|
|
}
|
|
|
|
/// "Mini AST" representation of a named expression.
|
|
class PrefixedIdentifier extends Expression {
|
|
final String prefix;
|
|
final Token operator;
|
|
final String identifier;
|
|
|
|
PrefixedIdentifier(this.prefix, this.operator, this.identifier);
|
|
|
|
@override
|
|
String toCode() {
|
|
return '$prefix.$identifier';
|
|
}
|
|
}
|
|
|
|
/// "Mini AST" representation of a string literal.
|
|
class StringLiteral extends Expression {
|
|
final String stringValue;
|
|
|
|
StringLiteral(this.stringValue);
|
|
}
|
|
|
|
/// "Mini AST" representation of a type name.
|
|
class TypeName {
|
|
final String name;
|
|
|
|
final List<TypeName>? typeArguments;
|
|
|
|
TypeName(this.name, this.typeArguments);
|
|
}
|
|
|
|
/// "Mini AST" representation of an expression which summary code generation
|
|
/// need not be concerned about.
|
|
class UnknownExpression extends Expression {}
|