blob: 46b483d06f28be8f30e16e6567d4457398a5011a [file]
/*
* The MIT License (MIT)
*
* Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp)
* Copyright (c) 2017-2020 by Ivan Kochurkin (Positive Technologies):
added ECMAScript 6 support, cleared and transformed to the universal grammar.
* Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go)
* Copyright (c) 2019 by Student Main (contributor -> ES2020)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false
// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging
parser grammar JavaScriptParser;
@header {
import org.jetbrains.kotlin.js.parser.antlr.JavaScriptParserBase;
import org.jetbrains.kotlin.js.parser.antlr.JavaScriptRuleContext;
}
options {
tokenVocab = JavaScriptLexer;
superClass = JavaScriptParserBase;
contextSuperClass = JavaScriptRuleContext;
}
program
: HashBangLine? sourceElements? EOF
;
sourceElement
: statement
;
optionalStatements
: statement* EOF
;
optionalSingleExpression
: singleExpression? EOF
;
optionalExpressionOrStatement
: singleExpression EOF
| statementList? EOF
;
optionalFunction
: functionDeclaration? EOF
;
statement
: block
| variableStatement
| importStatement
| exportStatement
| emptyStatement_
| classDeclaration
| functionDeclaration
| expressionStatement
| ifStatement
| iterationStatement
| continueStatement
| breakStatement
| returnStatement
| yieldStatement
| withStatement
| labelledStatement
| switchStatement
| throwStatement
| tryStatement
| debuggerStatement
;
block
: '{' statementList? '}'
;
statementList
: statement+
;
importStatement
: Import importFromBlock
;
importFromBlock
: importDefault? (importNamespace | importModuleItems) importFrom eos
| StringLiteral eos
;
importModuleItems
: '{' (importAliasName ',')* (importAliasName ','?)? '}'
;
importAliasName
: moduleExportName (As importedBinding)?
;
moduleExportName
: identifierName
| StringLiteral
;
// yield and await are permitted as BindingIdentifier in the grammar
importedBinding
: Identifier
| Yield
| Await
;
importDefault
: aliasName ','
;
importNamespace
: ('*' | identifierName) (As identifierName)?
;
importFrom
: From StringLiteral
;
aliasName
: identifierName (As identifierName)?
;
exportStatement
: Export Default? (exportFromBlock | declaration) eos # ExportDeclaration
| Export Default singleExpression eos # ExportDefaultDeclaration
;
exportFromBlock
: importNamespace importFrom eos
| exportModuleItems importFrom? eos
;
exportModuleItems
: '{' (exportAliasName ',')* (exportAliasName ','?)? '}'
;
exportAliasName
: moduleExportName (As moduleExportName)?
;
declaration
: variableStatement
| classDeclaration
| functionDeclaration
;
variableStatement
: variableDeclarationList eos
;
variableDeclarationList
: varModifier variableDeclaration (',' variableDeclaration)*
;
singleVariableDeclaration
: varModifier variableDeclaration
;
variableDeclaration
: assignable ('=' singleExpression)? // ECMAScript 6: Array & Object Matching
;
emptyStatement_
: SemiColon
;
expressionStatement
: {this.notOpenBraceAndNotFunction()}? expressionSequence eos
;
ifStatement
: If '(' expressionSequence ')' statement (Else statement)?
;
iterationStatement
: Do statement While '(' expressionSequence ')' eos # DoStatement
| While '(' expressionSequence ')' statement # WhileStatement
| For '(' (vars=expressionSequence | var=variableDeclarationList)?
';' condition=expressionSequence?
';' increment=expressionSequence?
')' statement # ForStatement
| For '(' (singleExpression | singleVariableDeclaration) In expressionSequence ')' statement # ForInStatement
| For Await? '(' (singleExpression | singleVariableDeclaration) Of expressionSequence ')' statement # ForOfStatement
;
varModifier // let, const - ECMAScript 6
: Var
| let_
| Const
;
continueStatement
: Continue ({this.notLineTerminator()}? identifier)? eos
;
breakStatement
: Break ({this.notLineTerminator()}? identifier)? eos
;
returnStatement
: Return ({this.notLineTerminator()}? expressionSequence)? eos
;
yieldStatement
: (Yield | YieldStar) ({this.notLineTerminator()}? expressionSequence)? eos
;
withStatement
: With '(' expressionSequence ')' statement
;
switchStatement
: Switch '(' expressionSequence ')' caseBlock
;
caseBlock
: '{' beforeDefault=caseClauses? (defaultClause afterDefault=caseClauses?)? '}'
;
caseClauses
: caseClause+
;
caseClause
: Case expressionSequence ':' statementList?
;
defaultClause
: Default ':' statementList?
;
labelledStatement
: identifier ':' statement
;
throwStatement
: Throw {this.notLineTerminator()}? expressionSequence eos
;
tryStatement
: Try block (catchProduction finallyProduction? | finallyProduction)
;
catchProduction
: Catch ('(' assignable? ')')? block
;
finallyProduction
: Finally block
;
debuggerStatement
: Debugger eos
;
functionDeclaration
: Async? Function_ '*'? identifier '(' formalParameterList? ')' functionBody
;
classDeclaration
: Class identifier classTail
;
classTail
: (Extends singleExpression)? '{' classElement* '}'
;
classElement
: (Static | {this.n("static")}? identifier)? methodDefinition
| (Static | {this.n("static")}? identifier)? fieldDefinition
| (Static | {this.n("static")}? identifier) block
| emptyStatement_
;
methodDefinition
: (Async {this.notLineTerminator()}?)? '*'? classElementName '(' formalParameterList? ')' functionBody
| '*'? getter '(' ')' functionBody
| '*'? setter '(' formalParameterList? ')' functionBody
;
fieldDefinition
: classElementName initializer?
;
classElementName
: propertyName
| privateIdentifier
;
privateIdentifier
: '#' identifierName
;
formalParameterList
: formalParameterArg (',' formalParameterArg)* (',' restParameterArg)?
| restParameterArg
;
formalParameterArg
: assignable ('=' singleExpression)? // ECMAScript 6: Initialization
;
restParameterArg // ECMAScript 6: Rest Parameter
: Ellipsis singleExpression
;
functionBody
: '{' sourceElements? '}'
;
sourceElements
: sourceElement+
;
arrayLiteral
: '[' ']'
| '[' elementList ']'
;
elementList
: arrayElement (',' arrayElement)*
;
arrayElement
: Ellipsis singleExpression
| singleExpression? // Nullable because of the array holes we need to preserve during parsing
;
propertyAssignment
: propertyName ':' singleExpression # PropertyExpressionAssignment
| '[' label=singleExpression ']' ':' value=singleExpression # ComputedPropertyExpressionAssignment
| Async? '*'? propertyName '(' formalParameterList? ')' functionBody # FunctionProperty
| getter '(' ')' functionBody # PropertyGetter
| setter '(' formalParameterArg ')' functionBody # PropertySetter
| Ellipsis singleExpression # SpreadProperty
| identifierName # PropertyShorthand
;
objectBindingPattern
: '{' '}'
| '{' restBindingElement ','? '}'
| '{' propertyBindingPattern (',' propertyBindingPattern)* (',' restBindingElement)? ','? '}'
;
propertyBindingPattern
: identifierName initializer? # RegularPropertyBindingPattern
| propertyName ':' bindingElement # NamedPropertyBindingPattern
;
arrayBindingPattern
: '[' ']'
| '[' restBindingElement ','? ']'
| '[' arrayItemList (',' restBindingElement)? ','?']'
;
arrayItemList
: bindingElement (',' arrayItemBinding)* // starts with an element
| ',' arrayItemBinding (',' arrayItemBinding)* // starts with a hole (consumes a comma)
;
arrayItemBinding
: bindingElement? // Nullable because of the array pattern holes we need to preserve during parsing
;
bindingElement
: assignable initializer?
;
restBindingElement
: Ellipsis identifierName
;
propertyName
: identifierName
| StringLiteral
| numericLiteral
| '[' singleExpression ']'
;
arguments
: '(' (argument (',' argument)* ','?)? ')'
;
argument
: Ellipsis? (singleExpression | identifier)
;
expressionSequence
: lhs=expressionSequence ',' rhs=expressionSequence
| singleExpression
;
singleExpression
: singleExpressionImpl
;
singleExpressionImpl
: anonymousFunction # FunctionExpression
| Class identifier? classTail # ClassExpression
| singleExpressionImpl '?.' singleExpressionImpl # OptionalChainExpression
| singleExpressionImpl '?.'? '[' expressionSequence ']' # MemberIndexExpression
| singleExpressionImpl '?'? '.' '#'? identifierName # MemberDotExpression
// Split to try `new Date()` first, then `new Date`.
| New identifier arguments # NewExpression
| New singleExpressionImpl arguments # NewExpression
| New singleExpressionImpl # NewExpression
| singleExpressionImpl arguments # ArgumentsExpression
| New '.' Target # MetaExpression // new.target
| Import '.' Meta # ImportMetaExpression
| singleExpressionImpl {this.notLineTerminator()}? '++' # PostIncrementExpression
| singleExpressionImpl {this.notLineTerminator()}? '--' # PostDecreaseExpression
| Delete singleExpressionImpl # DeleteExpression
| Void singleExpressionImpl # VoidExpression
| Typeof singleExpressionImpl # TypeofExpression
| '++' singleExpressionImpl # PreIncrementExpression
| '--' singleExpressionImpl # PreDecreaseExpression
| '+' singleExpressionImpl # UnaryPlusExpression
| '-' singleExpressionImpl # UnaryMinusExpression
| '~' singleExpressionImpl # BitNotExpression
| '!' singleExpressionImpl # NotExpression
| Await singleExpressionImpl # AwaitExpression
| <assoc = right> singleExpressionImpl '**' singleExpressionImpl # PowerExpression
| singleExpressionImpl ('*' | '/' | '%') singleExpressionImpl # MultiplicativeExpression
| singleExpressionImpl ('+' | '-') singleExpressionImpl # AdditiveExpression
| singleExpressionImpl '??' singleExpressionImpl # CoalesceExpression
| singleExpressionImpl ('<<' | '>>' | '>>>') singleExpressionImpl # BitShiftExpression
| singleExpressionImpl ('<' | '>' | '<=' | '>=') singleExpressionImpl # RelationalExpression
| singleExpressionImpl Instanceof singleExpressionImpl # InstanceofExpression
| singleExpressionImpl In singleExpressionImpl # InExpression
| singleExpressionImpl ('==' | '!=' | '===' | '!==') singleExpressionImpl # EqualityExpression
| singleExpressionImpl '&' singleExpressionImpl # BitAndExpression
| singleExpressionImpl '^' singleExpressionImpl # BitXOrExpression
| singleExpressionImpl '|' singleExpressionImpl # BitOrExpression
| singleExpressionImpl '&&' singleExpressionImpl # LogicalAndExpression
| singleExpressionImpl '||' singleExpressionImpl # LogicalOrExpression
| <assoc = right> singleExpressionImpl '?' singleExpressionImpl ':' singleExpressionImpl # TernaryExpression
| <assoc = right> lhs=singleExpressionImpl '=' rhs=singleExpressionImpl # AssignmentExpression
| <assoc = right> lhs=singleExpressionImpl assignmentOperator rhs=singleExpressionImpl # AssignmentOperatorExpression
| Import '(' singleExpressionImpl ')' # ImportExpression
| singleExpressionImpl templateStringLiteral # TemplateStringExpression // ECMAScript 6
| (Yield | YieldStar) ({this.notLineTerminator()}? expressionSequence)? # YieldExpression // ECMAScript 6
| This # ThisExpression
| identifier # IdentifierExpression
| Super # SuperExpression
| literal # LiteralExpression
| arrayLiteral # ArrayLiteralExpression
| objectLiteral # ObjectLiteralExpression
| '(' expressionSequence ')' # ParenthesizedExpression
;
initializer
// TODO: must be `= AssignmentExpression` and we have such label alredy but it doesn't respect the specification.
// See https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-Initializer
: '=' singleExpression
;
assignable
: identifier
| keyword
| arrayBindingPattern
| objectBindingPattern
;
objectLiteral
: '{' (propertyAssignment (',' propertyAssignment)* ','?)? '}'
;
anonymousFunction
: functionDeclaration # NamedFunction
| Async? Function_ '*'? '(' formalParameterList? ')' functionBody # AnonymousFunctionDecl
| Async? arrowFunctionParameters '=>' arrowFunctionBody # ArrowFunction
;
arrowFunctionParameters
: identifierName
| '(' formalParameterList? ')'
;
arrowFunctionBody
: singleExpression
| functionBody
;
assignmentOperator
: '*='
| '/='
| '%='
| '+='
| '-='
| '<<='
| '>>='
| '>>>='
| '&='
| '^='
| '|='
| '**='
| '??='
;
literal
: NullLiteral
| BooleanLiteral
| StringLiteral
| templateStringLiteral
| RegularExpressionLiteral
| numericLiteral
| bigintLiteral
;
templateStringLiteral
: BackTick templateStringAtom* BackTick
;
templateStringAtom
: TemplateStringAtom
| TemplateStringStartExpression singleExpression TemplateCloseBrace
;
numericLiteral
: DecimalLiteral
| HexIntegerLiteral
| OctalIntegerLiteral
| OctalIntegerLiteral2
| BinaryIntegerLiteral
;
bigintLiteral
: BigDecimalIntegerLiteral
| BigHexIntegerLiteral
| BigOctalIntegerLiteral
| BigBinaryIntegerLiteral
;
getter
: {this.n("get")}? identifier classElementName
;
setter
: {this.n("set")}? identifier classElementName
;
identifierName
: identifier
| reservedWord
;
identifier
: Identifier
| NonStrictLet
| Async
| As
| From
| Yield
| Of
| Target
| Meta
;
reservedWord
: keyword
| NullLiteral
| BooleanLiteral
;
keyword
: Break
| Do
| Instanceof
| Typeof
| Case
| Else
| New
| Var
| Catch
| Finally
| Return
| Void
| Continue
| For
| Switch
| While
| Debugger
| Function_
| This
| With
| Default
| If
| Throw
| Delete
| In
| Try
| Class
| Enum
| Extends
| Super
| Const
| Export
| Import
| Implements
| let_
| Private
| Public
| Interface
| Package
| Protected
| Static
| Yield
| YieldStar
| Async
| Await
| From
| As
| Of
;
let_
: NonStrictLet
| StrictLet
;
eos
: SemiColon
| EOF
| {this.lineTerminatorAhead()}?
| {this.closeBrace()}?
;