Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Unified Diff: lib/dartdoc/frog/parser.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/dartdoc/frog/parser.dart
diff --git a/lib/dartdoc/frog/parser.dart b/lib/dartdoc/frog/parser.dart
deleted file mode 100644
index 2ff8d33cbcca15252926e704d6bde63aa841f180..0000000000000000000000000000000000000000
--- a/lib/dartdoc/frog/parser.dart
+++ /dev/null
@@ -1,1748 +0,0 @@
-// Copyright (c) 2011, 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.
-
-// TODO(jimhug): Error recovery needs major work!
-/**
- * A simple recursive descent parser for the dart language.
- *
- * This parser is designed to be more permissive than the official
- * Dart grammar. It is expected that many grammar errors would be
- * reported by a later compiler phase. For example, a class is allowed
- * to extend an arbitrary number of base classes - this can be
- * very clearly detected and is reported in a later compiler phase.
- */
-class Parser {
- TokenSource tokenizer;
-
- final SourceFile source;
- /** Enables diet parse, which skips function bodies. */
- final bool diet;
- /**
- * Throw an IncompleteSourceException if the parser encounters a premature end
- * of file or an incomplete multiline string.
- */
- final bool throwOnIncomplete;
-
- /** Allow semicolons to be omitted at the end of lines. */
- // TODO(nweiz): make this work for more than just end-of-file
- final bool optionalSemicolons;
-
- /**
- * Allow the await keyword, when the await transformation is available (see
- * await/awaitc.dart).
- */
- bool get enableAwait() => experimentalAwaitPhase != null;
-
- /**
- * To resolve ambiguity in initializers between constructor body and lambda
- * expression.
- */
- bool _inhibitLambda = false;
-
- Token _previousToken;
- Token _peekToken;
-
- // When we encounter '(' in a method body we need to find the ')' to know it
- // we're parsing a lambda, paren-expr, or argument list. Closure formals are
- // followed by '=>' or '{'. This list is used to cache the tokens after any
- // nested parenthesis we find while peeking.
- // TODO(jmesserly): it's simpler and faster to cache this on the Token itself,
- // but that might add too much complexity for tools that need to invalidate.
- List<Token> _afterParens;
- int _afterParensIndex = 0;
-
- bool _recover = false;
-
- Parser(this.source, [this.diet = false, this.throwOnIncomplete = false,
- this.optionalSemicolons = false, int startOffset = 0]) {
- tokenizer = new Tokenizer(source, true, startOffset);
- _peekToken = tokenizer.next();
- _afterParens = <Token>[];
- }
-
- /** Generate an error if [source] has not been completely consumed. */
- void checkEndOfFile() {
- _eat(TokenKind.END_OF_FILE);
- }
-
- /** Guard to break out of parser when an unexpected end of file is found. */
- bool isPrematureEndOfFile() {
- if (throwOnIncomplete && _maybeEat(TokenKind.END_OF_FILE)) {
- throw new IncompleteSourceException(_previousToken);
- } else if (_maybeEat(TokenKind.END_OF_FILE)) {
- _error('unexpected end of file', _peekToken.span);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Recovers the parser after an error, by iterating until it finds one of
- * the provide [TokenKind] values.
- */
- bool _recoverTo(int kind1, [int kind2, int kind3]) {
- assert(_recover);
- while (!isPrematureEndOfFile()) {
- int kind = _peek();
- if (kind == kind1 || kind == kind2 || kind == kind3) {
- _recover = false; // Done recovering. Issue errors normally.
- return true;
- }
- _next();
- }
- // End of file without finding a match
- return false;
- }
-
- ///////////////////////////////////////////////////////////////////
- // Basic support methods
- ///////////////////////////////////////////////////////////////////
- int _peek() => _peekToken.kind;
-
- Token _next() {
- _previousToken = _peekToken;
- _peekToken = tokenizer.next();
- return _previousToken;
- }
-
- bool _peekKind(int kind) => _peekToken.kind == kind;
-
- /* Is the next token a legal identifier? This includes pseudo-keywords. */
- bool _peekIdentifier() => _isIdentifier(_peekToken.kind);
-
- bool _isIdentifier(kind) {
- return TokenKind.isIdentifier(kind)
- // Note: 'await' is not a pseudo-keyword. When [enableAwait] is true, it
- // is illegal to consider 'await' an identifier.
- || (!enableAwait && kind == TokenKind.AWAIT);
- }
-
- bool _maybeEat(int kind) {
- if (_peekToken.kind == kind) {
- _previousToken = _peekToken;
- _peekToken = tokenizer.next();
- return true;
- } else {
- return false;
- }
- }
-
- void _eat(int kind) {
- if (!_maybeEat(kind)) {
- _errorExpected(TokenKind.kindToString(kind));
- }
- }
-
- void _eatSemicolon() {
- if (optionalSemicolons && _peekKind(TokenKind.END_OF_FILE)) return;
- _eat(TokenKind.SEMICOLON);
- }
-
- void _errorExpected(String expected) {
- // Throw an IncompleteSourceException if that's the problem and
- // throwOnIncomplete is true
- if (throwOnIncomplete) isPrematureEndOfFile();
- var tok = _next();
- if (tok is ErrorToken && tok.message != null) {
- // give priority to tokenizer errors
- _error(tok.message, tok.span);
- } else {
- _error('expected $expected, but found $tok', tok.span);
- }
- }
-
- void _error(String message, [SourceSpan location=null]) {
- // Suppress error messages while we're trying to recover.
- if (_recover) return;
-
- if (location == null) {
- location = _peekToken.span;
- }
- world.fatal(message, location); // syntax errors are fatal for now
- _recover = true; // start error recovery
- }
-
- /** Skips from an opening '{' to the syntactically matching '}'. */
- void _skipBlock() {
- int depth = 1;
- _eat(TokenKind.LBRACE);
- while (true) {
- var tok = _next();
- if (tok.kind == TokenKind.LBRACE) {
- depth += 1;
- } else if (tok.kind == TokenKind.RBRACE) {
- depth -= 1;
- if (depth == 0) return;
- } else if (tok.kind == TokenKind.END_OF_FILE) {
- _error('unexpected end of file during diet parse', tok.span);
- return;
- }
- }
- }
-
- SourceSpan _makeSpan(int start) {
- return new SourceSpan(source, start, _previousToken.end);
- }
-
- ///////////////////////////////////////////////////////////////////
- // Top level productions
- ///////////////////////////////////////////////////////////////////
-
- /** Entry point to the parser for parsing a compilation unit (i.e. a file). */
- List<Definition> compilationUnit() {
- var ret = [];
- _maybeEat(TokenKind.HASHBANG);
-
- while (_peekKind(TokenKind.HASH)) {
- ret.add(directive());
- }
- _recover = false;
- while (!_maybeEat(TokenKind.END_OF_FILE)) {
- ret.add(topLevelDefinition());
- }
- _recover = false;
- return ret;
- }
-
- directive() {
- int start = _peekToken.start;
- _eat(TokenKind.HASH);
- var name = identifier();
- var args = arguments();
- _eatSemicolon();
- return new DirectiveDefinition(name, args, _makeSpan(start));
- }
-
- topLevelDefinition() {
- switch (_peek()) {
- case TokenKind.CLASS:
- return classDefinition(TokenKind.CLASS);
- case TokenKind.INTERFACE:
- return classDefinition(TokenKind.INTERFACE);
- case TokenKind.TYPEDEF:
- return functionTypeAlias();
- default:
- return declaration();
- }
- }
-
- /** Entry point to the parser for an eval unit (i.e. a repl command). */
- evalUnit() {
- switch (_peek()) {
- case TokenKind.CLASS:
- return classDefinition(TokenKind.CLASS);
- case TokenKind.INTERFACE:
- return classDefinition(TokenKind.INTERFACE);
- case TokenKind.TYPEDEF:
- return functionTypeAlias();
- default:
- return statement();
- }
- _recover = false;
- }
-
- ///////////////////////////////////////////////////////////////////
- // Definition productions
- ///////////////////////////////////////////////////////////////////
-
- classDefinition(int kind) {
- int start = _peekToken.start;
- _eat(kind);
- var name = identifierForType();
-
- var typeParams = null;
- if (_peekKind(TokenKind.LT)) {
- typeParams = typeParameters();
- }
-
- var _extends = null;
- if (_maybeEat(TokenKind.EXTENDS)) {
- _extends = typeList();
- }
-
- var _implements = null;
- if (_maybeEat(TokenKind.IMPLEMENTS)) {
- _implements = typeList();
- }
-
- var _native = null;
- if (_maybeEat(TokenKind.NATIVE)) {
- _native = maybeStringLiteral();
- if (_native != null) _native = new NativeType(_native);
- }
-
- bool oldFactory = _maybeEat(TokenKind.FACTORY);
- var defaultType = null;
- if (oldFactory || _maybeEat(TokenKind.DEFAULT)) {
- // TODO(jmesserly): keep old factory support for now. Remove soon.
- if (oldFactory) {
- world.warning('factory no longer supported, use "default" instead',
- _previousToken.span);
- }
-
- // Note: this can't be type() because it has type parameters not type
- // arguments.
- var baseType = nameTypeReference();
- var factTypeParams = null;
- if (_peekKind(TokenKind.LT)) {
- factTypeParams = typeParameters();
- }
- defaultType = new DefaultTypeReference(oldFactory,
- baseType, factTypeParams, _makeSpan(baseType.span.start));
- }
-
- var body = [];
- if (_maybeEat(TokenKind.LBRACE)) {
- while (!_maybeEat(TokenKind.RBRACE)) {
- body.add(declaration());
- if (_recover) {
- if (!_recoverTo(TokenKind.RBRACE, TokenKind.SEMICOLON)) break;
- _maybeEat(TokenKind.SEMICOLON);
- }
- }
- } else {
- _errorExpected('block starting with "{" or ";"');
- }
- return new TypeDefinition(kind == TokenKind.CLASS, name, typeParams,
- _extends, _implements, _native, defaultType, body, _makeSpan(start));
- }
-
- functionTypeAlias() {
- int start = _peekToken.start;
- _eat(TokenKind.TYPEDEF);
-
- var di = declaredIdentifier(false);
- var typeParams = null;
- if (_peekKind(TokenKind.LT)) {
- typeParams = typeParameters();
- }
- var formals = formalParameterList();
- _eatSemicolon();
-
- // TODO(jimhug): Validate that di.name is not a pseudo-keyword
- var func = new FunctionDefinition(null, di.type, di.name, formals,
- null, null, null, _makeSpan(start));
-
- return new FunctionTypeDefinition(func, typeParams, _makeSpan(start));
- }
-
- initializers() {
- _inhibitLambda = true;
- var ret = [];
- do {
- ret.add(expression());
- } while (_maybeEat(TokenKind.COMMA));
- _inhibitLambda = false;
- return ret;
- }
-
- functionBody(bool inExpression) {
- int start = _peekToken.start;
- if (_maybeEat(TokenKind.ARROW)) {
- var expr = expression();
- if (!inExpression) {
- _eatSemicolon();
- }
- return new ReturnStatement(expr, _makeSpan(start));
- } else if (_peekKind(TokenKind.LBRACE)) {
- if (diet) {
- _skipBlock();
- return new DietStatement(_makeSpan(start));
- } else {
- return block();
- }
- } else if (!inExpression) {
- if (_maybeEat(TokenKind.SEMICOLON)) {
- return null;
- }
- }
-
- _error('Expected function body (neither { nor => found)');
- }
-
- finishField(start, modifiers, type, name, value) {
- var names = [name];
- var values = [value];
-
- while (_maybeEat(TokenKind.COMMA)) {
- names.add(identifier());
- if (_maybeEat(TokenKind.ASSIGN)) {
- values.add(expression());
- } else {
- values.add(null);
- }
- }
-
- _eatSemicolon();
- return new VariableDefinition(modifiers, type, names, values,
- _makeSpan(start));
- }
-
- finishDefinition(int start, List<Token> modifiers, di) {
- switch(_peek()) {
- case TokenKind.LPAREN:
- var formals = formalParameterList();
- var inits = null, native = null;
- if (_maybeEat(TokenKind.COLON)) {
- inits = initializers();
- }
- if (_maybeEat(TokenKind.NATIVE)) {
- native = maybeStringLiteral();
- if (native == null) native = '';
- }
- var body = functionBody(/*inExpression:*/false);
- if (di.name == null) {
- // TODO(jimhug): Must be named constructor - verify how?
- di.name = di.type.name;
- }
- return new FunctionDefinition(modifiers, di.type, di.name, formals,
- inits, native, body, _makeSpan(start));
-
- case TokenKind.ASSIGN:
- _eat(TokenKind.ASSIGN);
- var value = expression();
- return finishField(start, modifiers, di.type, di.name, value);
-
- case TokenKind.COMMA:
- case TokenKind.SEMICOLON:
- return finishField(start, modifiers, di.type, di.name, null);
-
- default:
- // TODO(jimhug): This error message sucks.
- _errorExpected('declaration');
-
- return null;
- }
- }
-
- declaration([bool includeOperators=true]) {
- int start = _peekToken.start;
- if (_peekKind(TokenKind.FACTORY)) {
- return factoryConstructorDeclaration();
- }
-
- var modifiers = _readModifiers();
- return finishDefinition(start, modifiers,
- declaredIdentifier(includeOperators));
- }
-
- // TODO(jmesserly): do we still need this method?
- // I left it here for now to support old-style factories
- factoryConstructorDeclaration() {
- int start = _peekToken.start;
- var factoryToken = _next();
-
- var names = [identifier()];
- while (_maybeEat(TokenKind.DOT)) {
- names.add(identifier());
- }
- if (_peekKind(TokenKind.LT)) {
- var tp = typeParameters();
- world.warning('type parameters on factories are no longer supported, '
- 'place them on the class instead', _makeSpan(tp[0].span.start));
- }
-
- var name = null;
- var type = null;
- if (_maybeEat(TokenKind.DOT)) {
- name = identifier();
- } else {
- if (names.length > 1) {
- name = names.removeLast();
- } else {
- name = new Identifier('', names[0].span);
- }
- }
-
- if (names.length > 1) {
- // TODO(jimhug): This is nasty to support and currently unused.
- _error('unsupported qualified name for factory', names[0].span);
- }
- type = new NameTypeReference(false, names[0], null, names[0].span);
- var di = new DeclaredIdentifier(type, name, false, _makeSpan(start));
- return finishDefinition(start, [factoryToken], di);
- }
-
- ///////////////////////////////////////////////////////////////////
- // Statement productions
- ///////////////////////////////////////////////////////////////////
- Statement statement() {
- switch (_peek()) {
- case TokenKind.BREAK:
- return breakStatement();
- case TokenKind.CONTINUE:
- return continueStatement();
- case TokenKind.RETURN:
- return returnStatement();
- case TokenKind.THROW:
- return throwStatement();
- case TokenKind.ASSERT:
- return assertStatement();
-
- case TokenKind.WHILE:
- return whileStatement();
- case TokenKind.DO:
- return doStatement();
- case TokenKind.FOR:
- return forStatement();
-
- case TokenKind.IF:
- return ifStatement();
- case TokenKind.SWITCH:
- return switchStatement();
-
- case TokenKind.TRY:
- return tryStatement();
-
- case TokenKind.LBRACE:
- return block();
- case TokenKind.SEMICOLON:
- return emptyStatement();
-
- case TokenKind.FINAL:
- return declaration(false);
- case TokenKind.VAR:
- return declaration(false);
-
- default:
- // Covers var decl, func decl, labeled stmt and real expressions.
- return finishExpressionAsStatement(expression());
- }
- }
-
- finishExpressionAsStatement(expr) {
- // TODO(jimhug): This method looks very inefficient - bundle tests.
- int start = expr.span.start;
-
- if (_maybeEat(TokenKind.COLON)) {
- var label = _makeLabel(expr);
- return new LabeledStatement(label, statement(), _makeSpan(start));
- }
-
- if (expr is LambdaExpression) {
- if (expr.func.body is! BlockStatement) {
- _eatSemicolon();
- expr.func.span = _makeSpan(start);
- }
- return expr.func;
- } else if (expr is DeclaredIdentifier) {
- var value = null;
- if (_maybeEat(TokenKind.ASSIGN)) {
- value = expression();
- }
- return finishField(start, null, expr.type, expr.name, value);
- } else if (_isBin(expr, TokenKind.ASSIGN) &&
- (expr.x is DeclaredIdentifier)) {
- DeclaredIdentifier di = expr.x; // TODO(jimhug): inference should handle!
- return finishField(start, null, di.type, di.name, expr.y);
- } else if (_isBin(expr, TokenKind.LT) && _maybeEat(TokenKind.COMMA)) {
- var baseType = _makeType(expr.x);
- var typeArgs = [_makeType(expr.y)];
- var gt = _finishTypeArguments(baseType, 0, typeArgs);
- var name = identifier();
- var value = null;
- if (_maybeEat(TokenKind.ASSIGN)) {
- value = expression();
- }
- return finishField(expr.span.start, null, gt, name, value);
- } else {
- _eatSemicolon();
- return new ExpressionStatement(expr, _makeSpan(expr.span.start));
- }
- }
-
- Expression testCondition() {
- _eatLeftParen();
- var ret = expression();
- _eat(TokenKind.RPAREN);
- return ret;
- }
-
- /** Parses a block. Also is an entry point when parsing [DietStatement]. */
- BlockStatement block() {
- int start = _peekToken.start;
- _eat(TokenKind.LBRACE);
- var stmts = [];
- while (!_maybeEat(TokenKind.RBRACE)) {
- stmts.add(statement());
- if (_recover && !_recoverTo(TokenKind.RBRACE, TokenKind.SEMICOLON)) break;
- }
- _recover = false;
- return new BlockStatement(stmts, _makeSpan(start));
- }
-
- EmptyStatement emptyStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.SEMICOLON);
- return new EmptyStatement(_makeSpan(start));
- }
-
-
- IfStatement ifStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.IF);
- var test = testCondition();
- var trueBranch = statement();
- var falseBranch = null;
- if (_maybeEat(TokenKind.ELSE)) {
- falseBranch = statement();
- }
- return new IfStatement(test, trueBranch, falseBranch, _makeSpan(start));
- }
-
- WhileStatement whileStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.WHILE);
- var test = testCondition();
- var body = statement();
- return new WhileStatement(test, body, _makeSpan(start));
- }
-
- DoStatement doStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.DO);
- var body = statement();
- _eat(TokenKind.WHILE);
- var test = testCondition();
- _eatSemicolon();
- return new DoStatement(body, test, _makeSpan(start));
- }
-
- forStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.FOR);
- _eatLeftParen();
-
- var init = forInitializerStatement(start);
- if (init is ForInStatement) {
- return init;
- }
- var test = null;
- if (!_maybeEat(TokenKind.SEMICOLON)) {
- test = expression();
- _eatSemicolon();
- }
- var step = [];
- if (!_maybeEat(TokenKind.RPAREN)) {
- step.add(expression());
- while (_maybeEat(TokenKind.COMMA)) {
- step.add(expression());
- }
- _eat(TokenKind.RPAREN);
- }
-
- var body = statement();
-
- return new ForStatement(init, test, step, body, _makeSpan(start));
- }
-
- forInitializerStatement(int start) {
- if (_maybeEat(TokenKind.SEMICOLON)) {
- return null;
- } else {
- var init = expression();
- // Weird code here is needed to handle generic type and for in
- // TODO(jmesserly): unify with block in finishExpressionAsStatement
- if (_peekKind(TokenKind.COMMA) && _isBin(init, TokenKind.LT)) {
- _eat(TokenKind.COMMA);
- var baseType = _makeType(init.x);
- var typeArgs = [_makeType(init.y)];
- var gt = _finishTypeArguments(baseType, 0, typeArgs);
- var name = identifier();
- init = new DeclaredIdentifier(gt, name, false, _makeSpan(init.span.start));
- }
-
- if (_maybeEat(TokenKind.IN)) {
- return _finishForIn(start, _makeDeclaredIdentifier(init));
- } else {
- return finishExpressionAsStatement(init);
- }
- }
- }
-
- _finishForIn(int start, DeclaredIdentifier di) {
- var expr = expression();
- _eat(TokenKind.RPAREN);
- var body = statement();
- return new ForInStatement(di, expr, body,
- _makeSpan(start));
- }
-
- tryStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.TRY);
- var body = block();
- var catches = [];
-
- while (_peekKind(TokenKind.CATCH)) {
- catches.add(catchNode());
- }
-
- var finallyBlock = null;
- if (_maybeEat(TokenKind.FINALLY)) {
- finallyBlock = block();
- }
- return new TryStatement(body, catches, finallyBlock, _makeSpan(start));
- }
-
- catchNode() {
- int start = _peekToken.start;
- _eat(TokenKind.CATCH);
- _eatLeftParen();
- var exc = declaredIdentifier();
- var trace = null;
- if (_maybeEat(TokenKind.COMMA)) {
- trace = declaredIdentifier();
- }
- _eat(TokenKind.RPAREN);
- var body = block();
- return new CatchNode(exc, trace, body, _makeSpan(start));
- }
-
- switchStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.SWITCH);
- var test = testCondition();
- var cases = [];
- _eat(TokenKind.LBRACE);
- while (!_maybeEat(TokenKind.RBRACE)) {
- cases.add(caseNode());
- }
- return new SwitchStatement(test, cases, _makeSpan(start));
- }
-
- _peekCaseEnd() {
- var kind = _peek();
- //TODO(efortuna): also if the first is an identifier followed by a colon, we
- //have a label for the case statement.
- return kind == TokenKind.RBRACE || kind == TokenKind.CASE ||
- kind == TokenKind.DEFAULT;
- }
-
- caseNode() {
- int start = _peekToken.start;
- var label = null;
- if (_peekIdentifier()) {
- label = identifier();
- _eat(TokenKind.COLON);
- }
- var cases = [];
- while (true) {
- if (_maybeEat(TokenKind.CASE)) {
- cases.add(expression());
- _eat(TokenKind.COLON);
- } else if (_maybeEat(TokenKind.DEFAULT)) {
- cases.add(null);
- _eat(TokenKind.COLON);
- } else {
- break;
- }
- }
- if (cases.length == 0) {
- _error('case or default');
- }
- var stmts = [];
- while (!_peekCaseEnd()) {
- stmts.add(statement());
- if (_recover && !_recoverTo(
- TokenKind.RBRACE, TokenKind.CASE, TokenKind.DEFAULT)) {
- break;
- }
- }
- return new CaseNode(label, cases, stmts, _makeSpan(start));
- }
-
- returnStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.RETURN);
- var expr;
- if (_maybeEat(TokenKind.SEMICOLON)) {
- expr = null;
- } else {
- expr = expression();
- _eatSemicolon();
- }
- return new ReturnStatement(expr, _makeSpan(start));
- }
-
- throwStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.THROW);
- var expr;
- if (_maybeEat(TokenKind.SEMICOLON)) {
- expr = null;
- } else {
- expr = expression();
- _eatSemicolon();
- }
- return new ThrowStatement(expr, _makeSpan(start));
- }
-
- assertStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.ASSERT);
- _eatLeftParen();
- var expr = expression();
- _eat(TokenKind.RPAREN);
- _eatSemicolon();
- return new AssertStatement(expr, _makeSpan(start));
- }
-
- breakStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.BREAK);
- var name = null;
- if (_peekIdentifier()) {
- name = identifier();
- }
- _eatSemicolon();
- return new BreakStatement(name, _makeSpan(start));
- }
-
- continueStatement() {
- int start = _peekToken.start;
- _eat(TokenKind.CONTINUE);
- var name = null;
- if (_peekIdentifier()) {
- name = identifier();
- }
- _eatSemicolon();
- return new ContinueStatement(name, _makeSpan(start));
- }
-
-
- ///////////////////////////////////////////////////////////////////
- // Expression productions
- ///////////////////////////////////////////////////////////////////
- expression() {
- return infixExpression(0);
- }
-
- _makeType(expr) {
- if (expr is VarExpression) {
- return new NameTypeReference(false, expr.name, null, expr.span);
- } else if (expr is DotExpression) {
- var type = _makeType(expr.self);
- if (type.names == null) {
- type.names = [expr.name];
- } else {
- type.names.add(expr.name);
- }
- type.span = expr.span;
- return type;
- } else {
- _error('expected type reference');
- return null;
- }
- }
-
- infixExpression(int precedence) {
- return finishInfixExpression(unaryExpression(), precedence);
- }
-
- _finishDeclaredId(type) {
- var name = identifier();
- return finishPostfixExpression(
- new DeclaredIdentifier(type, name, false, _makeSpan(type.span.start)));
- }
-
- /**
- * Takes an initial binary expression of A < B and turns it into a
- * declared identifier included the A < B piece in the type.
- */
- _fixAsType(BinaryExpression x) {
- assert(_isBin(x, TokenKind.LT));
- // TODO(jimhug): good errors when expectations are violated
- if (_maybeEat(TokenKind.GT)) {
- // The simple case of A < B > just becomes a generic type
- var base = _makeType(x.x);
- var typeParam = _makeType(x.y);
- var type = new GenericTypeReference(base, [typeParam], 0,
- _makeSpan(x.span.start));
- return _finishDeclaredId(type);
- } else {
- // The case of A < B < kicks off a lot more parsing.
- assert(_peekKind(TokenKind.LT));
-
- var base = _makeType(x.x);
- var paramBase = _makeType(x.y);
- var firstParam = addTypeArguments(paramBase, 1);
-
- var type;
- if (firstParam.depth <= 0) {
- type = new GenericTypeReference(base, [firstParam], 0,
- _makeSpan(x.span.start));
- } else if (_maybeEat(TokenKind.COMMA)) {
- type = _finishTypeArguments(base, 0, [firstParam]);
- } else {
- _eat(TokenKind.GT);
- type = new GenericTypeReference(base, [firstParam], 0,
- _makeSpan(x.span.start));
- }
- return _finishDeclaredId(type);
- }
- }
-
- finishInfixExpression(Expression x, int precedence) {
- while (true) {
- int kind = _peek();
- var prec = TokenKind.infixPrecedence(_peek());
- if (prec >= precedence) {
- if (kind == TokenKind.LT || kind == TokenKind.GT) {
- if (_isBin(x, TokenKind.LT)) {
- // This must be a generic type according the the Dart grammar.
- // This rule is in the grammar to forbid A < B < C and
- // A < B > C as expressions both because they don't make sense
- // and to make it easier to disambiguate the generic types.
- // There are a number of other comparison operators that are
- // also unallowed to nest in this way, but in the spirit of this
- // "friendly" parser, those will be allowed until a later phase.
- return _fixAsType(x);
- }
- }
- var op = _next();
- if (op.kind == TokenKind.IS) {
- var isTrue = !_maybeEat(TokenKind.NOT);
- var typeRef = type();
- x = new IsExpression(isTrue, x, typeRef, _makeSpan(x.span.start));
- continue;
- }
- // Using prec + 1 ensures that a - b - c will group correctly.
- // Using prec for ASSIGN ops ensures that a = b = c groups correctly.
- var y = infixExpression(prec == 2 ? prec: prec+1);
- if (op.kind == TokenKind.CONDITIONAL) {
- _eat(TokenKind.COLON);
- // Using prec for so "a ? b : c ? d : e" groups correctly as
- // "a ? b : (c ? d : e)"
- var z = infixExpression(prec);
- x = new ConditionalExpression(x, y, z, _makeSpan(x.span.start));
- } else {
- x = new BinaryExpression(op, x, y, _makeSpan(x.span.start));
- }
- } else {
- break;
- }
- }
- return x;
- }
-
- _isPrefixUnaryOperator(int kind) {
- switch(kind) {
- case TokenKind.ADD:
- case TokenKind.SUB:
- case TokenKind.NOT:
- case TokenKind.BIT_NOT:
- case TokenKind.INCR:
- case TokenKind.DECR:
- return true;
- default:
- return false;
- }
- }
-
- unaryExpression() {
- int start = _peekToken.start;
- // peek for prefixOperators and incrementOperators
- if (_isPrefixUnaryOperator(_peek())) {
- var tok = _next();
- var expr = unaryExpression();
- return new UnaryExpression(tok, expr, _makeSpan(start));
- } else if (enableAwait && _maybeEat(TokenKind.AWAIT)) {
- var expr = unaryExpression();
- return new AwaitExpression(expr, _makeSpan(start));
- }
-
- return finishPostfixExpression(primary());
- }
-
- argument() {
- int start = _peekToken.start;
- var expr;
- var label = null;
- if (_maybeEat(TokenKind.ELLIPSIS)) {
- label = new Identifier('...', _makeSpan(start));
- }
- expr = expression();
- if (label == null && _maybeEat(TokenKind.COLON)) {
- label = _makeLabel(expr);
- expr = expression();
- }
- return new ArgumentNode(label, expr, _makeSpan(start));
- }
-
- arguments() {
- var args = [];
- _eatLeftParen();
- var saved = _inhibitLambda;
- _inhibitLambda = false;
- if (!_maybeEat(TokenKind.RPAREN)) {
- do {
- args.add(argument());
- } while (_maybeEat(TokenKind.COMMA));
- _eat(TokenKind.RPAREN);
- }
- _inhibitLambda = saved;
- return args;
- }
-
- finishPostfixExpression(expr) {
- switch(_peek()) {
- case TokenKind.LPAREN:
- return finishCallOrLambdaExpression(expr);
- case TokenKind.LBRACK:
- _eat(TokenKind.LBRACK);
- var index = expression();
- _eat(TokenKind.RBRACK);
- return finishPostfixExpression(new IndexExpression(expr, index,
- _makeSpan(expr.span.start)));
- case TokenKind.DOT:
- _eat(TokenKind.DOT);
- var name = identifier();
- var ret = new DotExpression(expr, name, _makeSpan(expr.span.start));
- return finishPostfixExpression(ret);
-
- case TokenKind.INCR:
- case TokenKind.DECR:
- var tok = _next();
- return new PostfixExpression(expr, tok, _makeSpan(expr.span.start));
-
- // These are pseudo-expressions supported for cover grammar
- // must be forbidden when parsing initializers.
- // TODO(jmesserly): is this still needed?
- case TokenKind.ARROW:
- case TokenKind.LBRACE:
- return expr;
-
- default:
- if (_peekIdentifier()) {
- return finishPostfixExpression(
- new DeclaredIdentifier(_makeType(expr), identifier(),
- false, _makeSpan(expr.span.start)));
- } else {
- return expr;
- }
- }
- }
-
- finishCallOrLambdaExpression(expr) {
- if (_atClosureParameters()) {
- var formals = formalParameterList();
- var body = functionBody(true);
- return _makeFunction(expr, formals, body);
- } else {
- if (expr is DeclaredIdentifier) {
- _error('illegal target for call, did you mean to declare a function?',
- expr.span);
- }
- var args = arguments();
- return finishPostfixExpression(
- new CallExpression(expr, args, _makeSpan(expr.span.start)));
- }
- }
-
- /** Checks if the given expression is a binary op of the given kind. */
- _isBin(expr, kind) {
- return expr is BinaryExpression && expr.op.kind == kind;
- }
-
- _makeLiteral(Value value) {
- return new LiteralExpression(value, value.span);
- }
-
- primary() {
- int start = _peekToken.start;
- switch (_peek()) {
- case TokenKind.THIS:
- _eat(TokenKind.THIS);
- return new ThisExpression(_makeSpan(start));
-
- case TokenKind.SUPER:
- _eat(TokenKind.SUPER);
- return new SuperExpression(_makeSpan(start));
-
- case TokenKind.CONST:
- _eat(TokenKind.CONST);
- if (_peekKind(TokenKind.LBRACK) || _peekKind(TokenKind.INDEX)) {
- return finishListLiteral(start, true, null);
- } else if (_peekKind(TokenKind.LBRACE)) {
- return finishMapLiteral(start, true, null, null);
- } else if (_peekKind(TokenKind.LT)) {
- return finishTypedLiteral(start, true);
- } else {
- return finishNewExpression(start, true);
- }
-
- case TokenKind.NEW:
- _eat(TokenKind.NEW);
- return finishNewExpression(start, false);
-
- case TokenKind.LPAREN:
- return _parenOrLambda();
-
- case TokenKind.LBRACK:
- case TokenKind.INDEX:
- return finishListLiteral(start, false, null);
- case TokenKind.LBRACE:
- return finishMapLiteral(start, false, null, null);
-
- // Literals
- case TokenKind.NULL:
- _eat(TokenKind.NULL);
- return _makeLiteral(Value.fromNull(_makeSpan(start)));
-
- // TODO(jimhug): Make Literal creation less wasteful - no dup span/text.
- case TokenKind.TRUE:
- _eat(TokenKind.TRUE);
- return _makeLiteral(Value.fromBool(true, _makeSpan(start)));
-
- case TokenKind.FALSE:
- _eat(TokenKind.FALSE);
- return _makeLiteral(Value.fromBool(false, _makeSpan(start)));
-
- case TokenKind.HEX_INTEGER:
- var t = _next();
- return _makeLiteral(Value.fromInt(t.value, t.span));
-
- case TokenKind.INTEGER:
- var t = _next();
- return _makeLiteral(Value.fromInt(Math.parseInt(t.text), t.span));
-
- case TokenKind.DOUBLE:
- var t = _next();
- return _makeLiteral(
- Value.fromDouble(Math.parseDouble(t.text), t.span));
-
- case TokenKind.STRING:
- case TokenKind.STRING_PART:
- return adjacentStrings();
-
- case TokenKind.LT:
- return finishTypedLiteral(start, false);
-
- case TokenKind.VOID:
- case TokenKind.VAR:
- case TokenKind.FINAL:
- return declaredIdentifier(false);
-
- default:
- if (!_peekIdentifier()) {
- // TODO(jimhug): Better error message.
- _errorExpected('expression');
- }
- return new VarExpression(identifier(), _makeSpan(start));
- }
- }
-
- adjacentStrings() {
- int start = _peekToken.start;
- List<Expression> strings = [];
- while (_peek() == TokenKind.STRING || _peek() == TokenKind.STRING_PART) {
- Expression part = null;
- if (_peek() == TokenKind.STRING) {
- var t = _next();
- part = _makeLiteral(Value.fromString(t.value, t.span));
- } else {
- part = stringInterpolation();
- }
- strings.add(part);
- }
- if (strings.length == 1) {
- return strings[0];
- } else {
- assert(!strings.isEmpty());
- return new StringConcatExpression(strings, _makeSpan(start));
- }
- }
-
- stringInterpolation() {
- int start = _peekToken.start;
- var pieces = new List<Expression>();
- var startQuote = null, endQuote = null;
- while(_peekKind(TokenKind.STRING_PART)) {
- var token = _next();
- pieces.add(_makeLiteral(Value.fromString(token.value, token.span)));
- if (_maybeEat(TokenKind.LBRACE)) {
- pieces.add(expression());
- _eat(TokenKind.RBRACE);
- } else if (_maybeEat(TokenKind.THIS)) {
- pieces.add(new ThisExpression(_previousToken.span));
- } else {
- var id = identifier();
- pieces.add(new VarExpression(id, id.span));
- }
- }
- var tok = _next();
- if (tok.kind != TokenKind.STRING) {
- _errorExpected('interpolated string');
- }
- pieces.add(_makeLiteral(Value.fromString(tok.value, tok.span)));
- var span = _makeSpan(start);
- return new StringInterpExpression(pieces, span);
- }
-
- String maybeStringLiteral() {
- var kind = _peek();
- if (kind == TokenKind.STRING) {
- var t = _next();
- return t.value;
- } else if (kind == TokenKind.STRING_PART) {
- _next();
- _errorExpected('string literal, but found interpolated string start');
- }
- return null;
- }
-
- _parenOrLambda() {
- int start = _peekToken.start;
- if (_atClosureParameters()) {
- var formals = formalParameterList();
- var body = functionBody(true);
- var func = new FunctionDefinition(null, null, null, formals, null, null,
- body, _makeSpan(start));
- return new LambdaExpression(func, func.span);
- } else {
- _eatLeftParen();
- var saved = _inhibitLambda;
- _inhibitLambda = false;
- var expr = expression();
- _eat(TokenKind.RPAREN);
- _inhibitLambda = saved;
- return new ParenExpression(expr, _makeSpan(start));
- }
- }
-
- bool _atClosureParameters() {
- if (_inhibitLambda) return false;
- Token after = _peekAfterCloseParen();
- return after.kind == TokenKind.ARROW || after.kind == TokenKind.LBRACE;
- }
-
- /** Eats an LPAREN, and advances our after-RPAREN lookahead. */
- _eatLeftParen() {
- _eat(TokenKind.LPAREN);
- _afterParensIndex++;
- }
-
- Token _peekAfterCloseParen() {
- if (_afterParensIndex < _afterParens.length) {
- return _afterParens[_afterParensIndex];
- }
-
- // Reset the queue
- _afterParensIndex = 0;
- _afterParens.clear();
-
- // Start copying tokens as we lookahead
- var tokens = <Token>[_next()]; // LPAREN
- _lookaheadAfterParens(tokens);
-
- // Put all the lookahead tokens back into the parser's token stream.
- var after = _peekToken;
- tokens.add(after);
- tokenizer = new DivertedTokenSource(tokens, this, tokenizer);
- _next(); // Re-synchronize parser lookahead state.
- return after;
- }
-
- /**
- * This scan for the matching RPAREN to the current LPAREN and saves this
- * result for all nested parentheses so we don't need to look-head again.
- */
- _lookaheadAfterParens(List<Token> tokens) {
- // Save a slot in the array. This will hold the token after the parens.
- int saved = _afterParens.length;
- _afterParens.add(null); // save a slot
- while (true) {
- Token token = _next();
- tokens.add(token);
- int kind = token.kind;
- if (kind == TokenKind.RPAREN || kind == TokenKind.END_OF_FILE) {
- _afterParens[saved] = _peekToken;
- return;
- } else if (kind == TokenKind.LPAREN) {
- // Scan anything inside these nested parenthesis
- _lookaheadAfterParens(tokens);
- }
- }
- }
-
- _typeAsIdentifier(type) {
- if (type.name.name == 'void') {
- _errorExpected('identifer, but found "${type.name.name}"');
- }
-
- // TODO(jimhug): lots of errors to check for
- return type.name;
- }
-
- _specialIdentifier(bool includeOperators) {
- int start = _peekToken.start;
- String name;
-
- switch (_peek()) {
- case TokenKind.ELLIPSIS:
- _eat(TokenKind.ELLIPSIS);
- _error('rest no longer supported', _previousToken.span);
- name = identifier().name;
- break;
- case TokenKind.THIS:
- _eat(TokenKind.THIS);
- _eat(TokenKind.DOT);
- name = 'this.${identifier().name}';
- break;
- case TokenKind.GET:
- if (!includeOperators) return null;
- _eat(TokenKind.GET);
- if (_peekIdentifier()) {
- name = 'get:${identifier().name}';
- } else {
- name = 'get';
- }
- break;
- case TokenKind.SET:
- if (!includeOperators) return null;
- _eat(TokenKind.SET);
- if (_peekIdentifier()) {
- name = 'set:${identifier().name}';
- } else {
- name = 'set';
- }
- break;
- case TokenKind.OPERATOR:
- if (!includeOperators) return null;
- _eat(TokenKind.OPERATOR);
- var kind = _peek();
- if (kind == TokenKind.NEGATE) {
- name = ':negate';
- _next();
- } else {
- name = TokenKind.binaryMethodName(kind);
- if (name == null) {
- // TODO(jimhug): This is a very useful error, but we have to
- // lose it because operator is a pseudo-keyword...
- //_errorExpected('legal operator name, but found: ${tok}');
- name = 'operator';
- } else {
- _next();
- }
- }
- break;
- default:
- return null;
- }
- return new Identifier(name, _makeSpan(start));
- }
-
- // always includes this and ... as legal names to simplify other code.
- declaredIdentifier([bool includeOperators=false]) {
- int start = _peekToken.start;
- var myType = null;
- var name = _specialIdentifier(includeOperators);
- bool isFinal = false;
- if (name == null) {
- myType = type();
- name = _specialIdentifier(includeOperators);
- if (name == null) {
- if (_peekIdentifier()) {
- name = identifier();
- } else if (myType is NameTypeReference && myType.names == null) {
- name = _typeAsIdentifier(myType);
- isFinal = myType.isFinal;
- myType = null;
- } else {
- // TODO(jimhug): Where do these errors get handled?
- }
- }
- }
- return new DeclaredIdentifier(myType, name, isFinal, _makeSpan(start));
- }
-
- finishNewExpression(int start, bool isConst) {
- var type = type();
- var name = null;
- if (_maybeEat(TokenKind.DOT)) {
- name = identifier();
- }
- var args = arguments();
- return new NewExpression(isConst, type, name, args, _makeSpan(start));
- }
-
- finishListLiteral(int start, bool isConst, TypeReference itemType) {
- if (_maybeEat(TokenKind.INDEX)) {
- // This is an empty array.
- return new ListExpression(isConst, itemType, [], _makeSpan(start));
- }
-
- var values = [];
- _eat(TokenKind.LBRACK);
- while (!_maybeEat(TokenKind.RBRACK)) {
- values.add(expression());
- if (_recover && !_recoverTo(TokenKind.RBRACK, TokenKind.COMMA)) break;
- if (!_maybeEat(TokenKind.COMMA)) {
- _eat(TokenKind.RBRACK);
- break;
- }
- }
- return new ListExpression(isConst, itemType, values, _makeSpan(start));
- }
-
- finishMapLiteral(int start, bool isConst,
- TypeReference keyType, TypeReference valueType) {
- var items = [];
- _eat(TokenKind.LBRACE);
- while (!_maybeEat(TokenKind.RBRACE)) {
- // This is deliberately overly permissive - checked in later pass.
- items.add(expression());
- _eat(TokenKind.COLON);
- items.add(expression());
- if (_recover && !_recoverTo(TokenKind.RBRACE, TokenKind.COMMA)) break;
- if (!_maybeEat(TokenKind.COMMA)) {
- _eat(TokenKind.RBRACE);
- break;
- }
- }
- return new MapExpression(isConst, keyType, valueType, items,
- _makeSpan(start));
- }
-
- finishTypedLiteral(int start, bool isConst) {
- var span = _makeSpan(start);
-
- final typeToBeNamedLater = new NameTypeReference(false, null, null, span);
- final genericType = addTypeArguments(typeToBeNamedLater, 0);
- final typeArgs = genericType.typeArguments;
-
- if (_peekKind(TokenKind.LBRACK) || _peekKind(TokenKind.INDEX)) {
- if (typeArgs.length != 1) {
- world.error('exactly one type argument expected for list',
- genericType.span);
- }
- return finishListLiteral(start, isConst, typeArgs[0]);
- } else if (_peekKind(TokenKind.LBRACE)) {
- var keyType, valueType;
- if (typeArgs.length == 1) {
- keyType = null;
- valueType = typeArgs[0];
- } else if (typeArgs.length == 2) {
- keyType = typeArgs[0];
- // making key explicit is just a warning.
- world.warning(
- 'a map literal takes one type argument specifying the value type',
- keyType.span);
- valueType = typeArgs[1];
- } // o.w. the type system will detect the mismatch in type arguments.
- return finishMapLiteral(start, isConst, keyType, valueType);
- } else {
- _errorExpected('array or map literal');
- }
- }
-
- ///////////////////////////////////////////////////////////////////
- // Some auxilary productions.
- ///////////////////////////////////////////////////////////////////
- _readModifiers() {
- var modifiers = null;
- while (true) {
- switch(_peek()) {
- case TokenKind.STATIC:
- case TokenKind.FINAL:
- case TokenKind.CONST:
- case TokenKind.ABSTRACT:
- case TokenKind.FACTORY:
- if (modifiers == null) modifiers = [];
- modifiers.add(_next());
- break;
- default:
- return modifiers;
- }
- }
-
- return null;
- }
-
- ParameterType typeParameter() {
- // non-recursive - so always starts from zero depth
- int start = _peekToken.start;
- var name = identifier();
- var myType = null;
- if (_maybeEat(TokenKind.EXTENDS)) {
- myType = type(1);
- }
-
- var tp = new TypeParameter(name, myType, _makeSpan(start));
- return new ParameterType(name.name, tp);
- }
-
- List<ParameterType> typeParameters() {
- // always starts from zero depth
- _eat(TokenKind.LT);
-
- bool closed = false;
- var ret = [];
- do {
- var tp = typeParameter();
- ret.add(tp);
- if (tp.typeParameter.extendsType is GenericTypeReference &&
- tp.typeParameter.extendsType.dynamic.depth == 0) {
- closed = true;
- break;
- }
- } while (_maybeEat(TokenKind.COMMA));
- if (!closed) {
- _eat(TokenKind.GT);
- }
- return ret;
- }
-
- int _eatClosingAngle(int depth) {
- if (_maybeEat(TokenKind.GT)) {
- return depth;
- } else if (depth > 0 && _maybeEat(TokenKind.SAR)) {
- return depth-1;
- } else if (depth > 1 && _maybeEat(TokenKind.SHR)) {
- return depth-2;
- } else {
- _errorExpected('>');
- return depth;
- }
- }
-
- addTypeArguments(TypeReference baseType, int depth) {
- _eat(TokenKind.LT);
- return _finishTypeArguments(baseType, depth, []);
- }
-
- _finishTypeArguments(TypeReference baseType, int depth, types) {
- var delta = -1;
- do {
- var myType = type(depth+1);
- types.add(myType);
- if (myType is GenericTypeReference && myType.depth <= depth) {
- // TODO(jimhug): Friendly error if peek(COMMA).
- delta = depth - myType.depth;
- break;
- }
- } while (_maybeEat(TokenKind.COMMA));
- if (delta >= 0) {
- depth -= delta;
- } else {
- depth = _eatClosingAngle(depth);
- }
-
- var span = _makeSpan(baseType.span.start);
- return new GenericTypeReference(baseType, types, depth, span);
- }
-
- typeList() {
- var types = [];
- do {
- types.add(type());
- } while (_maybeEat(TokenKind.COMMA));
-
- return types;
- }
-
- nameTypeReference() {
- int start = _peekToken.start;
- var name;
- var names = null;
- var typeArgs = null;
- var isFinal = false;
-
- switch (_peek()) {
- case TokenKind.VOID:
- return new SimpleTypeReference(world.voidType, _next().span);
- case TokenKind.VAR:
- return new SimpleTypeReference(world.varType, _next().span);
- case TokenKind.FINAL:
- _eat(TokenKind.FINAL);
- isFinal = true;
- name = identifier();
- break;
- default:
- name = identifier();
- break;
- }
-
- while (_maybeEat(TokenKind.DOT)) {
- if (names == null) names = [];
- names.add(identifier());
- }
-
- return new NameTypeReference(isFinal, name, names, _makeSpan(start));
- }
-
- type([int depth = 0]) {
- var typeRef = nameTypeReference();
-
- if (_peekKind(TokenKind.LT)) {
- return addTypeArguments(typeRef, depth);
- } else {
- return typeRef;
- }
- }
-
- formalParameter(bool inOptionalBlock) {
- int start = _peekToken.start;
- var isThis = false;
- var isRest = false;
- var di = declaredIdentifier(false);
- var type = di.type;
- var name = di.name;
-
- if (name == null) {
- _error('Formal parameter invalid', _makeSpan(start));
- }
-
- var value = null;
- if (_maybeEat(TokenKind.ASSIGN)) {
- if (!inOptionalBlock) {
- _error('default values only allowed inside [optional] section');
- }
- value = expression();
- } else if (_peekKind(TokenKind.LPAREN)) {
- var formals = formalParameterList();
- var func = new FunctionDefinition(null, type, name, formals,
- null, null, null, _makeSpan(start));
- type = new FunctionTypeReference(false, func, func.span);
- }
- if (inOptionalBlock && value == null) {
- value = _makeLiteral(Value.fromNull(_makeSpan(start)));
- }
-
- return new FormalNode(isThis, isRest, type, name, value, _makeSpan(start));
- }
-
- formalParameterList() {
- _eatLeftParen();
- var formals = [];
- var inOptionalBlock = false;
- if (!_maybeEat(TokenKind.RPAREN)) {
- if (_maybeEat(TokenKind.LBRACK)) {
- inOptionalBlock = true;
- }
- formals.add(formalParameter(inOptionalBlock));
- while (_maybeEat(TokenKind.COMMA)) {
- if (_maybeEat(TokenKind.LBRACK)) {
- if (inOptionalBlock) {
- _error('already inside an optional block', _previousToken.span);
- }
- inOptionalBlock = true;
- }
- formals.add(formalParameter(inOptionalBlock));
- }
- if (inOptionalBlock) {
- _eat(TokenKind.RBRACK);
- }
- _eat(TokenKind.RPAREN);
- }
- return formals;
- }
-
- // Type names are not allowed to use pseudo keywords
- identifierForType() {
- var tok = _next();
- if (!_isIdentifier(tok.kind)) {
- _error('expected identifier, but found $tok', tok.span);
- }
- if (tok.kind !== TokenKind.IDENTIFIER && tok.kind != TokenKind.NATIVE) {
- _error('$tok may not be used as a type name', tok.span);
- }
- return new Identifier(tok.text, _makeSpan(tok.start));
- }
-
- identifier() {
- var tok = _next();
- if (!_isIdentifier(tok.kind)) {
- _error('expected identifier, but found $tok', tok.span);
- }
-
- return new Identifier(tok.text, _makeSpan(tok.start));
- }
-
- ///////////////////////////////////////////////////////////////////
- // These last productions handle most ambiguities in grammar
- // They will convert expressions into other types.
- ///////////////////////////////////////////////////////////////////
-
- /**
- * Converts an [Expression], [Formals] and a [Statment] body into a
- * [FunctionDefinition].
- */
- _makeFunction(expr, formals, body) {
- var name, type;
- if (expr is VarExpression) {
- name = expr.name;
- type = null;
- } else if (expr is DeclaredIdentifier) {
- name = expr.name;
- type = expr.type;
- if (name == null) {
- _error('expected name and type', expr.span);
- }
- } else {
- _error('bad function body', expr.span);
- }
- var span = new SourceSpan(expr.span.file, expr.span.start, body.span.end);
- var func = new FunctionDefinition(null, type, name, formals, null, null,
- body, span);
- return new LambdaExpression(func, func.span);
- }
-
- /** Converts an expression to a [DeclaredIdentifier]. */
- _makeDeclaredIdentifier(e) {
- if (e is VarExpression) {
- return new DeclaredIdentifier(null, e.name, false, e.span);
- } else if (e is DeclaredIdentifier) {
- return e;
- } else {
- _error('expected declared identifier');
- return new DeclaredIdentifier(null, null, false, e.span);
- }
- }
-
- /** Converts an expression into a label. */
- _makeLabel(expr) {
- if (expr is VarExpression) {
- return expr.name;
- } else {
- _errorExpected('label');
- return null;
- }
- }
-}
-
-class IncompleteSourceException implements Exception {
- final Token token;
-
- IncompleteSourceException(this.token);
-
- String toString() {
- if (token.span == null) return 'Unexpected $token';
- return token.span.toMessageString('Unexpected $token');
- }
-}
-
-/**
- * Stores a token stream that will be used by the parser. Once the parser has
- * reached the end of this [TokenSource], it switches back to the
- * [previousTokenizer]
- */
-class DivertedTokenSource implements TokenSource {
- final List<Token> tokens;
- final Parser parser;
- final TokenSource previousTokenizer;
- DivertedTokenSource(this.tokens, this.parser, this.previousTokenizer);
-
- int _pos = 0;
- next() {
- var token = tokens[_pos];
- ++_pos;
- if (_pos == tokens.length) {
- parser.tokenizer = previousTokenizer;
- }
- return token;
- }
-}

Powered by Google App Engine
This is Rietveld 408576698