blob: 0601d26c0bd4d3d380d55d9955e21089f3dceaae [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 fasta.stack_listener;
import 'dart:collection' show
Queue;
import 'package:front_end/src/fasta/parser.dart' show
ErrorKind,
Listener;
import 'package:front_end/src/fasta/scanner.dart' show
BeginGroupToken,
Token;
import 'package:kernel/ast.dart' show
AsyncMarker;
import '../errors.dart' show
inputError,
internalError;
import '../quote.dart' show
unescapeString;
enum NullValue {
Arguments,
Block,
BreakTarget,
CascadeReceiver,
Combinators,
ContinueTarget,
Expression,
FieldInitializer,
FormalParameters,
FunctionBody,
IdentifierList,
Initializers,
Metadata,
Modifiers,
SwitchScope,
Type,
TypeArguments,
TypeList,
TypeVariable,
TypeVariables,
}
abstract class StackListener extends Listener {
final Queue<Object> stack = new Queue<Object>();
Uri get uri;
// TODO(ahe): This doesn't belong here. Only implemented by body_builder.dart
// and ast_builder.dart.
void finishFunction(
covariant formals, AsyncMarker asyncModifier, covariant body) {
return internalError("Unsupported operation");
}
// TODO(ahe): This doesn't belong here. Only implemented by body_builder.dart
// and ast_builder.dart.
void exitLocalScope() => internalError("Unsupported operation");
// TODO(ahe): This doesn't belong here. Only implemented by body_builder.dart
// and ast_builder.dart.
void prepareInitializers() => internalError("Unsupported operation");
void push(Object node) {
if (node == null) internalError("null not allowed.");
stack.addLast(node);
}
Object peek() {
Object node = stack.last;
return node is NullValue ? null : node;
}
Object pop() {
Object node = stack.removeLast();
return node is NullValue ? null : node;
}
Object popIfNotNull(Object value) {
return value == null ? null : pop();
}
List popList(int n) {
if (n == 0) return null;
List list = new List.filled(n, null, growable: true);
for (int i = n - 1; i >= 0; i--) {
list[i] = pop();
}
return list;
}
void debugEvent(String name) {
// printEvent(name);
}
void printEvent(String name) {
for (Object o in stack) {
String s = " $o";
int index = s.indexOf("\n");
if (index != -1) {
s = s.substring(0, index) + "...";
}
print(s);
}
print(name);
}
@override
void logEvent(String name) {
internalError("Unhandled event: $name in $runtimeType $uri:\n"
" ${stack.join('\n ')}");
}
@override
void handleIdentifier(Token token) {
debugEvent("handleIdentifier");
push(token.value);
}
@override
void endInitializer(Token token) {
debugEvent("Initializer");
}
void checkEmpty() {
if (stack.isNotEmpty) {
internalError("${runtimeType}: Stack not empty $uri:\n"
" ${stack.join('\n ')}");
}
if (recoverableErrors.isNotEmpty) {
// TODO(ahe): Handle recoverable errors better.
inputError(uri, recoverableErrors.first.beginOffset,
recoverableErrors);
}
}
@override
void endTopLevelDeclaration(Token token) {
debugEvent("TopLevelDeclaration");
checkEmpty();
}
@override
void endCompilationUnit(int count, Token token) {
debugEvent("CompilationUnit");
checkEmpty();
}
@override
void handleNoTypeArguments(Token token) {
debugEvent("NoTypeArguments");
push(NullValue.TypeArguments);
}
@override
void handleNoTypeVariables(Token token) {
debugEvent("NoTypeVariables");
push(NullValue.TypeVariables);
}
@override
void handleNoType(Token token) {
debugEvent("NoType");
push(NullValue.Type);
}
@override
void handleNoFormalParameters(Token token) {
debugEvent("NoFormalParameters");
push(NullValue.FormalParameters);
}
@override
void handleNoArguments(Token token) {
debugEvent("NoArguments");
push(NullValue.Arguments);
}
@override
void handleNoFunctionBody(Token token) {
debugEvent("NoFunctionBody");
push(NullValue.FunctionBody);
}
@override
void handleNoInitializers() {
debugEvent("NoInitializers");
push(NullValue.Initializers);
}
@override
void handleParenthesizedExpression(BeginGroupToken token) {
debugEvent("ParenthesizedExpression");
}
@override
void beginLiteralString(Token token) {
debugEvent("beginLiteralString");
push(token);
}
@override
void endLiteralString(int interpolationCount) {
debugEvent("endLiteralString");
if (interpolationCount == 0) {
Token token = pop();
push(unescapeString(token.value));
} else {
internalError("String interpolation not implemented.");
}
}
@override
void handleStringJuxtaposition(int literalCount) {
debugEvent("StringJuxtaposition");
push(popList(literalCount).join(""));
}
@override
void endCatchClause(Token token) {
debugEvent("CatchClause");
}
@override
void handleRecoverableError(Token token, ErrorKind kind, Map arguments) {
super.handleRecoverableError(token, kind, arguments);
debugEvent("Error: ${recoverableErrors.last}");
}
@override
Token handleUnrecoverableError(Token token, ErrorKind kind, Map arguments) {
throw inputError(uri, token.charOffset, "$kind $arguments");
}
}