Index: frog/leg/scanner/parser.dart |
=================================================================== |
--- frog/leg/scanner/parser.dart (revision 5925) |
+++ frog/leg/scanner/parser.dart (working copy) |
@@ -1,1592 +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. |
- |
-/** |
- * An event generating parser of Dart programs. This parser expects |
- * all tokens in a linked list. |
- */ |
-class Parser { |
- final Listener listener; |
- bool mayParseFunctionExpressions = true; |
- |
- Parser(Listener this.listener); |
- |
- void parseUnit(Token token) { |
- while (token.kind !== EOF_TOKEN) { |
- token = parseTopLevelDeclaration(token); |
- } |
- } |
- |
- Token parseTopLevelDeclaration(Token token) { |
- final String value = token.stringValue; |
- if (value === 'interface') { |
- return parseInterface(token); |
- } else if ((value === 'abstract') || (value === 'class')) { |
- return parseClass(token); |
- } else if (value === 'typedef') { |
- return parseNamedFunctionAlias(token); |
- } else if (value === '#') { |
- return parseScriptTags(token); |
- } else { |
- return parseTopLevelMember(token); |
- } |
- } |
- |
- Token parseInterface(Token token) { |
- Token interfaceKeyword = token; |
- listener.beginInterface(token); |
- token = parseIdentifier(token.next); |
- token = parseTypeVariablesOpt(token); |
- int supertypeCount = 0; |
- Token extendsKeyword = null; |
- if (optional('extends', token)) { |
- extendsKeyword = token; |
- do { |
- token = parseType(token.next); |
- ++supertypeCount; |
- } while (optional(',', token)); |
- } |
- token = parseDefaultClauseOpt(token); |
- token = parseInterfaceBody(token); |
- listener.endInterface(supertypeCount, interfaceKeyword, |
- extendsKeyword, token); |
- return token.next; |
- } |
- |
- Token parseInterfaceBody(Token token) { |
- return parseClassBody(token); |
- } |
- |
- Token parseNamedFunctionAlias(Token token) { |
- Token typedefKeyword = token; |
- listener.beginFunctionTypeAlias(token); |
- token = parseReturnTypeOpt(token.next); |
- token = parseIdentifier(token); |
- token = parseTypeVariablesOpt(token); |
- token = parseFormalParameters(token); |
- listener.endFunctionTypeAlias(typedefKeyword, token); |
- return expect(';', token); |
- } |
- |
- Token parseReturnTypeOpt(Token token) { |
- if (token.stringValue === 'void') { |
- listener.handleVoidKeyword(token); |
- return token.next; |
- } else { |
- return parseTypeOpt(token); |
- } |
- } |
- |
- Token parseFormalParameters(Token token) { |
- Token begin = token; |
- listener.beginFormalParameters(begin); |
- expect('(', token); |
- int parameterCount = 0; |
- if (optional(')', token.next)) { |
- listener.endFormalParameters(parameterCount, begin, token.next); |
- return token.next.next; |
- } |
- do { |
- ++parameterCount; |
- token = token.next; |
- if (optional('[', token)) { |
- token = parseOptionalFormalParameters(token); |
- break; |
- } |
- token = parseFormalParameter(token); |
- } while (optional(',', token)); |
- listener.endFormalParameters(parameterCount, begin, token); |
- return expect(')', token); |
- } |
- |
- Token parseFormalParameter(Token token) { |
- listener.beginFormalParameter(token); |
- token = parseModifiers(token); |
- // TODO(ahe): Validate that there are formal parameters if void. |
- token = parseReturnTypeOpt(token); |
- Token thisKeyword = null; |
- if (optional('this', token)) { |
- thisKeyword = token; |
- // TODO(ahe): Validate field initializers are only used in |
- // constructors, and not for function-typed arguments. |
- token = expect('.', token.next); |
- } |
- token = parseIdentifier(token); |
- if (optional('(', token)) { |
- token = parseFormalParameters(token); |
- listener.handleFunctionTypedFormalParameter(token); |
- } |
- if (optional('=', token)) { |
- // TODO(ahe): Validate that these are only used for optional parameters. |
- Token equal = token; |
- token = parseExpression(token.next); |
- listener.handleValuedFormalParameter(equal, token); |
- } |
- listener.endFormalParameter(token, thisKeyword); |
- return token; |
- } |
- |
- Token parseOptionalFormalParameters(Token token) { |
- Token begin = token; |
- listener.beginOptionalFormalParameters(begin); |
- assert(optional('[', token)); |
- int parameterCount = 0; |
- do { |
- token = token.next; |
- token = parseFormalParameter(token); |
- ++parameterCount; |
- } while (optional(',', token)); |
- listener.endOptionalFormalParameters(parameterCount, begin, token); |
- return expect(']', token); |
- } |
- |
- Token parseTypeOpt(Token token) { |
- String value = token.stringValue; |
- if (value === 'var') return parseType(token); |
- if (value !== 'this') { |
- Token peek = peekAfterType(token); |
- if (isIdentifier(peek) || optional('this', peek)) { |
- return parseType(token); |
- } |
- } |
- listener.handleNoType(token); |
- return token; |
- } |
- |
- bool isIdentifier(Token token) { |
- final kind = token.kind; |
- if (kind === IDENTIFIER_TOKEN) return true; |
- if (kind === KEYWORD_TOKEN) return token.value.isPseudo; |
- return false; |
- } |
- |
- Token parseDefaultClauseOpt(Token token) { |
- if (isDefaultKeyword(token)) { |
- // TODO(ahe): Remove support for 'factory' in this position. |
- Token defaultKeyword = token; |
- listener.beginDefaultClause(defaultKeyword); |
- token = parseIdentifier(token.next); |
- token = parseQualifiedRestOpt(token); |
- token = parseTypeVariablesOpt(token); |
- listener.endDefaultClause(defaultKeyword); |
- } else { |
- listener.handleNoDefaultClause(token); |
- } |
- return token; |
- } |
- |
- Token parseQualifiedRestOpt(Token token) { |
- if (optional('.', token)) { |
- Token period = token; |
- token = parseIdentifier(token.next); |
- listener.handleQualified(period); |
- } |
- return token; |
- } |
- |
- bool isDefaultKeyword(Token token) { |
- String value = token.stringValue; |
- if (value === 'default') return true; |
- if (value === 'factory') { |
- listener.recoverableError("expected 'default'", token: token); |
- return true; |
- } |
- return false; |
- } |
- |
- Token skipBlock(Token token) { |
- if (!optional('{', token)) { |
- return listener.expectedBlockToSkip(token); |
- } |
- BeginGroupToken beginGroupToken = token; |
- assert(beginGroupToken.endGroup === null || |
- beginGroupToken.endGroup.kind === $CLOSE_CURLY_BRACKET); |
- return beginGroupToken.endGroup; |
- } |
- |
- Token parseClass(Token token) { |
- Token begin = token; |
- listener.beginClassDeclaration(token); |
- if (optional('abstract', token)) { |
- // TODO(ahe): Notify listener about abstract modifier. |
- token = token.next; |
- } |
- token = parseIdentifier(token.next); |
- token = parseTypeVariablesOpt(token); |
- Token extendsKeyword; |
- if (optional('extends', token)) { |
- extendsKeyword = token; |
- token = parseType(token.next); |
- } else { |
- extendsKeyword = null; |
- listener.handleNoType(token); |
- } |
- Token implementsKeyword; |
- int interfacesCount = 0; |
- if (optional('implements', token)) { |
- do { |
- token = parseType(token.next); |
- ++interfacesCount; |
- } while (optional(',', token)); |
- } |
- token = parseClassBody(token); |
- listener.endClassDeclaration(interfacesCount, begin, extendsKeyword, |
- implementsKeyword, token); |
- return token.next; |
- } |
- |
- |
- Token parseStringPart(Token token) { |
- if (token.kind === STRING_TOKEN) { |
- listener.handleStringPart(token); |
- return token.next; |
- } else { |
- return listener.expected('string', token); |
- } |
- } |
- |
- Token parseIdentifier(Token token) { |
- if (isIdentifier(token)) { |
- listener.handleIdentifier(token); |
- } else { |
- listener.expectedIdentifier(token); |
- } |
- return token.next; |
- } |
- |
- Token expect(String string, Token token) { |
- if (string !== token.stringValue) { |
- if (string === '>') { |
- if (token.stringValue === '>>') { |
- Token gt = new Token(GT_INFO, token.charOffset + 1); |
- gt.next = token.next; |
- return gt; |
- } else if (token.stringValue === '>>>') { |
- Token gtgt = new Token(GT_GT_INFO, token.charOffset + 1); |
- gtgt.next = token.next; |
- return gtgt; |
- } |
- } |
- return listener.expected(string, token); |
- } |
- return token.next; |
- } |
- |
- Token parseTypeVariable(Token token) { |
- listener.beginTypeVariable(token); |
- token = parseIdentifier(token); |
- if (optional('extends', token)) { |
- token = parseType(token.next); |
- } else { |
- listener.handleNoType(token); |
- } |
- listener.endTypeVariable(token); |
- return token; |
- } |
- |
- bool optional(String value, Token token) => value === token.stringValue; |
- |
- bool notEofOrValue(String value, Token token) { |
- return token.kind !== EOF_TOKEN && value !== token.stringValue; |
- } |
- |
- Token parseType(Token token) { |
- Token begin = token; |
- if (isIdentifier(token)) { |
- token = parseIdentifier(token); |
- token = parseQualifiedRestOpt(token); |
- } else { |
- token = listener.expectedType(token); |
- } |
- token = parseTypeArgumentsOpt(token); |
- listener.endType(begin, token); |
- return token; |
- } |
- |
- Token parseTypeArgumentsOpt(Token token) { |
- return parseStuff(token, |
- (t) => listener.beginTypeArguments(t), |
- (t) => parseType(t), |
- (c, bt, et) => listener.endTypeArguments(c, bt, et), |
- (t) => listener.handleNoTypeArguments(t)); |
- } |
- |
- Token parseTypeVariablesOpt(Token token) { |
- return parseStuff(token, |
- (t) => listener.beginTypeVariables(t), |
- (t) => parseTypeVariable(t), |
- (c, bt, et) => listener.endTypeVariables(c, bt, et), |
- (t) => listener.handleNoTypeVariables(t)); |
- } |
- |
- // TODO(ahe): Clean this up. |
- Token parseStuff(Token token, Function beginStuff, Function stuffParser, |
- Function endStuff, Function handleNoStuff) { |
- if (optional('<', token)) { |
- Token begin = token; |
- beginStuff(begin); |
- int count = 0; |
- do { |
- token = stuffParser(token.next); |
- ++count; |
- } while (optional(',', token)); |
- endStuff(count, begin, token); |
- return expect('>', token); |
- } |
- handleNoStuff(token); |
- return token; |
- } |
- |
- Token parseTopLevelMember(Token token) { |
- Token start = token; |
- listener.beginTopLevelMember(token); |
- token = parseModifiers(token); |
- Token getOrSet = findGetOrSet(token); |
- if (token === getOrSet) token = token.next; |
- Token peek = peekAfterType(token); |
- if (isIdentifier(peek)) { |
- // Skip type. |
- token = peek; |
- } |
- if (token === getOrSet) token = token.next; |
- token = parseIdentifier(token); |
- bool isField; |
- while (true) { |
- // Loop to allow the listener to rewrite the token stream for |
- // error handling. |
- final String value = token.stringValue; |
- if (value === '(') { |
- isField = false; |
- break; |
- } else if ((value === '=') || (value === ';') || (value === ',')) { |
- isField = true; |
- break; |
- } else { |
- token = listener.unexpected(token); |
- if (token.kind === EOF_TOKEN) { |
- // TODO(ahe): This is a hack. It would be better to tell the |
- // listener more explicitly that it must pop an identifier. |
- listener.endTopLevelFields(1, start, token); |
- return token; |
- } |
- } |
- } |
- if (isField) { |
- int fieldCount = 1; |
- token = parseVariableInitializerOpt(token); |
- while (optional(',', token)) { |
- token = parseIdentifier(token.next); |
- token = parseVariableInitializerOpt(token); |
- ++fieldCount; |
- } |
- expectSemicolon(token); |
- listener.endTopLevelFields(fieldCount, start, token); |
- } else { |
- token = parseFormalParameters(token); |
- token = parseFunctionBody(token, false); |
- listener.endTopLevelMethod(start, getOrSet, token); |
- } |
- return token.next; |
- } |
- |
- Token parseVariableInitializerOpt(Token token) { |
- if (optional('=', token)) { |
- Token assignment = token; |
- listener.beginInitializer(token); |
- token = parseExpression(token.next); |
- listener.endInitializer(assignment); |
- } |
- return token; |
- } |
- |
- Token parseInitializersOpt(Token token) { |
- if (optional(':', token)) { |
- return parseInitializers(token); |
- } else { |
- listener.handleNoInitializers(); |
- return token; |
- } |
- } |
- |
- Token parseInitializers(Token token) { |
- Token begin = token; |
- listener.beginInitializers(begin); |
- expect(':', token); |
- int count = 0; |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = false; |
- do { |
- token = parseExpression(token.next); |
- ++count; |
- } while (optional(',', token)); |
- mayParseFunctionExpressions = old; |
- listener.endInitializers(count, begin, token); |
- return token; |
- } |
- |
- Token parseScriptTags(Token token) { |
- Token begin = token; |
- listener.beginScriptTag(token); |
- token = parseIdentifier(token.next); |
- token = expect('(', token); |
- token = parseLiteralStringOrRecoverExpression(token); |
- bool hasPrefix = false; |
- if (optional(',', token)) { |
- hasPrefix = true; |
- token = parseIdentifier(token.next); |
- token = expect(':', token); |
- token = parseLiteralStringOrRecoverExpression(token); |
- } |
- token = expect(')', token); |
- listener.endScriptTag(hasPrefix, begin, token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseLiteralStringOrRecoverExpression(Token token) { |
- if (token.kind === STRING_TOKEN) { |
- return parseLiteralString(token); |
- } else { |
- listener.recoverableError("unexpected", token: token); |
- return parseExpression(token); |
- } |
- } |
- |
- Token expectSemicolon(Token token) { |
- return expect(';', token); |
- } |
- |
- Token parseModifier(Token token) { |
- assert(('final' === token.stringValue) || |
- ('var' === token.stringValue) || |
- ('const' === token.stringValue) || |
- ('abstract' === token.stringValue) || |
- ('static' === token.stringValue)); |
- listener.handleModifier(token); |
- return token.next; |
- } |
- |
- Token parseModifiers(Token token) { |
- int count = 0; |
- while (token.kind === KEYWORD_TOKEN) { |
- final String value = token.stringValue; |
- if (('final' !== value) && |
- ('var' !== value) && |
- ('const' !== value) && |
- ('abstract' !== value) && |
- ('static' !== value)) |
- break; |
- token = parseModifier(token); |
- count++; |
- } |
- listener.handleModifiers(count); |
- return token; |
- } |
- |
- Token peekAfterType(Token token) { |
- // TODO(ahe): Also handle var? |
- if ('void' !== token.stringValue && !isIdentifier(token)) { |
- listener.unexpected(token); |
- } |
- // We are looking at "identifier ...". |
- Token peek = token.next; |
- if (peek.kind === PERIOD_TOKEN) { |
- if (isIdentifier(peek.next)) { |
- // Look past a library prefix. |
- peek = peek.next.next; |
- } |
- } |
- // We are looking at "qualified ...". |
- if (peek.kind === LT_TOKEN) { |
- // Possibly generic type. |
- // We are looking at "qualified '<'". |
- BeginGroupToken beginGroupToken = peek; |
- Token gtToken = beginGroupToken.endGroup; |
- if (gtToken !== null) { |
- // We are looking at "qualified '<' ... '>' ...". |
- return gtToken.next; |
- } |
- } |
- return peek; |
- } |
- |
- Token parseClassBody(Token token) { |
- Token begin = token; |
- listener.beginClassBody(token); |
- if (!optional('{', token)) { |
- token = listener.expectedClassBody(token); |
- } |
- token = token.next; |
- int count = 0; |
- while (notEofOrValue('}', token)) { |
- token = parseMember(token); |
- ++count; |
- } |
- listener.endClassBody(count, begin, token); |
- return token; |
- } |
- |
- bool isGetOrSet(Token token) { |
- final String value = token.stringValue; |
- return (value === 'get') || (value === 'set'); |
- } |
- |
- Token findGetOrSet(Token token) { |
- if (isGetOrSet(token)) { |
- if (optional('<', token.next)) { |
- // For example: get<T> ... |
- final Token peek = peekAfterType(token); |
- if (isGetOrSet(peek) && isIdentifier(peek.next)) { |
- // For example: get<T> get identifier |
- return peek; |
- } |
- } else { |
- // For example: get ... |
- if (isGetOrSet(token.next) && isIdentifier(token.next.next)) { |
- // For example: get get identifier |
- return token.next; |
- } else { |
- // For example: get identifier |
- return token; |
- } |
- } |
- } else if (token.stringValue !== 'operator') { |
- final Token peek = peekAfterType(token); |
- if (isGetOrSet(peek) && isIdentifier(peek.next)) { |
- // type? get identifier |
- return peek; |
- } |
- } |
- return null; |
- } |
- |
- Token parseMember(Token token) { |
- if (optional('factory', token)) { |
- return parseFactoryMethod(token); |
- } |
- Token start = token; |
- listener.beginMember(token); |
- token = parseModifiers(token); |
- Token getOrSet = findGetOrSet(token); |
- if (token === getOrSet) token = token.next; |
- Token peek = peekAfterType(token); |
- if (isIdentifier(peek) && token.stringValue !== 'operator') { |
- // Skip type. |
- token = peek; |
- } |
- if (token === getOrSet) token = token.next; |
- if (optional('operator', token)) { |
- token = parseOperatorName(token); |
- } else { |
- token = parseIdentifier(token); |
- } |
- bool isField; |
- while (true) { |
- // Loop to allow the listener to rewrite the token stream for |
- // error handling. |
- final String value = token.stringValue; |
- if ((value === '(') || (value === '.')) { |
- isField = false; |
- break; |
- } else if ((value === '=') || (value === ';') || (value === ',')) { |
- isField = true; |
- break; |
- } else { |
- token = listener.unexpected(token); |
- if (token.kind === EOF_TOKEN) { |
- // TODO(ahe): This is a hack, see parseTopLevelMember. |
- listener.endFields(1, start, token); |
- return token; |
- } |
- } |
- } |
- if (isField) { |
- int fieldCount = 1; |
- token = parseVariableInitializerOpt(token); |
- if (getOrSet !== null) { |
- listener.recoverableError("unexpected", token: getOrSet); |
- } |
- while (optional(',', token)) { |
- // TODO(ahe): Count these. |
- token = parseIdentifier(token.next); |
- token = parseVariableInitializerOpt(token); |
- ++fieldCount; |
- } |
- expectSemicolon(token); |
- listener.endFields(fieldCount, start, token); |
- } else { |
- token = parseQualifiedRestOpt(token); |
- token = parseFormalParameters(token); |
- token = parseInitializersOpt(token); |
- token = parseFunctionBody(token, false); |
- listener.endMethod(getOrSet, start, token); |
- } |
- return token.next; |
- } |
- |
- Token parseFactoryMethod(Token token) { |
- assert(optional('factory', token)); |
- Token factoryKeyword = token; |
- listener.beginFactoryMethod(factoryKeyword); |
- token = token.next; // Skip 'factory'. |
- token = parseIdentifier(token); |
- token = parseQualifiedRestOpt(token); |
- token = parseTypeVariablesOpt(token); |
- Token period = null; |
- if (optional('.', token)) { |
- period = token; |
- token = parseIdentifier(token.next); |
- } |
- token = parseFormalParameters(token); |
- token = parseFunctionBody(token, false); |
- listener.endFactoryMethod(factoryKeyword, period, token); |
- return token.next; |
- } |
- |
- Token parseOperatorName(Token token) { |
- assert(optional('operator', token)); |
- if (isUserDefinableOperator(token.next.stringValue)) { |
- Token operator = token; |
- token = token.next; |
- listener.handleOperatorName(operator, token); |
- return token.next; |
- } else { |
- return parseIdentifier(token); |
- } |
- } |
- |
- Token parseFunction(Token token, Token getOrSet) { |
- listener.beginFunction(token); |
- token = parseModifiers(token); |
- if (getOrSet === token) token = token.next; |
- token = parseReturnTypeOpt(token); |
- if (getOrSet === token) token = token.next; |
- listener.beginFunctionName(token); |
- if (optional('operator', token)) { |
- token = parseOperatorName(token); |
- } else { |
- token = parseIdentifier(token); |
- } |
- token = parseQualifiedRestOpt(token); |
- listener.endFunctionName(token); |
- token = parseFormalParameters(token); |
- token = parseInitializersOpt(token); |
- token = parseFunctionBody(token, false); |
- listener.endFunction(getOrSet, token); |
- return token.next; |
- } |
- |
- Token parseUnamedFunction(Token token) { |
- listener.beginUnamedFunction(token); |
- token = parseFormalParameters(token); |
- bool isBlock = optional('{', token); |
- token = parseFunctionBody(token, true); |
- listener.endUnamedFunction(token); |
- return isBlock ? token.next : token; |
- } |
- |
- Token parseFunctionDeclaration(Token token) { |
- listener.beginFunctionDeclaration(token); |
- token = parseFunction(token, null); |
- listener.endFunctionDeclaration(token); |
- return token; |
- } |
- |
- Token parseFunctionExpression(Token token) { |
- listener.beginFunction(token); |
- listener.handleModifiers(0); |
- token = parseReturnTypeOpt(token); |
- listener.beginFunctionName(token); |
- token = parseIdentifier(token); |
- listener.endFunctionName(token); |
- token = parseFormalParameters(token); |
- listener.handleNoInitializers(); |
- bool isBlock = optional('{', token); |
- token = parseFunctionBody(token, true); |
- listener.endFunction(null, token); |
- return isBlock ? token.next : token; |
- } |
- |
- Token parseFunctionBody(Token token, bool isExpression) { |
- if (optional(';', token)) { |
- listener.endFunctionBody(0, null, token); |
- return token; |
- } else if (optional('=>', token)) { |
- Token begin = token; |
- token = parseExpression(token.next); |
- if (!isExpression) { |
- expectSemicolon(token); |
- listener.endReturnStatement(true, begin, token); |
- } else { |
- listener.endReturnStatement(true, begin, null); |
- } |
- return token; |
- } |
- Token begin = token; |
- int statementCount = 0; |
- listener.beginFunctionBody(begin); |
- if (!optional('{', token)) { |
- return listener.expectedFunctionBody(token); |
- } else { |
- token = token.next; |
- } |
- while (notEofOrValue('}', token)) { |
- token = parseStatement(token); |
- ++statementCount; |
- } |
- listener.endFunctionBody(statementCount, begin, token); |
- expect('}', token); |
- return token; |
- } |
- |
- Token parseStatement(Token token) { |
- final value = token.stringValue; |
- if (token.kind === IDENTIFIER_TOKEN) { |
- return parseExpressionStatementOrDeclaration(token); |
- } else if (value === '{') { |
- return parseBlock(token); |
- } else if (value === 'return') { |
- return parseReturnStatement(token); |
- } else if (value === 'var' || value === 'final') { |
- return parseVariablesDeclaration(token); |
- } else if (value === 'if') { |
- return parseIfStatement(token); |
- } else if (value === 'for') { |
- return parseForStatement(token); |
- } else if (value === 'throw') { |
- return parseThrowStatement(token); |
- } else if (value === 'void') { |
- return parseExpressionStatementOrDeclaration(token); |
- } else if (value === 'while') { |
- return parseWhileStatement(token); |
- } else if (value === 'do') { |
- return parseDoWhileStatement(token); |
- } else if (value === 'try') { |
- return parseTryStatement(token); |
- } else if (value === 'switch') { |
- return parseSwitchStatement(token); |
- } else if (value === 'break') { |
- return parseBreakStatement(token); |
- } else if (value === 'continue') { |
- return parseContinueStatement(token); |
- } else if (value === ';') { |
- return parseEmptyStatement(token); |
- } else { |
- return parseExpressionStatement(token); |
- } |
- } |
- |
- Token parseReturnStatement(Token token) { |
- Token begin = token; |
- listener.beginReturnStatement(begin); |
- assert('return' === token.stringValue); |
- token = token.next; |
- if (optional(';', token)) { |
- listener.endReturnStatement(false, begin, token); |
- } else { |
- token = parseExpression(token); |
- listener.endReturnStatement(true, begin, token); |
- } |
- return expectSemicolon(token); |
- } |
- |
- Token peekIdentifierAfterType(Token token) { |
- Token peek = peekAfterType(token); |
- if (peek !== null && isIdentifier(peek)) { |
- // We are looking at "type identifier". |
- return peek; |
- } else { |
- return null; |
- } |
- } |
- |
- Token parseExpressionStatementOrDeclaration(Token token) { |
- assert(isIdentifier(token) || token.stringValue === 'void'); |
- Token identifier = peekIdentifierAfterType(token); |
- if (identifier !== null) { |
- assert(isIdentifier(identifier)); |
- Token afterId = identifier.next; |
- int afterIdKind = afterId.kind; |
- if (afterIdKind === EQ_TOKEN || |
- afterIdKind === SEMICOLON_TOKEN || |
- afterIdKind === COMMA_TOKEN) { |
- // We are looking at "type identifier" followed by '=', ';', ','. |
- return parseVariablesDeclaration(token); |
- } else if (afterIdKind === OPEN_PAREN_TOKEN) { |
- // We are looking at "type identifier '('". |
- BeginGroupToken beginParen = afterId; |
- Token endParen = beginParen.endGroup; |
- Token afterParens = endParen.next; |
- if (optional('{', afterParens) || optional('=>', afterParens)) { |
- // We are looking at "type identifier '(' ... ')'" followed |
- // by '=>' or '{'. |
- return parseFunctionDeclaration(token); |
- } |
- } |
- // Fall-through to expression statement. |
- } else { |
- if (optional(':', token.next)) { |
- return parseLabeledStatement(token); |
- } else if (optional('(', token.next)) { |
- BeginGroupToken begin = token.next; |
- String afterParens = begin.endGroup.next.stringValue; |
- if (afterParens === '{' || afterParens === '=>') { |
- return parseFunctionDeclaration(token); |
- } |
- } |
- } |
- return parseExpressionStatement(token); |
- } |
- |
- Token parseLabeledStatement(Token token) { |
- listener.beginLabeledStatement(token); |
- token = parseIdentifier(token); |
- Token colon = token; |
- token = expect(':', token); |
- token = parseStatement(token); |
- listener.endLabeledStatement(colon); |
- return token; |
- } |
- |
- Token parseExpressionStatement(Token token) { |
- listener.beginExpressionStatement(token); |
- token = parseExpression(token); |
- listener.endExpressionStatement(token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseExpression(Token token) { |
- return parsePrecedenceExpression(token, 2); |
- } |
- |
- Token parseConditionalExpressionRest(Token token) { |
- assert(optional('?', token)); |
- Token question = token; |
- token = parseExpression(token.next); |
- Token colon = token; |
- token = expect(':', token); |
- token = parseExpression(token); |
- listener.handleConditionalExpression(question, colon); |
- return token; |
- } |
- |
- Token parsePrecedenceExpression(Token token, int precedence) { |
- assert(precedence >= 2); |
- assert(precedence <= POSTFIX_PRECEDENCE); |
- token = parseUnaryExpression(token); |
- PrecedenceInfo info = token.info; |
- int tokenLevel = info.precedence; |
- for (int level = tokenLevel; level >= precedence; --level) { |
- while (tokenLevel === level) { |
- Token operator = token; |
- if (tokenLevel === ASSIGNMENT_PRECEDENCE) { |
- // Right associative, so we recurse at the same precedence |
- // level. |
- token = parsePrecedenceExpression(token.next, level); |
- listener.handleAssignmentExpression(operator); |
- } else if (tokenLevel === POSTFIX_PRECEDENCE) { |
- if (info === PERIOD_INFO) { |
- // Left associative, so we recurse at the next higher |
- // precedence level. However, POSTFIX_PRECEDENCE is the |
- // highest level, so we just call parseUnaryExpression |
- // directly. |
- token = parseUnaryExpression(token.next); |
- listener.handleBinaryExpression(operator); |
- } else if ((info === OPEN_PAREN_INFO) || |
- (info === OPEN_SQUARE_BRACKET_INFO)) { |
- token = parseArgumentOrIndexStar(token); |
- } else if ((info === PLUS_PLUS_INFO) || |
- (info === MINUS_MINUS_INFO)) { |
- listener.handleUnaryPostfixAssignmentExpression(token); |
- token = token.next; |
- } else { |
- token = listener.unexpected(token); |
- } |
- } else if (info === IS_INFO) { |
- token = parseIsOperatorRest(token); |
- } else if (info === QUESTION_INFO) { |
- token = parseConditionalExpressionRest(token); |
- } else { |
- // Left associative, so we recurse at the next higher |
- // precedence level. |
- token = parsePrecedenceExpression(token.next, level + 1); |
- listener.handleBinaryExpression(operator); |
- } |
- info = token.info; |
- tokenLevel = info.precedence; |
- } |
- } |
- return token; |
- } |
- |
- Token parseUnaryExpression(Token token) { |
- String value = token.stringValue; |
- // Prefix: |
- if (value === '+') { |
- // Dart only allows "prefix plus" as an initial part of a |
- // decimal literal. We scan it as a separate token and let |
- // the parser listener combine it with the digits. |
- Token next = token.next; |
- if (next.charOffset === token.charOffset + 1) { |
- if (next.kind === INT_TOKEN) { |
- listener.handleLiteralInt(token); |
- return next.next; |
- } |
- if (next.kind === DOUBLE_TOKEN) { |
- listener.handleLiteralDouble(token); |
- return next.next; |
- } |
- } |
- listener.recoverableError("Unexpected token '+'", token: token); |
- return parsePrecedenceExpression(next, POSTFIX_PRECEDENCE); |
- } else if ((value === '!') || |
- (value === '-') || |
- (value === '~')) { |
- Token operator = token; |
- // Right associative, so we recurse at the same precedence |
- // level. |
- token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE); |
- listener.handleUnaryPrefixExpression(operator); |
- } else if ((value === '++') || value === '--') { |
- // TODO(ahe): Validate this is used correctly. |
- Token operator = token; |
- // Right associative, so we recurse at the same precedence |
- // level. |
- token = parsePrecedenceExpression(token.next, POSTFIX_PRECEDENCE); |
- listener.handleUnaryPrefixAssignmentExpression(operator); |
- } else { |
- token = parsePrimary(token); |
- } |
- return token; |
- } |
- |
- Token parseArgumentOrIndexStar(Token token) { |
- while (true) { |
- if (optional('[', token)) { |
- Token openSquareBracket = token; |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = true; |
- token = parseExpression(token.next); |
- mayParseFunctionExpressions = old; |
- listener.handleIndexedExpression(openSquareBracket, token); |
- token = expect(']', token); |
- } else if (optional('(', token)) { |
- token = parseArguments(token); |
- listener.endSend(token); |
- } else { |
- break; |
- } |
- } |
- return token; |
- } |
- |
- Token parsePrimary(Token token) { |
- final kind = token.kind; |
- if (kind === IDENTIFIER_TOKEN) { |
- return parseSendOrFunctionLiteral(token); |
- } else if (kind === INT_TOKEN || kind === HEXADECIMAL_TOKEN) { |
- return parseLiteralInt(token); |
- } else if (kind === DOUBLE_TOKEN) { |
- return parseLiteralDouble(token); |
- } else if (kind === STRING_TOKEN) { |
- return parseLiteralString(token); |
- } else if (kind === KEYWORD_TOKEN) { |
- final value = token.stringValue; |
- if ((value === 'true') || (value === 'false')) { |
- return parseLiteralBool(token); |
- } else if (value === 'null') { |
- return parseLiteralNull(token); |
- } else if (value === 'this') { |
- return parseThisExpression(token); |
- } else if (value === 'super') { |
- return parseSuperExpression(token); |
- } else if (value === 'new') { |
- return parseNewExpression(token); |
- } else if (value === 'const') { |
- return parseConstExpression(token); |
- } else if (value === 'void') { |
- return parseFunctionExpression(token); |
- } else if (isIdentifier(token)) { |
- return parseSendOrFunctionLiteral(token); |
- } else { |
- return listener.expectedExpression(token); |
- } |
- } else if (kind === OPEN_PAREN_TOKEN) { |
- return parseParenthesizedExpressionOrFunctionLiteral(token); |
- } else if ((kind === LT_TOKEN) || |
- (kind === OPEN_SQUARE_BRACKET_TOKEN) || |
- (kind === OPEN_CURLY_BRACKET_TOKEN) || |
- token.stringValue === '[]') { |
- return parseLiteralListOrMap(token); |
- } else { |
- return listener.expectedExpression(token); |
- } |
- } |
- |
- Token parseParenthesizedExpressionOrFunctionLiteral(Token token) { |
- BeginGroupToken beginGroup = token; |
- int kind = beginGroup.endGroup.next.kind; |
- if (mayParseFunctionExpressions && |
- (kind === FUNCTION_TOKEN || kind === OPEN_CURLY_BRACKET_TOKEN)) { |
- return parseUnamedFunction(token); |
- } else { |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = true; |
- token = parseParenthesizedExpression(token); |
- mayParseFunctionExpressions = old; |
- return token; |
- } |
- } |
- |
- Token parseParenthesizedExpression(Token token) { |
- BeginGroupToken begin = token; |
- token = expect('(', token); |
- token = parseExpression(token); |
- if (begin.endGroup !== token) { |
- listener.unexpected(token); |
- token = begin.endGroup; |
- } |
- listener.handleParenthesizedExpression(begin); |
- return expect(')', token); |
- } |
- |
- Token parseThisExpression(Token token) { |
- listener.handleThisExpression(token); |
- token = token.next; |
- if (optional('(', token)) { |
- // Constructor forwarding. |
- token = parseArguments(token); |
- listener.endSend(token); |
- } |
- return token; |
- } |
- |
- Token parseSuperExpression(Token token) { |
- listener.handleSuperExpression(token); |
- token = token.next; |
- if (optional('(', token)) { |
- // Super constructor. |
- token = parseArguments(token); |
- listener.endSend(token); |
- } |
- return token; |
- } |
- |
- Token parseLiteralListOrMap(Token token) { |
- Token constKeyword = null; |
- if (optional('const', token)) { |
- constKeyword = token; |
- token = token.next; |
- } |
- token = parseTypeArgumentsOpt(token); |
- Token beginToken = token; |
- int count = 0; |
- if (optional('{', token)) { |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = true; |
- do { |
- if (optional('}', token.next)) { |
- token = token.next; |
- break; |
- } |
- token = parseMapLiteralEntry(token.next); |
- ++count; |
- } while (optional(',', token)); |
- mayParseFunctionExpressions = old; |
- listener.handleLiteralMap(count, beginToken, constKeyword, token); |
- return expect('}', token); |
- } else if (optional('[', token)) { |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = true; |
- do { |
- if (optional(']', token.next)) { |
- token = token.next; |
- break; |
- } |
- token = parseExpression(token.next); |
- ++count; |
- } while (optional(',', token)); |
- mayParseFunctionExpressions = old; |
- listener.handleLiteralList(count, beginToken, constKeyword, token); |
- return expect(']', token); |
- } else if (optional('[]', token)) { |
- listener.handleLiteralList(0, token, constKeyword, token); |
- return token.next; |
- } else { |
- listener.unexpected(token); |
- } |
- } |
- |
- Token parseMapLiteralEntry(Token token) { |
- listener.beginLiteralMapEntry(token); |
- // Assume the listener rejects non-string keys. |
- token = parseExpression(token); |
- Token colon = token; |
- token = expect(':', token); |
- token = parseExpression(token); |
- listener.endLiteralMapEntry(colon, token); |
- return token; |
- } |
- |
- Token parseSendOrFunctionLiteral(Token token) { |
- if (!mayParseFunctionExpressions) return parseSend(token); |
- Token peek = peekAfterType(token); |
- if (peek.kind === IDENTIFIER_TOKEN && isFunctionDeclaration(peek.next)) { |
- return parseFunctionExpression(token); |
- } else if (isFunctionDeclaration(token.next)) { |
- return parseFunctionExpression(token); |
- } else { |
- return parseSend(token); |
- } |
- } |
- |
- bool isFunctionDeclaration(Token token) { |
- if (optional('(', token)) { |
- BeginGroupToken begin = token; |
- String afterParens = begin.endGroup.next.stringValue; |
- if (afterParens === '{' || afterParens === '=>') { |
- return true; |
- } |
- } |
- return false; |
- } |
- |
- Token parseNewExpression(Token token) { |
- Token newKeyword = token; |
- token = expect('new', token); |
- token = parseType(token); |
- bool named = false; |
- if (optional('.', token)) { |
- named = true; |
- token = parseIdentifier(token.next); |
- } |
- if (optional('(', token)) { |
- token = parseArguments(token); |
- } else { |
- listener.handleNoArguments(token); |
- token = listener.unexpected(token); |
- } |
- listener.handleNewExpression(newKeyword, named); |
- return token; |
- } |
- |
- Token parseConstExpression(Token token) { |
- Token constKeyword = token; |
- token = expect('const', token); |
- final String value = token.stringValue; |
- if ((value === '<') || |
- (value === '[') || |
- (value === '[]') || |
- (value === '{')) { |
- return parseLiteralListOrMap(constKeyword); |
- } |
- token = parseType(token); |
- bool named = false; |
- if (optional('.', token)) { |
- named = true; |
- token = parseIdentifier(token.next); |
- } |
- expect('(', token); |
- token = parseArguments(token); |
- listener.handleConstExpression(constKeyword, named); |
- return token; |
- } |
- |
- Token parseLiteralInt(Token token) { |
- listener.handleLiteralInt(token); |
- return token.next; |
- } |
- |
- Token parseLiteralDouble(Token token) { |
- listener.handleLiteralDouble(token); |
- return token.next; |
- } |
- |
- Token parseLiteralString(Token token) { |
- token = parseSingleLiteralString(token); |
- int count = 1; |
- while (token.kind === STRING_TOKEN) { |
- token = parseSingleLiteralString(token); |
- count++; |
- } |
- if (count > 1) { |
- listener.handleStringJuxtaposition(count); |
- } |
- return token; |
- } |
- |
- Token parseSingleLiteralString(Token token) { |
- listener.beginLiteralString(token); |
- token = token.next; |
- int interpolationCount = 0; |
- while (optional('\${', token)) { |
- token = token.next; |
- token = parseExpression(token); |
- token = expect('}', token); |
- token = parseStringPart(token); |
- ++interpolationCount; |
- } |
- listener.endLiteralString(interpolationCount); |
- return token; |
- } |
- |
- Token parseLiteralBool(Token token) { |
- listener.handleLiteralBool(token); |
- return token.next; |
- } |
- |
- Token parseLiteralNull(Token token) { |
- listener.handleLiteralNull(token); |
- return token.next; |
- } |
- |
- Token parseSend(Token token) { |
- listener.beginSend(token); |
- token = parseIdentifier(token); |
- token = parseArgumentsOpt(token); |
- listener.endSend(token); |
- return token; |
- } |
- |
- Token parseArgumentsOpt(Token token) { |
- if (!optional('(', token)) { |
- listener.handleNoArguments(token); |
- return token; |
- } else { |
- return parseArguments(token); |
- } |
- } |
- |
- Token parseArguments(Token token) { |
- Token begin = token; |
- listener.beginArguments(begin); |
- assert('(' === token.stringValue); |
- int argumentCount = 0; |
- if (optional(')', token.next)) { |
- listener.endArguments(argumentCount, begin, token.next); |
- return token.next.next; |
- } |
- bool old = mayParseFunctionExpressions; |
- mayParseFunctionExpressions = true; |
- do { |
- Token colon = null; |
- if (optional(':', token.next.next)) { |
- token = parseIdentifier(token.next); |
- colon = token; |
- } |
- token = parseExpression(token.next); |
- if (colon !== null) listener.handleNamedArgument(colon); |
- ++argumentCount; |
- } while (optional(',', token)); |
- mayParseFunctionExpressions = old; |
- listener.endArguments(argumentCount, begin, token); |
- return expect(')', token); |
- } |
- |
- Token parseIsOperatorRest(Token token) { |
- assert(optional('is', token)); |
- Token operator = token; |
- Token not = null; |
- if (optional('!', token.next)) { |
- token = token.next; |
- not = token; |
- } |
- token = parseType(token.next); |
- listener.handleIsOperator(operator, not, token); |
- if (optional('is', token)) { |
- // The is-operator cannot be chained, but it can take part of |
- // expressions like: foo is Foo || foo is Bar. |
- listener.unexpected(token); |
- } |
- return token; |
- } |
- |
- Token parseVariablesDeclaration(Token token) { |
- token = parseVariablesDeclarationNoSemicolon(token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseVariablesDeclarationNoSemicolon(Token token) { |
- int count = 1; |
- listener.beginVariablesDeclaration(token); |
- token = parseModifiers(token); |
- token = parseTypeOpt(token); |
- token = parseOptionallyInitializedIdentifier(token); |
- while (optional(',', token)) { |
- token = parseOptionallyInitializedIdentifier(token.next); |
- ++count; |
- } |
- listener.endVariablesDeclaration(count, token); |
- return token; |
- } |
- |
- Token parseOptionallyInitializedIdentifier(Token token) { |
- listener.beginInitializedIdentifier(token); |
- token = parseIdentifier(token); |
- token = parseVariableInitializerOpt(token); |
- listener.endInitializedIdentifier(); |
- return token; |
- } |
- |
- Token parseIfStatement(Token token) { |
- Token ifToken = token; |
- listener.beginIfStatement(ifToken); |
- token = expect('if', token); |
- token = parseParenthesizedExpression(token); |
- token = parseStatement(token); |
- Token elseToken = null; |
- if (optional('else', token)) { |
- elseToken = token; |
- token = parseStatement(token.next); |
- } |
- listener.endIfStatement(ifToken, elseToken); |
- return token; |
- } |
- |
- Token parseForStatement(Token token) { |
- Token forToken = token; |
- listener.beginForStatement(forToken); |
- token = expect('for', token); |
- token = expect('(', token); |
- token = parseVariablesDeclarationOrExpressionOpt(token); |
- if (optional('in', token)) { |
- return parseForInRest(forToken, token); |
- } else { |
- return parseForRest(forToken, token); |
- } |
- } |
- |
- Token parseVariablesDeclarationOrExpressionOpt(Token token) { |
- final String value = token.stringValue; |
- if (value === ';') { |
- listener.handleNoExpression(token); |
- return token; |
- } else if ((value === 'var') || (value === 'final')) { |
- return parseVariablesDeclarationNoSemicolon(token); |
- } |
- Token identifier = peekIdentifierAfterType(token); |
- if (identifier !== null) { |
- assert(isIdentifier(identifier)); |
- Token afterId = identifier.next; |
- int afterIdKind = afterId.kind; |
- if (afterIdKind === EQ_TOKEN || afterIdKind === SEMICOLON_TOKEN || |
- afterIdKind === COMMA_TOKEN || optional('in', afterId)) { |
- return parseVariablesDeclarationNoSemicolon(token); |
- } |
- } |
- return parseExpression(token); |
- } |
- |
- Token parseForRest(Token forToken, Token token) { |
- token = expectSemicolon(token); |
- if (optional(';', token)) { |
- token = parseEmptyStatement(token); |
- } else { |
- token = parseExpressionStatement(token); |
- } |
- int expressionCount = 0; |
- while (true) { |
- if (optional(')', token)) break; |
- token = parseExpression(token); |
- ++expressionCount; |
- if (optional(',', token)) { |
- token = token.next; |
- } else { |
- break; |
- } |
- } |
- token = expect(')', token); |
- token = parseStatement(token); |
- listener.endForStatement(expressionCount, forToken, token); |
- return token; |
- } |
- |
- Token parseForInRest(Token forToken, Token token) { |
- assert(optional('in', token)); |
- Token inKeyword = token; |
- token = parseExpression(token.next); |
- token = expect(')', token); |
- token = parseStatement(token); |
- listener.endForInStatement(forToken, inKeyword, token); |
- return token; |
- } |
- |
- Token parseWhileStatement(Token token) { |
- Token whileToken = token; |
- listener.beginWhileStatement(whileToken); |
- token = expect('while', token); |
- token = parseParenthesizedExpression(token); |
- token = parseStatement(token); |
- listener.endWhileStatement(whileToken, token); |
- return token; |
- } |
- |
- Token parseDoWhileStatement(Token token) { |
- Token doToken = token; |
- listener.beginDoWhileStatement(doToken); |
- token = expect('do', token); |
- token = parseStatement(token); |
- Token whileToken = token; |
- token = expect('while', token); |
- token = parseParenthesizedExpression(token); |
- listener.endDoWhileStatement(doToken, whileToken, token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseBlock(Token token) { |
- Token begin = token; |
- listener.beginBlock(begin); |
- int statementCount = 0; |
- token = expect('{', token); |
- while (notEofOrValue('}', token)) { |
- token = parseStatement(token); |
- ++statementCount; |
- } |
- listener.endBlock(statementCount, begin, token); |
- return expect('}', token); |
- } |
- |
- Token parseThrowStatement(Token token) { |
- Token throwToken = token; |
- listener.beginThrowStatement(throwToken); |
- token = expect('throw', token); |
- if (optional(';', token)) { |
- listener.endRethrowStatement(throwToken, token); |
- return token.next; |
- } else { |
- token = parseExpression(token); |
- listener.endThrowStatement(throwToken, token); |
- return expectSemicolon(token); |
- } |
- } |
- |
- Token parseTryStatement(Token token) { |
- assert(optional('try', token)); |
- Token tryKeyword = token; |
- listener.beginTryStatement(tryKeyword); |
- token = parseBlock(token.next); |
- int catchCount = 0; |
- while (optional('catch', token)) { |
- Token catchKeyword = token; |
- // TODO(ahe): Validate the "parameters". |
- token = parseFormalParameters(token.next); |
- token = parseBlock(token); |
- ++catchCount; |
- listener.handleCatchBlock(catchKeyword); |
- } |
- Token finallyKeyword = null; |
- if (optional('finally', token)) { |
- finallyKeyword = token; |
- token = parseBlock(token.next); |
- listener.handleFinallyBlock(finallyKeyword); |
- } |
- listener.endTryStatement(catchCount, tryKeyword, finallyKeyword); |
- return token; |
- } |
- |
- Token parseSwitchStatement(Token token) { |
- assert(optional('switch', token)); |
- Token switchKeyword = token; |
- listener.beginSwitchStatement(switchKeyword); |
- token = parseParenthesizedExpression(token.next); |
- token = parseSwitchBlock(token); |
- listener.endSwitchStatement(switchKeyword, token); |
- return token.next; |
- } |
- |
- Token parseSwitchBlock(Token token) { |
- Token begin = token; |
- listener.beginSwitchBlock(begin); |
- token = expect('{', token); |
- int caseCount = 0; |
- while (token.kind !== EOF_TOKEN) { |
- if (optional('}', token)) { |
- break; |
- } |
- token = parseSwitchCase(token); |
- ++caseCount; |
- } |
- listener.endSwitchBlock(caseCount, begin, token); |
- expect('}', token); |
- return token; |
- } |
- |
- Token parseSwitchCase(Token token) { |
- Token begin = token; |
- Token defaultKeyword = null; |
- Token label = null; |
- // First an optional label. |
- if (isIdentifier(token)) { |
- label = token; |
- token = parseIdentifier(token); |
- token = expect(':', token); |
- } |
- // Then one or more case expressions, the last of which may be |
- // 'default' instead. |
- int expressionCount = 0; |
- String value = token.stringValue; |
- do { |
- if (value === 'default') { |
- defaultKeyword = token; |
- token = expect(':', token.next); |
- break; |
- } |
- token = expect('case', token); |
- token = parseExpression(token); |
- token = expect(':', token); |
- expressionCount++; |
- value = token.stringValue; |
- } while (value === 'case' || value === 'default'); |
- // Finally zero or more statements. |
- int statementCount = 0; |
- while (token.kind !== EOF_TOKEN) { |
- String value; |
- if (isIdentifier(token) && optional(':', token.next)) { |
- // Skip label. |
- value = token.next.next.stringValue; |
- } else { |
- value = token.stringValue; |
- } |
- if (value === 'case' || value === 'default' || value === '}') { |
- break; |
- } else { |
- token = parseStatement(token); |
- ++statementCount; |
- } |
- } |
- listener.handleSwitchCase(label, expressionCount, defaultKeyword, |
- statementCount, begin, token); |
- return token; |
- } |
- |
- Token parseBreakStatement(Token token) { |
- assert(optional('break', token)); |
- Token breakKeyword = token; |
- token = token.next; |
- bool hasTarget = false; |
- if (isIdentifier(token)) { |
- token = parseIdentifier(token); |
- hasTarget = true; |
- } |
- listener.handleBreakStatement(hasTarget, breakKeyword, token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseContinueStatement(Token token) { |
- assert(optional('continue', token)); |
- Token continueKeyword = token; |
- token = token.next; |
- bool hasTarget = false; |
- if (isIdentifier(token)) { |
- token = parseIdentifier(token); |
- hasTarget = true; |
- } |
- listener.handleContinueStatement(hasTarget, continueKeyword, token); |
- return expectSemicolon(token); |
- } |
- |
- Token parseEmptyStatement(Token token) { |
- listener.handleEmptyStatement(token); |
- return expectSemicolon(token); |
- } |
-} |