Index: pkg/fasta/lib/src/source/diet_listener.dart |
diff --git a/pkg/fasta/lib/src/source/diet_listener.dart b/pkg/fasta/lib/src/source/diet_listener.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6e63f0c48e3d0cd5f47dff6943d0ed575354d38f |
--- /dev/null |
+++ b/pkg/fasta/lib/src/source/diet_listener.dart |
@@ -0,0 +1,471 @@ |
+// 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.diet_listener; |
+ |
+import 'package:kernel/ast.dart' show |
+ AsyncMarker; |
+ |
+import 'package:kernel/class_hierarchy.dart' show |
+ ClassHierarchy; |
+ |
+import 'package:kernel/core_types.dart' show |
+ CoreTypes; |
+ |
+import 'package:dart_parser/src/parser.dart' show |
+ Parser, |
+ optional; |
+ |
+import 'package:dart_scanner/src/token.dart' show |
+ BeginGroupToken, |
+ Token; |
+ |
+import '../errors.dart' show |
+ Crash, |
+ InputError, |
+ inputError, |
+ internalError; |
+ |
+import 'stack_listener.dart' show |
+ StackListener; |
+ |
+import '../kernel/body_builder.dart' show |
+ BodyBuilder; |
+ |
+import '../builder/builder.dart'; |
+ |
+import '../analyzer/analyzer.dart'; |
+ |
+import '../builder/scope.dart' show |
+ Scope; |
+ |
+import '../ast_kind.dart' show |
+ AstKind; |
+ |
+import 'source_library_builder.dart' show |
+ SourceLibraryBuilder; |
+ |
+import 'source_class_builder.dart' show |
+ isConstructorName; |
+ |
+class DietListener extends StackListener { |
+ final SourceLibraryBuilder library; |
+ |
+ final ElementStore elementStore; |
+ |
+ final ClassHierarchy hierarchy; |
+ |
+ final CoreTypes coreTypes; |
+ |
+ final AstKind astKind; |
+ |
+ ClassBuilder currentClass; |
+ |
+ /// For top-level declarations, this is the library scope. For class members, |
+ /// this is the instance scope of [currentClass]. |
+ Scope memberScope; |
+ |
+ DietListener(SourceLibraryBuilder library, this.elementStore, this.hierarchy, |
+ this.coreTypes, this.astKind) |
+ : library = library, |
+ memberScope = library.scope; |
+ |
+ Uri get uri => library.uri; |
+ |
+ void discard(int n) { |
+ for (int i =0; i < n; i++) { |
+ pop(); |
+ } |
+ } |
+ |
+ void endMetadataStar(int count, bool forParameter) { |
+ debugEvent("MetadataStar"); |
+ } |
+ |
+ void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { |
+ debugEvent("Metadata"); |
+ popIfNotNull(periodBeforeName); |
+ discard(1); |
+ } |
+ |
+ void endPartOf(Token partKeyword, Token semicolon) { |
+ debugEvent("PartOf"); |
+ discard(1); |
+ } |
+ |
+ void handleNoArguments(Token token) { |
+ debugEvent("NoArguments"); |
+ } |
+ |
+ void handleModifiers(int count) { |
+ debugEvent("Modifiers"); |
+ } |
+ |
+ void handleNoTypeArguments(Token token) { |
+ debugEvent("NoTypeArguments"); |
+ } |
+ |
+ void handleNoType(Token token) { |
+ debugEvent("NoType"); |
+ } |
+ |
+ void endType(Token beginToken, Token endToken) { |
+ debugEvent("Type"); |
+ discard(1); |
+ } |
+ |
+ void endTypeList(int count) { |
+ debugEvent("TypeList"); |
+ } |
+ |
+ void endMixinApplication() { |
+ debugEvent("MixinApplication"); |
+ } |
+ |
+ void endTypeArguments(int count, Token beginToken, Token endToken) { |
+ debugEvent("TypeArguments"); |
+ } |
+ |
+ void endInitializer(Token assignmentOperator) { |
+ debugEvent("Initializer"); |
+ } |
+ |
+ void handleNoTypeVariables(Token token) { |
+ debugEvent("NoTypeVariables"); |
+ } |
+ |
+ void endFormalParameters(int count, Token beginToken, Token endToken) { |
+ debugEvent("FormalParameters"); |
+ assert(count == 0); // Count is always 0 as the diet parser skips formals. |
+ if (identical(peek(), "-") && identical(beginToken.next, endToken)) { |
+ pop(); |
+ push("unary-"); |
+ } |
+ push(beginToken); |
+ } |
+ |
+ void handleNoFormalParameters(Token token) { |
+ debugEvent("NoFormalParameters"); |
+ if (identical(peek(), "-")) { |
+ pop(); |
+ push("unary-"); |
+ } |
+ push(token); |
+ } |
+ |
+ void endFunctionTypeAlias(Token typedefKeyword, Token endToken) { |
+ debugEvent("FunctionTypeAlias"); |
+ discard(2); // Name + endToken. |
+ checkEmpty(); |
+ } |
+ |
+ void endFields(int count, Token beginToken, Token endToken) { |
+ debugEvent("Fields"); |
+ List<String> names = popList(count); |
+ Builder builder = lookupBuilder(beginToken, null, names.first); |
+ buildFields(beginToken, false, builder.isInstanceMember); |
+ } |
+ |
+ void handleAsyncModifier(Token asyncToken, Token startToken) { |
+ debugEvent("AsyncModifier"); |
+ } |
+ |
+ void endTopLevelMethod(Token beginToken, Token getOrSet, Token endToken) { |
+ debugEvent("TopLevelMethod"); |
+ Token bodyToken = pop(); |
+ String name = pop(); |
+ checkEmpty(); |
+ buildFunctionBody(bodyToken, lookupBuilder(beginToken, getOrSet, name)); |
+ } |
+ |
+ void handleNoFunctionBody(Token token) { |
+ debugEvent("NoFunctionBody"); |
+ } |
+ |
+ void endTopLevelFields(int count, Token beginToken, Token endToken) { |
+ debugEvent("TopLevelFields"); |
+ discard(count); |
+ buildFields(beginToken, true, false); |
+ } |
+ |
+ void handleVoidKeyword(Token token) { |
+ debugEvent("VoidKeyword"); |
+ } |
+ |
+ void handleNoInitializers() { |
+ debugEvent("NoInitializers"); |
+ } |
+ |
+ void endInitializers(int count, Token beginToken, Token endToken) { |
+ debugEvent("Initializers"); |
+ } |
+ |
+ void handleQualified(Token period) { |
+ debugEvent("handleQualified"); |
+ // TODO(ahe): Shared with outline_builder.dart. |
+ String name = pop(); |
+ String receiver = pop(); |
+ push("$receiver.$name"); |
+ } |
+ |
+ void endLibraryName(Token libraryKeyword, Token semicolon) { |
+ debugEvent("endLibraryName"); |
+ discard(1); |
+ } |
+ |
+ void beginLiteralString(Token token) { |
+ debugEvent("beginLiteralString"); |
+ } |
+ |
+ void endLiteralString(int interpolationCount) { |
+ debugEvent("endLiteralString"); |
+ discard(interpolationCount); |
+ } |
+ |
+ void handleStringJuxtaposition(int literalCount) { |
+ debugEvent("StringJuxtaposition"); |
+ } |
+ |
+ void endDottedName(int count, Token firstIdentifier) { |
+ debugEvent("DottedName"); |
+ discard(count); |
+ } |
+ |
+ void endConditionalUri(Token ifKeyword, Token equalitySign) { |
+ debugEvent("ConditionalUri"); |
+ } |
+ |
+ void endConditionalUris(int count) { |
+ debugEvent("ConditionalUris"); |
+ } |
+ |
+ void handleOperatorName(Token operatorKeyword, Token token) { |
+ debugEvent("OperatorName"); |
+ push(token.stringValue); |
+ } |
+ |
+ void endIdentifierList(int count) { |
+ debugEvent("IdentifierList"); |
+ discard(count); |
+ } |
+ |
+ void endShow(Token showKeyword) { |
+ debugEvent("Show"); |
+ } |
+ |
+ void endHide(Token hideKeyword) { |
+ debugEvent("Hide"); |
+ } |
+ |
+ void endCombinators(int count) { |
+ debugEvent("Combinators"); |
+ } |
+ |
+ void endImport(Token importKeyword, Token DeferredKeyword, Token asKeyword, |
+ Token semicolon) { |
+ debugEvent("Import"); |
+ popIfNotNull(asKeyword); |
+ } |
+ |
+ void endExport(Token exportKeyword, Token semicolon) { |
+ debugEvent("Export"); |
+ } |
+ |
+ void endPart(Token partKeyword, Token semicolon) { |
+ debugEvent("Part"); |
+ } |
+ |
+ void endTypeVariable(Token token, Token extendsOrSuper) { |
+ debugEvent("TypeVariable"); |
+ discard(1); |
+ } |
+ |
+ void endTypeVariables(int count, Token beginToken, Token endToken) { |
+ debugEvent("TypeVariables"); |
+ } |
+ |
+ void handleModifier(Token token) { |
+ debugEvent("Modifier"); |
+ } |
+ |
+ void endConstructorReference( |
+ Token start, Token periodBeforeName, Token endToken) { |
+ debugEvent("ConstructorReference"); |
+ popIfNotNull(periodBeforeName); |
+ } |
+ |
+ void endFactoryMethod(Token beginToken, Token endToken) { |
+ debugEvent("FactoryMethod"); |
+ BeginGroupToken bodyToken = pop(); |
+ String name = pop(); |
+ checkEmpty(); |
+ if (bodyToken == null || optional("=", bodyToken.endGroup.next)) { |
+ return; |
+ } |
+ buildFunctionBody(bodyToken, lookupBuilder(beginToken, null, name)); |
+ } |
+ |
+ void endRedirectingFactoryBody(Token beginToken, Token endToken) { |
+ debugEvent("RedirectingFactoryBody"); |
+ discard(1); // ConstructorReference. |
+ } |
+ |
+ void endMethod(Token getOrSet, Token beginToken, Token endToken) { |
+ debugEvent("Method"); |
+ Token bodyToken = pop(); |
+ String name = pop(); |
+ checkEmpty(); |
+ if (bodyToken == null) { |
+ return; |
+ } |
+ buildFunctionBody(bodyToken, lookupBuilder(beginToken, getOrSet, name)); |
+ } |
+ |
+ StackListener createListener(MemberBuilder builder, Scope memberScope, |
+ bool isInstanceMember, [Scope formalParameterScope]) { |
+ switch (astKind) { |
+ case AstKind.Kernel: |
+ return new BodyBuilder(library, builder, memberScope, |
+ formalParameterScope, hierarchy, coreTypes, currentClass, |
+ isInstanceMember); |
+ |
+ case AstKind.Analyzer: |
+ return new AstBuilder(library, builder, elementStore, memberScope); |
+ } |
+ |
+ return internalError("Unknown $astKind"); |
+ } |
+ |
+ void buildFunctionBody(Token token, ProcedureBuilder builder) { |
+ Scope typeParameterScope = builder.computeTypeParameterScope(memberScope); |
+ Scope formalParameterScope = |
+ builder.computeFormalParameterScope(typeParameterScope); |
+ assert(typeParameterScope != null); |
+ assert(formalParameterScope != null); |
+ parseFunctionBody( |
+ createListener(builder, typeParameterScope, builder.isInstanceMember, |
+ formalParameterScope), |
+ token); |
+ } |
+ |
+ void buildFields(Token token, bool isTopLevel, bool isInstanceMember) { |
+ parseFields(createListener(null, memberScope, isInstanceMember), |
+ token, isTopLevel); |
+ } |
+ |
+ void endMember() { |
+ debugEvent("Member"); |
+ checkEmpty(); |
+ } |
+ |
+ void beginClassBody(Token token) { |
+ debugEvent("beginClassBody"); |
+ String name = pop(); |
+ assert(currentClass == null); |
+ currentClass = lookupBuilder(token, null, name); |
+ assert(memberScope == library.scope); |
+ memberScope = currentClass.computeInstanceScope(memberScope); |
+ } |
+ |
+ void endClassBody(int memberCount, Token beginToken, Token endToken) { |
+ debugEvent("ClassBody"); |
+ currentClass = null; |
+ checkEmpty(); |
+ memberScope = library.scope; |
+ } |
+ |
+ void endClassDeclaration(int interfacesCount, Token beginToken, |
+ Token extendsKeyword, Token implementsKeyword, Token endToken) { |
+ debugEvent("ClassDeclaration"); |
+ checkEmpty(); |
+ } |
+ |
+ void endEnum(Token enumKeyword, Token endBrace, int count) { |
+ debugEvent("Enum"); |
+ discard(count); |
+ pop(); // Name. |
+ checkEmpty(); |
+ } |
+ |
+ void endNamedMixinApplication( |
+ Token classKeyword, Token implementsKeyword, Token endToken) { |
+ debugEvent("NamedMixinApplication"); |
+ pop(); // Name. |
+ checkEmpty(); |
+ } |
+ |
+ void parseFunctionBody(StackListener listener, Token token) { |
+ try { |
+ Parser parser = new Parser(listener); |
+ token = parser.parseFormalParametersOpt(token); |
+ var formals = listener.pop(); |
+ listener.checkEmpty(); |
+ listener.prepareInitializers(); |
+ token = parser.parseInitializersOpt(token); |
+ token = parser.parseAsyncModifier(token); |
+ AsyncMarker asyncModifier = listener.pop(); |
+ bool isExpression = false; |
+ bool allowAbstract = true; |
+ parser.parseFunctionBody(token, isExpression, allowAbstract); |
+ var body = listener.pop(); |
+ listener.checkEmpty(); |
+ listener.finishFunction(formals, asyncModifier, body); |
+ } on InputError { |
+ rethrow; |
+ } catch (e, s) { |
+ throw new Crash(uri, token.charOffset, e, s); |
+ } |
+ } |
+ |
+ void parseFields(StackListener listener, Token token, bool isTopLevel) { |
+ Parser parser = new Parser(listener); |
+ if (isTopLevel) { |
+ token = parser.parseTopLevelMember(token); |
+ } else { |
+ token = parser.parseMember(token); |
+ } |
+ listener.checkEmpty(); |
+ } |
+ |
+ Builder lookupBuilder(Token token, Token getOrSet, String name) { |
+ Builder builder; |
+ if (currentClass != null) { |
+ builder = currentClass.members[name]; |
+ if (builder == null && isConstructorName(name, currentClass.name)) { |
+ int index = name.indexOf("."); |
+ name = index == -1 ? "" : name.substring(index + 1); |
+ builder = currentClass.members[name]; |
+ } |
+ } else { |
+ builder = library.members[name]; |
+ } |
+ if (builder == null) { |
+ return internalError("@${token.charOffset}: builder not found: $name"); |
+ } |
+ if (builder.next != null) { |
+ Builder getterBuilder; |
+ Builder setterBuilder; |
+ Builder current = builder; |
+ while (current != null) { |
+ if (current.isGetter && getterBuilder == null) { |
+ getterBuilder = current; |
+ } else if (current.isSetter && setterBuilder == null) { |
+ setterBuilder = current; |
+ } else { |
+ return inputError(uri, token.charOffset, "Duplicated name: $name"); |
+ } |
+ current = current.next; |
+ } |
+ assert(getOrSet != null); |
+ if (optional("get", getOrSet)) return getterBuilder; |
+ if (optional("set", getOrSet)) return setterBuilder; |
+ } |
+ return builder; |
+ } |
+ |
+ void debugEvent(String name) { |
+ // print(" ${stack.join('\n ')}"); |
+ // print(name); |
+ } |
+} |