// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // CHANGES: // // v0.4 Added support for 'unified collections' (spreads and control flow // in collection literals). // // v0.3 Updated to use ANTLR v4 rather than antlr3. // // v0.2 Changed top level variable declarations to avoid redundant and // misleading occurrence of (FINAL|CONST). // // v0.1 First version available in the SDK github repository. Covers the // Dart language as specified in the language specification based on the // many grammar rule snippets. That grammar was then adjusted to remove // known issues (e.g., misplaced metadata) and to resolve ambiguities. grammar Dart; @parser::header{ import java.util.Stack; } @lexer::header{ import java.util.Stack; } @parser::members { static String filePath = null; static boolean errorHasOccurred = false; /// Must be invoked before the first error is reported for a library. /// Will print the name of the library and indicate that it has errors. static void prepareForErrors() { errorHasOccurred = true; System.err.println("Syntax error in " + filePath + ":"); } /// Parse library, return true if success, false if errors occurred. public boolean parseLibrary(String filePath) throws RecognitionException { this.filePath = filePath; errorHasOccurred = false; libraryDefinition(); return !errorHasOccurred; } // Enable the parser to treat AWAIT/YIELD as keywords in the body of an // `async`, `async*`, or `sync*` function. Access via methods below. private Stack asyncEtcAreKeywords = new Stack(); { asyncEtcAreKeywords.push(false); } // Use this to indicate that we are now entering an `async`, `async*`, // or `sync*` function. void startAsyncFunction() { asyncEtcAreKeywords.push(true); } // Use this to indicate that we are now entering a function which is // neither `async`, `async*`, nor `sync*`. void startNonAsyncFunction() { asyncEtcAreKeywords.push(false); } // Use this to indicate that we are now leaving any funciton. void endFunction() { asyncEtcAreKeywords.pop(); } // Whether we can recognize AWAIT/YIELD as an identifier/typeIdentifier. boolean asyncEtcPredicate(int tokenId) { if (tokenId == AWAIT || tokenId == YIELD) { return !asyncEtcAreKeywords.peek(); } return false; } } @lexer::members{ public static final int BRACE_NORMAL = 1; public static final int BRACE_SINGLE = 2; public static final int BRACE_DOUBLE = 3; public static final int BRACE_THREE_SINGLE = 4; public static final int BRACE_THREE_DOUBLE = 5; // Enable the parser to handle string interpolations via brace matching. // The top of the `braceLevels` stack describes the most recent unmatched // '{'. This is needed in order to enable/disable certain lexer rules. // // NORMAL: Most recent unmatched '{' was not string literal related. // SINGLE: Most recent unmatched '{' was `'...${`. // DOUBLE: Most recent unmatched '{' was `"...${`. // THREE_SINGLE: Most recent unmatched '{' was `'''...${`. // THREE_DOUBLE: Most recent unmatched '{' was `"""...${`. // // Access via functions below. private Stack braceLevels = new Stack(); // Whether we are currently in a string literal context, and which one. boolean currentBraceLevel(int braceLevel) { if (braceLevels.empty()) return false; return braceLevels.peek() == braceLevel; } // Use this to indicate that we are now entering a specific '{...}'. // Call it after accepting the '{'. void enterBrace() { braceLevels.push(BRACE_NORMAL); } void enterBraceSingleQuote() { braceLevels.push(BRACE_SINGLE); } void enterBraceDoubleQuote() { braceLevels.push(BRACE_DOUBLE); } void enterBraceThreeSingleQuotes() { braceLevels.push(BRACE_THREE_SINGLE); } void enterBraceThreeDoubleQuotes() { braceLevels.push(BRACE_THREE_DOUBLE); } // Use this to indicate that we are now exiting a specific '{...}', // no matter which kind. Call it before accepting the '}'. void exitBrace() { // We might raise a parse error here if the stack is empty, but the // parsing rules should ensure that we get a parse error anyway, and // it is not a big problem for the spec parser even if it misinterprets // the brace structure of some programs with syntax errors. if (!braceLevels.empty()) braceLevels.pop(); } } // ---------------------------------------- Grammar rules. libraryDefinition : FEFF? SCRIPT_TAG? libraryName? importOrExport* partDirective* (metadata topLevelDefinition)* EOF ; topLevelDefinition : classDeclaration | mixinDeclaration | enumType | typeAlias | EXTERNAL functionSignature ';' | EXTERNAL getterSignature ';' | EXTERNAL setterSignature ';' | getterSignature functionBody | setterSignature functionBody | functionSignature functionBody | (FINAL | CONST) type? staticFinalDeclarationList ';' | topLevelVariableDeclaration ';' ; topLevelVariableDeclaration : varOrType identifier ('=' expression)? (',' initializedIdentifier)* ; declaredIdentifier : COVARIANT? finalConstVarOrType identifier ; finalConstVarOrType : FINAL type? | CONST type? | varOrType ; varOrType : VAR | type ; initializedIdentifier : identifier ('=' expression)? ; initializedIdentifierList : initializedIdentifier (',' initializedIdentifier)* ; functionSignature : type? identifierNotFUNCTION formalParameterPart ; functionBodyPrefix : ASYNC? '=>' | (ASYNC | ASYNC '*' | SYNC '*')? LBRACE ; functionBody : '=>' { startNonAsyncFunction(); } expression { endFunction(); } ';' | { startNonAsyncFunction(); } block { endFunction(); } | ASYNC '=>' { startAsyncFunction(); } expression { endFunction(); } ';' | (ASYNC | ASYNC '*' | SYNC '*') { startAsyncFunction(); } block { endFunction(); } ; block : LBRACE statements RBRACE ; formalParameterPart : typeParameters? formalParameterList ; formalParameterList : '(' ')' | '(' normalFormalParameters (','? | ',' optionalFormalParameters) ')' | '(' optionalFormalParameters ')' ; normalFormalParameters : normalFormalParameter (',' normalFormalParameter)* ; optionalFormalParameters : optionalPositionalFormalParameters | namedFormalParameters ; optionalPositionalFormalParameters : '[' defaultFormalParameter (',' defaultFormalParameter)* ','? ']' ; namedFormalParameters : LBRACE defaultNamedParameter (',' defaultNamedParameter)* ','? RBRACE ; normalFormalParameter : metadata normalFormalParameterNoMetadata ; normalFormalParameterNoMetadata : functionFormalParameter | fieldFormalParameter | simpleFormalParameter ; // NB: It is an anomaly that a functionFormalParameter cannot be FINAL. functionFormalParameter : COVARIANT? type? identifierNotFUNCTION formalParameterPart ; simpleFormalParameter : declaredIdentifier | COVARIANT? identifier ; // NB: It is an anomaly that VAR can be a return type (`var this.x()`). fieldFormalParameter : finalConstVarOrType? THIS '.' identifier formalParameterPart? ; defaultFormalParameter : normalFormalParameter ('=' expression)? ; defaultNamedParameter : normalFormalParameter ((':' | '=') expression)? ; typeWithParameters : typeIdentifier typeParameters? ; classDeclaration : ABSTRACT? CLASS typeWithParameters superclass? mixins? interfaces? LBRACE (metadata classMemberDefinition)* RBRACE | ABSTRACT? CLASS mixinApplicationClass ; mixinDeclaration : MIXIN typeIdentifier typeParameters? (ON typeNotVoidNotFunctionList)? interfaces? LBRACE (metadata mixinMemberDefinition)* RBRACE ; mixins : WITH typeNotVoidNotFunctionList ; classMemberDefinition : methodSignature functionBody | declaration ';' ; // TODO: We will probably want to make this more strict. mixinMemberDefinition : classMemberDefinition ; methodSignature : constructorSignature initializers | factoryConstructorSignature | STATIC? functionSignature | STATIC? getterSignature | STATIC? setterSignature | operatorSignature | constructorSignature ; // https://github.com/dart-lang/sdk/issues/29501 reports on the problem which // was solved by adding a case for redirectingFactoryConstructorSignature. // TODO(eernst): Close that issue when this is integrated into the spec. // https://github.com/dart-lang/sdk/issues/29502 reports on the problem that // than external const factory constructor declaration cannot be derived by // the spec grammar (and also not by this grammar). The following fixes were // introduced for that: Added the 'factoryConstructorSignature' case below in // 'declaration'; also added 'CONST?' in the 'factoryConstructorSignature' // rule, such that const factories in general are allowed. // TODO(eernst): Close that issue when this is integrated into the spec. // TODO(eernst): Note that `EXTERNAL? STATIC? functionSignature` includes // `STATIC functionSignature`, but a static function cannot be abstract. // We might want to make that a syntax error rather than a static semantic // check. declaration : EXTERNAL factoryConstructorSignature | EXTERNAL constantConstructorSignature | EXTERNAL constructorSignature | (EXTERNAL STATIC?)? getterSignature | (EXTERNAL STATIC?)? setterSignature | EXTERNAL? operatorSignature | STATIC (FINAL | CONST) type? staticFinalDeclarationList | FINAL type? initializedIdentifierList | (STATIC | COVARIANT)? (VAR | type) initializedIdentifierList | EXTERNAL? STATIC? functionSignature | redirectingFactoryConstructorSignature | constantConstructorSignature (redirection | initializers)? | constructorSignature (redirection | initializers)? ; staticFinalDeclarationList : staticFinalDeclaration (',' staticFinalDeclaration)* ; staticFinalDeclaration : identifier '=' expression ; operatorSignature : type? OPERATOR operator formalParameterList ; operator : '~' | binaryOperator | '[' ']' | '[' ']' '=' ; binaryOperator : multiplicativeOperator | additiveOperator | shiftOperator | relationalOperator | '==' | bitwiseOperator ; getterSignature : type? GET identifier ; setterSignature : type? SET identifier formalParameterList ; constructorSignature : constructorName formalParameterList ; constructorName : typeIdentifier ('.' identifier)? ; redirection : ':' THIS ('.' identifier)? arguments ; initializers : ':' superCallOrFieldInitializer (',' superCallOrFieldInitializer)* ; superCallOrFieldInitializer : SUPER arguments | SUPER '.' identifier arguments | fieldInitializer | assertClause ; fieldInitializer : (THIS '.')? identifier '=' conditionalExpression cascadeSection* ; factoryConstructorSignature : CONST? FACTORY constructorName formalParameterList ; redirectingFactoryConstructorSignature : CONST? FACTORY constructorName formalParameterList '=' constructorDesignation ; constantConstructorSignature : CONST constructorName formalParameterList ; superclass : EXTENDS typeNotVoidNotFunction ; interfaces : IMPLEMENTS typeNotVoidNotFunctionList ; mixinApplicationClass : typeWithParameters '=' mixinApplication ';' ; mixinApplication : typeNotVoidNotFunction mixins interfaces? ; enumType : ENUM typeIdentifier LBRACE enumEntry (',' enumEntry)* (',')? RBRACE ; enumEntry : metadata identifier ; typeParameter : metadata typeIdentifier (EXTENDS typeNotVoid)? ; typeParameters : '<' typeParameter (',' typeParameter)* '>' ; metadata : ('@' metadatum)* ; metadatum : constructorDesignation arguments | qualified ; expression : functionExpression | throwExpression | assignableExpression assignmentOperator expression | conditionalExpression cascadeSection* ; expressionWithoutCascade : functionExpressionWithoutCascade | throwExpressionWithoutCascade | assignableExpression assignmentOperator expressionWithoutCascade | conditionalExpression ; expressionList : expression (',' expression)* ; primary : thisExpression | SUPER unconditionalAssignableSelector | constObjectExpression | newExpression | functionPrimary | '(' expression ')' | literal | identifier ; literal : nullLiteral | booleanLiteral | numericLiteral | stringLiteral | symbolLiteral | setOrMapLiteral | listLiteral ; nullLiteral : NULL ; numericLiteral : NUMBER | HEX_NUMBER ; booleanLiteral : TRUE | FALSE ; stringLiteral : (multiLineString | singleLineString)+ ; stringLiteralWithoutInterpolation : singleLineStringWithoutInterpolation+ ; setOrMapLiteral : CONST? typeArguments? LBRACE elements? RBRACE ; listLiteral : CONST? typeArguments? '[' elements? ']' ; elements : element (',' element)* ','? ; element : expressionElement | mapEntry | spreadElement | ifElement | forElement ; expressionElement : expression ; mapEntry : expression ':' expression ; spreadElement : ('...' | '...?') expression ; ifElement : IF '(' expression ')' element (ELSE element)? ; forElement : AWAIT? FOR '(' forLoopParts ')' element ; throwExpression : THROW expression ; throwExpressionWithoutCascade : THROW expressionWithoutCascade ; functionExpression : formalParameterPart functionExpressionBody ; functionExpressionBody : '=>' { startNonAsyncFunction(); } expression { endFunction(); } | ASYNC '=>' { startAsyncFunction(); } expression { endFunction(); } ; functionExpressionBodyPrefix : ASYNC? '=>' ; functionExpressionWithoutCascade : formalParameterPart functionExpressionWithoutCascadeBody ; functionExpressionWithoutCascadeBody : '=>' { startNonAsyncFunction(); } expressionWithoutCascade { endFunction(); } | ASYNC '=>' { startAsyncFunction(); } expressionWithoutCascade { endFunction(); } ; functionPrimary : formalParameterPart functionPrimaryBody ; functionPrimaryBody : { startNonAsyncFunction(); } block { endFunction(); } | (ASYNC | ASYNC '*' | SYNC '*') { startAsyncFunction(); } block { endFunction(); } ; functionPrimaryBodyPrefix : (ASYNC | ASYNC '*' | SYNC '*')? LBRACE ; thisExpression : THIS ; newExpression : NEW constructorDesignation arguments ; constObjectExpression : CONST constructorDesignation arguments ; arguments : '(' (argumentList ','?)? ')' ; argumentList : namedArgument (',' namedArgument)* | expressionList (',' namedArgument)* ; namedArgument : label expression ; cascadeSection : '..' (cascadeSelector argumentPart*) (assignableSelector argumentPart*)* (assignmentOperator expressionWithoutCascade)? ; cascadeSelector : '[' expression ']' | identifier ; assignmentOperator : '=' | compoundAssignmentOperator ; compoundAssignmentOperator : '*=' | '/=' | '~/=' | '%=' | '+=' | '-=' | '<<=' | '>' '>' '>' '=' | '>' '>' '=' | '&=' | '^=' | '|=' | '??=' ; conditionalExpression : ifNullExpression ('?' expressionWithoutCascade ':' expressionWithoutCascade)? ; ifNullExpression : logicalOrExpression ('??' logicalOrExpression)* ; logicalOrExpression : logicalAndExpression ('||' logicalAndExpression)* ; logicalAndExpression : equalityExpression ('&&' equalityExpression)* ; equalityExpression : relationalExpression (equalityOperator relationalExpression)? | SUPER equalityOperator relationalExpression ; equalityOperator : '==' | '!=' ; relationalExpression : bitwiseOrExpression (typeTest | typeCast | relationalOperator bitwiseOrExpression)? | SUPER relationalOperator bitwiseOrExpression ; relationalOperator : '>' '=' | '>' | '<=' | '<' ; bitwiseOrExpression : bitwiseXorExpression ('|' bitwiseXorExpression)* | SUPER ('|' bitwiseXorExpression)+ ; bitwiseXorExpression : bitwiseAndExpression ('^' bitwiseAndExpression)* | SUPER ('^' bitwiseAndExpression)+ ; bitwiseAndExpression : shiftExpression ('&' shiftExpression)* | SUPER ('&' shiftExpression)+ ; bitwiseOperator : '&' | '^' | '|' ; shiftExpression : additiveExpression (shiftOperator additiveExpression)* | SUPER (shiftOperator additiveExpression)+ ; shiftOperator : '<<' | '>' '>' '>' | '>' '>' ; additiveExpression : multiplicativeExpression (additiveOperator multiplicativeExpression)* | SUPER (additiveOperator multiplicativeExpression)+ ; additiveOperator : '+' | '-' ; multiplicativeExpression : unaryExpression (multiplicativeOperator unaryExpression)* | SUPER (multiplicativeOperator unaryExpression)+ ; multiplicativeOperator : '*' | '/' | '%' | '~/' ; unaryExpression : prefixOperator unaryExpression | awaitExpression | postfixExpression | (minusOperator | tildeOperator) SUPER | incrementOperator assignableExpression ; prefixOperator : minusOperator | negationOperator | tildeOperator ; minusOperator : '-' ; negationOperator : '!' ; tildeOperator : '~' ; awaitExpression : AWAIT unaryExpression ; postfixExpression : assignableExpression postfixOperator | constructorInvocation selector* | primary selector* ; constructorInvocation : typeName typeArguments '.' identifier arguments ; postfixOperator : incrementOperator ; selector : assignableSelector | argumentPart ; argumentPart : typeArguments? arguments ; incrementOperator : '++' | '--' ; assignableExpression : SUPER unconditionalAssignableSelector | constructorInvocation assignableSelectorPart+ | primary assignableSelectorPart+ | identifier ; assignableSelectorPart : argumentPart* assignableSelector ; unconditionalAssignableSelector : '[' expression ']' | '.' identifier ; assignableSelector : unconditionalAssignableSelector | '?.' identifier ; identifierNotFUNCTION : IDENTIFIER | ABSTRACT // Built-in identifier. | AS // Built-in identifier. | COVARIANT // Built-in identifier. | DEFERRED // Built-in identifier. | DYNAMIC // Built-in identifier. | EXPORT // Built-in identifier. | EXTERNAL // Built-in identifier. | FACTORY // Built-in identifier. | GET // Built-in identifier. | IMPLEMENTS // Built-in identifier. | IMPORT // Built-in identifier. | INTERFACE // Built-in identifier. | LIBRARY // Built-in identifier. | MIXIN // Built-in identifier. | OPERATOR // Built-in identifier. | PART // Built-in identifier. | SET // Built-in identifier. | STATIC // Built-in identifier. | TYPEDEF // Built-in identifier. | ASYNC // Not a built-in identifier. | HIDE // Not a built-in identifier. | OF // Not a built-in identifier. | ON // Not a built-in identifier. | SHOW // Not a built-in identifier. | SYNC // Not a built-in identifier. | { asyncEtcPredicate(getCurrentToken().getType()) }? (AWAIT|YIELD) ; identifier : identifierNotFUNCTION | FUNCTION // Built-in identifier that can be used as a type. ; qualified : typeIdentifier | typeIdentifier '.' identifier | typeIdentifier '.' typeIdentifier '.' identifier ; typeIdentifier : IDENTIFIER | DYNAMIC // Built-in identifier that can be used as a type. | ASYNC // Not a built-in identifier. | HIDE // Not a built-in identifier. | OF // Not a built-in identifier. | ON // Not a built-in identifier. | SHOW // Not a built-in identifier. | SYNC // Not a built-in identifier. | { asyncEtcPredicate(getCurrentToken().getType()) }? (AWAIT|YIELD) ; typeTest : isOperator typeNotVoid ; isOperator : IS '!'? ; typeCast : asOperator typeNotVoid ; asOperator : AS ; statements : statement* ; statement : label* nonLabelledStatement ; // Exception in the language specification: An expressionStatement cannot // start with LBRACE. We force anything that starts with LBRACE to be a block, // which will prevent an expressionStatement from starting with LBRACE, and // which will not interfere with the recognition of any other case. If we // add another statement which can start with LBRACE we must adjust this // check. nonLabelledStatement : block | localVariableDeclaration | forStatement | whileStatement | doStatement | switchStatement | ifStatement | rethrowStatement | tryStatement | breakStatement | continueStatement | returnStatement | localFunctionDeclaration | assertStatement | yieldStatement | yieldEachStatement | expressionStatement ; expressionStatement : expression? ';' ; localVariableDeclaration : metadata initializedVariableDeclaration ';' ; initializedVariableDeclaration : declaredIdentifier ('=' expression)? (',' initializedIdentifier)* ; localFunctionDeclaration : metadata functionSignature functionBody ; ifStatement : IF '(' expression ')' statement (ELSE statement | ()) ; forStatement : AWAIT? FOR '(' forLoopParts ')' statement ; forLoopParts : metadata declaredIdentifier IN expression | metadata identifier IN expression | forInitializerStatement expression? ';' expressionList? ; // The localVariableDeclaration cannot be CONST, but that can // be enforced in a later phase, and the grammar allows it. forInitializerStatement : localVariableDeclaration | expression? ';' ; whileStatement : WHILE '(' expression ')' statement ; doStatement : DO statement WHILE '(' expression ')' ';' ; switchStatement : SWITCH '(' expression ')' LBRACE switchCase* defaultCase? RBRACE ; switchCase : label* CASE expression ':' statements ; defaultCase : label* DEFAULT ':' statements ; rethrowStatement : RETHROW ';' ; tryStatement : TRY block (onParts finallyPart? | finallyPart) ; onPart : catchPart block | ON typeNotVoid catchPart? block ; onParts : onPart onParts | onPart ; catchPart : CATCH '(' identifier (',' identifier)? ')' ; finallyPart : FINALLY block ; returnStatement : RETURN expression? ';' ; label : identifier ':' ; breakStatement : BREAK identifier? ';' ; continueStatement : CONTINUE identifier? ';' ; yieldStatement : YIELD expression ';' ; yieldEachStatement : YIELD '*' expression ';' ; assertStatement : assertClause ';' ; assertClause : ASSERT '(' expression (',' expression)? ','? ')' ; libraryName : metadata LIBRARY identifier ('.' identifier)* ';' ; importOrExport : libraryImport | libraryExport ; libraryImport : metadata importSpecification ; importSpecification : IMPORT uri (AS identifier)? combinator* ';' | IMPORT uri DEFERRED AS identifier combinator* ';' ; combinator : SHOW identifierList | HIDE identifierList ; identifierList : identifier (',' identifier)* ; libraryExport : metadata EXPORT uri combinator* ';' ; partDirective : metadata PART uri ';' ; partHeader : metadata PART OF identifier ('.' identifier)* ';' ; partDeclaration : partHeader topLevelDefinition* EOF ; uri : stringLiteralWithoutInterpolation ; type : functionTypeTails | typeNotFunction functionTypeTails | typeNotFunction ; typeNotFunction : typeNotVoidNotFunction | VOID ; typeNotVoid : functionType | typeNotVoidNotFunction ; typeNotVoidNotFunction : typeName typeArguments? | FUNCTION ; typeName : typeIdentifier ('.' typeIdentifier)? ; typeArguments : '<' typeList '>' ; typeList : type (',' type)* ; typeNotVoidNotFunctionList : typeNotVoidNotFunction (',' typeNotVoidNotFunction)* ; typeAlias : TYPEDEF typeIdentifier typeParameters? '=' functionType ';' | TYPEDEF functionTypeAlias ; functionTypeAlias : functionPrefix formalParameterPart ';' ; functionPrefix : type identifier | identifier ; functionTypeTail : FUNCTION typeParameters? parameterTypeList ; functionTypeTails : functionTypeTail functionTypeTails | functionTypeTail ; functionType : functionTypeTails | typeNotFunction functionTypeTails ; parameterTypeList : '(' ')' | '(' normalParameterTypes ',' optionalParameterTypes ')' | '(' normalParameterTypes ','? ')' | '(' optionalParameterTypes ')' ; normalParameterTypes : normalParameterType (',' normalParameterType)* ; normalParameterType : typedIdentifier | type ; optionalParameterTypes : optionalPositionalParameterTypes | namedParameterTypes ; optionalPositionalParameterTypes : '[' normalParameterTypes ','? ']' ; namedParameterTypes : LBRACE typedIdentifier (',' typedIdentifier)* ','? RBRACE ; typedIdentifier : type identifier ; constructorDesignation : qualified | typeName typeArguments ('.' identifier)? ; symbolLiteral : '#' (operator | (identifier ('.' identifier)*)) ; singleLineStringWithoutInterpolation : RAW_SINGLE_LINE_STRING | SINGLE_LINE_STRING_DQ_BEGIN_END | SINGLE_LINE_STRING_SQ_BEGIN_END ; singleLineString : RAW_SINGLE_LINE_STRING | SINGLE_LINE_STRING_SQ_BEGIN_END | SINGLE_LINE_STRING_SQ_BEGIN_MID expression (SINGLE_LINE_STRING_SQ_MID_MID expression)* SINGLE_LINE_STRING_SQ_MID_END | SINGLE_LINE_STRING_DQ_BEGIN_END | SINGLE_LINE_STRING_DQ_BEGIN_MID expression (SINGLE_LINE_STRING_DQ_MID_MID expression)* SINGLE_LINE_STRING_DQ_MID_END ; multiLineString : RAW_MULTI_LINE_STRING | MULTI_LINE_STRING_SQ_BEGIN_END | MULTI_LINE_STRING_SQ_BEGIN_MID expression (MULTI_LINE_STRING_SQ_MID_MID expression)* MULTI_LINE_STRING_SQ_MID_END | MULTI_LINE_STRING_DQ_BEGIN_END | MULTI_LINE_STRING_DQ_BEGIN_MID expression (MULTI_LINE_STRING_DQ_MID_MID expression)* MULTI_LINE_STRING_DQ_MID_END ; // ---------------------------------------- Lexer rules. fragment LETTER : 'a' .. 'z' | 'A' .. 'Z' ; fragment DIGIT : '0' .. '9' ; fragment EXPONENT : ('e' | 'E') ('+' | '-')? DIGIT+ ; fragment HEX_DIGIT : ('a' | 'b' | 'c' | 'd' | 'e' | 'f') | ('A' | 'B' | 'C' | 'D' | 'E' | 'F') | DIGIT ; FINAL : 'final' ; CONST : 'const' ; VAR : 'var' ; VOID : 'void' ; ASYNC : 'async' ; THIS : 'this' ; ABSTRACT : 'abstract' ; AS : 'as' ; SYNC : 'sync' ; CLASS : 'class' ; WITH : 'with' ; STATIC : 'static' ; DYNAMIC : 'dynamic' ; EXTERNAL : 'external' ; GET : 'get' ; SET : 'set' ; OPERATOR : 'operator' ; SUPER : 'super' ; FACTORY : 'factory' ; EXTENDS : 'extends' ; IMPLEMENTS : 'implements' ; ENUM : 'enum' ; NULL : 'null' ; TRUE : 'true' ; FALSE : 'false' ; THROW : 'throw' ; NEW : 'new' ; AWAIT : 'await' ; DEFERRED : 'deferred' ; EXPORT : 'export' ; IMPORT : 'import' ; INTERFACE : 'interface' ; LIBRARY : 'library' ; MIXIN : 'mixin' ; PART : 'part' ; TYPEDEF : 'typedef' ; IS : 'is' ; IF : 'if' ; ELSE : 'else' ; WHILE : 'while' ; FOR : 'for' ; IN : 'in' ; DO : 'do' ; SWITCH : 'switch' ; CASE : 'case' ; DEFAULT : 'default' ; RETHROW : 'rethrow' ; TRY : 'try' ; ON : 'on' ; CATCH : 'catch' ; FINALLY : 'finally' ; RETURN : 'return' ; BREAK : 'break' ; CONTINUE : 'continue' ; YIELD : 'yield' ; SHOW : 'show' ; HIDE : 'hide' ; OF : 'of' ; ASSERT : 'assert' ; COVARIANT : 'covariant' ; FUNCTION : 'Function' ; NUMBER : DIGIT+ '.' DIGIT+ EXPONENT? | DIGIT+ EXPONENT? | '.' DIGIT+ EXPONENT? ; HEX_NUMBER : '0x' HEX_DIGIT+ | '0X' HEX_DIGIT+ ; RAW_SINGLE_LINE_STRING : 'r' '\'' (~('\'' | '\r' | '\n'))* '\'' | 'r' '"' (~('"' | '\r' | '\n'))* '"' ; RAW_MULTI_LINE_STRING : 'r' '"""' (.)*? '"""' | 'r' '\'\'\'' (.)*? '\'\'\'' ; fragment SIMPLE_STRING_INTERPOLATION : '$' IDENTIFIER_NO_DOLLAR ; fragment STRING_CONTENT_SQ : ~('\\' | '\'' | '$' | '\r' | '\n') | '\\' ~( '\r' | '\n') | SIMPLE_STRING_INTERPOLATION ; SINGLE_LINE_STRING_SQ_BEGIN_END : '\'' STRING_CONTENT_SQ* '\'' ; SINGLE_LINE_STRING_SQ_BEGIN_MID : '\'' STRING_CONTENT_SQ* '${' { enterBraceSingleQuote(); } ; SINGLE_LINE_STRING_SQ_MID_MID : { currentBraceLevel(BRACE_SINGLE) }? { exitBrace(); } '}' STRING_CONTENT_SQ* '${' { enterBraceSingleQuote(); } ; SINGLE_LINE_STRING_SQ_MID_END : { currentBraceLevel(BRACE_SINGLE) }? { exitBrace(); } '}' STRING_CONTENT_SQ* '\'' ; fragment STRING_CONTENT_DQ : ~('\\' | '"' | '$' | '\r' | '\n') | '\\' ~('\r' | '\n') | SIMPLE_STRING_INTERPOLATION ; SINGLE_LINE_STRING_DQ_BEGIN_END : '"' STRING_CONTENT_DQ* '"' ; SINGLE_LINE_STRING_DQ_BEGIN_MID : '"' STRING_CONTENT_DQ* '${' { enterBraceDoubleQuote(); } ; SINGLE_LINE_STRING_DQ_MID_MID : { currentBraceLevel(BRACE_DOUBLE) }? { exitBrace(); } '}' STRING_CONTENT_DQ* '${' { enterBraceDoubleQuote(); } ; SINGLE_LINE_STRING_DQ_MID_END : { currentBraceLevel(BRACE_DOUBLE) }? { exitBrace(); } '}' STRING_CONTENT_DQ* '"' ; fragment QUOTES_SQ : | '\'' | '\'\'' ; // Read string contents, which may be almost anything, but stop when seeing // '\'\'\'' and when seeing '${'. We do this by allowing all other // possibilities including escapes, simple interpolation, and fewer than // three '\''. fragment STRING_CONTENT_TSQ : QUOTES_SQ (~('\\' | '$' | '\'') | '\\' . | SIMPLE_STRING_INTERPOLATION) ; MULTI_LINE_STRING_SQ_BEGIN_END : '\'\'\'' STRING_CONTENT_TSQ* '\'\'\'' ; MULTI_LINE_STRING_SQ_BEGIN_MID : '\'\'\'' STRING_CONTENT_TSQ* QUOTES_SQ '${' { enterBraceThreeSingleQuotes(); } ; MULTI_LINE_STRING_SQ_MID_MID : { currentBraceLevel(BRACE_THREE_SINGLE) }? { exitBrace(); } '}' STRING_CONTENT_TSQ* QUOTES_SQ '${' { enterBraceThreeSingleQuotes(); } ; MULTI_LINE_STRING_SQ_MID_END : { currentBraceLevel(BRACE_THREE_SINGLE) }? { exitBrace(); } '}' STRING_CONTENT_TSQ* '\'\'\'' ; fragment QUOTES_DQ : | '"' | '""' ; // Read string contents, which may be almost anything, but stop when seeing // '"""' and when seeing '${'. We do this by allowing all other possibilities // including escapes, simple interpolation, and fewer-than-three '"'. fragment STRING_CONTENT_TDQ : QUOTES_DQ (~('\\' | '$' | '"') | '\\' . | SIMPLE_STRING_INTERPOLATION) ; MULTI_LINE_STRING_DQ_BEGIN_END : '"""' STRING_CONTENT_TDQ* '"""' ; MULTI_LINE_STRING_DQ_BEGIN_MID : '"""' STRING_CONTENT_TDQ* QUOTES_DQ '${' { enterBraceThreeDoubleQuotes(); } ; MULTI_LINE_STRING_DQ_MID_MID : { currentBraceLevel(BRACE_THREE_DOUBLE) }? { exitBrace(); } '}' STRING_CONTENT_TDQ* QUOTES_DQ '${' { enterBraceThreeDoubleQuotes(); } ; MULTI_LINE_STRING_DQ_MID_END : { currentBraceLevel(BRACE_THREE_DOUBLE) }? { exitBrace(); } '}' STRING_CONTENT_TDQ* '"""' ; LBRACE : '{' { enterBrace(); } ; RBRACE : { currentBraceLevel(BRACE_NORMAL) }? { exitBrace(); } '}' ; fragment IDENTIFIER_START_NO_DOLLAR : LETTER | '_' ; fragment IDENTIFIER_PART_NO_DOLLAR : IDENTIFIER_START_NO_DOLLAR | DIGIT ; fragment IDENTIFIER_NO_DOLLAR : IDENTIFIER_START_NO_DOLLAR IDENTIFIER_PART_NO_DOLLAR* ; fragment IDENTIFIER_START : IDENTIFIER_START_NO_DOLLAR | '$' ; fragment IDENTIFIER_PART : IDENTIFIER_START | DIGIT ; SCRIPT_TAG : '#!' (~('\r' | '\n'))* NEWLINE ; IDENTIFIER : IDENTIFIER_START IDENTIFIER_PART* ; SINGLE_LINE_COMMENT : '//' (~('\r' | '\n'))* NEWLINE? { skip(); } ; MULTI_LINE_COMMENT : '/*' (MULTI_LINE_COMMENT | .)*? '*/' { skip(); } ; fragment NEWLINE : ('\r' | '\n' | '\r\n') ; FEFF : '\uFEFF' ; WS : (' ' | '\t' | '\r' | '\n')+ { skip(); } ;