blob: a7915a45ff5c30a0829307c429c04525980a9ee3 [file] [log] [blame]
// 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.
/// Listener used in combination with `TopLevelParser` to extract the URIs of
/// import, part, and export directives.
library front_end.src.fasta.source.directive_listener;
import 'package:_fe_analyzer_shared/src/scanner/token.dart' show Token;
import 'package:_fe_analyzer_shared/src/parser/identifier_context.dart';
import 'package:_fe_analyzer_shared/src/parser/listener.dart';
import 'package:_fe_analyzer_shared/src/parser/quote.dart';
import '../fasta_codes.dart' show messageExpectedBlockToSkip;
/// Listener that records imports, exports, and part directives.
///
/// This is normally used in combination with the `TopLevelParser`, which skips
/// over the body of declarations like classes and function that are irrelevant
/// for directives. Note that on correct programs directives cannot occur after
/// any top-level declaration, but we recommend to continue parsing the entire
/// file in order to gracefully handle input errors.
class DirectiveListener extends Listener {
/// Import directives with URIs and combinators.
final List<NamespaceDirective> imports = <NamespaceDirective>[];
/// Export directives with URIs and combinators.
final List<NamespaceDirective> exports = <NamespaceDirective>[];
/// Collects URIs that occur on any part directive.
final Set<String?> parts = new Set<String?>();
bool _inPart = false;
String? _uri;
List<NamespaceCombinator>? _combinators;
List<String>? _combinatorNames;
DirectiveListener();
@override
beginExport(Token export) {
_combinators = <NamespaceCombinator>[];
}
@override
void beginHide(Token hide) {
_combinatorNames = <String>[];
}
@override
beginImport(Token import) {
_combinators = <NamespaceCombinator>[];
}
@override
void beginLiteralString(Token token) {
if (_combinators != null || _inPart) {
_uri = unescapeString(token.lexeme, token, this);
}
}
@override
beginPart(Token part) {
_inPart = true;
}
@override
void beginShow(Token show) {
_combinatorNames = <String>[];
}
@override
endExport(Token export, Token semicolon) {
exports.add(new NamespaceDirective.export(_uri, _combinators));
_uri = null;
_combinators = null;
}
@override
void endHide(Token hide) {
_combinators!.add(new NamespaceCombinator.hide(_combinatorNames!));
_combinatorNames = null;
}
@override
endImport(Token? import, Token? semicolon) {
imports.add(new NamespaceDirective.import(_uri, _combinators));
_uri = null;
_combinators = null;
}
@override
endPart(Token part, Token semicolon) {
parts.add(_uri);
_uri = null;
_inPart = false;
}
@override
void endShow(Token show) {
_combinators!.add(new NamespaceCombinator.show(_combinatorNames!));
_combinatorNames = null;
}
@override
void handleIdentifier(Token token, IdentifierContext context) {
if (_combinatorNames != null && context == IdentifierContext.combinator) {
_combinatorNames!.add(token.lexeme);
}
}
/// By default, native clauses are not handled and an error is thrown.
@override
void handleNativeFunctionBodySkipped(Token nativeToken, Token semicolon) {
super.handleRecoverableError(
messageExpectedBlockToSkip, nativeToken, nativeToken);
}
}
class NamespaceCombinator {
final bool isShow;
final Set<String> names;
NamespaceCombinator.hide(List<String> names)
: isShow = false,
names = names.toSet();
NamespaceCombinator.show(List<String> names)
: isShow = true,
names = names.toSet();
}
class NamespaceDirective {
final bool isImport;
final String? uri;
final List<NamespaceCombinator>? combinators;
NamespaceDirective.export(this.uri, this.combinators) : isImport = false;
NamespaceDirective.import(this.uri, this.combinators) : isImport = true;
}