| Index: lib/src/directive_parser.dart
|
| diff --git a/lib/src/directive_parser.dart b/lib/src/directive_parser.dart
|
| deleted file mode 100644
|
| index 53377352a28c11ed9cc5548b1e6d492ef491dcd8..0000000000000000000000000000000000000000
|
| --- a/lib/src/directive_parser.dart
|
| +++ /dev/null
|
| @@ -1,388 +0,0 @@
|
| -// Copyright (c) 2012, 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.
|
| -
|
| -/**
|
| - * A mini parser that extracts top-level directives (library, imports, exports,
|
| - * and parts) from a dart source file.
|
| - */
|
| -library directive_parser;
|
| -
|
| -import 'info.dart' show DartCodeInfo, DartDirectiveInfo;
|
| -import 'messages.dart' show Messages;
|
| -import 'file_system/path.dart';
|
| -
|
| -/**
|
| - * Parse and extract top-level directives from [code].
|
| - *
|
| - * Adds emitted error/warning messages to [messages], if [messages] is
|
| - * supplied.
|
| - */
|
| -DartCodeInfo parseDartCode(String code, Path file, {Messages messages}) {
|
| - messages = messages == null ? new Messages.silent() : messages;
|
| - return new _DirectiveParser(messages, file).parse(code);
|
| -}
|
| -
|
| -/** A parser that extracts top-level directives. */
|
| -// TODO(sigmund): add source-span to error messages
|
| -class _DirectiveParser {
|
| - /** Path to the source file containing the code (for error messages). */
|
| - Path file;
|
| -
|
| - /** Tokenizer used to parse the input until the end of the directives. */
|
| - _DirectiveTokenizer tokenizer;
|
| -
|
| - /** Extracted library identifier, if any. */
|
| - String libraryName;
|
| -
|
| - /** Extracted part-of identifier, if any. */
|
| - String partName;
|
| -
|
| - /** Extracted imports, exports, and parts, if any. */
|
| - List<DartDirectiveInfo> directives = <DartDirectiveInfo>[];
|
| -
|
| - /** Helper for reporting error messages. */
|
| - Messages messages;
|
| -
|
| - /** Last token read by the parser. */
|
| - Token token;
|
| -
|
| - _DirectiveParser(this.messages, this.file);
|
| -
|
| - /** Parse and extract directives from [code]. */
|
| - DartCodeInfo parse(String code) {
|
| - tokenizer = new _DirectiveTokenizer(code);
|
| - parseTopLevel();
|
| - return new DartCodeInfo(libraryName, partName, directives,
|
| - code.substring(token.start));
|
| - }
|
| -
|
| - /**
|
| - * Parse top-level directives and comments, but unlike normal Dart code, stop
|
| - * as soon as we find actual code.
|
| - */
|
| - void parseTopLevel() {
|
| - token = tokenizer.next();
|
| - while (token.kind != Token.EOF) {
|
| - if (token.kind == Token.IDENTIFIER) {
|
| - if (token.value == 'library') {
|
| - parseLibrary();
|
| - } else if (token.value == 'part') {
|
| - parsePart();
|
| - } else if (token.value == 'import') {
|
| - parseImport();
|
| - } else if (token.value == 'export') {
|
| - parseExport();
|
| - } else {
|
| - break;
|
| - }
|
| - } else if (token.kind != Token.COMMENT) {
|
| - break;
|
| - }
|
| - token = tokenizer.next();
|
| - }
|
| - }
|
| -
|
| - /** Parse library declarations: 'library foo.bar;' */
|
| - parseLibrary() {
|
| - libraryName = parseQualifiedName();
|
| - expectToken(Token.SEMICOLON);
|
| - }
|
| -
|
| - /**
|
| - * Parse either a part declaration or part inclusions. For instance,
|
| - * part of foo.bar;
|
| - * or
|
| - * part "foo";
|
| - */
|
| - parsePart() {
|
| - token = tokenizer.next();
|
| - if (token.kind == Token.IDENTIFIER && token.value == 'of') {
|
| - partName = parseQualifiedName();
|
| - } else if (token.kind == Token.STRING) {
|
| - directives.add(new DartDirectiveInfo('part', token.value));
|
| - token = tokenizer.next();
|
| - } else {
|
| - messages.error('unexpected token: ${token}', null, file: file);
|
| - }
|
| - expectToken(Token.SEMICOLON);
|
| - }
|
| -
|
| - /** Parse a qualified name, such as `one.two.three`. */
|
| - parseQualifiedName() {
|
| - List<String> segments = [];
|
| - while (true) {
|
| - token = tokenizer.next();
|
| - if (token.kind != Token.IDENTIFIER) {
|
| - messages.error('invalid qualified name: $token', null, file: file);
|
| - return null;
|
| - }
|
| - segments.add(token.value);
|
| - token = tokenizer.next();
|
| - if (token.kind == Token.SEMICOLON) break;
|
| - if (token.kind != Token.DOT) {
|
| - messages.error('invalid qualified name: $token', null, file: file);
|
| - return null;
|
| - }
|
| - }
|
| - return segments.join('.');
|
| - }
|
| -
|
| - /** Parse an import, with optional prefix and show/hide combinators. */
|
| - parseImport() {
|
| - token = tokenizer.next();
|
| - if (token.kind != Token.STRING) {
|
| - // TODO(sigmund): add file name and span information here.
|
| - messages.error('expected an import url, but found ${token}', null,
|
| - file: file);
|
| - return;
|
| - }
|
| - var uri = token.value;
|
| - token = tokenizer.next();
|
| - while (token.kind == Token.STRING) {
|
| - uri = '$uri${token.value}';
|
| - token = tokenizer.next();
|
| - }
|
| -
|
| - // Parse the optional prefix.
|
| - var prefix;
|
| - if (token.kind == Token.IDENTIFIER && token.value == 'as') {
|
| - token = tokenizer.next();
|
| - if (token.kind != Token.IDENTIFIER) {
|
| - messages.error('expected an identifier as prefix, but found ${token}',
|
| - null, file: file);
|
| - return;
|
| - }
|
| - prefix = token.value;
|
| - token = tokenizer.next();
|
| - }
|
| -
|
| - // Parse the optional show/hide combinators.
|
| - var hide;
|
| - var show;
|
| - while (token.kind == Token.IDENTIFIER) {
|
| - if (token.value == 'hide') {
|
| - if (hide == null) hide = [];
|
| - hide.addAll(parseIdentifierList());
|
| - } else if (token.value == 'show') {
|
| - if (show == null) show = [];
|
| - show.addAll(parseIdentifierList());
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| -
|
| - expectToken(Token.SEMICOLON);
|
| - directives.add(new DartDirectiveInfo('import', uri, prefix, hide, show));
|
| - }
|
| -
|
| - /** Parse an export, with optional show/hide combinators. */
|
| - parseExport() {
|
| - token = tokenizer.next();
|
| - if (token.kind != Token.STRING) {
|
| - messages.error('expected an export url, but found ${token}', null,
|
| - file: file);
|
| - return;
|
| - }
|
| - var uri = token.value;
|
| -
|
| - // Parse the optional show/hide combinators.
|
| - token = tokenizer.next();
|
| - var hide;
|
| - var show;
|
| - while (token.kind == Token.IDENTIFIER) {
|
| - if (token.value == 'hide') {
|
| - if (hide == null) hide = [];
|
| - hide.addAll(parseIdentifierList());
|
| - } else if (token.value == 'show') {
|
| - if (show == null) show = [];
|
| - show.addAll(parseIdentifierList());
|
| - }
|
| - }
|
| -
|
| - expectToken(Token.SEMICOLON);
|
| - directives.add(new DartDirectiveInfo('export', uri, null, hide, show));
|
| - }
|
| -
|
| - /** Parse a list of identifiers of the form `id1, id2, id3` */
|
| - List<String> parseIdentifierList() {
|
| - var list = [];
|
| - do {
|
| - token = tokenizer.next();
|
| - if (!expectToken(Token.IDENTIFIER)) return list;
|
| - list.add(token.value);
|
| - token = tokenizer.next();
|
| - } while (token.kind == Token.COMMA);
|
| - return list;
|
| - }
|
| -
|
| - /** Report an error if the last token is not of the expected kind. */
|
| - bool expectToken(int kind) {
|
| - if (token.kind != kind) {
|
| - messages.error(
|
| - 'expected <${Token.KIND_NAMES[kind]}>, but got ${token}', null,
|
| - file: file);
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -}
|
| -
|
| -/** Set of tokens that we parse out of the dart code. */
|
| -class Token {
|
| - /** Kind of token, one of the constants below. */
|
| - final int kind;
|
| -
|
| - /** Value in the token (filled only for identifiers and strings). */
|
| - final String value;
|
| -
|
| - /** Start location for the token in the input string. */
|
| - final int start;
|
| -
|
| - /** End location for the token in the input string. */
|
| - final int end;
|
| -
|
| - const Token(this.kind, this.start, this.end, [this.value]);
|
| -
|
| - toString() => '<#Token ${KIND_NAMES[kind]}, $value>';
|
| -
|
| - static const int COMMENT = 0;
|
| - static const int STRING = 1;
|
| - static const int IDENTIFIER = 2;
|
| - static const int SEMICOLON = 3;
|
| - static const int DOT = 4;
|
| - static const int COMMA = 5;
|
| - static const int EOF = 6;
|
| - static const List<String> KIND_NAMES =
|
| - const ['comment', 'string', 'id', 'semicolon', 'dot', 'comma', 'eof'];
|
| -
|
| -}
|
| -
|
| -/**
|
| - * A simple tokenizer that understands comments, identifiers, strings,
|
| - * separators, and practically nothing else.
|
| - */
|
| -class _DirectiveTokenizer {
|
| - int pos = 0;
|
| - String _data;
|
| -
|
| - _DirectiveTokenizer(this._data);
|
| -
|
| - /** Return the next token. */
|
| - Token next() {
|
| - while (true) {
|
| - if (pos >= _data.length) return new Token(Token.EOF, pos, pos);
|
| - if (!isWhiteSpace(peek())) break;
|
| - nextChar();
|
| - }
|
| -
|
| - var c = peek();
|
| - switch (c) {
|
| - case _SLASH:
|
| - if (peek(1) == _SLASH) return lineComment();
|
| - if (peek(1) == _STAR) return blockComment();
|
| - break;
|
| - case _SINGLE_QUOTE:
|
| - case _DOUBLE_QUOTE:
|
| - return string();
|
| - case _SEMICOLON:
|
| - pos++;
|
| - return new Token(Token.SEMICOLON, pos - 1, pos);
|
| - case _DOT:
|
| - pos++;
|
| - return new Token(Token.DOT, pos - 1, pos);
|
| - case _COMMA:
|
| - pos++;
|
| - return new Token(Token.COMMA, pos - 1, pos);
|
| - default:
|
| - if (isIdentifierStart(c)) return identifier();
|
| - break;
|
| - }
|
| - return new Token(Token.EOF, pos, pos);
|
| - }
|
| -
|
| - int nextChar() => _data.charCodeAt(pos++);
|
| - int peek([int skip = 0]) => _data.charCodeAt(pos + skip);
|
| -
|
| - /** Advance parsing until the end of a string (no tripple quotes allowed). */
|
| - Token string() {
|
| - // TODO(sigmund): add support for multi-line strings, and raw strings.
|
| - int start = pos;
|
| - int startQuote = nextChar();
|
| - bool escape = false;
|
| - while (true) {
|
| - if (pos >= _data.length) return new Token(Token.EOF, start, pos);
|
| - int c = nextChar();
|
| - if (c == startQuote && !escape) break;
|
| - escape = !escape && c == _BACKSLASH;
|
| - }
|
| - return new Token(Token.STRING, start, pos,
|
| - _data.substring(start + 1, pos - 1));
|
| - }
|
| -
|
| - /** Advance parsing until the end of an identifier. */
|
| - Token identifier() {
|
| - int start = pos;
|
| - while (pos < _data.length && isIdentifierChar(peek())) pos++;
|
| - return new Token(Token.IDENTIFIER, start, pos, _data.substring(start, pos));
|
| - }
|
| -
|
| - /** Advance parsing until the end of a line comment. */
|
| - Token lineComment() {
|
| - int start = pos;
|
| - while (pos < _data.length && peek() != _LF) pos++;
|
| - return new Token(Token.COMMENT, start, pos);
|
| - }
|
| -
|
| - /** Advance parsing until the end of a block comment (nesting is allowed). */
|
| - Token blockComment() {
|
| - var start = pos;
|
| - var commentNesting = 0;
|
| - pos += 2;
|
| - while (pos < _data.length) {
|
| - if (peek() == _STAR && peek(1) == _SLASH) {
|
| - pos += 2;
|
| - if (commentNesting == 0) break;
|
| - commentNesting--;
|
| - } else if (peek() == _SLASH && peek(1) == _STAR) {
|
| - pos += 2;
|
| - commentNesting++;
|
| - } else {
|
| - pos++;
|
| - }
|
| - }
|
| - return new Token(Token.COMMENT, start, pos);
|
| - }
|
| -
|
| - bool isWhiteSpace(int c) => c == _LF || c == _SPACE || c == _CR || c == _TAB;
|
| - bool isIdentifierStart(int c) => c == _UNDERSCORE || isLetter(c);
|
| - bool isIdentifierChar(int c) => isIdentifierStart(c) || isNumber(c);
|
| - bool isNumber(int c) => c >= _ZERO && c <= _NINE;
|
| - bool isLetter(int c) =>
|
| - (c >= _LOWER_A && c <= _LOWER_Z) ||
|
| - (c >= _UPPER_A && c <= _UPPER_Z);
|
| -
|
| -
|
| - // The following constant character values are used for tokenizing.
|
| -
|
| - static const int _TAB = 9;
|
| - static const int _LF = 10;
|
| - static const int _CR = 13;
|
| - static const int _SPACE = 32;
|
| - static const int _DOUBLE_QUOTE = 34; // "
|
| - static const int _DOLLAR = 36; // $
|
| - static const int _SINGLE_QUOTE = 39; // '
|
| - static const int _STAR = 42; // *
|
| - static const int _COMMA = 44; // ,
|
| - static const int _DOT = 46; // .
|
| - static const int _SLASH = 47; // /
|
| - static const int _ZERO = 48; // 0
|
| - static const int _NINE = 57; // 9
|
| - static const int _SEMICOLON = 59; // ;
|
| - static const int _UPPER_A = 65; // A
|
| - static const int _UPPER_Z = 90; // Z
|
| - static const int _BACKSLASH = 92; // \
|
| - static const int _UNDERSCORE = 95; // _
|
| - static const int _LOWER_A = 97; // a
|
| - static const int _LOWER_Z = 122; // z
|
| -}
|
|
|