aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ecma_todo.md1498
-rw-r--r--src/web/component/navigation/index.css66
-rw-r--r--src/web/component/navigation/index.html11
-rw-r--r--src/web/component/navigation/index.js11
-rw-r--r--src/web/component/navigation/root.zig8
-rw-r--r--src/web/component/root.zig1
-rw-r--r--src/web/index.css1
-rw-r--r--src/web/index.html2
-rw-r--r--src/z/js/parser/ast.zig674
-rw-r--r--src/z/js/parser/lexical_grammar.zig1099
-rw-r--r--src/z/js/parser/parser.zig117
-rw-r--r--src/z/js/parser/result.zig3
-rw-r--r--src/z/js/parser/root.zig93
-rw-r--r--src/z/js/parser/syntactic_grammar.zig1907
-rw-r--r--src/z/z.js21
15 files changed, 5511 insertions, 1 deletions
diff --git a/ecma_todo.md b/ecma_todo.md
new file mode 100644
index 0000000..197f81a
--- /dev/null
+++ b/ecma_todo.md
@@ -0,0 +1,1498 @@
+
+# A.1 Lexical Grammar
+
+
+- [x] SourceCharacter ::
+ - [x] any Unicode code point
+
+- [ ] InputElementDiv ::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+ - [ ] Comment
+ - [ ] CommonToken
+ - [ ] DivPunctuator
+ - [ ] RightBracePunctuator
+
+- [ ] InputElementRegExp ::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+ - [ ] Comment
+ - [ ] CommonToken
+ - [ ] RightBracePunctuator
+ - [ ] RegularExpressionLiteral
+
+- [ ] InputElementRegExpOrTemplateTail ::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+ - [ ] Comment
+ - [ ] CommonToken
+ - [ ] RegularExpressionLiteral
+ - [ ] TemplateSubstitutionTail
+
+- [ ] InputElementTemplateTail ::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+ - [ ] Comment
+ - [ ] CommonToken
+ - [ ] DivPunctuator
+ - [ ] TemplateSubstitutionTail
+
+- [ ] InputElementHashbangOrRegExp ::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+ - [ ] Comment
+ - [ ] CommonToken
+ - [ ] HashbangComment
+ - [ ] RegularExpressionLiteral
+
+- [ ] WhiteSpace ::
+ - [ ] <TAB>
+ - [ ] <VT>
+ - [ ] <FF>
+ - [ ] <ZWNBSP>
+ - [ ] <USP>
+
+- [ ] LineTerminator ::
+ - [ ] <LF>
+ - [ ] <CR>
+ - [ ] <LS>
+ - [ ] <PS>
+
+- [ ] LineTerminatorSequence ::
+ - [ ] <LF>
+ - [ ] <CR> [lookahead ≠ <LF>]
+ - [ ] <LS>
+ - [ ] <PS>
+ - [ ] <CR> <LF>
+
+- [ ] Comment ::
+ - [ ] MultiLineComment
+ - [ ] SingleLineComment
+
+- [ ] MultiLineComment ::
+ - [ ] /* MultiLineCommentCharsopt */
+
+- [ ] MultiLineCommentChars ::
+ - [ ] MultiLineNotAsteriskChar MultiLineCommentCharsopt
+ - [ ] * PostAsteriskCommentCharsopt
+
+- [ ] PostAsteriskCommentChars ::
+ - [ ] MultiLineNotForwardSlashOrAsteriskChar MultiLineCommentCharsopt
+ - [ ] * PostAsteriskCommentCharsopt
+
+- [ ] MultiLineNotAsteriskChar ::
+ - [ ] SourceCharacter but not *
+
+- [ ] MultiLineNotForwardSlashOrAsteriskChar ::
+ - [ ] SourceCharacter but not one of / or *
+
+- [ ] SingleLineComment ::
+ - [ ] // SingleLineCommentCharsopt
+
+- [ ] SingleLineCommentChars ::
+ - [ ] SingleLineCommentChar SingleLineCommentCharsopt
+
+- [ ] SingleLineCommentChar ::
+ - [ ] SourceCharacter but not LineTerminator
+
+- [ ] HashbangComment ::
+ - [ ] #! SingleLineCommentCharsopt
+
+- [ ] CommonToken ::
+ - [ ] IdentifierName
+ - [ ] PrivateIdentifier
+ - [ ] Punctuator
+ - [ ] NumericLiteral
+ - [ ] StringLiteral
+ - [ ] Template
+
+- [ ] PrivateIdentifier ::
+ - [ ] # IdentifierName
+
+- [ ] IdentifierName ::
+ - [ ] IdentifierStart
+ - [ ] IdentifierName IdentifierPart
+
+- [ ] IdentifierStart ::
+ - [ ] IdentifierStartChar
+ - [ ] \ UnicodeEscapeSequence
+
+- [ ] IdentifierPart ::
+ - [ ] IdentifierPartChar
+ - [ ] \ UnicodeEscapeSequence
+
+- [ ] IdentifierStartChar ::
+ - [ ] UnicodeIDStart
+$
+_
+
+- [ ] IdentifierPartChar ::
+ - [ ] UnicodeIDContinue
+$
+ - [ ] AsciiLetter :: one of
+ - [ ] a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+
+- [ ] UnicodeIDStart ::
+ - [ ] any Unicode code point with the Unicode property “ID_Start”
+
+- [ ] UnicodeIDContinue ::
+ - [ ] any Unicode code point with the Unicode property “ID_Continue”
+ - [ ] ReservedWord :: one of
+ - [ ] await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield
+
+- [ ] Punctuator ::
+ - [ ] OptionalChainingPunctuator
+ - [ ] OtherPunctuator
+
+- [ ] OptionalChainingPunctuator ::
+ - [ ] ?. [lookahead ∉ DecimalDigit]
+ - [ ] OtherPunctuator :: one of
+ - [ ] { ( ) [ ] . ... ; , < > <= >= == != === !== + - * % ** ++ -- << >> >>> & | ^ ! ~ && || ?? ? : = += -= *= %= **= <<= >>= >>>= &= |= ^= &&= ||= ??= =>
+
+- [ ] DivPunctuator ::
+/
+/=
+
+- [ ] RightBracePunctuator ::
+}
+
+- [ ] NullLiteral ::
+ - [ ] null
+
+- [ ] BooleanLiteral ::
+ - [ ] true
+ - [ ] false
+
+- [ ] NumericLiteralSeparator ::
+_
+
+- [ ] NumericLiteral ::
+ - [ ] DecimalLiteral
+ - [ ] DecimalBigIntegerLiteral
+ - [ ] NonDecimalIntegerLiteral[+Sep]
+ - [ ] NonDecimalIntegerLiteral[+Sep] BigIntLiteralSuffix
+ - [ ] LegacyOctalIntegerLiteral
+
+- [ ] DecimalBigIntegerLiteral ::
+ - [ ] 0 BigIntLiteralSuffix
+ - [ ] NonZeroDigit DecimalDigits[+Sep]opt BigIntLiteralSuffix
+ - [ ] NonZeroDigit NumericLiteralSeparator DecimalDigits[+Sep] BigIntLiteralSuffix
+
+- [ ] NonDecimalIntegerLiteral[Sep] ::
+ - [ ] BinaryIntegerLiteral[?Sep]
+ - [ ] OctalIntegerLiteral[?Sep]
+ - [ ] HexIntegerLiteral[?Sep]
+
+- [ ] BigIntLiteralSuffix ::
+n
+
+- [ ] DecimalLiteral ::
+ - [ ] DecimalIntegerLiteral . DecimalDigits[+Sep]opt ExponentPart[+Sep]opt
+ - [ ] . DecimalDigits[+Sep] ExponentPart[+Sep]opt
+ - [ ] DecimalIntegerLiteral ExponentPart[+Sep]opt
+
+- [ ] DecimalIntegerLiteral ::
+0
+ - [ ] NonZeroDigit
+ - [ ] NonZeroDigit NumericLiteralSeparatoropt DecimalDigits[+Sep]
+ - [ ] NonOctalDecimalIntegerLiteral
+
+- [ ] DecimalDigits[Sep] ::
+ - [ ] DecimalDigit
+ - [ ] DecimalDigits[?Sep] DecimalDigit
+ - [ ] [+Sep] DecimalDigits[+Sep] NumericLiteralSeparator DecimalDigit
+ - [ ] DecimalDigit :: one of
+ - [ ] 0 1 2 3 4 5 6 7 8 9
+ - [ ] NonZeroDigit :: one of
+ - [ ] 1 2 3 4 5 6 7 8 9
+
+- [ ] ExponentPart[Sep] ::
+ - [ ] ExponentIndicator SignedInteger[?Sep]
+ - [ ] ExponentIndicator :: one of
+ - [ ] e E
+
+- [ ] SignedInteger[Sep] ::
+ - [ ] DecimalDigits[?Sep]
+ - [ ] + DecimalDigits[?Sep]
+ - [ ] - DecimalDigits[?Sep]
+
+- [ ] BinaryIntegerLiteral[Sep] ::
+ - [ ] 0b BinaryDigits[?Sep]
+ - [ ] 0B BinaryDigits[?Sep]
+
+- [ ] BinaryDigits[Sep] ::
+ - [ ] BinaryDigit
+ - [ ] BinaryDigits[?Sep] BinaryDigit
+ - [ ] [+Sep] BinaryDigits[+Sep] NumericLiteralSeparator BinaryDigit
+ - [ ] BinaryDigit :: one of
+ - [ ] 0 1
+
+- [ ] OctalIntegerLiteral[Sep] ::
+ - [ ] 0o OctalDigits[?Sep]
+ - [ ] 0O OctalDigits[?Sep]
+
+- [ ] OctalDigits[Sep] ::
+ - [ ] OctalDigit
+ - [ ] OctalDigits[?Sep] OctalDigit
+ - [ ] [+Sep] OctalDigits[+Sep] NumericLiteralSeparator OctalDigit
+
+- [ ] LegacyOctalIntegerLiteral ::
+ - [ ] 0 OctalDigit
+ - [ ] LegacyOctalIntegerLiteral OctalDigit
+
+- [ ] NonOctalDecimalIntegerLiteral ::
+ - [ ] 0 NonOctalDigit
+ - [ ] LegacyOctalLikeDecimalIntegerLiteral NonOctalDigit
+ - [ ] NonOctalDecimalIntegerLiteral DecimalDigit
+
+- [ ] LegacyOctalLikeDecimalIntegerLiteral ::
+ - [ ] 0 OctalDigit
+ - [ ] LegacyOctalLikeDecimalIntegerLiteral OctalDigit
+ - [ ] OctalDigit :: one of
+ - [ ] 0 1 2 3 4 5 6 7
+ - [ ] NonOctalDigit :: one of
+ - [ ] 8 9
+
+- [ ] HexIntegerLiteral[Sep] ::
+ - [ ] 0x HexDigits[?Sep]
+ - [ ] 0X HexDigits[?Sep]
+
+- [ ] HexDigits[Sep] ::
+ - [ ] HexDigit
+ - [ ] HexDigits[?Sep] HexDigit
+ - [ ] [+Sep] HexDigits[+Sep] NumericLiteralSeparator HexDigit
+ - [ ] HexDigit :: one of
+ - [ ] 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F
+
+- [ ] StringLiteral ::
+ - [ ] " DoubleStringCharactersopt "
+ - [ ] ' SingleStringCharactersopt '
+
+- [ ] DoubleStringCharacters ::
+ - [ ] DoubleStringCharacter DoubleStringCharactersopt
+
+- [ ] SingleStringCharacters ::
+ - [ ] SingleStringCharacter SingleStringCharactersopt
+
+- [ ] DoubleStringCharacter ::
+ - [ ] SourceCharacter but not one of " or \ or LineTerminator
+ - [ ] <LS>
+ - [ ] <PS>
+ - [ ] \ EscapeSequence
+ - [ ] LineContinuation
+
+- [ ] SingleStringCharacter ::
+ - [ ] SourceCharacter but not one of ' or \ or LineTerminator
+ - [ ] <LS>
+ - [ ] <PS>
+ - [ ] \ EscapeSequence
+ - [ ] LineContinuation
+
+- [ ] LineContinuation ::
+ - [ ] \ LineTerminatorSequence
+
+- [ ] EscapeSequence ::
+ - [ ] CharacterEscapeSequence
+ - [ ] 0 [lookahead ∉ DecimalDigit]
+ - [ ] LegacyOctalEscapeSequence
+ - [ ] NonOctalDecimalEscapeSequence
+ - [ ] HexEscapeSequence
+ - [ ] UnicodeEscapeSequence
+
+- [ ] CharacterEscapeSequence ::
+ - [ ] SingleEscapeCharacter
+ - [ ] NonEscapeCharacter
+ - [ ] SingleEscapeCharacter :: one of
+ - [ ] ' " \ b f n r t v
+
+- [ ] NonEscapeCharacter ::
+ - [ ] SourceCharacter but not one of EscapeCharacter or LineTerminator
+
+- [ ] EscapeCharacter ::
+ - [ ] SingleEscapeCharacter
+ - [ ] DecimalDigit
+x
+u
+
+- [ ] LegacyOctalEscapeSequence ::
+ - [ ] 0 [lookahead ∈ { 8, 9 }]
+ - [ ] NonZeroOctalDigit [lookahead ∉ OctalDigit]
+ - [ ] ZeroToThree OctalDigit [lookahead ∉ OctalDigit]
+ - [ ] FourToSeven OctalDigit
+ - [ ] ZeroToThree OctalDigit OctalDigit
+
+- [ ] NonZeroOctalDigit ::
+ - [ ] OctalDigit but not 0
+ - [ ] ZeroToThree :: one of
+ - [ ] 0 1 2 3
+ - [ ] FourToSeven :: one of
+ - [ ] 4 5 6 7
+ - [ ] NonOctalDecimalEscapeSequence :: one of
+ - [ ] 8 9
+
+- [ ] HexEscapeSequence ::
+ - [ ] x HexDigit HexDigit
+
+- [ ] UnicodeEscapeSequence ::
+ - [ ] u Hex4Digits
+ - [ ] u{ CodePoint }
+
+- [ ] Hex4Digits ::
+ - [ ] HexDigit HexDigit HexDigit HexDigit
+
+- [ ] RegularExpressionLiteral ::
+ - [ ] / RegularExpressionBody / RegularExpressionFlags
+
+- [ ] RegularExpressionBody ::
+ - [ ] RegularExpressionFirstChar RegularExpressionChars
+
+- [ ] RegularExpressionChars ::
+ - [ ] [empty]
+ - [ ] RegularExpressionChars RegularExpressionChar
+
+- [ ] RegularExpressionFirstChar ::
+ - [ ] RegularExpressionNonTerminator but not one of * or \ or / or [
+ - [ ] RegularExpressionBackslashSequence
+ - [ ] RegularExpressionClass
+
+- [ ] RegularExpressionChar ::
+ - [ ] RegularExpressionNonTerminator but not one of \ or / or [
+ - [ ] RegularExpressionBackslashSequence
+ - [ ] RegularExpressionClass
+
+- [ ] RegularExpressionBackslashSequence ::
+ - [ ] \ RegularExpressionNonTerminator
+
+- [ ] RegularExpressionNonTerminator ::
+ - [ ] SourceCharacter but not LineTerminator
+
+- [ ] RegularExpressionClass ::
+ - [ ] [ RegularExpressionClassChars ]
+
+- [ ] RegularExpressionClassChars ::
+ - [ ] [empty]
+ - [ ] RegularExpressionClassChars RegularExpressionClassChar
+
+- [ ] RegularExpressionClassChar ::
+ - [ ] RegularExpressionNonTerminator but not one of ] or \
+ - [ ] RegularExpressionBackslashSequence
+
+- [ ] RegularExpressionFlags ::
+ - [ ] [empty]
+ - [ ] RegularExpressionFlags IdentifierPartChar
+
+- [ ] Template ::
+ - [ ] NoSubstitutionTemplate
+ - [ ] TemplateHead
+
+- [ ] NoSubstitutionTemplate ::
+ - [ ] ` TemplateCharactersopt `
+
+- [ ] TemplateHead ::
+ - [ ] ` TemplateCharactersopt ${
+
+- [ ] TemplateSubstitutionTail ::
+ - [ ] TemplateMiddle
+ - [ ] TemplateTail
+
+- [ ] TemplateMiddle ::
+ - [ ] } TemplateCharactersopt ${
+
+- [ ] TemplateTail ::
+ - [ ] } TemplateCharactersopt `
+
+- [ ] TemplateCharacters ::
+ - [ ] TemplateCharacter TemplateCharactersopt
+
+- [ ] TemplateCharacter ::
+ - [ ] $ [lookahead ≠ {]
+ - [ ] \ TemplateEscapeSequence
+ - [ ] \ NotEscapeSequence
+ - [ ] LineContinuation
+ - [ ] LineTerminatorSequence
+ - [ ] SourceCharacter but not one of ` or \ or $ or LineTerminator
+
+- [ ] TemplateEscapeSequence ::
+ - [ ] CharacterEscapeSequence
+ - [ ] 0 [lookahead ∉ DecimalDigit]
+ - [ ] HexEscapeSequence
+ - [ ] UnicodeEscapeSequence
+
+- [ ] NotEscapeSequence ::
+ - [ ] 0 DecimalDigit
+ - [ ] DecimalDigit but not 0
+ - [ ] x [lookahead ∉ HexDigit]
+ - [ ] x HexDigit [lookahead ∉ HexDigit]
+ - [ ] u [lookahead ∉ HexDigit] [lookahead ≠ {]
+ - [ ] u HexDigit [lookahead ∉ HexDigit]
+ - [ ] u HexDigit HexDigit [lookahead ∉ HexDigit]
+ - [ ] u HexDigit HexDigit HexDigit [lookahead ∉ HexDigit]
+ - [ ] u { [lookahead ∉ HexDigit]
+ - [ ] u { NotCodePoint [lookahead ∉ HexDigit]
+ - [ ] u { CodePoint [lookahead ∉ HexDigit] [lookahead ≠ }]
+
+- [ ] NotCodePoint ::
+ - [ ] HexDigits[~Sep] but only if the MV of HexDigits > 0x10FFFF
+
+- [ ] CodePoint ::
+ - [ ] HexDigits[~Sep] but only if the MV of HexDigits ≤ 0x10FFFF
+
+# A.2 Expressions
+
+IdentifierReference[Yield, Await] :
+ - [ ] Identifier
+ - [ ] [~Yield] yield
+ - [ ] [~Await] await
+BindingIdentifier[Yield, Await] :
+ - [ ] Identifier
+ - [ ] yield
+ - [ ] await
+LabelIdentifier[Yield, Await] :
+ - [ ] Identifier
+ - [ ] [~Yield] yield
+ - [ ] [~Await] await
+Identifier :
+ - [ ] IdentifierName but not ReservedWord
+PrimaryExpression[Yield, Await] :
+ - [ ] this
+ - [ ] IdentifierReference[?Yield, ?Await]
+ - [ ] Literal
+ - [ ] ArrayLiteral[?Yield, ?Await]
+ - [ ] ObjectLiteral[?Yield, ?Await]
+ - [ ] FunctionExpression
+ - [ ] ClassExpression[?Yield, ?Await]
+ - [ ] GeneratorExpression
+ - [ ] AsyncFunctionExpression
+ - [ ] AsyncGeneratorExpression
+ - [ ] RegularExpressionLiteral
+ - [ ] TemplateLiteral[?Yield, ?Await, ~Tagged]
+ - [ ] CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
+CoverParenthesizedExpressionAndArrowParameterList[Yield, Await] :
+ - [ ] ( Expression[+In, ?Yield, ?Await] )
+ - [ ] ( Expression[+In, ?Yield, ?Await] , )
+ - [ ] ( )
+ - [ ] ( ... BindingIdentifier[?Yield, ?Await] )
+ - [ ] ( ... BindingPattern[?Yield, ?Await] )
+ - [ ] ( Expression[+In, ?Yield, ?Await] , ... BindingIdentifier[?Yield, ?Await] )
+ - [ ] ( Expression[+In, ?Yield, ?Await] , ... BindingPattern[?Yield, ?Await] )
+
+ - [ ] When processing an instance of the production
+ - [ ] PrimaryExpression[Yield, Await] : CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
+the interpretation of CoverParenthesizedExpressionAndArrowParameterList is refined using the following grammar:
+ParenthesizedExpression[Yield, Await] :
+ - [ ] ( Expression[+In, ?Yield, ?Await] )
+
+
+Literal :
+ - [ ] NullLiteral
+ - [ ] BooleanLiteral
+ - [ ] NumericLiteral
+ - [ ] StringLiteral
+ArrayLiteral[Yield, Await] :
+ - [ ] [ Elisionopt ]
+ - [ ] [ ElementList[?Yield, ?Await] ]
+ - [ ] [ ElementList[?Yield, ?Await] , Elisionopt ]
+ElementList[Yield, Await] :
+ - [ ] Elisionopt AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] Elisionopt SpreadElement[?Yield, ?Await]
+ - [ ] ElementList[?Yield, ?Await] , Elisionopt AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] ElementList[?Yield, ?Await] , Elisionopt SpreadElement[?Yield, ?Await]
+Elision :
+,
+ - [ ] Elision ,
+SpreadElement[Yield, Await] :
+ - [ ] ... AssignmentExpression[+In, ?Yield, ?Await]
+ObjectLiteral[Yield, Await] :
+ - [ ] { }
+ - [ ] { PropertyDefinitionList[?Yield, ?Await] }
+ - [ ] { PropertyDefinitionList[?Yield, ?Await] , }
+PropertyDefinitionList[Yield, Await] :
+ - [ ] PropertyDefinition[?Yield, ?Await]
+ - [ ] PropertyDefinitionList[?Yield, ?Await] , PropertyDefinition[?Yield, ?Await]
+PropertyDefinition[Yield, Await] :
+ - [ ] IdentifierReference[?Yield, ?Await]
+ - [ ] CoverInitializedName[?Yield, ?Await]
+ - [ ] PropertyName[?Yield, ?Await] : AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] MethodDefinition[?Yield, ?Await]
+ - [ ] ... AssignmentExpression[+In, ?Yield, ?Await]
+PropertyName[Yield, Await] :
+ - [ ] LiteralPropertyName
+ - [ ] ComputedPropertyName[?Yield, ?Await]
+LiteralPropertyName :
+ - [ ] IdentifierName
+ - [ ] StringLiteral
+ - [ ] NumericLiteral
+ComputedPropertyName[Yield, Await] :
+ - [ ] [ AssignmentExpression[+In, ?Yield, ?Await] ]
+CoverInitializedName[Yield, Await] :
+ - [ ] IdentifierReference[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]
+Initializer[In, Yield, Await] :
+ - [ ] = AssignmentExpression[?In, ?Yield, ?Await]
+TemplateLiteral[Yield, Await, Tagged] :
+ - [ ] NoSubstitutionTemplate
+ - [ ] SubstitutionTemplate[?Yield, ?Await, ?Tagged]
+SubstitutionTemplate[Yield, Await, Tagged] :
+ - [ ] TemplateHead Expression[+In, ?Yield, ?Await] TemplateSpans[?Yield, ?Await, ?Tagged]
+TemplateSpans[Yield, Await, Tagged] :
+ - [ ] TemplateTail
+ - [ ] TemplateMiddleList[?Yield, ?Await, ?Tagged] TemplateTail
+TemplateMiddleList[Yield, Await, Tagged] :
+ - [ ] TemplateMiddle Expression[+In, ?Yield, ?Await]
+ - [ ] TemplateMiddleList[?Yield, ?Await, ?Tagged] TemplateMiddle Expression[+In, ?Yield, ?Await]
+MemberExpression[Yield, Await] :
+ - [ ] PrimaryExpression[?Yield, ?Await]
+ - [ ] MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]
+ - [ ] MemberExpression[?Yield, ?Await] . IdentifierName
+ - [ ] MemberExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
+ - [ ] SuperProperty[?Yield, ?Await]
+ - [ ] MetaProperty
+ - [ ] new MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
+ - [ ] MemberExpression[?Yield, ?Await] . PrivateIdentifier
+SuperProperty[Yield, Await] :
+ - [ ] super [ Expression[+In, ?Yield, ?Await] ]
+ - [ ] super . IdentifierName
+MetaProperty :
+ - [ ] NewTarget
+ - [ ] ImportMeta
+NewTarget :
+ - [ ] new . target
+ImportMeta :
+ - [ ] import . meta
+NewExpression[Yield, Await] :
+ - [ ] MemberExpression[?Yield, ?Await]
+ - [ ] new NewExpression[?Yield, ?Await]
+CallExpression[Yield, Await] :
+ - [ ] CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await]
+ - [ ] SuperCall[?Yield, ?Await]
+ - [ ] ImportCall[?Yield, ?Await]
+ - [ ] CallExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
+ - [ ] CallExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]
+ - [ ] CallExpression[?Yield, ?Await] . IdentifierName
+ - [ ] CallExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
+ - [ ] CallExpression[?Yield, ?Await] . PrivateIdentifier
+
+ - [ ] When processing an instance of the production
+ - [ ] CallExpression[Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await]
+the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following grammar:
+CallMemberExpression[Yield, Await] :
+ - [ ] MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
+
+
+SuperCall[Yield, Await] :
+ - [ ] super Arguments[?Yield, ?Await]
+ImportCall[Yield, Await] :
+ - [ ] import ( AssignmentExpression[+In, ?Yield, ?Await] ,opt )
+ - [ ] import ( AssignmentExpression[+In, ?Yield, ?Await] , AssignmentExpression[+In, ?Yield, ?Await] ,opt )
+Arguments[Yield, Await] :
+ - [ ] ( )
+ - [ ] ( ArgumentList[?Yield, ?Await] )
+ - [ ] ( ArgumentList[?Yield, ?Await] , )
+ArgumentList[Yield, Await] :
+ - [ ] AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] ... AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] ArgumentList[?Yield, ?Await] , AssignmentExpression[+In, ?Yield, ?Await]
+ - [ ] ArgumentList[?Yield, ?Await] , ... AssignmentExpression[+In, ?Yield, ?Await]
+OptionalExpression[Yield, Await] :
+ - [ ] MemberExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+ - [ ] CallExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+ - [ ] OptionalExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+OptionalChain[Yield, Await] :
+ - [ ] ?. Arguments[?Yield, ?Await]
+ - [ ] ?. [ Expression[+In, ?Yield, ?Await] ]
+ - [ ] ?. IdentifierName
+ - [ ] ?. TemplateLiteral[?Yield, ?Await, +Tagged]
+ - [ ] ?. PrivateIdentifier
+ - [ ] OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await]
+ - [ ] OptionalChain[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ]
+ - [ ] OptionalChain[?Yield, ?Await] . IdentifierName
+ - [ ] OptionalChain[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
+ - [ ] OptionalChain[?Yield, ?Await] . PrivateIdentifier
+LeftHandSideExpression[Yield, Await] :
+ - [ ] NewExpression[?Yield, ?Await]
+ - [ ] CallExpression[?Yield, ?Await]
+ - [ ] OptionalExpression[?Yield, ?Await]
+UpdateExpression[Yield, Await] :
+ - [ ] LeftHandSideExpression[?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] ++
+ - [ ] LeftHandSideExpression[?Yield, ?Await] [no LineTerminator here] --
+ - [ ] ++ UnaryExpression[?Yield, ?Await]
+ - [ ] -- UnaryExpression[?Yield, ?Await]
+UnaryExpression[Yield, Await] :
+ - [ ] UpdateExpression[?Yield, ?Await]
+ - [ ] delete UnaryExpression[?Yield, ?Await]
+ - [ ] void UnaryExpression[?Yield, ?Await]
+ - [ ] typeof UnaryExpression[?Yield, ?Await]
+ - [ ] + UnaryExpression[?Yield, ?Await]
+ - [ ] - UnaryExpression[?Yield, ?Await]
+ - [ ] ~ UnaryExpression[?Yield, ?Await]
+ - [ ] ! UnaryExpression[?Yield, ?Await]
+ - [ ] [+Await] CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield]
+CoverAwaitExpressionAndAwaitUsingDeclarationHead[Yield] :
+ - [ ] await UnaryExpression[?Yield, +Await]
+
+ - [ ] When processing an instance of the production
+ - [ ] UnaryExpression[Yield, Await] : [+Await] CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield]
+the interpretation of CoverAwaitExpressionAndAwaitUsingDeclarationHead is refined using the following grammar:
+AwaitExpression[Yield] :
+ - [ ] await UnaryExpression[?Yield, +Await]
+
+
+ExponentiationExpression[Yield, Await] :
+ - [ ] UnaryExpression[?Yield, ?Await]
+ - [ ] UpdateExpression[?Yield, ?Await] ** ExponentiationExpression[?Yield, ?Await]
+MultiplicativeExpression[Yield, Await] :
+ - [ ] ExponentiationExpression[?Yield, ?Await]
+ - [ ] MultiplicativeExpression[?Yield, ?Await] MultiplicativeOperator ExponentiationExpression[?Yield, ?Await]
+ - [ ] MultiplicativeOperator : one of
+ - [ ] * / %
+AdditiveExpression[Yield, Await] :
+ - [ ] MultiplicativeExpression[?Yield, ?Await]
+ - [ ] AdditiveExpression[?Yield, ?Await] + MultiplicativeExpression[?Yield, ?Await]
+ - [ ] AdditiveExpression[?Yield, ?Await] - MultiplicativeExpression[?Yield, ?Await]
+ShiftExpression[Yield, Await] :
+ - [ ] AdditiveExpression[?Yield, ?Await]
+ - [ ] ShiftExpression[?Yield, ?Await] << AdditiveExpression[?Yield, ?Await]
+ - [ ] ShiftExpression[?Yield, ?Await] >> AdditiveExpression[?Yield, ?Await]
+ - [ ] ShiftExpression[?Yield, ?Await] >>> AdditiveExpression[?Yield, ?Await]
+RelationalExpression[In, Yield, Await] :
+ - [ ] ShiftExpression[?Yield, ?Await]
+ - [ ] RelationalExpression[?In, ?Yield, ?Await] < ShiftExpression[?Yield, ?Await]
+ - [ ] RelationalExpression[?In, ?Yield, ?Await] > ShiftExpression[?Yield, ?Await]
+ - [ ] RelationalExpression[?In, ?Yield, ?Await] <= ShiftExpression[?Yield, ?Await]
+ - [ ] RelationalExpression[?In, ?Yield, ?Await] >= ShiftExpression[?Yield, ?Await]
+ - [ ] RelationalExpression[?In, ?Yield, ?Await] instanceof ShiftExpression[?Yield, ?Await]
+ - [ ] [+In] RelationalExpression[+In, ?Yield, ?Await] in ShiftExpression[?Yield, ?Await]
+ - [ ] [+In] PrivateIdentifier in ShiftExpression[?Yield, ?Await]
+EqualityExpression[In, Yield, Await] :
+ - [ ] RelationalExpression[?In, ?Yield, ?Await]
+ - [ ] EqualityExpression[?In, ?Yield, ?Await] == RelationalExpression[?In, ?Yield, ?Await]
+ - [ ] EqualityExpression[?In, ?Yield, ?Await] != RelationalExpression[?In, ?Yield, ?Await]
+ - [ ] EqualityExpression[?In, ?Yield, ?Await] === RelationalExpression[?In, ?Yield, ?Await]
+ - [ ] EqualityExpression[?In, ?Yield, ?Await] !== RelationalExpression[?In, ?Yield, ?Await]
+BitwiseANDExpression[In, Yield, Await] :
+ - [ ] EqualityExpression[?In, ?Yield, ?Await]
+ - [ ] BitwiseANDExpression[?In, ?Yield, ?Await] & EqualityExpression[?In, ?Yield, ?Await]
+BitwiseXORExpression[In, Yield, Await] :
+ - [ ] BitwiseANDExpression[?In, ?Yield, ?Await]
+ - [ ] BitwiseXORExpression[?In, ?Yield, ?Await] ^ BitwiseANDExpression[?In, ?Yield, ?Await]
+BitwiseORExpression[In, Yield, Await] :
+ - [ ] BitwiseXORExpression[?In, ?Yield, ?Await]
+ - [ ] BitwiseORExpression[?In, ?Yield, ?Await] | BitwiseXORExpression[?In, ?Yield, ?Await]
+LogicalANDExpression[In, Yield, Await] :
+ - [ ] BitwiseORExpression[?In, ?Yield, ?Await]
+ - [ ] LogicalANDExpression[?In, ?Yield, ?Await] && BitwiseORExpression[?In, ?Yield, ?Await]
+LogicalORExpression[In, Yield, Await] :
+ - [ ] LogicalANDExpression[?In, ?Yield, ?Await]
+ - [ ] LogicalORExpression[?In, ?Yield, ?Await] || LogicalANDExpression[?In, ?Yield, ?Await]
+CoalesceExpression[In, Yield, Await] :
+ - [ ] CoalesceExpressionHead[?In, ?Yield, ?Await] ?? BitwiseORExpression[?In, ?Yield, ?Await]
+CoalesceExpressionHead[In, Yield, Await] :
+ - [ ] CoalesceExpression[?In, ?Yield, ?Await]
+ - [ ] BitwiseORExpression[?In, ?Yield, ?Await]
+ShortCircuitExpression[In, Yield, Await] :
+ - [ ] LogicalORExpression[?In, ?Yield, ?Await]
+ - [ ] CoalesceExpression[?In, ?Yield, ?Await]
+ConditionalExpression[In, Yield, Await] :
+ - [ ] ShortCircuitExpression[?In, ?Yield, ?Await]
+ - [ ] ShortCircuitExpression[?In, ?Yield, ?Await] ? AssignmentExpression[+In, ?Yield, ?Await] : AssignmentExpression[?In, ?Yield, ?Await]
+AssignmentExpression[In, Yield, Await] :
+ - [ ] ConditionalExpression[?In, ?Yield, ?Await]
+ - [ ] [+Yield] YieldExpression[?In, ?Await]
+ - [ ] ArrowFunction[?In, ?Yield, ?Await]
+ - [ ] AsyncArrowFunction[?In, ?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] = AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] AssignmentOperator AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] &&= AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] ||= AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] LeftHandSideExpression[?Yield, ?Await] ??= AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] AssignmentOperator : one of
+ - [ ] *= /= %= += -= <<= >>= >>>= &= ^= |= **=
+
+ - [ ] In certain circumstances when processing an instance of the production
+ - [ ] AssignmentExpression[In, Yield, Await] : LeftHandSideExpression[?Yield, ?Await] = AssignmentExpression[?In, ?Yield, ?Await]
+the interpretation of LeftHandSideExpression is refined using the following grammar:
+AssignmentPattern[Yield, Await] :
+ - [ ] ObjectAssignmentPattern[?Yield, ?Await]
+ - [ ] ArrayAssignmentPattern[?Yield, ?Await]
+ObjectAssignmentPattern[Yield, Await] :
+ - [ ] { }
+ - [ ] { AssignmentRestProperty[?Yield, ?Await] }
+ - [ ] { AssignmentPropertyList[?Yield, ?Await] }
+ - [ ] { AssignmentPropertyList[?Yield, ?Await] , AssignmentRestProperty[?Yield, ?Await]opt }
+ArrayAssignmentPattern[Yield, Await] :
+ - [ ] [ Elisionopt AssignmentRestElement[?Yield, ?Await]opt ]
+ - [ ] [ AssignmentElementList[?Yield, ?Await] ]
+ - [ ] [ AssignmentElementList[?Yield, ?Await] , Elisionopt AssignmentRestElement[?Yield, ?Await]opt ]
+AssignmentRestProperty[Yield, Await] :
+ - [ ] ... DestructuringAssignmentTarget[?Yield, ?Await]
+AssignmentPropertyList[Yield, Await] :
+ - [ ] AssignmentProperty[?Yield, ?Await]
+ - [ ] AssignmentPropertyList[?Yield, ?Await] , AssignmentProperty[?Yield, ?Await]
+AssignmentElementList[Yield, Await] :
+ - [ ] AssignmentElisionElement[?Yield, ?Await]
+ - [ ] AssignmentElementList[?Yield, ?Await] , AssignmentElisionElement[?Yield, ?Await]
+AssignmentElisionElement[Yield, Await] :
+ - [ ] Elisionopt AssignmentElement[?Yield, ?Await]
+AssignmentProperty[Yield, Await] :
+ - [ ] IdentifierReference[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt
+ - [ ] PropertyName[?Yield, ?Await] : AssignmentElement[?Yield, ?Await]
+AssignmentElement[Yield, Await] :
+ - [ ] DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt
+AssignmentRestElement[Yield, Await] :
+ - [ ] ... DestructuringAssignmentTarget[?Yield, ?Await]
+DestructuringAssignmentTarget[Yield, Await] :
+ - [ ] LeftHandSideExpression[?Yield, ?Await]
+
+
+Expression[In, Yield, Await] :
+ - [ ] AssignmentExpression[?In, ?Yield, ?Await]
+ - [ ] Expression[?In, ?Yield, ?Await] , AssignmentExpression[?In, ?Yield, ?Await]
+
+# A.3 Statements
+
+Statement[Yield, Await, Return] :
+ - [ ] BlockStatement[?Yield, ?Await, ?Return]
+ - [ ] VariableStatement[?Yield, ?Await]
+ - [ ] EmptyStatement
+ - [ ] ExpressionStatement[?Yield, ?Await]
+ - [ ] IfStatement[?Yield, ?Await, ?Return]
+ - [ ] BreakableStatement[?Yield, ?Await, ?Return]
+ - [ ] ContinueStatement[?Yield, ?Await]
+ - [ ] BreakStatement[?Yield, ?Await]
+ - [ ] [+Return] ReturnStatement[?Yield, ?Await]
+ - [ ] WithStatement[?Yield, ?Await, ?Return]
+ - [ ] LabelledStatement[?Yield, ?Await, ?Return]
+ - [ ] ThrowStatement[?Yield, ?Await]
+ - [ ] TryStatement[?Yield, ?Await, ?Return]
+ - [ ] DebuggerStatement
+Declaration[Yield, Await] :
+ - [ ] HoistableDeclaration[?Yield, ?Await, ~Default]
+ - [ ] ClassDeclaration[?Yield, ?Await, ~Default]
+ - [ ] LexicalDeclaration[+In, ?Yield, ?Await]
+HoistableDeclaration[Yield, Await, Default] :
+ - [ ] FunctionDeclaration[?Yield, ?Await, ?Default]
+ - [ ] GeneratorDeclaration[?Yield, ?Await, ?Default]
+ - [ ] AsyncFunctionDeclaration[?Yield, ?Await, ?Default]
+ - [ ] AsyncGeneratorDeclaration[?Yield, ?Await, ?Default]
+BreakableStatement[Yield, Await, Return] :
+ - [ ] IterationStatement[?Yield, ?Await, ?Return]
+ - [ ] SwitchStatement[?Yield, ?Await, ?Return]
+BlockStatement[Yield, Await, Return] :
+ - [ ] Block[?Yield, ?Await, ?Return]
+Block[Yield, Await, Return] :
+ - [ ] { StatementList[?Yield, ?Await, ?Return]opt }
+StatementList[Yield, Await, Return] :
+ - [ ] StatementListItem[?Yield, ?Await, ?Return]
+ - [ ] StatementList[?Yield, ?Await, ?Return] StatementListItem[?Yield, ?Await, ?Return]
+StatementListItem[Yield, Await, Return] :
+ - [ ] Statement[?Yield, ?Await, ?Return]
+ - [ ] Declaration[?Yield, ?Await]
+LexicalDeclaration[In, Yield, Await] :
+ - [ ] LetOrConst BindingList[?In, ?Yield, ?Await, +Pattern] ;
+ - [ ] UsingDeclaration[?In, ?Yield, ?Await]
+ - [ ] [+Await] AwaitUsingDeclaration[?In, ?Yield]
+LetOrConst :
+ - [ ] let
+ - [ ] const
+UsingDeclaration[In, Yield, Await] :
+ - [ ] using [no LineTerminator here] BindingList[?In, ?Yield, ?Await, ~Pattern] ;
+AwaitUsingDeclaration[In, Yield] :
+ - [ ] CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield] [no LineTerminator here] BindingList[?In, ?Yield, +Await, ~Pattern] ;
+BindingList[In, Yield, Await, Pattern] :
+ - [ ] LexicalBinding[?In, ?Yield, ?Await, ?Pattern]
+ - [ ] BindingList[?In, ?Yield, ?Await, ?Pattern] , LexicalBinding[?In, ?Yield, ?Await, ?Pattern]
+LexicalBinding[In, Yield, Await, Pattern] :
+ - [ ] BindingIdentifier[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]opt
+ - [ ] [+Pattern] BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]
+
+ - [ ] When processing an instance of the production
+ - [ ] AwaitUsingDeclaration[In, Yield] : CoverAwaitExpressionAndAwaitUsingDeclarationHead[?Yield] [no LineTerminator here] BindingList[?In, ?Yield, +Await, ~Pattern] ;
+the interpretation of CoverAwaitExpressionAndAwaitUsingDeclarationHead is refined using the following grammar:
+AwaitUsingDeclarationHead :
+ - [ ] await [no LineTerminator here] using
+
+
+VariableStatement[Yield, Await] :
+ - [ ] var VariableDeclarationList[+In, ?Yield, ?Await] ;
+VariableDeclarationList[In, Yield, Await] :
+ - [ ] VariableDeclaration[?In, ?Yield, ?Await]
+ - [ ] VariableDeclarationList[?In, ?Yield, ?Await] , VariableDeclaration[?In, ?Yield, ?Await]
+VariableDeclaration[In, Yield, Await] :
+ - [ ] BindingIdentifier[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]opt
+ - [ ] BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]
+BindingPattern[Yield, Await] :
+ - [ ] ObjectBindingPattern[?Yield, ?Await]
+ - [ ] ArrayBindingPattern[?Yield, ?Await]
+ObjectBindingPattern[Yield, Await] :
+ - [ ] { }
+ - [ ] { BindingRestProperty[?Yield, ?Await] }
+ - [ ] { BindingPropertyList[?Yield, ?Await] }
+ - [ ] { BindingPropertyList[?Yield, ?Await] , BindingRestProperty[?Yield, ?Await]opt }
+ArrayBindingPattern[Yield, Await] :
+ - [ ] [ Elisionopt BindingRestElement[?Yield, ?Await]opt ]
+ - [ ] [ BindingElementList[?Yield, ?Await] ]
+ - [ ] [ BindingElementList[?Yield, ?Await] , Elisionopt BindingRestElement[?Yield, ?Await]opt ]
+BindingRestProperty[Yield, Await] :
+ - [ ] ... BindingIdentifier[?Yield, ?Await]
+BindingPropertyList[Yield, Await] :
+ - [ ] BindingProperty[?Yield, ?Await]
+ - [ ] BindingPropertyList[?Yield, ?Await] , BindingProperty[?Yield, ?Await]
+BindingElementList[Yield, Await] :
+ - [ ] BindingElisionElement[?Yield, ?Await]
+ - [ ] BindingElementList[?Yield, ?Await] , BindingElisionElement[?Yield, ?Await]
+BindingElisionElement[Yield, Await] :
+ - [ ] Elisionopt BindingElement[?Yield, ?Await]
+BindingProperty[Yield, Await] :
+ - [ ] SingleNameBinding[?Yield, ?Await]
+ - [ ] PropertyName[?Yield, ?Await] : BindingElement[?Yield, ?Await]
+BindingElement[Yield, Await] :
+ - [ ] SingleNameBinding[?Yield, ?Await]
+ - [ ] BindingPattern[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt
+SingleNameBinding[Yield, Await] :
+ - [ ] BindingIdentifier[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt
+BindingRestElement[Yield, Await] :
+ - [ ] ... BindingIdentifier[?Yield, ?Await]
+ - [ ] ... BindingPattern[?Yield, ?Await]
+EmptyStatement :
+;
+ExpressionStatement[Yield, Await] :
+ - [ ] [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }] Expression[+In, ?Yield, ?Await] ;
+IfStatement[Yield, Await, Return] :
+ - [ ] if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else Statement[?Yield, ?Await, ?Return]
+ - [ ] if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [lookahead ≠ else]
+IterationStatement[Yield, Await, Return] :
+ - [ ] DoWhileStatement[?Yield, ?Await, ?Return]
+ - [ ] WhileStatement[?Yield, ?Await, ?Return]
+ - [ ] ForStatement[?Yield, ?Await, ?Return]
+ - [ ] ForInOfStatement[?Yield, ?Await, ?Return]
+DoWhileStatement[Yield, Await, Return] :
+ - [ ] do Statement[?Yield, ?Await, ?Return] while ( Expression[+In, ?Yield, ?Await] ) ;
+WhileStatement[Yield, Await, Return] :
+ - [ ] while ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ForStatement[Yield, Await, Return] :
+ - [ ] for ( [lookahead ≠ let [] Expression[~In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( var VariableDeclarationList[~In, ?Yield, ?Await] ; Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( LexicalDeclaration[~In, ?Yield, ?Await] Expression[+In, ?Yield, ?Await]opt ; Expression[+In, ?Yield, ?Await]opt ) Statement[?Yield, ?Await, ?Return]
+ForInOfStatement[Yield, Await, Return] :
+ - [ ] for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( var ForBinding[?Yield, ?Await, +Pattern] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( ForDeclaration[?Yield, ?Await, ~Using] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] for ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] [+Await] for await ( var ForBinding[?Yield, ?Await, +Pattern] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ - [ ] [+Await] for await ( [lookahead ≠ using of] ForDeclaration[?Yield, ?Await, +Using] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+ForDeclaration[Yield, Await, Using] :
+ - [ ] LetOrConst ForBinding[?Yield, ?Await, +Pattern]
+ - [ ] [+Using] using [no LineTerminator here] ForBinding[?Yield, ?Await, ~Pattern]
+ - [ ] [+Using, +Await] await [no LineTerminator here] using [no LineTerminator here] ForBinding[?Yield, +Await, ~Pattern]
+ForBinding[Yield, Await, Pattern] :
+ - [ ] BindingIdentifier[?Yield, ?Await]
+ - [ ] [+Pattern] BindingPattern[?Yield, ?Await]
+ContinueStatement[Yield, Await] :
+ - [ ] continue ;
+ - [ ] continue [no LineTerminator here] LabelIdentifier[?Yield, ?Await] ;
+BreakStatement[Yield, Await] :
+ - [ ] break ;
+ - [ ] break [no LineTerminator here] LabelIdentifier[?Yield, ?Await] ;
+ReturnStatement[Yield, Await] :
+ - [ ] return ;
+ - [ ] return [no LineTerminator here] Expression[+In, ?Yield, ?Await] ;
+WithStatement[Yield, Await, Return] :
+ - [ ] with ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return]
+SwitchStatement[Yield, Await, Return] :
+ - [ ] switch ( Expression[+In, ?Yield, ?Await] ) CaseBlock[?Yield, ?Await, ?Return]
+CaseBlock[Yield, Await, Return] :
+ - [ ] { CaseClauses[?Yield, ?Await, ?Return]opt }
+ - [ ] { CaseClauses[?Yield, ?Await, ?Return]opt DefaultClause[?Yield, ?Await, ?Return] CaseClauses[?Yield, ?Await, ?Return]opt }
+CaseClauses[Yield, Await, Return] :
+ - [ ] CaseClause[?Yield, ?Await, ?Return]
+ - [ ] CaseClauses[?Yield, ?Await, ?Return] CaseClause[?Yield, ?Await, ?Return]
+CaseClause[Yield, Await, Return] :
+ - [ ] case Expression[+In, ?Yield, ?Await] : StatementList[?Yield, ?Await, ?Return]opt
+DefaultClause[Yield, Await, Return] :
+ - [ ] default : StatementList[?Yield, ?Await, ?Return]opt
+LabelledStatement[Yield, Await, Return] :
+ - [ ] LabelIdentifier[?Yield, ?Await] : LabelledItem[?Yield, ?Await, ?Return]
+LabelledItem[Yield, Await, Return] :
+ - [ ] Statement[?Yield, ?Await, ?Return]
+ - [ ] FunctionDeclaration[?Yield, ?Await, ~Default]
+ThrowStatement[Yield, Await] :
+ - [ ] throw [no LineTerminator here] Expression[+In, ?Yield, ?Await] ;
+TryStatement[Yield, Await, Return] :
+ - [ ] try Block[?Yield, ?Await, ?Return] Catch[?Yield, ?Await, ?Return]
+ - [ ] try Block[?Yield, ?Await, ?Return] Finally[?Yield, ?Await, ?Return]
+ - [ ] try Block[?Yield, ?Await, ?Return] Catch[?Yield, ?Await, ?Return] Finally[?Yield, ?Await, ?Return]
+Catch[Yield, Await, Return] :
+ - [ ] catch ( CatchParameter[?Yield, ?Await] ) Block[?Yield, ?Await, ?Return]
+ - [ ] catch Block[?Yield, ?Await, ?Return]
+Finally[Yield, Await, Return] :
+ - [ ] finally Block[?Yield, ?Await, ?Return]
+CatchParameter[Yield, Await] :
+ - [ ] BindingIdentifier[?Yield, ?Await]
+ - [ ] BindingPattern[?Yield, ?Await]
+DebuggerStatement :
+ - [ ] debugger ;
+
+# A.4 Functions and Classes
+
+UniqueFormalParameters[Yield, Await] :
+ - [ ] FormalParameters[?Yield, ?Await]
+FormalParameters[Yield, Await] :
+ - [ ] [empty]
+ - [ ] FunctionRestParameter[?Yield, ?Await]
+ - [ ] FormalParameterList[?Yield, ?Await]
+ - [ ] FormalParameterList[?Yield, ?Await] ,
+ - [ ] FormalParameterList[?Yield, ?Await] , FunctionRestParameter[?Yield, ?Await]
+FormalParameterList[Yield, Await] :
+ - [ ] FormalParameter[?Yield, ?Await]
+ - [ ] FormalParameterList[?Yield, ?Await] , FormalParameter[?Yield, ?Await]
+FunctionRestParameter[Yield, Await] :
+ - [ ] BindingRestElement[?Yield, ?Await]
+FormalParameter[Yield, Await] :
+ - [ ] BindingElement[?Yield, ?Await]
+FunctionDeclaration[Yield, Await, Default] :
+ - [ ] function BindingIdentifier[?Yield, ?Await] ( FormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
+ - [ ] [+Default] function ( FormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
+FunctionExpression :
+ - [ ] function BindingIdentifier[~Yield, ~Await]opt ( FormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
+FunctionBody[Yield, Await] :
+ - [ ] FunctionStatementList[?Yield, ?Await]
+FunctionStatementList[Yield, Await] :
+ - [ ] StatementList[?Yield, ?Await, +Return]opt
+ArrowFunction[In, Yield, Await] :
+ - [ ] ArrowParameters[?Yield, ?Await] [no LineTerminator here] => ConciseBody[?In]
+ArrowParameters[Yield, Await] :
+ - [ ] BindingIdentifier[?Yield, ?Await]
+ - [ ] CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
+ConciseBody[In] :
+ - [ ] [lookahead ≠ {] ExpressionBody[?In, ~Await]
+ - [ ] { FunctionBody[~Yield, ~Await] }
+ExpressionBody[In, Await] :
+ - [ ] AssignmentExpression[?In, ~Yield, ?Await]
+
+ - [ ] When processing an instance of the production
+ - [ ] ArrowParameters[Yield, Await] : CoverParenthesizedExpressionAndArrowParameterList[?Yield, ?Await]
+the interpretation of CoverParenthesizedExpressionAndArrowParameterList is refined using the following grammar:
+ArrowFormalParameters[Yield, Await] :
+ - [ ] ( UniqueFormalParameters[?Yield, ?Await] )
+
+
+AsyncArrowFunction[In, Yield, Await] :
+ - [ ] async [no LineTerminator here] AsyncArrowBindingIdentifier[?Yield] [no LineTerminator here] => AsyncConciseBody[?In]
+ - [ ] CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] => AsyncConciseBody[?In]
+AsyncConciseBody[In] :
+ - [ ] [lookahead ≠ {] ExpressionBody[?In, +Await]
+ - [ ] { AsyncFunctionBody }
+AsyncArrowBindingIdentifier[Yield] :
+ - [ ] BindingIdentifier[?Yield, +Await]
+CoverCallExpressionAndAsyncArrowHead[Yield, Await] :
+ - [ ] MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
+
+ - [ ] When processing an instance of the production
+ - [ ] AsyncArrowFunction[In, Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] [no LineTerminator here] => AsyncConciseBody[?In]
+the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following grammar:
+AsyncArrowHead :
+ - [ ] async [no LineTerminator here] ArrowFormalParameters[~Yield, +Await]
+
+
+MethodDefinition[Yield, Await] :
+ - [ ] ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
+ - [ ] GeneratorMethod[?Yield, ?Await]
+ - [ ] AsyncMethod[?Yield, ?Await]
+ - [ ] AsyncGeneratorMethod[?Yield, ?Await]
+ - [ ] get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] }
+ - [ ] set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
+PropertySetParameterList :
+ - [ ] FormalParameter[~Yield, ~Await]
+GeneratorDeclaration[Yield, Await, Default] :
+ - [ ] function * BindingIdentifier[?Yield, ?Await] ( FormalParameters[+Yield, ~Await] ) { GeneratorBody }
+ - [ ] [+Default] function * ( FormalParameters[+Yield, ~Await] ) { GeneratorBody }
+GeneratorExpression :
+ - [ ] function * BindingIdentifier[+Yield, ~Await]opt ( FormalParameters[+Yield, ~Await] ) { GeneratorBody }
+GeneratorMethod[Yield, Await] :
+ - [ ] * ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[+Yield, ~Await] ) { GeneratorBody }
+GeneratorBody :
+ - [ ] FunctionBody[+Yield, ~Await]
+YieldExpression[In, Await] :
+ - [ ] yield
+ - [ ] yield [no LineTerminator here] AssignmentExpression[?In, +Yield, ?Await]
+ - [ ] yield [no LineTerminator here] * AssignmentExpression[?In, +Yield, ?Await]
+AsyncGeneratorDeclaration[Yield, Await, Default] :
+ - [ ] async [no LineTerminator here] function * BindingIdentifier[?Yield, ?Await] ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody }
+ - [ ] [+Default] async [no LineTerminator here] function * ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody }
+AsyncGeneratorExpression :
+ - [ ] async [no LineTerminator here] function * BindingIdentifier[+Yield, +Await]opt ( FormalParameters[+Yield, +Await] ) { AsyncGeneratorBody }
+AsyncGeneratorMethod[Yield, Await] :
+ - [ ] async [no LineTerminator here] * ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[+Yield, +Await] ) { AsyncGeneratorBody }
+AsyncGeneratorBody :
+ - [ ] FunctionBody[+Yield, +Await]
+AsyncFunctionDeclaration[Yield, Await, Default] :
+ - [ ] async [no LineTerminator here] function BindingIdentifier[?Yield, ?Await] ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody }
+ - [ ] [+Default] async [no LineTerminator here] function ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody }
+AsyncFunctionExpression :
+ - [ ] async [no LineTerminator here] function BindingIdentifier[~Yield, +Await]opt ( FormalParameters[~Yield, +Await] ) { AsyncFunctionBody }
+AsyncMethod[Yield, Await] :
+ - [ ] async [no LineTerminator here] ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, +Await] ) { AsyncFunctionBody }
+AsyncFunctionBody :
+ - [ ] FunctionBody[~Yield, +Await]
+AwaitExpression[Yield] :
+ - [ ] await UnaryExpression[?Yield, +Await]
+ClassDeclaration[Yield, Await, Default] :
+ - [ ] class BindingIdentifier[?Yield, ?Await] ClassTail[?Yield, ?Await]
+ - [ ] [+Default] class ClassTail[?Yield, ?Await]
+ClassExpression[Yield, Await] :
+ - [ ] class BindingIdentifier[?Yield, ?Await]opt ClassTail[?Yield, ?Await]
+ClassTail[Yield, Await] :
+ - [ ] ClassHeritage[?Yield, ?Await]opt { ClassBody[?Yield, ?Await]opt }
+ClassHeritage[Yield, Await] :
+ - [ ] extends LeftHandSideExpression[?Yield, ?Await]
+ClassBody[Yield, Await] :
+ - [ ] ClassElementList[?Yield, ?Await]
+ClassElementList[Yield, Await] :
+ - [ ] ClassElement[?Yield, ?Await]
+ - [ ] ClassElementList[?Yield, ?Await] ClassElement[?Yield, ?Await]
+ClassElement[Yield, Await] :
+ - [ ] MethodDefinition[?Yield, ?Await]
+ - [ ] static MethodDefinition[?Yield, ?Await]
+ - [ ] FieldDefinition[?Yield, ?Await] ;
+ - [ ] static FieldDefinition[?Yield, ?Await] ;
+ - [ ] ClassStaticBlock
+;
+FieldDefinition[Yield, Await] :
+ - [ ] ClassElementName[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt
+ClassElementName[Yield, Await] :
+ - [ ] PropertyName[?Yield, ?Await]
+ - [ ] PrivateIdentifier
+ClassStaticBlock :
+ - [ ] static { ClassStaticBlockBody }
+ClassStaticBlockBody :
+ - [ ] ClassStaticBlockStatementList
+ClassStaticBlockStatementList :
+ - [ ] StatementList[~Yield, +Await, ~Return]opt
+
+# A.5 Scripts and Modules
+
+Script :
+ - [ ] ScriptBodyopt
+ScriptBody :
+ - [ ] StatementList[~Yield, ~Await, ~Return]
+Module :
+ - [ ] ModuleBodyopt
+ModuleBody :
+ - [ ] ModuleItemList
+ModuleItemList :
+ - [ ] ModuleItem
+ - [ ] ModuleItemList ModuleItem
+ModuleItem :
+ - [ ] ImportDeclaration
+ - [ ] ExportDeclaration
+ - [ ] StatementListItem[~Yield, +Await, ~Return]
+ModuleExportName :
+ - [ ] IdentifierName
+ - [ ] StringLiteral
+ImportDeclaration :
+ - [ ] import ImportClause FromClause WithClauseopt ;
+ - [ ] import ModuleSpecifier WithClauseopt ;
+ImportClause :
+ - [ ] ImportedDefaultBinding
+ - [ ] NameSpaceImport
+ - [ ] NamedImports
+ - [ ] ImportedDefaultBinding , NameSpaceImport
+ - [ ] ImportedDefaultBinding , NamedImports
+ImportedDefaultBinding :
+ - [ ] ImportedBinding
+NameSpaceImport :
+ - [ ] * as ImportedBinding
+NamedImports :
+ - [ ] { }
+ - [ ] { ImportsList }
+ - [ ] { ImportsList , }
+FromClause :
+ - [ ] from ModuleSpecifier
+ImportsList :
+ - [ ] ImportSpecifier
+ - [ ] ImportsList , ImportSpecifier
+ImportSpecifier :
+ - [ ] ImportedBinding
+ - [ ] ModuleExportName as ImportedBinding
+ModuleSpecifier :
+ - [ ] StringLiteral
+ImportedBinding :
+ - [ ] BindingIdentifier[~Yield, +Await]
+WithClause :
+ - [ ] with { }
+ - [ ] with { WithEntries ,opt }
+WithEntries :
+ - [ ] AttributeKey : StringLiteral
+ - [ ] AttributeKey : StringLiteral , WithEntries
+AttributeKey :
+ - [ ] IdentifierName
+ - [ ] StringLiteral
+ExportDeclaration :
+ - [ ] export ExportFromClause FromClause WithClauseopt ;
+ - [ ] export NamedExports ;
+ - [ ] export VariableStatement[~Yield, +Await]
+ - [ ] export [lookahead ∉ { using, await }] Declaration[~Yield, +Await]
+ - [ ] export default HoistableDeclaration[~Yield, +Await, +Default]
+ - [ ] export default ClassDeclaration[~Yield, +Await, +Default]
+ - [ ] export default [lookahead ∉ { function, async [no LineTerminator here] function, class }] AssignmentExpression[+In, ~Yield, +Await] ;
+ExportFromClause :
+*
+ - [ ] * as ModuleExportName
+ - [ ] NamedExports
+NamedExports :
+ - [ ] { }
+ - [ ] { ExportsList }
+ - [ ] { ExportsList , }
+ExportsList :
+ - [ ] ExportSpecifier
+ - [ ] ExportsList , ExportSpecifier
+ExportSpecifier :
+ - [ ] ModuleExportName
+ - [ ] ModuleExportName as ModuleExportName
+
+# A.6 Number Conversions
+
+
+- [ ] StringNumericLiteral :::
+ - [ ] StrWhiteSpaceopt
+ - [ ] StrWhiteSpaceopt StrNumericLiteral StrWhiteSpaceopt
+
+- [ ] StrWhiteSpace :::
+ - [ ] StrWhiteSpaceChar StrWhiteSpaceopt
+
+- [ ] StrWhiteSpaceChar :::
+ - [ ] WhiteSpace
+ - [ ] LineTerminator
+
+- [ ] StrNumericLiteral :::
+ - [ ] StrDecimalLiteral
+ - [ ] NonDecimalIntegerLiteral[~Sep]
+
+- [ ] StrDecimalLiteral :::
+ - [ ] StrUnsignedDecimalLiteral
+ - [ ] + StrUnsignedDecimalLiteral
+ - [ ] - StrUnsignedDecimalLiteral
+
+- [ ] StrUnsignedDecimalLiteral :::
+ - [ ] Infinity
+ - [ ] DecimalDigits[~Sep] . DecimalDigits[~Sep]opt ExponentPart[~Sep]opt
+ - [ ] . DecimalDigits[~Sep] ExponentPart[~Sep]opt
+ - [ ] DecimalDigits[~Sep] ExponentPart[~Sep]opt
+
+ - [ ] All grammar symbols not explicitly defined by the StringNumericLiteral grammar have the definitions used in the Lexical Grammar for numeric literals.
+
+- [ ] StringIntegerLiteral :::
+ - [ ] StrWhiteSpaceopt
+ - [ ] StrWhiteSpaceopt StrIntegerLiteral StrWhiteSpaceopt
+
+- [ ] StrIntegerLiteral :::
+ - [ ] SignedInteger[~Sep]
+ - [ ] NonDecimalIntegerLiteral[~Sep]
+
+# A.7 Time Zone Offset String Format
+
+
+- [ ] UTCOffset :::
+ - [ ] ASCIISign Hour
+ - [ ] ASCIISign Hour HourSubcomponents[+Extended]
+ - [ ] ASCIISign Hour HourSubcomponents[~Extended]
+ - [ ] ASCIISign ::: one of
+ - [ ] + -
+
+- [ ] Hour :::
+ - [ ] 0 DecimalDigit
+ - [ ] 1 DecimalDigit
+20
+21
+22
+23
+
+- [ ] HourSubcomponents[Extended] :::
+ - [ ] TimeSeparator[?Extended] MinuteSecond
+ - [ ] TimeSeparator[?Extended] MinuteSecond TimeSeparator[?Extended] MinuteSecond TemporalDecimalFractionopt
+
+- [ ] TimeSeparator[Extended] :::
+[+Extended] :
+ - [ ] [~Extended] [empty]
+
+- [ ] MinuteSecond :::
+ - [ ] 0 DecimalDigit
+ - [ ] 1 DecimalDigit
+ - [ ] 2 DecimalDigit
+ - [ ] 3 DecimalDigit
+ - [ ] 4 DecimalDigit
+ - [ ] 5 DecimalDigit
+
+- [ ] TemporalDecimalFraction :::
+ - [ ] TemporalDecimalSeparator DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit DecimalDigit
+ - [ ] TemporalDecimalSeparator ::: one of
+ - [ ] . ,
+
+# A.8 Regular Expressions
+
+
+- [ ] Pattern[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+ - [ ] Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+
+- [ ] Disjunction[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+ - [ ] Alternative[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+ - [ ] Alternative[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] | Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+
+- [ ] Alternative[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+ - [ ] [empty]
+ - [ ] Alternative[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] Term[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+
+- [ ] Term[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+ - [ ] Assertion[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+ - [ ] Atom[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups]
+ - [ ] Atom[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] Quantifier
+
+- [ ] Assertion[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+^
+$
+\b
+\B
+ - [ ] (?= Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+ - [ ] (?! Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+ - [ ] (?<= Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+ - [ ] (?<! Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+
+- [ ] Quantifier ::
+ - [ ] QuantifierPrefix
+ - [ ] QuantifierPrefix ?
+
+- [ ] QuantifierPrefix ::
+*
++
+?
+ - [ ] { DecimalDigits[~Sep] }
+ - [ ] { DecimalDigits[~Sep] ,}
+ - [ ] { DecimalDigits[~Sep] , DecimalDigits[~Sep] }
+
+- [ ] Atom[UnicodeMode, UnicodeSetsMode, NamedCaptureGroups] ::
+ - [ ] PatternCharacter
+.
+ - [ ] \ AtomEscape[?UnicodeMode, ?NamedCaptureGroups]
+ - [ ] CharacterClass[?UnicodeMode, ?UnicodeSetsMode]
+ - [ ] ( GroupSpecifier[?UnicodeMode]opt Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+ - [ ] (? RegularExpressionModifiers : Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+ - [ ] (? RegularExpressionModifiers - RegularExpressionModifiers : Disjunction[?UnicodeMode, ?UnicodeSetsMode, ?NamedCaptureGroups] )
+
+- [ ] RegularExpressionModifiers ::
+ - [ ] [empty]
+ - [ ] RegularExpressionModifiers RegularExpressionModifier
+ - [ ] RegularExpressionModifier :: one of
+ - [ ] i m s
+ - [ ] SyntaxCharacter :: one of
+ - [ ] ^ $ \ . * + ? ( ) [ ] { } |
+
+- [ ] PatternCharacter ::
+ - [ ] SourceCharacter but not SyntaxCharacter
+
+- [ ] AtomEscape[UnicodeMode, NamedCaptureGroups] ::
+ - [ ] DecimalEscape
+ - [ ] CharacterClassEscape[?UnicodeMode]
+ - [ ] CharacterEscape[?UnicodeMode]
+ - [ ] [+NamedCaptureGroups] k GroupName[?UnicodeMode]
+
+- [ ] CharacterEscape[UnicodeMode] ::
+ - [ ] ControlEscape
+ - [ ] c AsciiLetter
+ - [ ] 0 [lookahead ∉ DecimalDigit]
+ - [ ] HexEscapeSequence
+ - [ ] RegExpUnicodeEscapeSequence[?UnicodeMode]
+ - [ ] IdentityEscape[?UnicodeMode]
+ - [ ] ControlEscape :: one of
+ - [ ] f n r t v
+
+- [ ] GroupSpecifier[UnicodeMode] ::
+ - [ ] ? GroupName[?UnicodeMode]
+
+- [ ] GroupName[UnicodeMode] ::
+ - [ ] < RegExpIdentifierName[?UnicodeMode] >
+
+- [ ] RegExpIdentifierName[UnicodeMode] ::
+ - [ ] RegExpIdentifierStart[?UnicodeMode]
+ - [ ] RegExpIdentifierName[?UnicodeMode] RegExpIdentifierPart[?UnicodeMode]
+
+- [ ] RegExpIdentifierStart[UnicodeMode] ::
+ - [ ] IdentifierStartChar
+ - [ ] \ RegExpUnicodeEscapeSequence[+UnicodeMode]
+ - [ ] [~UnicodeMode] UnicodeLeadSurrogate UnicodeTrailSurrogate
+
+- [ ] RegExpIdentifierPart[UnicodeMode] ::
+ - [ ] IdentifierPartChar
+ - [ ] \ RegExpUnicodeEscapeSequence[+UnicodeMode]
+ - [ ] [~UnicodeMode] UnicodeLeadSurrogate UnicodeTrailSurrogate
+
+- [ ] RegExpUnicodeEscapeSequence[UnicodeMode] ::
+ - [ ] [+UnicodeMode] u HexLeadSurrogate \u HexTrailSurrogate
+ - [ ] [+UnicodeMode] u HexLeadSurrogate
+ - [ ] [+UnicodeMode] u HexTrailSurrogate
+ - [ ] [+UnicodeMode] u HexNonSurrogate
+ - [ ] [~UnicodeMode] u Hex4Digits
+ - [ ] [+UnicodeMode] u{ CodePoint }
+
+- [ ] UnicodeLeadSurrogate ::
+ - [ ] any Unicode code point in the inclusive interval from U+D800 to U+DBFF
+
+- [ ] UnicodeTrailSurrogate ::
+ - [ ] any Unicode code point in the inclusive interval from U+DC00 to U+DFFF
+
+ - [ ] Each \u HexTrailSurrogate for which the choice of associated u HexLeadSurrogate is ambiguous shall be associated with the nearest possible u HexLeadSurrogate that would otherwise have no corresponding \u HexTrailSurrogate.
+
+
+
+- [ ] HexLeadSurrogate ::
+ - [ ] Hex4Digits but only if the MV of Hex4Digits is in the inclusive interval from 0xD800 to 0xDBFF
+
+- [ ] HexTrailSurrogate ::
+ - [ ] Hex4Digits but only if the MV of Hex4Digits is in the inclusive interval from 0xDC00 to 0xDFFF
+
+- [ ] HexNonSurrogate ::
+ - [ ] Hex4Digits but only if the MV of Hex4Digits is not in the inclusive interval from 0xD800 to 0xDFFF
+
+- [ ] IdentityEscape[UnicodeMode] ::
+ - [ ] [+UnicodeMode] SyntaxCharacter
+ - [ ] [+UnicodeMode] /
+ - [ ] [~UnicodeMode] SourceCharacter but not UnicodeIDContinue
+
+- [ ] DecimalEscape ::
+ - [ ] NonZeroDigit DecimalDigits[~Sep]opt [lookahead ∉ DecimalDigit]
+
+- [ ] CharacterClassEscape[UnicodeMode] ::
+d
+D
+s
+S
+w
+W
+ - [ ] [+UnicodeMode] p{ UnicodePropertyValueExpression }
+ - [ ] [+UnicodeMode] P{ UnicodePropertyValueExpression }
+
+- [ ] UnicodePropertyValueExpression ::
+ - [ ] UnicodePropertyName = UnicodePropertyValue
+ - [ ] LoneUnicodePropertyNameOrValue
+
+- [ ] UnicodePropertyName ::
+ - [ ] UnicodePropertyNameCharacters
+
+- [ ] UnicodePropertyNameCharacters ::
+ - [ ] UnicodePropertyNameCharacter UnicodePropertyNameCharactersopt
+
+- [ ] UnicodePropertyValue ::
+ - [ ] UnicodePropertyValueCharacters
+
+- [ ] LoneUnicodePropertyNameOrValue ::
+ - [ ] UnicodePropertyValueCharacters
+
+- [ ] UnicodePropertyValueCharacters ::
+ - [ ] UnicodePropertyValueCharacter UnicodePropertyValueCharactersopt
+
+- [ ] UnicodePropertyValueCharacter ::
+ - [ ] UnicodePropertyNameCharacter
+ - [ ] DecimalDigit
+
+- [ ] UnicodePropertyNameCharacter ::
+ - [ ] AsciiLetter
+_
+
+- [ ] CharacterClass[UnicodeMode, UnicodeSetsMode] ::
+ - [ ] [ [lookahead ≠ ^] ClassContents[?UnicodeMode, ?UnicodeSetsMode] ]
+ - [ ] [^ ClassContents[?UnicodeMode, ?UnicodeSetsMode] ]
+
+- [ ] ClassContents[UnicodeMode, UnicodeSetsMode] ::
+ - [ ] [empty]
+ - [ ] [~UnicodeSetsMode] NonemptyClassRanges[?UnicodeMode]
+ - [ ] [+UnicodeSetsMode] ClassSetExpression
+
+- [ ] NonemptyClassRanges[UnicodeMode] ::
+ - [ ] ClassAtom[?UnicodeMode]
+ - [ ] ClassAtom[?UnicodeMode] NonemptyClassRangesNoDash[?UnicodeMode]
+ - [ ] ClassAtom[?UnicodeMode] - ClassAtom[?UnicodeMode] ClassContents[?UnicodeMode, ~UnicodeSetsMode]
+
+- [ ] NonemptyClassRangesNoDash[UnicodeMode] ::
+ - [ ] ClassAtom[?UnicodeMode]
+ - [ ] ClassAtomNoDash[?UnicodeMode] NonemptyClassRangesNoDash[?UnicodeMode]
+ - [ ] ClassAtomNoDash[?UnicodeMode] - ClassAtom[?UnicodeMode] ClassContents[?UnicodeMode, ~UnicodeSetsMode]
+
+- [ ] ClassAtom[UnicodeMode] ::
+-
+ - [ ] ClassAtomNoDash[?UnicodeMode]
+
+- [ ] ClassAtomNoDash[UnicodeMode] ::
+ - [ ] SourceCharacter but not one of \ or ] or -
+ - [ ] \ ClassEscape[?UnicodeMode]
+
+- [ ] ClassEscape[UnicodeMode] ::
+b
+ - [ ] [+UnicodeMode] -
+ - [ ] CharacterClassEscape[?UnicodeMode]
+ - [ ] CharacterEscape[?UnicodeMode]
+
+- [ ] ClassSetExpression ::
+ - [ ] ClassUnion
+ - [ ] ClassIntersection
+ - [ ] ClassSubtraction
+
+- [ ] ClassUnion ::
+ - [ ] ClassSetRange ClassUnionopt
+ - [ ] ClassSetOperand ClassUnionopt
+
+- [ ] ClassIntersection ::
+ - [ ] ClassSetOperand && [lookahead ≠ &] ClassSetOperand
+ - [ ] ClassIntersection && [lookahead ≠ &] ClassSetOperand
+
+- [ ] ClassSubtraction ::
+ - [ ] ClassSetOperand -- ClassSetOperand
+ - [ ] ClassSubtraction -- ClassSetOperand
+
+- [ ] ClassSetRange ::
+ - [ ] ClassSetCharacter - ClassSetCharacter
+
+- [ ] ClassSetOperand ::
+ - [ ] NestedClass
+ - [ ] ClassStringDisjunction
+ - [ ] ClassSetCharacter
+
+- [ ] NestedClass ::
+ - [ ] [ [lookahead ≠ ^] ClassContents[+UnicodeMode, +UnicodeSetsMode] ]
+ - [ ] [^ ClassContents[+UnicodeMode, +UnicodeSetsMode] ]
+ - [ ] \ CharacterClassEscape[+UnicodeMode]
+
+- [ ] ClassStringDisjunction ::
+ - [ ] \q{ ClassStringDisjunctionContents }
+
+- [ ] ClassStringDisjunctionContents ::
+ - [ ] ClassString
+ - [ ] ClassString | ClassStringDisjunctionContents
+
+- [ ] ClassString ::
+ - [ ] [empty]
+ - [ ] NonEmptyClassString
+
+- [ ] NonEmptyClassString ::
+ - [ ] ClassSetCharacter NonEmptyClassStringopt
+
+- [ ] ClassSetCharacter ::
+ - [ ] [lookahead ∉ ClassSetReservedDoublePunctuator] SourceCharacter but not ClassSetSyntaxCharacter
+ - [ ] \ CharacterEscape[+UnicodeMode]
+ - [ ] \ ClassSetReservedPunctuator
+\b
+ - [ ] ClassSetReservedDoublePunctuator :: one of
+ - [ ] && !! ## $$ %% ** ++ ,, .. :: ;; << == >> ?? @@ ^^ `` ~~
+ - [ ] ClassSetSyntaxCharacter :: one of
+ - [ ] ( ) [ ] { } / - \ |
+ - [ ] ClassSetReservedPunctuator :: one of
+ - [ ] & - ! # % , : ; < = > @ ` ~
diff --git a/src/web/component/navigation/index.css b/src/web/component/navigation/index.css
new file mode 100644
index 0000000..836e3b5
--- /dev/null
+++ b/src/web/component/navigation/index.css
@@ -0,0 +1,66 @@
+#toggle {
+ --radius-open: 200px;
+
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ width: 45px;
+ height: 45px;
+ border-radius: 100%;
+ background: red;
+ z-index: 500;
+ cursor: pointer;
+ text-align: center;
+ align-content: center;
+ transition:
+ border-radius 0.1s ease,
+ right 0.1s ease,
+ bottom 0.1s ease,
+ width 0.1s ease,
+ height 0.1s ease;
+
+ .bi {
+ baseline-shift: -2px;
+ }
+
+ .items {
+ position: relative;
+ top: 0px;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ }
+
+ .item {
+ display: none;
+ width: 45px;
+ height: 45px;
+ border-radius: 100%;
+ background: blue;
+ position: absolute;
+ right: calc(cos(18deg * var(--index)) * var(--radius-open));
+ bottom: calc(sin(18deg * var(--index)) * var(--radius-open));
+ font-size: calc(var(--index) * 1px);
+ }
+
+ .item {
+ --index: sibling-index();
+ }
+}
+
+#toggle.open {
+ width: var(--radius-open);
+ height: var(--radius-open);
+ bottom: 0px;
+ right: 0px;
+ border-radius: 0px;
+ border-top-left-radius: 250px;
+
+ .bi {
+ display: none;
+ }
+
+ .item {
+ display: block;
+ }
+}
diff --git a/src/web/component/navigation/index.html b/src/web/component/navigation/index.html
new file mode 100644
index 0000000..b3b5ce0
--- /dev/null
+++ b/src/web/component/navigation/index.html
@@ -0,0 +1,11 @@
+<div id="toggle">
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-three-dots-vertical" viewBox="0 0 16 16">
+ <path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
+ </svg>
+ <div class="items">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ </div>
+</div>
diff --git a/src/web/component/navigation/index.js b/src/web/component/navigation/index.js
new file mode 100644
index 0000000..2c996a0
--- /dev/null
+++ b/src/web/component/navigation/index.js
@@ -0,0 +1,11 @@
+
+const toggle = Z.by_id('toggle');
+
+toggle.onclick = (event) => {
+ toggle.classList.toggle('open');
+ event.stopPropagation();
+};
+
+document.addEventListener('click', () => {
+ toggle.classList.remove('open');
+});
diff --git a/src/web/component/navigation/root.zig b/src/web/component/navigation/root.zig
new file mode 100644
index 0000000..919cddb
--- /dev/null
+++ b/src/web/component/navigation/root.zig
@@ -0,0 +1,8 @@
+const z = @import("z");
+
+pub const component: z.Component = .{
+ .name = .navigation,
+ .body = @embedFile("index.html"),
+ .style = @embedFile("index.css"),
+ .script = @embedFile("index.js"),
+};
diff --git a/src/web/component/root.zig b/src/web/component/root.zig
index 226f3f7..ba2f565 100644
--- a/src/web/component/root.zig
+++ b/src/web/component/root.zig
@@ -3,4 +3,5 @@ const z = @import("z");
pub const components: []const z.Component = &.{
@import("image/root.zig").component,
@import("timeline/root.zig").component,
+ @import("navigation/root.zig").component,
};
diff --git a/src/web/index.css b/src/web/index.css
index 6b1927f..bcdecae 100644
--- a/src/web/index.css
+++ b/src/web/index.css
@@ -1,6 +1,7 @@
html {
height: 100vh;
width: 100vw;
+ overflow: hidden;
}
body {
diff --git a/src/web/index.html b/src/web/index.html
index d2bc6fe..bcbb165 100644
--- a/src/web/index.html
+++ b/src/web/index.html
@@ -1,2 +1,4 @@
<z-component type="timeline" id="timeline">
</z-component>
+<z-component type="navigation" id="navigation">
+</z-component>
diff --git a/src/z/js/parser/ast.zig b/src/z/js/parser/ast.zig
new file mode 100644
index 0000000..1fb8797
--- /dev/null
+++ b/src/z/js/parser/ast.zig
@@ -0,0 +1,674 @@
+pub const Loc = struct {
+ start: usize,
+ end: usize,
+};
+
+pub const Node = union(enum) {
+ program: Program,
+
+ stmt: Stmt,
+ expr: Expr,
+ pat: Pat,
+
+ decl: Decl,
+ literal: Lit,
+ property: Prop,
+ template: Template,
+ spread: Spread,
+ decorator: Decorator,
+
+ import_specifier: ImportSpecifier,
+ export_specifier: ExportSpecifier,
+ import_attr: ImportAttribute,
+ catch_clause: CatchClause,
+ switch_case: SwitchCase,
+};
+
+pub const Program = struct {
+ body: []const Node,
+ loc: Loc,
+};
+
+pub const StmtType = union(enum) {
+ block: BlockStmt,
+ empty: void,
+ debugger: void,
+ expr: ExprStmt,
+ @"if": IfStmt,
+ @"while": WhileStmt,
+ do_while: DoWhileStmt,
+ @"for": ForStmt,
+ for_in: ForInStmt,
+ for_of: ForOfStmt,
+ @"continue": ContinueStmt,
+ @"break": BreakStmt,
+ @"return": ReturnStmt,
+ @"switch": SwitchStmt,
+ throw: ThrowStmt,
+ @"try": TryStmt,
+ labelled: LabelledStmt,
+ with: WithStmt,
+ variable: VarStmt,
+ using: UsingStmt,
+ import: ImportDecl,
+ @"export": ExportDecl,
+};
+
+pub const Stmt = struct {
+ loc: Loc,
+ data: StmtType,
+};
+
+pub const ExprType = union(enum) {
+ identifier: []const u8,
+ this: void,
+ super: void,
+ null: void,
+ bool: bool,
+ number: f64,
+ string: []const u8,
+ regex: Regex,
+ array: ArrayExpr,
+ object: ObjectExpr,
+ func: FnExpr,
+ arrow: ArrowFn,
+ class: ClassExpr,
+ template: TemplateLit,
+ tagged_template: TaggedTemplate,
+ member: MemberExpr,
+ computed_member: ComputedMember,
+ call: CallExpr,
+ new: NewExpr,
+ chain: ChainExpr,
+ unary: UnaryExpr,
+ binary: BinaryExpr,
+ update: UpdateExpr,
+ conditional: IfExpr,
+ assign: AssignExpr,
+ sequence: SeqExpr,
+ spread: SpreadElem,
+ yield: YieldExpr,
+ await: AwaitExpr,
+ meta_prop: MetaProp,
+ parenthesized: *const Expr,
+ private_ident: []const u8,
+};
+
+pub const Expr = struct {
+ loc: Loc,
+ data: ExprType,
+};
+
+pub const PatType = union(enum) {
+ ident: []const u8,
+ object: ObjectPat,
+ array: ArrayPat,
+ assign: AssignPat,
+ rest: RestPat,
+};
+
+pub const Pat = struct {
+ loc: Loc,
+ data: PatType,
+};
+
+pub const DeclType = union(enum) {
+ @"fn": FnDecl,
+ class: ClassDecl,
+ @"var": VarDecl,
+ lexical: LexicalDecl,
+ using: UsingDecl,
+ import: ImportDecl,
+ @"export": ExportDecl,
+ export_default: ExportDefault,
+};
+
+pub const Decl = struct {
+ loc: Loc,
+ data: DeclType,
+};
+
+pub const LitType = union(enum) {
+ null: void,
+ bool: bool,
+ number: f64,
+ string: []const u8,
+ regex: Regex,
+ bigint: []const u8,
+};
+
+pub const Lit = struct {
+ loc: Loc,
+ data: LitType,
+};
+
+pub const Regex = struct {
+ pattern: []const u8,
+ flags: []const u8,
+};
+
+pub const PropType = union(enum) {
+ init: PropInit,
+ get: PropGet,
+ set: PropSet,
+ shorthand: []const u8,
+ spread: SpreadElem,
+ method: FnExpr,
+};
+
+pub const Prop = struct {
+ loc: Loc,
+ key: PropKey,
+ data: PropType,
+};
+
+pub const PropKey = union(enum) {
+ ident: []const u8,
+ string: []const u8,
+ number: f64,
+ computed: *const Expr,
+ private: []const u8,
+};
+
+pub const PropInit = struct {
+ key: PropKey,
+ value: *const Expr,
+};
+
+pub const PropGet = struct {
+ key: PropKey,
+ body: BlockStmt,
+};
+
+pub const PropSet = struct {
+ key: PropKey,
+ param: Pat,
+ body: BlockStmt,
+};
+
+pub const SpreadElem = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const BlockStmt = struct {
+ loc: Loc,
+ body: []const Stmt,
+};
+
+pub const ExprStmt = struct {
+ loc: Loc,
+ expr: *const Expr,
+};
+
+pub const IfStmt = struct {
+ loc: Loc,
+ condition: *const Expr,
+ consequent: *const Stmt,
+ alternate: ?*const Stmt,
+};
+
+pub const WhileStmt = struct {
+ loc: Loc,
+ condition: *const Expr,
+ body: *const Stmt,
+};
+
+pub const DoWhileStmt = struct {
+ loc: Loc,
+ body: *const Stmt,
+ condition: *const Expr,
+};
+
+pub const ForStmt = struct {
+ loc: Loc,
+ init: ?union(enum) {
+ expr: *const Expr,
+ decl: *const Decl,
+ },
+ condition: ?*const Expr,
+ update: ?*const Expr,
+ body: *const Stmt,
+};
+
+pub const ForInStmt = struct {
+ loc: Loc,
+ left: union(enum) {
+ expr: *const Expr,
+ decl: *const Decl,
+ },
+ right: *const Expr,
+ body: *const Stmt,
+};
+
+pub const ForOfStmt = struct {
+ loc: Loc,
+ await_token: bool,
+ left: union(enum) {
+ expr: *const Expr,
+ decl: *const Decl,
+ },
+ right: *const Expr,
+ body: *const Stmt,
+};
+
+pub const ContinueStmt = struct {
+ loc: Loc,
+ label: ?[]const u8,
+};
+
+pub const BreakStmt = struct {
+ loc: Loc,
+ label: ?[]const u8,
+};
+
+pub const ReturnStmt = struct {
+ loc: Loc,
+ arg: ?*const Expr,
+};
+
+pub const SwitchStmt = struct {
+ loc: Loc,
+ discriminant: *const Expr,
+ cases: []const SwitchCase,
+};
+
+pub const SwitchCase = struct {
+ loc: Loc,
+ condition: ?*const Expr,
+ consequent: []const Stmt,
+};
+
+pub const ThrowStmt = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const TryStmt = struct {
+ loc: Loc,
+ block: BlockStmt,
+ handler: ?CatchClause,
+ finalizer: ?BlockStmt,
+};
+
+pub const CatchClause = struct {
+ loc: Loc,
+ param: ?Pat,
+ body: BlockStmt,
+};
+
+pub const LabelledStmt = struct {
+ loc: Loc,
+ label: []const u8,
+ body: *const Stmt,
+};
+
+pub const WithStmt = struct {
+ loc: Loc,
+ obj: *const Expr,
+ body: *const Stmt,
+};
+
+pub const VarStmt = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+};
+
+pub const UsingStmt = struct {
+ loc: Loc,
+ decls: []const VarDeclarator,
+};
+
+pub const VarKind = enum {
+ @"var",
+ let,
+ @"const",
+};
+
+pub const VarDecl = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+};
+
+pub const LexicalDecl = struct {
+ loc: Loc,
+ kind: VarKind,
+ decls: []const VarDeclarator,
+};
+
+pub const UsingDecl = struct {
+ loc: Loc,
+ is_await: bool,
+ decls: []const VarDeclarator,
+};
+
+pub const VarDeclarator = struct {
+ loc: Loc,
+ id: Pat,
+ init: ?*const Expr,
+};
+
+pub const ObjectPat = struct {
+ loc: Loc,
+ props: []const PatProp,
+ rest: ?*const Pat,
+};
+
+pub const PatProp = union(enum) {
+ key_value: struct {
+ key: PropKey,
+ value: *const Pat,
+ },
+ shorthand: []const u8,
+ rest: *const Pat,
+};
+
+pub const ArrayPat = struct {
+ loc: Loc,
+ elems: []const ?Pat,
+ rest: ?*const Pat,
+};
+
+pub const AssignPat = struct {
+ loc: Loc,
+ left: *const Pat,
+ right: *const Expr,
+};
+
+pub const RestPat = struct {
+ loc: Loc,
+ arg: *const Pat,
+};
+
+pub const FnType = union(enum) {
+ normal: void,
+ generator: void,
+ async: void,
+ async_generator: void,
+};
+
+pub const FnDecl = struct {
+ loc: Loc,
+ fn_type: FnType,
+ id: ?[]const u8,
+ params: []const Pat,
+ body: BlockStmt,
+};
+
+pub const FnExpr = struct {
+ loc: Loc,
+ fn_type: FnType,
+ id: ?[]const u8,
+ params: []const Pat,
+ body: BlockStmt,
+};
+
+pub const ArrowFn = struct {
+ loc: Loc,
+ async_token: bool,
+ params: []const Pat,
+ body: union(enum) {
+ block: BlockStmt,
+ expr: *const Expr,
+ },
+};
+
+pub const ClassDecl = struct {
+ loc: Loc,
+ id: ?[]const u8,
+ super_class: ?*const Expr,
+ body: []const ClassElem,
+};
+
+pub const ClassExpr = struct {
+ loc: Loc,
+ id: ?[]const u8,
+ super_class: ?*const Expr,
+ body: []const ClassElem,
+};
+
+pub const ClassElem = union(enum) {
+ method: FnExpr,
+ get: PropGet,
+ set: PropSet,
+ field: FieldDef,
+ static_block: BlockStmt,
+ static_method: FnExpr,
+ static_get: PropGet,
+ static_set: PropSet,
+ static_field: FieldDef,
+};
+
+pub const FieldDef = struct {
+ loc: Loc,
+ key: PropKey,
+ value: ?*const Expr,
+};
+
+pub const ImportDecl = struct {
+ loc: Loc,
+ specifiers: []const ImportSpecifier,
+ source: []const u8,
+ attributes: []const ImportAttribute,
+};
+
+pub const ImportSpecifier = union(enum) {
+ default: []const u8,
+ namespace: []const u8,
+ named: struct { imported: []const u8, local: []const u8 },
+};
+
+pub const ImportAttribute = struct {
+ key: []const u8,
+ value: []const u8,
+};
+
+pub const ExportDecl = struct {
+ loc: Loc,
+ declaration: ?*const Decl,
+ specifiers: ?[]const ExportSpecifier,
+ source: ?[]const u8,
+};
+
+pub const ExportSpecifier = struct {
+ loc: Loc,
+ exported: []const u8,
+ local: []const u8,
+};
+
+pub const ExportDefault = struct {
+ loc: Loc,
+ declaration: *const Decl,
+};
+
+pub const ArrayExpr = struct {
+ loc: Loc,
+ elems: []const ?Expr,
+};
+
+pub const ObjectExpr = struct {
+ loc: Loc,
+ props: []const Prop,
+};
+
+pub const MemberExpr = struct {
+ loc: Loc,
+ obj: *const Expr,
+ prop: union(enum) {
+ ident: []const u8,
+ private: []const u8,
+ },
+};
+
+pub const ComputedMember = struct {
+ loc: Loc,
+ obj: *const Expr,
+ expr: *const Expr,
+};
+
+pub const CallExpr = struct {
+ loc: Loc,
+ callee: *const Expr,
+ args: []const Expr,
+ optional: bool,
+};
+
+pub const NewExpr = struct {
+ loc: Loc,
+ callee: *const Expr,
+ args: []const Expr,
+};
+
+pub const ChainExpr = struct {
+ loc: Loc,
+ expr: *const Expr,
+};
+
+pub const UnaryOp = enum {
+ @"-",
+ @"+",
+ @"!",
+ @"~",
+ typeof,
+ void,
+ delete,
+};
+
+pub const UnaryExpr = struct {
+ loc: Loc,
+ op: UnaryOp,
+ arg: *const Expr,
+ prefix: bool,
+};
+
+pub const BinaryOp = enum {
+ @"==",
+ @"!=",
+ @"===",
+ @"!==",
+ @"<",
+ @"<=",
+ @">",
+ @">=",
+ @"<<",
+ @">>",
+ @">>>",
+ @"+",
+ @"-",
+ @"*",
+ @"/",
+ @"%",
+ @"**",
+ @"|",
+ @"^",
+ @"&",
+ in,
+ instanceof,
+ @"||",
+ @"&&",
+ @"??",
+};
+
+pub const BinaryExpr = struct {
+ loc: Loc,
+ op: BinaryOp,
+ left: *const Expr,
+ right: *const Expr,
+};
+
+pub const UpdateOp = enum {
+ @"++",
+ @"--",
+};
+
+pub const UpdateExpr = struct {
+ loc: Loc,
+ op: UpdateOp,
+ arg: *const Expr,
+ prefix: bool,
+};
+
+pub const IfExpr = struct {
+ loc: Loc,
+ condition: *const Expr,
+ consequent: *const Expr,
+ alternate: *const Expr,
+};
+
+pub const AssignOp = enum {
+ @"=",
+ @"+=",
+ @"-=",
+ @"*=",
+ @"/=",
+ @"%=",
+ @"**=",
+ @"<<=",
+ @">>=",
+ @">>>=",
+ @"|=",
+ @"^=",
+ @"&=",
+ @"||=",
+ @"&&=",
+ @"??=",
+};
+
+pub const AssignExpr = struct {
+ loc: Loc,
+ op: AssignOp,
+ left: *const Expr,
+ right: *const Expr,
+};
+
+pub const SeqExpr = struct {
+ loc: Loc,
+ exprs: []const Expr,
+};
+
+pub const SpreadElem = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const YieldExpr = struct {
+ loc: Loc,
+ arg: ?*const Expr,
+ delegate: bool,
+};
+
+pub const AwaitExpr = struct {
+ loc: Loc,
+ arg: *const Expr,
+};
+
+pub const MetaProp = struct {
+ loc: Loc,
+ meta: []const u8,
+ prop: []const u8,
+};
+
+pub const TemplateLit = struct {
+ loc: Loc,
+ quasis: []const TemplateElem,
+ exprs: []const Expr,
+};
+
+pub const TemplateElem = struct {
+ loc: Loc,
+ value: []const u8,
+ tail: bool,
+};
+
+pub const TaggedTemplate = struct {
+ loc: Loc,
+ tag: *const Expr,
+ quasi: TemplateLit,
+};
+
+pub const Decorator = struct {
+ loc: Loc,
+ expr: *const Expr,
+};
diff --git a/src/z/js/parser/lexical_grammar.zig b/src/z/js/parser/lexical_grammar.zig
new file mode 100644
index 0000000..bd54e8f
--- /dev/null
+++ b/src/z/js/parser/lexical_grammar.zig
@@ -0,0 +1,1099 @@
+const std = @import("std");
+
+pub const Loc = struct {
+ start: usize,
+ end: usize,
+};
+
+pub const TokenType = enum {
+ // Keywords
+ await,
+ @"break",
+ case,
+ @"catch",
+ class,
+ @"const",
+ @"continue",
+ debugger,
+ default,
+ delete,
+ do,
+ @"else",
+ @"enum",
+ @"export",
+ extends,
+ false,
+ finally,
+ @"for",
+ function,
+ @"if",
+ import,
+ in,
+ instanceof,
+ let,
+ new,
+ null,
+ @"return",
+ super,
+ @"switch",
+ this,
+ throw,
+ true,
+ @"try",
+ typeof,
+ @"var",
+ void,
+ @"while",
+ with,
+ yield,
+ using,
+
+ // Identifiers and literals
+ identifier,
+ private_identifier,
+ number,
+ string,
+ bigint,
+ regex,
+ template_head,
+ template_middle,
+ template_tail,
+ no_sub_template,
+
+ // Punctuators
+ lbrace,
+ rbrace,
+ lparen,
+ rparen,
+ lbracket,
+ rbracket,
+ dot,
+ semicolon,
+ comma,
+ @"...",
+ @"<",
+ @">",
+ @"<=",
+ @">=",
+ @"==",
+ @"!=",
+ @"===",
+ @"!==",
+ @"+",
+ @"-",
+ @"*",
+ @"%",
+ @"**",
+ @"++",
+ @"--",
+ @"<<",
+ @">>",
+ @">>>",
+ @"&",
+ @"|",
+ @"^",
+ @"!",
+ @"~",
+ @"&&",
+ @"||",
+ @"??",
+ @"?",
+ @":",
+ @"=",
+ @"+=",
+ @"-=",
+ @"*=",
+ @"%=",
+ @"**=",
+ @"<<=",
+ @">>=",
+ @">>>=",
+ @"&=",
+ @"|=",
+ @"^=",
+ @"&&=",
+ @"||=",
+ @"??=",
+ @"=>",
+ @"/",
+ @"/=",
+ @"?.",
+ @".",
+ @"#",
+
+ // Special
+ eof,
+ unknown,
+
+ pub fn isKeyword(self: TokenType) bool {
+ return switch (self) {
+ .await,
+ .@"break",
+ .case,
+ .@"catch",
+ .class,
+ .@"const",
+ .@"continue",
+ .debugger,
+ .default,
+ .delete,
+ .do,
+ .@"else",
+ .@"enum",
+ .@"export",
+ .extends,
+ .false,
+ .finally,
+ .@"for",
+ .function,
+ .@"if",
+ .import,
+ .in,
+ .instanceof,
+ .let,
+ .new,
+ .null,
+ .@"return",
+ .super,
+ .@"switch",
+ .this,
+ .throw,
+ .true,
+ .@"try",
+ .typeof,
+ .@"var",
+ .void,
+ .@"while",
+ .with,
+ .yield,
+ .using,
+ => true,
+ else => false,
+ };
+ }
+
+ pub fn isIdentifier(self: TokenType) bool {
+ return self == .identifier or self.isKeyword();
+ }
+};
+
+pub const Token = struct {
+ kind: TokenType,
+ loc: Loc,
+ slice: []const u8,
+};
+
+pub const Lexer = struct {
+ source: []const u8,
+ pos: usize,
+
+ const Self = @This();
+
+ pub fn init(source: []const u8) Self {
+ return .{ .source = source, .pos = 0 };
+ }
+
+ pub fn tokenize(source: []const u8) []const Token {
+ var lexer = Self.init(source);
+ var tokens: [4096]Token = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const tok = lexer.next();
+ tokens[count] = tok;
+ count += 1;
+ if (tok.kind == .eof) break;
+ if (count >= tokens.len - 1) {
+ tokens[count] = .{ .kind = .eof, .loc = .{ .start = lexer.pos, .end = lexer.pos }, .slice = "" };
+ break;
+ }
+ }
+
+ return tokens[0..count];
+ }
+
+ fn ch(self: Self) ?u8 {
+ if (self.pos >= self.source.len) return null;
+ return self.source[self.pos];
+ }
+
+ fn chAt(self: Self, offset: usize) ?u8 {
+ const idx = self.pos + offset;
+ if (idx >= self.source.len) return null;
+ return self.source[idx];
+ }
+
+ fn advance(self: *Self) void {
+ if (self.pos < self.source.len) self.pos += 1;
+ }
+
+ fn isWhiteSpace(c: u8) bool {
+ return switch (c) {
+ ' ', '\t', 0x0B, 0x0C => true,
+ else => false,
+ };
+ }
+
+ fn isLineTerminator(c: u8) bool {
+ return switch (c) {
+ '\n', '\r' => true,
+ 0x2028, 0x2029 => true,
+ else => false,
+ };
+ }
+
+ fn isDigit(c: u8) bool {
+ return c >= '0' and c <= '9';
+ }
+
+ fn isHexDigit(c: u8) bool {
+ return switch (c) {
+ '0'...'9', 'a'...'f', 'A'...'F' => true,
+ else => false,
+ };
+ }
+
+ fn isOctalDigit(c: u8) bool {
+ return c >= '0' and c <= '7';
+ }
+
+ fn isIdStart(c: u8) bool {
+ return switch (c) {
+ 'a'...'z', 'A'...'Z', '_', '$' => true,
+ else => c > 0x7F,
+ };
+ }
+
+ fn isIdContinue(c: u8) bool {
+ return isIdStart(c) or isDigit(c) or c == 0x200C or c == 0x200D;
+ }
+
+ fn skipWhiteSpace(self: *Self) void {
+ while (self.ch()) |c| {
+ if (isWhiteSpace(c) or isLineTerminator(c)) {
+ self.advance();
+ } else {
+ break;
+ }
+ }
+ }
+
+ fn skipSingleLineComment(self: *Self) void {
+ self.advance();
+ self.advance();
+ while (self.ch()) |c| {
+ if (isLineTerminator(c)) break;
+ self.advance();
+ }
+ }
+
+ fn skipMultiLineComment(self: *Self) void {
+ self.advance();
+ self.advance();
+ while (self.ch()) |c| {
+ if (c == '*' and self.chAt(1)) |n| {
+ if (n == '/') {
+ self.advance();
+ self.advance();
+ return;
+ }
+ }
+ self.advance();
+ }
+ }
+
+ fn skipHashbang(self: *Self) void {
+ if (self.ch()) |c| {
+ if (c == '#') {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '!') {
+ self.advance();
+ while (self.ch()) |ch| {
+ if (isLineTerminator(ch)) break;
+ self.advance();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn scanString(self: *Self, quote: u8) Token {
+ const start = self.pos;
+ self.advance();
+
+ while (self.ch()) |c| {
+ if (c == quote) {
+ self.advance();
+ return .{
+ .kind = .string,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start + 1 .. self.pos - 1],
+ };
+ }
+ if (c == '\\') {
+ self.advance();
+ if (self.ch()) |_| self.advance();
+ } else if (isLineTerminator(c) and c != 0x2028 and c != 0x2029) {
+ break;
+ } else {
+ self.advance();
+ }
+ }
+
+ return .{
+ .kind = .string,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start + 1 .. self.pos],
+ };
+ }
+
+ fn scanTemplate(self: *Self, start_kind: TokenType) Token {
+ const start = self.pos;
+ if (start_kind == .template_middle or start_kind == .template_head) {
+ self.advance();
+ }
+ return self.scanTemplateRest(start);
+ }
+
+ fn scanTemplateRest(self: *Self, start: usize) Token {
+ while (self.ch()) |c| {
+ if (c == '`') {
+ self.advance();
+ return .{
+ .kind = .no_sub_template,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ if (c == '$' and self.chAt(1)) |n| {
+ if (n == '{') {
+ self.advance();
+ self.advance();
+ return .{
+ .kind = .template_head,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 2],
+ };
+ }
+ }
+ if (c == '\\') {
+ self.advance();
+ if (self.ch()) |_| self.advance();
+ } else {
+ self.advance();
+ }
+ }
+
+ return .{
+ .kind = .no_sub_template,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanTemplateMiddleOrTail(self: *Self) Token {
+ const start = self.pos;
+ self.advance();
+
+ while (self.ch()) |c| {
+ if (c == '`') {
+ self.advance();
+ return .{
+ .kind = .template_tail,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ if (c == '$' and self.chAt(1)) |n| {
+ if (n == '{') {
+ self.advance();
+ self.advance();
+ return .{
+ .kind = .template_middle,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 2],
+ };
+ }
+ }
+ if (c == '\\') {
+ self.advance();
+ if (self.ch()) |_| self.advance();
+ } else {
+ self.advance();
+ }
+ }
+
+ return .{
+ .kind = .template_tail,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanNumber(self: *Self) Token {
+ const start = self.pos;
+
+ if (self.ch()) |c| {
+ if (c == '0') {
+ self.advance();
+ if (self.ch()) |n| {
+ switch (n) {
+ 'x', 'X' => return self.scanHex(start),
+ 'b', 'B' => return self.scanBinary(start),
+ 'o', 'O' => return self.scanOctal(start),
+ 'n' => {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ },
+ 'e', 'E' => return self.scanDecimalAfterInt(start),
+ '.', '0'...'9' => return self.scanDecimalAfterInt(start),
+ else => {},
+ }
+ }
+ }
+ }
+
+ return self.scanDecimal(start);
+ }
+
+ fn scanDecimal(self: *Self, start: usize) Token {
+ while (self.ch()) |c| {
+ if (!isDigit(c) and c != '_') break;
+ self.advance();
+ }
+
+ if (self.ch()) |c| {
+ if (c == '.') {
+ self.advance();
+ while (self.ch()) |c2| {
+ if (!isDigit(c2) and c2 != '_') break;
+ self.advance();
+ }
+ }
+ if (self.ch()) |c2| {
+ if (c2 == 'e' or c2 == 'E') {
+ return self.scanExponent(start);
+ }
+ }
+ if (self.ch()) |c2| {
+ if (c2 == 'n') {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ }
+ }
+
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanDecimalAfterInt(self: *Self, start: usize) Token {
+ while (self.ch()) |c| {
+ if (!isDigit(c) and c != '_') break;
+ self.advance();
+ }
+ if (self.ch()) |c| {
+ if (c == '.') {
+ self.advance();
+ while (self.ch()) |c2| {
+ if (!isDigit(c2) and c2 != '_') break;
+ self.advance();
+ }
+ }
+ if (self.ch()) |c2| {
+ if (c2 == 'e' or c2 == 'E') {
+ return self.scanExponent(start);
+ }
+ }
+ if (self.ch()) |c2| {
+ if (c2 == 'n') {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ }
+ }
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanExponent(self: *Self, start: usize) Token {
+ self.advance();
+ if (self.ch()) |c| {
+ if (c == '+' or c == '-') self.advance();
+ }
+ while (self.ch()) |c| {
+ if (!isDigit(c) and c != '_') break;
+ self.advance();
+ }
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanHex(self: *Self, start: usize) Token {
+ self.advance();
+ while (self.ch()) |c| {
+ if (!isHexDigit(c) and c != '_') break;
+ self.advance();
+ }
+ if (self.ch()) |c| {
+ if (c == 'n') {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ }
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanBinary(self: *Self, start: usize) Token {
+ self.advance();
+ while (self.ch()) |c| {
+ if (c != '0' and c != '1' and c != '_') break;
+ self.advance();
+ }
+ if (self.ch()) |c| {
+ if (c == 'n') {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ }
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanOctal(self: *Self, start: usize) Token {
+ self.advance();
+ while (self.ch()) |c| {
+ if (!isOctalDigit(c) and c != '_') break;
+ self.advance();
+ }
+ if (self.ch()) |c| {
+ if (c == 'n') {
+ self.advance();
+ return .{
+ .kind = .bigint,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start .. self.pos - 1],
+ };
+ }
+ }
+ return .{
+ .kind = .number,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanIdentifierOrKeyword(self: *Self) Token {
+ const start = self.pos;
+ while (self.ch()) |c| {
+ if (!isIdContinue(c)) break;
+ self.advance();
+ }
+
+ const slice = self.source[start..self.pos];
+ const kind = keywordFromString(slice);
+
+ return .{
+ .kind = kind,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = slice,
+ };
+ }
+
+ fn scanPrivateIdentifier(self: *Self) Token {
+ const start = self.pos;
+ self.advance();
+ while (self.ch()) |c| {
+ if (!isIdContinue(c)) break;
+ self.advance();
+ }
+ return .{
+ .kind = .private_identifier,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanRegex(self: *Self) Token {
+ const start = self.pos;
+ self.advance();
+
+ var in_class = false;
+ while (self.ch()) |c| {
+ if (c == '\\') {
+ self.advance();
+ if (self.ch()) |_| self.advance();
+ } else if (c == '[') {
+ in_class = true;
+ self.advance();
+ } else if (c == ']') {
+ in_class = false;
+ self.advance();
+ } else if (c == '/' and !in_class) {
+ self.advance();
+ break;
+ } else if (isLineTerminator(c)) {
+ break;
+ } else {
+ self.advance();
+ }
+ }
+
+ while (self.ch()) |c| {
+ if (!isIdContinue(c)) break;
+ self.advance();
+ }
+
+ return .{
+ .kind = .regex,
+ .loc = .{ .start = start, .end = self.pos },
+ .slice = self.source[start..self.pos],
+ };
+ }
+
+ fn scanPunctuator(self: *Self) Token {
+ const start = self.pos;
+ const c = self.ch().?;
+
+ switch (c) {
+ '{' => {
+ self.advance();
+ return simple(.lbrace, start);
+ },
+ '}' => {
+ self.advance();
+ return simple(.rbrace, start);
+ },
+ '(' => {
+ self.advance();
+ return simple(.lparen, start);
+ },
+ ')' => {
+ self.advance();
+ return simple(.rparen, start);
+ },
+ '[' => {
+ self.advance();
+ return simple(.lbracket, start);
+ },
+ ']' => {
+ self.advance();
+ return simple(.rbracket, start);
+ },
+ ';' => {
+ self.advance();
+ return simple(.semicolon, start);
+ },
+ ',' => {
+ self.advance();
+ return simple(.comma, start);
+ },
+ ':' => {
+ self.advance();
+ return simple(.@":", start);
+ },
+ '?' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '?') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"??=", start);
+ }
+ }
+ return simple(.@"??", start);
+ }
+ if (n == '.') {
+ if (self.chAt(1)) |n2| {
+ if (!isDigit(n2)) {
+ self.advance();
+ return simple(.@"?.", start);
+ }
+ } else {
+ self.advance();
+ return simple(.@"?.", start);
+ }
+ }
+ }
+ return simple(.@"?", start);
+ },
+ '~' => {
+ self.advance();
+ return simple(.@"~", start);
+ },
+ '.' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '.' and self.chAt(1)) |n2| {
+ if (n2 == '.') {
+ self.advance();
+ self.advance();
+ return simple(.@"...", start);
+ }
+ }
+ }
+ return simple(.@".", start);
+ },
+ '#' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (isIdStart(n)) {
+ return self.scanPrivateIdentifier();
+ }
+ }
+ return simple(.@"#", start);
+ },
+ '+' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '+') {
+ self.advance();
+ return simple(.@"++", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"+=", start);
+ }
+ }
+ return simple(.@"+", start);
+ },
+ '-' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '-') {
+ self.advance();
+ return simple(.@"--", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"-=", start);
+ }
+ }
+ return simple(.@"-", start);
+ },
+ '*' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '*') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"**=", start);
+ }
+ }
+ return simple(.@"**", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"*=", start);
+ }
+ }
+ return simple(.@"*", start);
+ },
+ '%' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '=') {
+ self.advance();
+ return simple(.@"%=", start);
+ }
+ }
+ return simple(.@"%", start);
+ },
+ '=' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '=') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"===", start);
+ }
+ }
+ return simple(.@"==", start);
+ }
+ if (n == '>') {
+ self.advance();
+ return simple(.@"=>", start);
+ }
+ }
+ return simple(.@"=", start);
+ },
+ '!' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '=') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"!==", start);
+ }
+ }
+ return simple(.@"!=", start);
+ }
+ }
+ return simple(.@"!", start);
+ },
+ '<' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '<') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"<<=", start);
+ }
+ }
+ return simple(.@"<<", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"<=", start);
+ }
+ }
+ return simple(.@"<", start);
+ },
+ '>' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '>') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '>') {
+ self.advance();
+ if (self.ch()) |n3| {
+ if (n3 == '=') {
+ self.advance();
+ return simple(.@">>>=", start);
+ }
+ }
+ return simple(.@">>>", start);
+ }
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@">>=", start);
+ }
+ }
+ return simple(.@">>", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@">=", start);
+ }
+ }
+ return simple(.@">", start);
+ },
+ '&' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '&') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"&&=", start);
+ }
+ }
+ return simple(.@"&&", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"&=", start);
+ }
+ }
+ return simple(.@"&", start);
+ },
+ '|' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '|') {
+ self.advance();
+ if (self.ch()) |n2| {
+ if (n2 == '=') {
+ self.advance();
+ return simple(.@"||=", start);
+ }
+ }
+ return simple(.@"||", start);
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"|=", start);
+ }
+ }
+ return simple(.@"|", start);
+ },
+ '^' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '=') {
+ self.advance();
+ return simple(.@"^=", start);
+ }
+ }
+ return simple(.@"^", start);
+ },
+ '`' => {
+ self.advance();
+ return self.scanTemplateRest(start);
+ },
+ '\'' => return self.scanString('\''),
+ '"' => return self.scanString('"'),
+ '/' => {
+ self.advance();
+ if (self.ch()) |n| {
+ if (n == '/') {
+ self.skipSingleLineComment();
+ return self.next();
+ }
+ if (n == '*') {
+ self.skipMultiLineComment();
+ return self.next();
+ }
+ if (n == '=') {
+ self.advance();
+ return simple(.@"/=", start);
+ }
+ }
+ return simple(.@"/", start);
+ },
+ else => {
+ if (isLineTerminator(c) or isWhiteSpace(c)) {
+ self.skipWhiteSpace();
+ return self.next();
+ }
+ if (isDigit(c)) return self.scanNumber();
+ if (isIdStart(c)) return self.scanIdentifierOrKeyword();
+ if (c == '\\') {
+ self.advance();
+ return self.scanIdentifierOrKeyword();
+ }
+ self.advance();
+ return simple(.unknown, start);
+ },
+ }
+ }
+
+ fn simple(kind: TokenType, start: usize) Token {
+ _ = start;
+ return .{ .kind = kind, .loc = undefined, .slice = "" };
+ }
+
+ fn simpleWithSlice(comptime kind: TokenType, start: usize, slice: []const u8) Token {
+ return .{ .kind = kind, .loc = undefined, .slice = slice };
+ }
+
+ pub fn next(self: *Self) Token {
+ const start = self.pos;
+ if (self.ch()) |c| {
+ _ = c;
+ // Handle hashbang at beginning
+ if (self.pos == 0 and self.ch()) |c2| {
+ if (c2 == '#') {
+ self.skipHashbang();
+ return self.next();
+ }
+ }
+ return self.scanPunctuator();
+ }
+ return simple(.eof, self.pos);
+ }
+
+ fn keywordFromString(s: []const u8) TokenType {
+ const keywords = std.ComptimeStringMap(TokenType, .{
+ .{ "await", .await },
+ .{ "break", .@"break" },
+ .{ "case", .case },
+ .{ "catch", .@"catch" },
+ .{ "class", .class },
+ .{ "const", .@"const" },
+ .{ "continue", .@"continue" },
+ .{ "debugger", .debugger },
+ .{ "default", .default },
+ .{ "delete", .delete },
+ .{ "do", .do },
+ .{ "else", .@"else" },
+ .{ "enum", .@"enum" },
+ .{ "export", .@"export" },
+ .{ "extends", .extends },
+ .{ "false", .false },
+ .{ "finally", .finally },
+ .{ "for", .@"for" },
+ .{ "function", .function },
+ .{ "if", .@"if" },
+ .{ "import", .import },
+ .{ "in", .in },
+ .{ "instanceof", .instanceof },
+ .{ "let", .let },
+ .{ "new", .new },
+ .{ "null", .null },
+ .{ "return", .@"return" },
+ .{ "super", .super },
+ .{ "switch", .@"switch" },
+ .{ "this", .this },
+ .{ "throw", .throw },
+ .{ "true", .true },
+ .{ "try", .@"try" },
+ .{ "typeof", .typeof },
+ .{ "var", .@"var" },
+ .{ "void", .void },
+ .{ "while", .@"while" },
+ .{ "with", .with },
+ .{ "yield", .yield },
+ .{ "using", .using },
+ });
+
+ if (keywords.get(s)) |k| return k;
+ return .identifier;
+ }
+
+ pub fn tokenizeComptime(comptime source: []const u8) []const Token {
+ comptime {
+ return tokenize(source);
+ }
+ }
+};
diff --git a/src/z/js/parser/parser.zig b/src/z/js/parser/parser.zig
new file mode 100644
index 0000000..f269401
--- /dev/null
+++ b/src/z/js/parser/parser.zig
@@ -0,0 +1,117 @@
+const std = @import("std");
+
+const mod = @import("root.zig");
+const Result = mod.Result;
+
+pub fn Parser(T: type) type {
+ const R = Result(T);
+
+ return struct {
+ parse: *const fn ([]const u8) anyerror!R,
+
+ pub fn parseAll(self: *const @This(), buffer: []const u8) !T {
+ const value, const next = try self.parse(buffer);
+
+ if (next.len > 0) {
+ return error.NotExhaustive;
+ }
+
+ return value;
+ }
+
+ pub fn fromType(P: type) @This() {
+ return .{ .parse = P.parse };
+ }
+
+ pub fn variants(V: type) @This() {
+ if (@typeInfo(T) != .@"union") {
+ @compileError("variants is only allowed with an union as result type");
+ }
+
+ return .{
+ .parse = struct {
+ fn parse(buffer: []const u8) !R {
+ inline for (std.meta.fields(T)) |field| {
+ if (@field(V, field.name).parse(buffer)) |result| {
+ const value, const next = result;
+ return .{
+ @unionInit(T, field.name, value),
+ next,
+ };
+ } else |_| {}
+ }
+
+ return error.Variants;
+ }
+ }.parse,
+ };
+ }
+
+ pub fn any(P: type, comptime parsers: []const struct { Parser(P), T }) @This() {
+ return .{
+ .parse = struct {
+ fn parse(buffer: []const u8) !R {
+ inline for (parsers) |pair| {
+ const parser, const value = pair;
+
+ if (parser.parse(buffer)) |result| {
+ _, const next = result;
+ return .{ value, next };
+ } else |_| {}
+ }
+ return error.Any;
+ }
+ }.parse,
+ };
+ }
+ };
+}
+
+test "variants" {
+ const Char = union(enum) {
+ upper: u8,
+ lower: u8,
+ space: void,
+ };
+
+ const parser: Parser(Char) = .variants(struct {
+ const upper: Parser(u8) = .any(void, &.{
+ .{ mod.builtin.literal("A"), 'A' },
+ .{ mod.builtin.literal("B"), 'B' },
+ });
+
+ const lower: Parser(u8) = .any(void, &.{
+ .{ mod.builtin.literal("a"), 'a' },
+ .{ mod.builtin.literal("b"), 'b' },
+ });
+
+ const space: Parser(void) = mod.builtin.literal(" ");
+ });
+
+ {
+ const result = try parser.parseAll("a");
+ try std.testing.expect(result.lower == 'a');
+ }
+
+ {
+ const result = try parser.parseAll("b");
+ try std.testing.expect(result.lower == 'b');
+ }
+
+ {
+ const result = try parser.parseAll("A");
+ try std.testing.expect(result.upper == 'A');
+ }
+
+ {
+ const result = try parser.parseAll("B");
+ try std.testing.expect(result.upper == 'B');
+ }
+
+ {
+ const result = try parser.parseAll(" ");
+ try std.testing.expect(result == .space);
+ }
+
+ try std.testing.expect(parser.parseAll("c") == error.Variants);
+}
diff --git a/src/z/js/parser/result.zig b/src/z/js/parser/result.zig
new file mode 100644
index 0000000..106dabe
--- /dev/null
+++ b/src/z/js/parser/result.zig
@@ -0,0 +1,3 @@
+pub fn Result(T: type) type {
+ return struct { T, []const u8 };
+}
diff --git a/src/z/js/parser/root.zig b/src/z/js/parser/root.zig
new file mode 100644
index 0000000..6d41c7b
--- /dev/null
+++ b/src/z/js/parser/root.zig
@@ -0,0 +1,93 @@
+const std = @import("std");
+
+pub const lexical_grammar = @import("lexical_grammar.zig");
+pub const ast = @import("ast.zig");
+pub const syntactic_grammar = @import("syntactic_grammar.zig");
+pub const Result = @import("result.zig").Result;
+pub const Parser = @import("parser.zig").Parser;
+
+test "simple literal" {
+ const source = "42;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+ try std.testing.expect(result.body[0].data == .expr);
+}
+
+test "variable declaration" {
+ const source = "var x = 1;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+ try std.testing.expect(result.body[0].data == .variable);
+}
+
+test "function declaration" {
+ const source = "function foo() { return 1; }";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+ try std.testing.expect(result.body[0].data == .function);
+}
+
+test "if statement" {
+ const source = "if (x > 0) { x = 1; } else { x = 2; }";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+ try std.testing.expect(result.body[0].data == .@"if");
+}
+
+test "binary expression" {
+ const source = "a + b * c;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "arrow function" {
+ const source = "const f = (x) => x + 1;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "object literal" {
+ const source = "var obj = { a: 1, b: 2 };";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "class declaration" {
+ const source = "class Foo { constructor() { } }";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "for loop" {
+ const source = "for (var i = 0; i < 10; i++) { break; }";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "try catch" {
+ const source = "try { x; } catch (e) { y; }";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "multiple statements" {
+ const source = "var a = 1; var b = 2; var c = a + b;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 3);
+}
+
+test "array literal with spread" {
+ const source = "var arr = [1, ...rest, 3];";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test "template literal" {
+ const source = "const s = `hello ${name} world`;";
+ const result = try syntactic_grammar.parse(source);
+ try std.testing.expect(result.body.len == 1);
+}
+
+test {
+ _ = std.testing.refAllDecls(@This());
+}
diff --git a/src/z/js/parser/syntactic_grammar.zig b/src/z/js/parser/syntactic_grammar.zig
new file mode 100644
index 0000000..065e827
--- /dev/null
+++ b/src/z/js/parser/syntactic_grammar.zig
@@ -0,0 +1,1907 @@
+const std = @import("std");
+
+const lex = @import("lexical_grammar.zig");
+const Token = lex.Token;
+const TokenType = lex.TokenType;
+const Loc = lex.Loc;
+
+const ast = @import("ast.zig");
+const Expr = ast.Expr;
+const ExprType = ast.ExprType;
+const Stmt = ast.Stmt;
+const StmtType = ast.StmtType;
+const Pat = ast.Pat;
+const PatType = ast.PatType;
+const Decl = ast.Decl;
+const DeclType = ast.DeclType;
+const Lit = ast.Lit;
+const LitType = ast.LitType;
+
+pub const ParseError = error{
+ UnexpectedToken,
+ ExpectedToken,
+ ExpectedExpression,
+ ExpectedStatement,
+ ExpectedIdentifier,
+ UnclosedString,
+ InvalidNumber,
+ InvalidEscape,
+ UnterminatedComment,
+ ExpectedParameterName,
+ ExpectedPropertyName,
+ UnterminatedTemplate,
+ InvalidAssignmentTarget,
+ TooManyTokens,
+};
+
+const BP = struct {
+ const comma = 1;
+ const assign = 2;
+ const yield = 3;
+ const conditional = 4;
+ const coalesce = 5;
+ const log_or = 6;
+ const log_and = 7;
+ const bit_or = 8;
+ const bit_xor = 9;
+ const bit_and = 10;
+ const equality = 11;
+ const relational = 12;
+ const shift = 13;
+ const additive = 14;
+ const multiplicative = 15;
+ const exponentiation = 16;
+ const unary = 17;
+ const update = 18;
+ const call = 19;
+ const member = 20;
+};
+
+pub const NodeArena = struct {
+ exprs: [8192]Expr = undefined,
+ expr_count: usize = 0,
+ stmts: [8192]Stmt = undefined,
+ stmt_count: usize = 0,
+ decls: [1024]Decl = undefined,
+ decl_count: usize = 0,
+ pats: [1024]Pat = undefined,
+ pat_count: usize = 0,
+ lits: [1024]Lit = undefined,
+ lit_count: usize = 0,
+ string_buf: [65536]u8 = undefined,
+ string_pos: usize = 0,
+
+ fn exprPtr(self: *NodeArena, data: ExprType, loc: Loc) *const Expr {
+ const idx = self.expr_count;
+ self.exprs[idx] = .{ .loc = loc, .data = data };
+ self.expr_count += 1;
+ return &self.exprs[idx];
+ }
+
+ fn stmtPtr(self: *NodeArena, data: StmtType, loc: Loc) *const Stmt {
+ const idx = self.stmt_count;
+ self.stmts[idx] = .{ .loc = loc, .data = data };
+ self.stmt_count += 1;
+ return &self.stmts[idx];
+ }
+
+ fn declPtr(self: *NodeArena, data: DeclType, loc: Loc) *const Decl {
+ const idx = self.decl_count;
+ self.decls[idx] = .{ .loc = loc, .data = data };
+ self.decl_count += 1;
+ return &self.decls[idx];
+ }
+
+ fn patPtr(self: *NodeArena, data: PatType, loc: Loc) *const Pat {
+ const idx = self.pat_count;
+ self.pats[idx] = .{ .loc = loc, .data = data };
+ self.pat_count += 1;
+ return &self.pats[idx];
+ }
+
+ fn litPtr(self: *NodeArena, data: LitType, loc: Loc) *const Lit {
+ const idx = self.lit_count;
+ self.lits[idx] = .{ .loc = loc, .data = data };
+ self.lit_count += 1;
+ return &self.lits[idx];
+ }
+};
+
+pub const Parser = struct {
+ tokens: []const Token,
+ pos: usize,
+ source: []const u8,
+ arena: *NodeArena,
+
+ pub fn init(tokens: []const Token, source: []const u8, arena: *NodeArena) @This() {
+ return .{ .tokens = tokens, .pos = 0, .source = source, .arena = arena };
+ }
+
+ fn tok(self: *@This()) Token {
+ return self.tokens[self.pos];
+ }
+
+ fn peek(self: *@This()) TokenType {
+ return self.tokens[self.pos].kind;
+ }
+
+ fn peekAt(self: *@This(), offset: usize) TokenType {
+ const idx = self.pos + offset;
+ if (idx >= self.tokens.len) return .eof;
+ return self.tokens[idx].kind;
+ }
+
+ fn advance(self: *@This()) Token {
+ const t = self.tokens[self.pos];
+ self.pos += 1;
+ return t;
+ }
+
+ fn expect(self: *@This(), kind: TokenType) ParseError!Token {
+ if (self.peek() != kind) return ParseError.ExpectedToken;
+ return self.advance();
+ }
+
+ fn match(self: *@This(), kind: TokenType) bool {
+ if (self.peek() == kind) {
+ self.advance();
+ return true;
+ }
+ return false;
+ }
+
+ fn matchKeyword(self: *@This(), kw: TokenType) bool {
+ if (self.peek() == kw) {
+ self.advance();
+ return true;
+ }
+ return false;
+ }
+
+ fn isLineTerminatorBeforeNext(self: *@This()) bool {
+ if (self.pos == 0) return false;
+ const prev_token = self.tokens[self.pos - 1];
+ const next_token = self.tokens[self.pos];
+ // Check if there's a line terminator between prev and next tokens
+ var i = prev_token.loc.end;
+ while (i < next_token.loc.start) : (i += 1) {
+ if (i >= self.source.len) break;
+ const c = self.source[i];
+ switch (c) {
+ '\n', '\r', 0x2028, 0x2029 => return true,
+ else => {},
+ }
+ }
+ return false;
+ }
+
+ fn as_identifier(self: *@This(), tok: Token) []const u8 {
+ return self.source[tok.loc.start..tok.loc.end];
+ }
+
+ fn tokenSlice(self: *@This(), tok: Token) []const u8 {
+ return self.source[tok.loc.start..tok.loc.end];
+ }
+
+ pub fn parseProgram(self: *@This()) ParseError!ast.Program {
+ var stmts: [2048]Stmt = undefined;
+ var count: usize = 0;
+
+ while (self.peek() != .eof) {
+ const item = try self.parseStatementListItem();
+ stmts[count] = item;
+ count += 1;
+ if (count >= stmts.len) return ParseError.TooManyTokens;
+ }
+
+ const body = stmts[0..count];
+ const loc: Loc = if (body.len > 0) .{ .start = body[0].loc.start, .end = body[body.len - 1].loc.end } else .{ .start = 0, .end = 0 };
+
+ return .{ .body = body, .loc = loc };
+ }
+
+ fn parseStatementListItem(self: *@This()) ParseError!Stmt {
+ switch (self.peek()) {
+ .function => return (try self.parseFunctionDecl(.normal, .stmt)).?,
+ .async => {
+ if (self.peekAt(1) == .function) {
+ return (try self.parseFunctionDecl(.async, .stmt)).?;
+ }
+ if (self.peekAt(1) == .@"*" and self.peekAt(2) == .function) {
+ return (try self.parseFunctionDecl(.async_generator, .stmt)).?;
+ }
+ return self.parseStatement();
+ },
+ .class => return (try self.parseClassDecl(.stmt)).?,
+ .@"const", .let => return self.parseLexicalDecl(),
+ .@"var" => return self.parseVarStmt(),
+ .using => return self.parseUsingStmt(),
+ .import => return try self.parseImport(),
+ .@"export" => return try self.parseExport(),
+ else => return self.parseStatement(),
+ }
+ }
+
+ fn parseStatement(self: *@This()) ParseError!Stmt {
+ switch (self.peek()) {
+ .lbrace => return self.parseBlock(),
+ .semicolon => {
+ const t = self.advance();
+ return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .empty = {} } };
+ },
+ .@"if" => return self.parseIf(),
+ .@"while" => return self.parseWhile(),
+ .do => return self.parseDoWhile(),
+ .@"for" => return self.parseFor(),
+ .@"continue" => return self.parseContinue(),
+ .@"break" => return self.parseBreak(),
+ .@"return" => return self.parseReturn(),
+ .@"switch" => return self.parseSwitch(),
+ .throw => return self.parseThrow(),
+ .@"try" => return self.parseTry(),
+ .debugger => {
+ const t = self.advance();
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .debugger = {} } };
+ },
+ .with => return self.parseWith(),
+ .function => return (try self.parseFunctionDecl(.normal, .stmt)).?,
+ .async => {
+ if (self.peekAt(1) == .function) {
+ return (try self.parseFunctionDecl(.async, .stmt)).?;
+ }
+ if (self.peekAt(1) == .@"*") return self.parseExpressionStatement();
+ if (self.peekAt(1).isIdentifier()) {
+ return self.parseExpressionStatement();
+ }
+ return self.parseExpressionStatement();
+ },
+ .class => return (try self.parseClassDecl(.stmt)).?,
+ .@"const", .let => return self.parseLexicalDecl(),
+ .@"var" => return self.parseVarStmt(),
+ .import => return try self.parseImport(),
+ .@"export" => return try self.parseExport(),
+ else => return self.parseExpressionStatement(),
+ }
+ }
+
+ fn parseBlock(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var stmts: [1024]Stmt = undefined;
+ var count: usize = 0;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ const s = try self.parseStatementListItem();
+ stmts[count] = s;
+ count += 1;
+ if (count >= stmts.len) return ParseError.TooManyTokens;
+ }
+
+ const end_tok = try self.expect(.rbrace);
+ const end = end_tok.loc.end;
+
+ const block = Stmt{
+ .loc = .{ .start = start, .end = end },
+ .data = .{ .block = .{ .loc = .{ .start = start, .end = end }, .body = stmts[0..count] } },
+ };
+ return block;
+ }
+
+ fn parseIf(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ try self.expect(.lparen);
+ const condition = try self.parseExpr(0);
+ try self.expect(.rparen);
+ const consequent = try self.statementPointer();
+ var alternate: ?*const Stmt = null;
+ if (self.matchKeyword(.@"else")) {
+ alternate = try self.statementPointer();
+ }
+ const end = if (alternate) |a| a.loc.end else consequent.loc.end;
+ return Stmt{
+ .loc = .{ .start = start, .end = end },
+ .data = .{ .@"if" = .{ .loc = .{ .start = start, .end = end }, .condition = condition, .consequent = consequent, .alternate = alternate } },
+ };
+ }
+
+ fn parseWhile(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ try self.expect(.lparen);
+ const condition = try self.parseExpr(0);
+ try self.expect(.rparen);
+ const body = try self.statementPointer();
+ return Stmt{
+ .loc = .{ .start = start, .end = body.loc.end },
+ .data = .{ .@"while" = .{ .loc = .{ .start = start, .end = body.loc.end }, .condition = condition, .body = body } },
+ };
+ }
+
+ fn parseDoWhile(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ const body = try self.statementPointer();
+ try self.expect(.@"while");
+ try self.expect(.lparen);
+ const condition = try self.parseExpr(0);
+ try self.expect(.rparen);
+ self.semicolon();
+ return Stmt{
+ .loc = .{ .start = start, .end = condition.loc.end },
+ .data = .{ .do_while = .{ .loc = .{ .start = start, .end = condition.loc.end }, .body = body, .condition = condition } },
+ };
+ }
+
+ fn parseFor(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ try self.expect(.lparen);
+
+ var init: ?union(enum) { expr: *const Expr, decl: *const Decl } = null;
+ var condition: ?*const Expr = null;
+ var update: ?*const Expr = null;
+
+ if (self.peek() != .semicolon) {
+ if (self.peek() == .@"var" or self.peek() == .let or self.peek() == .@"const") {
+ const decl = try self.parseForDeclaration();
+ init = .{ .decl = decl };
+ } else {
+ const expr = try self.parseExpr(0);
+ init = .{ .expr = expr };
+ }
+ }
+
+ if (self.matchKeyword(.in)) {
+ const right = try self.parseExpr(0);
+ try self.expect(.rparen);
+ const body = try self.statementPointer();
+ return Stmt{
+ .loc = .{ .start = start, .end = body.loc.end },
+ .data = .{ .for_in = .{ .loc = .{ .start = start, .end = body.loc.end }, .left = init.?, .right = right, .body = body } },
+ };
+ }
+
+ if (self.matchKeyword(.of)) {
+ const right = try self.parseExpr(0);
+ try self.expect(.rparen);
+ const body = try self.statementPointer();
+ return Stmt{
+ .loc = .{ .start = start, .end = body.loc.end },
+ .data = .{ .for_of = .{ .loc = .{ .start = start, .end = body.loc.end }, .await_token = false, .left = init.?, .right = right, .body = body } },
+ };
+ }
+
+ try self.expect(.semicolon);
+ if (self.peek() != .semicolon and self.peek() != .rparen) {
+ condition = try self.parseExpr(0);
+ }
+ try self.expect(.semicolon);
+ if (self.peek() != .rparen) {
+ update = try self.parseExpr(0);
+ }
+ try self.expect(.rparen);
+ const body = try self.statementPointer();
+ return Stmt{
+ .loc = .{ .start = start, .end = body.loc.end },
+ .data = .{ .@"for" = .{ .loc = .{ .start = start, .end = body.loc.end }, .init = init, .condition = condition, .update = update, .body = body } },
+ };
+ }
+
+ fn parseForDeclaration(self: *@This()) ParseError!*const Decl {
+ const kind_tok = self.advance();
+ var decls: [128]ast.VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try self.parseBindingPattern();
+ var init: ?*const Expr = null;
+ if (self.match(.@"=")) {
+ init = try self.parseExpr(0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = init };
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+
+ const kind: ast.VarKind = switch (kind_tok.kind) {
+ .@"var" => .@"var",
+ .let => .let,
+ .@"const" => .@"const",
+ else => return ParseError.UnexpectedToken,
+ };
+
+ return self.arena.declPtr(.{ .@"var" = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } }, .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end });
+ }
+
+ fn parseContinue(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var label: ?[]const u8 = null;
+ if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) {
+ const t = self.advance();
+ label = self.tokenSlice(t);
+ }
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = start + 8 }, .data = .{ .@"continue" = .{ .loc = .{ .start = start, .end = start + 8 }, .label = label } } };
+ }
+
+ fn parseBreak(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var label: ?[]const u8 = null;
+ if (self.peek() == .identifier and !self.isLineTerminatorBeforeNext()) {
+ const t = self.advance();
+ label = self.tokenSlice(t);
+ }
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = start + 5 }, .data = .{ .@"break" = .{ .loc = .{ .start = start, .end = start + 5 }, .label = label } } };
+ }
+
+ fn parseReturn(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var arg: ?*const Expr = null;
+ if (self.peek() != .semicolon and self.peek() != .rbrace and self.peek() != .eof and !self.isLineTerminatorBeforeNext()) {
+ arg = try self.parseExpr(0);
+ }
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = arg.?.loc.end }, .data = .{ .@"return" = .{ .loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 6 }, .arg = arg } } };
+ }
+
+ fn parseSwitch(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ try self.expect(.lparen);
+ const discriminant = try self.parseExpr(0);
+ try self.expect(.rparen);
+ try self.expect(.lbrace);
+
+ var cases: [256]ast.SwitchCase = undefined;
+ var case_count: usize = 0;
+
+ while (self.peek() == .case or self.peek() == .default) {
+ const case_start = self.tok().loc.start;
+ if (self.matchKeyword(.case)) {
+ const case_test = try self.parseExpr(0);
+ try self.expect(.@":");
+ var consequent: [128]Stmt = undefined;
+ var cons_count: usize = 0;
+ while (self.peek() != .case and self.peek() != .default and self.peek() != .rbrace and self.peek() != .eof) {
+ const s = try self.parseStatementListItem();
+ consequent[cons_count] = s;
+ cons_count += 1;
+ }
+ cases[case_count] = .{ .loc = .{ .start = case_start, .end = self.tok().loc.start }, .condition = case_test, .consequent = consequent[0..cons_count] };
+ case_count += 1;
+ } else if (self.matchKeyword(.default)) {
+ try self.expect(.@":");
+ var consequent: [128]Stmt = undefined;
+ var cons_count: usize = 0;
+ while (self.peek() != .case and self.peek() != .default and self.peek() != .rbrace and self.peek() != .eof) {
+ const s = try self.parseStatementListItem();
+ consequent[cons_count] = s;
+ cons_count += 1;
+ }
+ cases[case_count] = .{ .loc = .{ .start = case_start, .end = self.tok().loc.start }, .condition = null, .consequent = consequent[0..cons_count] };
+ case_count += 1;
+ }
+ }
+
+ const end_tok = try self.expect(.rbrace);
+ return Stmt{
+ .loc = .{ .start = start, .end = end_tok.loc.end },
+ .data = .{ .@"switch" = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .discriminant = discriminant, .cases = cases[0..case_count] } },
+ };
+ }
+
+ fn parseThrow(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ if (self.isLineTerminatorBeforeNext()) return ParseError.ExpectedExpression;
+ const arg = try self.parseExpr(0);
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = arg.loc.end }, .data = .{ .throw = .{ .loc = .{ .start = start, .end = arg.loc.end }, .arg = arg } } };
+ }
+
+ fn parseTry(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ const block = try self.parseBlockStmt();
+ var handler: ?ast.CatchClause = null;
+ var finalizer: ?ast.BlockStmt = null;
+
+ if (self.matchKeyword(.@"catch")) {
+ var param: ?Pat = null;
+ if (self.match(.lparen)) {
+ const pat = try self.parseBindingPattern();
+ param = pat;
+ try self.expect(.rparen);
+ }
+ const catch_body = try self.parseBlockStmt();
+ handler = .{ .loc = .{ .start = block.loc.start, .end = catch_body.loc.end }, .param = param, .body = catch_body };
+ }
+
+ if (self.matchKeyword(.finally)) {
+ const finally_body = try self.parseBlockStmt();
+ finalizer = finally_body;
+ }
+
+ const end = if (finalizer) |f| f.loc.end else if (handler) |h| h.loc.end else block.loc.end;
+ return Stmt{
+ .loc = .{ .start = start, .end = end },
+ .data = .{ .@"try" = .{ .loc = .{ .start = start, .end = end }, .block = block, .handler = handler, .finalizer = finalizer } },
+ };
+ }
+
+ fn parseWith(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ try self.expect(.lparen);
+ const obj = try self.parseExpr(0);
+ try self.expect(.rparen);
+ const body = try self.statementPointer();
+ return Stmt{
+ .loc = .{ .start = start, .end = body.loc.end },
+ .data = .{ .with = .{ .loc = .{ .start = start, .end = body.loc.end }, .obj = obj, .body = body } },
+ };
+ }
+
+ fn parseBlockStmt(self: *@This()) ParseError!ast.BlockStmt {
+ const start = try self.expect(.lbrace);
+ var stmts: [1024]Stmt = undefined;
+ var count: usize = 0;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ const s = try self.parseStatementListItem();
+ stmts[count] = s;
+ count += 1;
+ if (count >= stmts.len) return ParseError.TooManyTokens;
+ }
+
+ const end = try self.expect(.rbrace);
+ return .{ .loc = .{ .start = start.loc.start, .end = end.loc.end }, .body = stmts[0..count] };
+ }
+
+ fn parseVarStmt(self: *@This()) ParseError!Stmt {
+ const kind_tok = self.advance();
+ var decls: [128]ast.VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try self.parseBindingPattern();
+ var init: ?*const Expr = null;
+ if (self.match(.@"=")) {
+ init = try self.parseExpr(0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = init };
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+
+ self.semicolon();
+ const kind: ast.VarKind = .@"var";
+ return Stmt{
+ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end },
+ .data = .{ .variable = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } },
+ };
+ }
+
+ fn parseLexicalDecl(self: *@This()) ParseError!Stmt {
+ const kind_tok = self.advance();
+ var decls: [128]ast.VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const pat = try self.parseBindingPattern();
+ var init: ?*const Expr = null;
+ if (kind_tok.kind == .@"const") {
+ if (!self.match(.@"=")) return ParseError.ExpectedToken;
+ init = try self.parseExpr(0);
+ } else if (self.match(.@"=")) {
+ init = try self.parseExpr(0);
+ }
+ decls[count] = .{ .loc = pat.loc, .id = pat, .init = init };
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+
+ self.semicolon();
+ const kind: ast.VarKind = switch (kind_tok.kind) {
+ .let => .let,
+ .@"const" => .@"const",
+ else => return ParseError.UnexpectedToken,
+ };
+ return Stmt{
+ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end },
+ .data = .{ .lexical = .{ .loc = .{ .start = kind_tok.loc.start, .end = decls[count - 1].loc.end }, .kind = kind, .decls = decls[0..count] } },
+ };
+ }
+
+ fn parseUsingStmt(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var decls: [128]ast.VarDeclarator = undefined;
+ var count: usize = 0;
+
+ while (true) {
+ const id_tok = try self.expect(.identifier);
+ const id = self.tokenSlice(id_tok);
+ var init: ?*const Expr = null;
+ if (self.match(.@"=")) {
+ init = try self.parseExpr(0);
+ }
+ decls[count] = .{ .loc = .{ .start = id_tok.loc.start, .end = if (init) |e| e.loc.end else id_tok.loc.end }, .id = Pat{ .loc = .{ .start = id_tok.loc.start, .end = id_tok.loc.end }, .data = .{ .ident = id } }, .init = init };
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+
+ self.semicolon();
+ return Stmt{
+ .loc = .{ .start = start, .end = decls[count - 1].loc.end },
+ .data = .{ .using = .{ .loc = .{ .start = start, .end = decls[count - 1].loc.end }, .decls = decls[0..count] } },
+ };
+ }
+
+ fn parseExpressionStatement(self: *@This()) ParseError!Stmt {
+ const expr = try self.parseExpr(0);
+ self.semicolon();
+ return Stmt{ .loc = expr.loc, .data = .{ .expr = .{ .loc = expr.loc, .expr = expr } } };
+ }
+
+ fn parseFunctionDecl(self: *@This(), fn_type: ast.FnType, mode: enum { stmt, expr }) ParseError!?Stmt {
+ const start = self.tok().loc.start;
+
+ if (fn_type == .async) {
+ _ = self.advance(); // async
+ } else if (fn_type == .async_generator) {
+ _ = self.advance(); // async
+ _ = self.advance(); // *
+ }
+
+ _ = self.advance(); // function
+
+ if (fn_type == .normal and self.peek() == .@"*") {
+ self.advance();
+ fn_type = .generator;
+ }
+
+ var id: ?[]const u8 = null;
+ if (self.peek().isIdentifier()) {
+ const id_tok = self.advance();
+ id = self.tokenSlice(id_tok);
+ }
+
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+
+ const end = body.loc.end;
+
+ if (mode == .stmt) {
+ return Stmt{
+ .loc = .{ .start = start, .end = end },
+ .data = .{ .function = .{ .loc = .{ .start = start, .end = end }, .fn_type = fn_type, .id = id, .params = params, .body = body } },
+ };
+ }
+ return null;
+ }
+
+ fn parseFnExpr(self: *@This()) ParseError!*const Expr {
+ const start = self.advance().loc.start;
+
+ var fn_type: ast.FnType = .normal;
+ if (self.peek() == .@"*") {
+ fn_type = .generator;
+ self.advance();
+ }
+
+ var id: ?[]const u8 = null;
+ if (self.peek() == .identifier or self.peek().isKeywordButNotLet()) {
+ const id_tok = self.advance();
+ id = self.tokenSlice(id_tok);
+ }
+
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+
+ return self.arena.exprPtr(
+ .{ .func = .{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = fn_type, .id = id, .params = params, .body = body } },
+ .{ .start = start, .end = body.loc.end },
+ );
+ }
+
+ fn parseParams(self: *@This()) ParseError![]const Pat {
+ try self.expect(.lparen);
+ var params: [128]Pat = undefined;
+ var count: usize = 0;
+
+ if (self.peek() != .rparen) {
+ while (true) {
+ if (self.peek() == .@"...") {
+ self.advance();
+ const pat = try self.parseBindingPattern();
+ params[count] = Pat{ .loc = pat.loc, .data = .{ .rest = .{ .loc = pat.loc, .arg = self.arena.patPtr(pat.data, pat.loc) } } };
+ count += 1;
+ break;
+ }
+ const pat = try self.parseBindingPattern();
+ if (self.match(.@"=")) {
+ const expr = try self.parseExpr(0);
+ params[count] = Pat{ .loc = .{ .start = pat.loc.start, .end = expr.loc.end }, .data = .{ .assign = .{ .loc = .{ .start = pat.loc.start, .end = expr.loc.end }, .left = self.arena.patPtr(pat.data, pat.loc), .right = expr } } };
+ } else {
+ params[count] = pat;
+ }
+ count += 1;
+ if (!self.match(.comma)) break;
+ if (self.peek() == .@"...") {
+ self.advance();
+ const rest_pat = try self.parseBindingPattern();
+ params[count] = Pat{ .loc = rest_pat.loc, .data = .{ .rest = .{ .loc = rest_pat.loc, .arg = self.arena.patPtr(rest_pat.data, rest_pat.loc) } } };
+ count += 1;
+ break;
+ }
+ }
+ }
+
+ try self.expect(.rparen);
+ return params[0..count];
+ }
+
+ fn parseFunctionBody(self: *@This()) ParseError!ast.BlockStmt {
+ return self.parseBlockStmt();
+ }
+
+ fn parseClassDecl(self: *@This(), mode: enum { stmt, expr }) ParseError!?Stmt {
+ const start = self.advance().loc.start;
+
+ var id: ?[]const u8 = null;
+ if (self.peek() == .identifier) {
+ const id_tok = self.advance();
+ id = self.tokenSlice(id_tok);
+ }
+
+ var super_class: ?*const Expr = null;
+ if (self.matchKeyword(.extends)) {
+ super_class = try self.parseExpr(0);
+ }
+
+ try self.expect(.lbrace);
+ var elems: [128]ast.ClassElem = undefined;
+ var elem_count: usize = 0;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ const elem = try self.parseClassElement();
+ elems[elem_count] = elem;
+ elem_count += 1;
+ if (elem_count >= elems.len) return ParseError.TooManyTokens;
+ }
+
+ const end_tok = try self.expect(.rbrace);
+
+ if (mode == .stmt) {
+ return Stmt{
+ .loc = .{ .start = start, .end = end_tok.loc.end },
+ .data = .{ .class = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .id = id, .super_class = super_class, .body = elems[0..elem_count] } },
+ };
+ }
+ return null;
+ }
+
+ fn parseClassExpr(self: *@This()) ParseError!*const Expr {
+ const start = self.advance().loc.start;
+
+ var id: ?[]const u8 = null;
+ if (self.peek() == .identifier) {
+ const id_tok = self.advance();
+ id = self.tokenSlice(id_tok);
+ }
+
+ var super_class: ?*const Expr = null;
+ if (self.matchKeyword(.extends)) {
+ super_class = try self.parseExpr(0);
+ }
+
+ try self.expect(.lbrace);
+ var elems: [128]ast.ClassElem = undefined;
+ var elem_count: usize = 0;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ const elem = try self.parseClassElement();
+ elems[elem_count] = elem;
+ elem_count += 1;
+ }
+
+ const end_tok = try self.expect(.rbrace);
+ return self.arena.exprPtr(
+ .{ .class = .{ .loc = .{ .start = start, .end = end_tok.loc.end }, .id = id, .super_class = super_class, .body = elems[0..elem_count] } },
+ .{ .start = start, .end = end_tok.loc.end },
+ );
+ }
+
+ fn parseClassElement(self: *@This()) ParseError!ast.ClassElem {
+ const start = self.tok().loc.start;
+ const is_static = self.matchKeyword(.static);
+
+ if (is_static and self.peek() == .lbrace) {
+ const body = try self.parseBlockStmt();
+ return ast.ClassElem{ .static_block = body };
+ }
+
+ if (self.matchKeyword(.get)) {
+ const key = try self.parsePropKey();
+ try self.expect(.lparen);
+ try self.expect(.rparen);
+ const body = try self.parseFunctionBody();
+ if (is_static) return ast.ClassElem{ .static_get = .{ .key = key, .body = body } };
+ return ast.ClassElem{ .get = .{ .key = key, .body = body } };
+ }
+
+ if (self.matchKeyword(.set)) {
+ const key = try self.parsePropKey();
+ try self.expect(.lparen);
+ const param = try self.parseBindingPattern();
+ try self.expect(.rparen);
+ const body = try self.parseFunctionBody();
+ if (is_static) return ast.ClassElem{ .static_set = .{ .key = key, .param = param, .body = body } };
+ return ast.ClassElem{ .set = .{ .key = key, .param = param, .body = body } };
+ }
+
+ if (self.match(.@"*")) {
+ const key = try self.parsePropKey();
+ try self.expect(.lparen);
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+ const fn_expr = ast.FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .generator, .id = null, .params = params, .body = body };
+ if (is_static) return ast.ClassElem{ .static_method = fn_expr };
+ return ast.ClassElem{ .method = fn_expr };
+ }
+
+ const key = try self.parsePropKey();
+
+ if (self.peek() == .lparen) {
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+ const fn_expr = ast.FnExpr{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .normal, .id = null, .params = params, .body = body };
+ if (is_static) return ast.ClassElem{ .static_method = fn_expr };
+ return ast.ClassElem{ .method = fn_expr };
+ }
+
+ var value: ?*const Expr = null;
+ if (self.match(.@"=")) {
+ value = try self.parseExpr(0);
+ }
+ if (self.peek() == .semicolon) self.advance();
+
+ const field = ast.FieldDef{ .loc = .{ .start = start, .end = if (value) |v| v.loc.end else self.tok().loc.start }, .key = key, .value = value };
+ if (is_static) return ast.ClassElem{ .static_field = field };
+ return ast.ClassElem{ .field = field };
+ }
+
+ fn parseImport(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+ var specifiers: [32]ast.ImportSpecifier = undefined;
+ var spec_count: usize = 0;
+
+ if (self.peek() != .string and self.peek() != .lparen) {
+ if (self.peek() == .identifier or self.peek().isKeyword()) {
+ const local_tok = self.advance();
+ const local = self.tokenSlice(local_tok);
+ specifiers[spec_count] = .{ .default = local };
+ spec_count += 1;
+ if (self.match(.comma)) {}
+ }
+
+ if (self.match(.@"*")) {
+ try self.expect(.identifier); // as
+ const ns_tok = try self.expect(.identifier);
+ const ns = self.tokenSlice(ns_tok);
+ specifiers[spec_count] = .{ .namespace = ns };
+ spec_count += 1;
+ } else if (self.peek() == .lbrace) {
+ self.advance();
+ if (self.peek() != .rbrace) {
+ while (true) {
+ const imported_tok = try self.expect(.identifier);
+ const imported = self.tokenSlice(imported_tok);
+ var local = imported;
+ if (self.match(.identifier)) {
+ local = self.tokenSlice(self.tokens[self.pos - 1]);
+ }
+ specifiers[spec_count] = .{ .named = .{ .imported = imported, .local = local } };
+ spec_count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+ try self.expect(.rbrace);
+ }
+ }
+
+ var source: []const u8 = "";
+ var attrs: [8]ast.ImportAttribute = undefined;
+ var attr_count: usize = 0;
+
+ if (self.matchKeyword(.from)) {
+ const str_tok = try self.expect(.string);
+ source = self.tokenSlice(str_tok);
+ } else if (self.peek() == .string) {
+ const str_tok = self.advance();
+ source = self.tokenSlice(str_tok);
+ }
+
+ if (self.matchKeyword(.with)) {
+ try self.expect(.lbrace);
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ const key_tok = self.advance();
+ const key = self.tokenSlice(key_tok);
+ try self.expect(.@":");
+ const val_tok = try self.expect(.string);
+ const val = self.tokenSlice(val_tok);
+ attrs[attr_count] = .{ .key = key, .value = val };
+ attr_count += 1;
+ _ = self.match(.comma);
+ }
+ try self.expect(.rbrace);
+ }
+
+ try self.expect(.semicolon);
+ return Stmt{
+ .loc = .{ .start = start, .end = self.tok().loc.start },
+ .data = .{ .import = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .specifiers = specifiers[0..spec_count], .source = source, .attributes = attrs[0..attr_count] } },
+ };
+ }
+
+ fn parseExport(self: *@This()) ParseError!Stmt {
+ const start = self.advance().loc.start;
+
+ if (self.matchKeyword(.default)) {
+ if (self.peek() == .function or self.peek() == .async or self.peek() == .class) {
+ const decl = try self.parseStatementListItem();
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = decl.loc.end }, .data = decl.data };
+ }
+ const expr = try self.parseExpr(0);
+ self.semicolon();
+ return Stmt{ .loc = .{ .start = start, .end = expr.loc.end }, .data = .{ .expr = .{ .loc = expr.loc, .expr = expr } } };
+ }
+
+ if (self.peek() == .@"*") {
+ self.advance();
+ try self.expect(.identifier); // from
+ const source_tok = try self.expect(.string);
+ _ = self.tokenSlice(source_tok);
+ try self.expect(.semicolon);
+ return Stmt{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .empty = {} } };
+ }
+
+ if (self.peek() == .lbrace) {
+ self.advance();
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ _ = self.advance();
+ _ = self.match(.identifier); // as
+ if (!self.match(.comma)) break;
+ }
+ try self.expect(.rbrace);
+ if (self.matchKeyword(.from)) {
+ _ = self.advance();
+ }
+ try self.expect(.semicolon);
+ return Stmt{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .empty = {} } };
+ }
+
+ const decl = try self.parseStatementListItem();
+ self.semicolon();
+ return decl;
+ }
+
+ // Expression parsing with precedence climbing
+ fn parseExpr(self: *@This(), min_bp: u32) ParseError!*const Expr {
+ var left = try self.parsePrimaryExpr();
+
+ while (true) {
+ const bp = self.bp(self.peek());
+ if (bp <= min_bp) break;
+
+ switch (self.peek()) {
+ .@"=",
+ .@"+=",
+ .@"-=",
+ .@"*=",
+ .@"/=",
+ .@"%=",
+ .@"**=",
+ .@"<<=",
+ .@">>=",
+ .@">>>=",
+ .@"&=",
+ .@"|=",
+ .@"^=",
+ .@"&&=",
+ .@"||=",
+ .@"??=",
+ => {
+ self.advance();
+ const op = self.assignOpFromToken();
+ const right = try self.parseExpr(BP.assign);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .assign = .{ .loc = loc, .op = op, .left = left, .right = right } }, loc);
+ },
+
+ .@"?" => {
+ self.advance();
+ const consequent = try self.parseExpr(0);
+ try self.expect(.@":");
+ const alternate = try self.parseExpr(BP.conditional);
+ const loc = .{ .start = left.loc.start, .end = alternate.loc.end };
+ left = self.arena.exprPtr(.{ .conditional = .{ .loc = loc, .condition = left, .consequent = consequent, .alternate = alternate } }, loc);
+ },
+
+ .@"??" => {
+ self.advance();
+ const right = try self.parseExpr(BP.coalesce);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"??", .left = left, .right = right } }, loc);
+ },
+
+ .@"||" => {
+ self.advance();
+ const right = try self.parseExpr(BP.log_or);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"||", .left = left, .right = right } }, loc);
+ },
+
+ .@"&&" => {
+ self.advance();
+ const right = try self.parseExpr(BP.log_and);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&&", .left = left, .right = right } }, loc);
+ },
+
+ .@"|" => {
+ self.advance();
+ const right = try self.parseExpr(BP.bit_or);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"|", .left = left, .right = right } }, loc);
+ },
+
+ .@"^" => {
+ self.advance();
+ const right = try self.parseExpr(BP.bit_xor);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"^", .left = left, .right = right } }, loc);
+ },
+
+ .@"&" => {
+ self.advance();
+ const right = try self.parseExpr(BP.bit_and);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"&", .left = left, .right = right } }, loc);
+ },
+
+ .@"==" => {
+ self.advance();
+ const right = try self.parseExpr(BP.equality);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"==", .left = left, .right = right } }, loc);
+ },
+
+ .@"!=" => {
+ self.advance();
+ const right = try self.parseExpr(BP.equality);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!=", .left = left, .right = right } }, loc);
+ },
+
+ .@"===" => {
+ self.advance();
+ const right = try self.parseExpr(BP.equality);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"===", .left = left, .right = right } }, loc);
+ },
+
+ .@"!==" => {
+ self.advance();
+ const right = try self.parseExpr(BP.equality);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"!==", .left = left, .right = right } }, loc);
+ },
+
+ .@"<" => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<", .left = left, .right = right } }, loc);
+ },
+
+ .@">" => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">", .left = left, .right = right } }, loc);
+ },
+
+ .@"<=" => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<=", .left = left, .right = right } }, loc);
+ },
+
+ .@">=" => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">=", .left = left, .right = right } }, loc);
+ },
+
+ .in => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .in, .left = left, .right = right } }, loc);
+ },
+
+ .instanceof => {
+ self.advance();
+ const right = try self.parseExpr(BP.relational);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .instanceof, .left = left, .right = right } }, loc);
+ },
+
+ .@"<<" => {
+ self.advance();
+ const right = try self.parseExpr(BP.shift);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"<<", .left = left, .right = right } }, loc);
+ },
+
+ .@">>" => {
+ self.advance();
+ const right = try self.parseExpr(BP.shift);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>", .left = left, .right = right } }, loc);
+ },
+
+ .@">>>" => {
+ self.advance();
+ const right = try self.parseExpr(BP.shift);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@">>>", .left = left, .right = right } }, loc);
+ },
+
+ .@"+" => {
+ self.advance();
+ const right = try self.parseExpr(BP.additive);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"+", .left = left, .right = right } }, loc);
+ },
+
+ .@"-" => {
+ self.advance();
+ const right = try self.parseExpr(BP.additive);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"-", .left = left, .right = right } }, loc);
+ },
+
+ .@"*" => {
+ self.advance();
+ const right = try self.parseExpr(BP.multiplicative);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"*", .left = left, .right = right } }, loc);
+ },
+
+ .@"/" => {
+ self.advance();
+ const right = try self.parseExpr(BP.multiplicative);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"/", .left = left, .right = right } }, loc);
+ },
+
+ .@"%" => {
+ self.advance();
+ const right = try self.parseExpr(BP.multiplicative);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"%", .left = left, .right = right } }, loc);
+ },
+
+ .@"**" => {
+ self.advance();
+ const right = try self.parseExpr(BP.exponentiation - 1);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ left = self.arena.exprPtr(.{ .binary = .{ .loc = loc, .op = .@"**", .left = left, .right = right } }, loc);
+ },
+
+ .lparen => {
+ // Call expression
+ self.advance();
+ var args: [256]Expr = undefined;
+ var arg_count: usize = 0;
+
+ if (self.peek() != .rparen) {
+ while (true) {
+ if (self.peek() == .@"...") {
+ self.advance();
+ const arg = try self.parseExpr(0);
+ args[arg_count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = self.arena.exprPtr(arg.data, arg.loc) } } };
+ arg_count += 1;
+ } else {
+ const arg = try self.parseExpr(0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ }
+ if (!self.match(.comma)) break;
+ }
+ }
+
+ try self.expect(.rparen);
+ const loc = .{ .start = left.loc.start, .end = self.tok().loc.start };
+ left = self.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = false } }, loc);
+ },
+
+ .@"?." => {
+ self.advance();
+ if (self.peek() == .lparen) {
+ // optional call
+ self.advance();
+ var args: [256]Expr = undefined;
+ var arg_count: usize = 0;
+ if (self.peek() != .rparen) {
+ while (true) {
+ const arg = try self.parseExpr(0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+ try self.expect(.rparen);
+ const loc = .{ .start = left.loc.start, .end = self.tok().loc.start };
+ left = self.arena.exprPtr(.{ .call = .{ .loc = loc, .callee = left, .args = args[0..arg_count], .optional = true } }, loc);
+ } else if (self.peek() == .lbracket) {
+ self.advance();
+ const expr = try self.parseExpr(0);
+ try self.expect(.rbracket);
+ const loc = .{ .start = left.loc.start, .end = self.tok().loc.start };
+ left = self.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc);
+ } else if (self.peek() == .identifier or self.peek().isKeyword()) {
+ const prop_tok = self.advance();
+ const prop = self.tokenSlice(prop_tok);
+ const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc);
+ } else if (self.peek() == .private_identifier) {
+ const prop_tok = self.advance();
+ const prop = self.tokenSlice(prop_tok);
+ const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc);
+ }
+ },
+
+ .@"[" => {
+ self.advance();
+ const expr = try self.parseExpr(0);
+ try self.expect(.rbracket);
+ const loc = .{ .start = left.loc.start, .end = self.tok().loc.start };
+ left = self.arena.exprPtr(.{ .computed_member = .{ .loc = loc, .obj = left, .expr = expr } }, loc);
+ },
+
+ .@"." => {
+ self.advance();
+ if (self.peek() == .private_identifier) {
+ const prop_tok = self.advance();
+ const prop = self.tokenSlice(prop_tok);
+ const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .private = prop } } }, loc);
+ } else {
+ const prop_tok = try self.expect(.identifier);
+ const prop = self.tokenSlice(prop_tok);
+ const loc = .{ .start = left.loc.start, .end = prop_tok.loc.end };
+ left = self.arena.exprPtr(.{ .member = .{ .loc = loc, .obj = left, .prop = .{ .ident = prop } } }, loc);
+ }
+ },
+
+ .template_head, .no_sub_template => {
+ const template = try self.parseTemplateLit();
+ const loc = .{ .start = left.loc.start, .end = template.loc.end };
+ left = self.arena.exprPtr(.{ .tagged_template = .{ .loc = loc, .tag = left, .quasi = template } }, loc);
+ },
+
+ .@"++" => {
+ if (!self.isLineTerminatorBeforeNext()) {
+ self.advance();
+ const loc = .{ .start = left.loc.start, .end = left.loc.end + 2 };
+ left = self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"++", .arg = left, .prefix = false } }, loc);
+ } else break;
+ },
+
+ .@"--" => {
+ if (!self.isLineTerminatorBeforeNext()) {
+ self.advance();
+ const loc = .{ .start = left.loc.start, .end = left.loc.end + 2 };
+ left = self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"--", .arg = left, .prefix = false } }, loc);
+ } else break;
+ },
+
+ .comma => {
+ self.advance();
+ const right = try self.parseExpr(BP.comma);
+ const loc = .{ .start = left.loc.start, .end = right.loc.end };
+ var exprs: [2]Expr = .{ left.*, right.* };
+ left = self.arena.exprPtr(.{ .sequence = .{ .loc = loc, .exprs = &exprs } }, loc);
+ },
+
+ else => break,
+ }
+ }
+
+ return left;
+ }
+
+ fn parsePrimaryExpr(self: *@This()) ParseError!*const Expr {
+ const start = self.tok().loc.start;
+
+ switch (self.peek()) {
+ .this => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .this = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .super => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .super = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .null => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .null = {} }, .{ .start = start, .end = t.loc.end });
+ },
+ .true => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .bool = true }, .{ .start = start, .end = t.loc.end });
+ },
+ .false => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .bool = false }, .{ .start = start, .end = t.loc.end });
+ },
+ .number => {
+ const t = self.advance();
+ const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0;
+ return self.arena.exprPtr(.{ .number = val }, .{ .start = start, .end = t.loc.end });
+ },
+ .string => {
+ const t = self.advance();
+ return self.arena.exprPtr(.{ .string = self.tokenSlice(t) }, .{ .start = start, .end = t.loc.end });
+ },
+ .bigint => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ return self.arena.exprPtr(.{ .number = 0 }, .{ .start = start, .end = t.loc.end });
+ },
+ .regex => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ const slash_pos = std.mem.indexOfScalar(u8, slice[1..], '/') orelse 0;
+ const pattern = slice[1 .. slash_pos + 1];
+ const flags = slice[slash_pos + 2 ..];
+ return self.arena.exprPtr(
+ .{ .regex = .{ .pattern = pattern, .flags = flags } },
+ .{ .start = start, .end = t.loc.end },
+ );
+ },
+ .identifier => {
+ const t = self.advance();
+ const name = self.tokenSlice(t);
+ return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = t.loc.end });
+ },
+ .private_identifier => {
+ const t = self.advance();
+ const name = self.tokenSlice(t);
+ return self.arena.exprPtr(.{ .private_ident = name }, .{ .start = start, .end = t.loc.end });
+ },
+ .lbrace => return self.parseObjLiteral(),
+ .lbracket => return self.parseArrayLiteral(),
+ .lparen => {
+ self.advance();
+ if (self.peek() == .rparen) {
+ self.advance();
+ if (self.peek() == .@"=>") {
+ return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = &.{} });
+ }
+ return self.arena.exprPtr(.{ .identifier = "" }, .{ .start = start, .end = self.tok().loc.start });
+ }
+ if (self.peek() == .@"...") {
+ self.advance();
+ const arg = try self.parseExpr(0);
+ try self.expect(.rparen);
+ if (self.peek() == .@"=>") {
+ return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = arg.loc.end }, .params = &.{} });
+ }
+ const loc = .{ .start = start, .end = self.tok().loc.start };
+ return self.arena.exprPtr(.{ .spread = .{ .loc = loc, .arg = arg } }, loc);
+ }
+ const expr = try self.parseExpr(0);
+ if (self.peek() == .comma) {
+ var params: [128]Pat = undefined;
+ var count: usize = 0;
+ params[count] = Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } };
+ count += 1;
+ while (self.match(.comma)) {
+ const p = try self.parseBindingPattern();
+ params[count] = p;
+ count += 1;
+ }
+ try self.expect(.rparen);
+ if (self.peek() == .@"=>") {
+ return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = params[0..count] });
+ }
+ return self.arena.exprPtr(.{ .identifier = "" }, .{ .start = start, .end = self.tok().loc.start });
+ }
+ try self.expect(.rparen);
+ if (self.peek() == .@"=>") {
+ const pat = Pat{ .loc = expr.loc, .data = .{ .ident = expr.data.identifier } };
+ return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = self.tok().loc.start }, .params = &.{pat} });
+ }
+ return self.arena.exprPtr(.{ .parenthesized = expr }, .{ .start = start, .end = self.tok().loc.start });
+ },
+ .function => return self.parseFnExpr(),
+ .class => return self.parseClassExpr(),
+ .async => {
+ self.advance();
+ if (self.peek() == .function) {
+ return self.parseFnExpr();
+ }
+ // async arrow function
+ const id_tok = try self.expect(.identifier);
+ const name = self.tokenSlice(id_tok);
+ if (self.peek() == .@"=>") {
+ const pat = Pat{ .loc = .{ .start = start, .end = id_tok.loc.end }, .data = .{ .ident = name } };
+ return self.parseArrowExpr(.{ .loc = .{ .start = start, .end = id_tok.loc.end }, .params = &.{pat} });
+ }
+ return self.arena.exprPtr(.{ .identifier = name }, .{ .start = start, .end = id_tok.loc.end });
+ },
+ .new => {
+ self.advance();
+ const callee = try self.parseExpr(BP.member);
+ var args: [256]Expr = undefined;
+ var arg_count: usize = 0;
+ if (self.peek() == .lparen) {
+ self.advance();
+ if (self.peek() != .rparen) {
+ while (true) {
+ const arg = try self.parseExpr(0);
+ args[arg_count] = arg.*;
+ arg_count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+ try self.expect(.rparen);
+ }
+ const loc = .{ .start = start, .end = if (arg_count > 0) args[arg_count - 1].loc.end else callee.loc.end };
+ return self.arena.exprPtr(.{ .new = .{ .loc = loc, .callee = callee, .args = args[0..arg_count] } }, loc);
+ },
+ .delete, .typeof, .void, .@"!", .@"~", .@"+", .@"-" => {
+ const op = self.unaryOpFromToken();
+ self.advance();
+ const arg = try self.parseExpr(BP.unary);
+ const loc = .{ .start = start, .end = arg.loc.end };
+ return self.arena.exprPtr(.{ .unary = .{ .loc = loc, .op = op, .arg = arg, .prefix = true } }, loc);
+ },
+ .@"++" => {
+ self.advance();
+ const arg = try self.parseExpr(BP.update);
+ const loc = .{ .start = start, .end = arg.loc.end };
+ return self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"++", .arg = arg, .prefix = true } }, loc);
+ },
+ .@"--" => {
+ self.advance();
+ const arg = try self.parseExpr(BP.update);
+ const loc = .{ .start = start, .end = arg.loc.end };
+ return self.arena.exprPtr(.{ .update = .{ .loc = loc, .op = .@"--", .arg = arg, .prefix = true } }, loc);
+ },
+ .await => {
+ self.advance();
+ const arg = try self.parseExpr(BP.unary);
+ const loc = .{ .start = start, .end = arg.loc.end };
+ return self.arena.exprPtr(.{ .await = .{ .loc = loc, .arg = arg } }, loc);
+ },
+ .yield => {
+ self.advance();
+ var delegate = false;
+ var arg: ?*const Expr = null;
+ if (!self.isLineTerminatorBeforeNext()) {
+ if (self.match(.@"*")) {
+ delegate = true;
+ }
+ if (self.peek() != .rbrace and self.peek() != .rparen and self.peek() != .rbracket and self.peek() != .semicolon and self.peek() != .comma and self.peek() != .@":" and self.peek() != .eof) {
+ arg = try self.parseExpr(0);
+ }
+ }
+ const loc = .{ .start = start, .end = if (arg) |a| a.loc.end else start + 5 };
+ return self.arena.exprPtr(.{ .yield = .{ .loc = loc, .arg = arg, .delegate = delegate } }, loc);
+ },
+ .template_head, .no_sub_template => return self.arena.exprPtr(.{ .template = try self.parseTemplateLit() }, .{ .start = start, .end = self.tok().loc.start }),
+ .import => {
+ self.advance();
+ try self.expect(.lparen);
+ const source = try self.parseExpr(0);
+ try self.expect(.rparen);
+ return self.arena.exprPtr(.{ .identifier = "import" }, .{ .start = start, .end = self.tok().loc.start });
+ },
+ .@"#" => {
+ self.advance();
+ const id_tok = try self.expect(.identifier);
+ return self.arena.exprPtr(.{ .private_ident = self.tokenSlice(id_tok) }, .{ .start = start, .end = id_tok.loc.end });
+ },
+ else => {
+ if (self.peek().isIdentifier()) {
+ const t = self.advance();
+ const name = self.tokenSlice(t);
+ if (self.peek() == .@"=>") {
+ const pat = Pat{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .data = .{ .ident = name } };
+ return self.parseArrowExpr(.{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .params = &.{pat} });
+ }
+ return self.arena.exprPtr(.{ .identifier = name }, .{ .start = t.loc.start, .end = t.loc.end });
+ }
+ return ParseError.ExpectedExpression;
+ },
+ }
+ }
+
+ fn parseArrowExpr(self: *@This(), params_info: struct { loc: Loc, params: []const Pat }) ParseError!*const Expr {
+ _ = params_info;
+ try self.expect(.@"=>");
+ const async_token = false;
+
+ if (self.peek() == .lbrace) {
+ const body = try self.parseFunctionBody();
+ return self.arena.exprPtr(
+ .{ .arrow = .{ .loc = .{ .start = params_info.loc.start, .end = body.loc.end }, .async_token = async_token, .params = params_info.params, .body = .{ .block = body } } },
+ .{ .start = params_info.loc.start, .end = body.loc.end },
+ );
+ }
+
+ const expr = try self.parseExpr(0);
+ return self.arena.exprPtr(
+ .{ .arrow = .{ .loc = .{ .start = params_info.loc.start, .end = expr.loc.end }, .async_token = async_token, .params = params_info.params, .body = .{ .expr = expr } } },
+ .{ .start = params_info.loc.start, .end = expr.loc.end },
+ );
+ }
+
+ fn parseObjLiteral(self: *@This()) ParseError!*const Expr {
+ const start = self.advance().loc.start;
+ var props: [128]ast.Prop = undefined;
+ var count: usize = 0;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ if (self.peek() == .@"...") {
+ self.advance();
+ const arg = try self.parseExpr(0);
+ props[count] = .{ .loc = arg.loc, .key = .{ .ident = "" }, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
+ count += 1;
+ } else if (self.peek() == .identifier) {
+ const name_tok = self.tok();
+ const name = self.tokenSlice(name_tok);
+ self.advance();
+ if (self.peek() == .@":") {
+ self.advance();
+ const value = try self.parseExpr(0);
+ props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = value.loc.end }, .key = .{ .ident = name }, .data = .{ .init = .{ .key = .{ .ident = name }, .value = value } } };
+ count += 1;
+ } else if (self.peek() == .@"(") {
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = body.loc.end }, .key = .{ .ident = name }, .data = .{ .method = .{ .loc = .{ .start = name_tok.loc.start, .end = body.loc.end }, .fn_type = .normal, .id = null, .params = params, .body = body } } };
+ count += 1;
+ } else if (self.peek() == .@"=") {
+ self.advance();
+ const value = try self.parseExpr(0);
+ props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = value.loc.end }, .key = .{ .ident = name }, .data = .{ .init = .{ .key = .{ .ident = name }, .value = value } } };
+ count += 1;
+ } else if (self.peek() == .@"," or self.peek() == .rbrace) {
+ props[count] = .{ .loc = .{ .start = name_tok.loc.start, .end = name_tok.loc.end }, .key = .{ .ident = name }, .data = .{ .shorthand = name } };
+ count += 1;
+ }
+ } else if (self.peek() == .number or self.peek() == .string) {
+ const key = try self.parsePropKey();
+ try self.expect(.@":");
+ const value = try self.parseExpr(0);
+ props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
+ count += 1;
+ } else if (self.peek() == .lbracket) {
+ const key = try self.parsePropKey();
+ try self.expect(.@":");
+ const value = try self.parseExpr(0);
+ props[count] = .{ .loc = .{ .start = start, .end = value.loc.end }, .key = key, .data = .{ .init = .{ .key = key, .value = value } } };
+ count += 1;
+ } else if (self.matchKeyword(.get)) {
+ const key = try self.parsePropKey();
+ try self.expect(.lparen);
+ try self.expect(.rparen);
+ const body = try self.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .get = .{ .key = key, .body = body } } };
+ count += 1;
+ } else if (self.matchKeyword(.set)) {
+ const key = try self.parsePropKey();
+ try self.expect(.lparen);
+ const param = try self.parseBindingPattern();
+ try self.expect(.rparen);
+ const body = try self.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .set = .{ .key = key, .param = param, .body = body } } };
+ count += 1;
+ } else if (self.match(.@"*")) {
+ const key = try self.parsePropKey();
+ const params = try self.parseParams();
+ const body = try self.parseFunctionBody();
+ props[count] = .{ .loc = .{ .start = start, .end = body.loc.end }, .key = key, .data = .{ .method = .{ .loc = .{ .start = start, .end = body.loc.end }, .fn_type = .generator, .id = null, .params = params, .body = body } } };
+ count += 1;
+ } else {
+ break;
+ }
+
+ if (!self.match(.comma)) break;
+ }
+
+ try self.expect(.rbrace);
+ return self.arena.exprPtr(.{ .object = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .props = props[0..count] } }, .{ .start = start, .end = self.tok().loc.start });
+ }
+
+ fn parseArrayLiteral(self: *@This()) ParseError!*const Expr {
+ const start = self.advance().loc.start;
+ var elems: [256]?Expr = undefined;
+ var count: usize = 0;
+
+ while (self.peek() != .rbracket and self.peek() != .eof) {
+ if (self.match(.comma)) {
+ elems[count] = null;
+ count += 1;
+ } else if (self.peek() == .@"...") {
+ self.advance();
+ const arg = try self.parseExpr(0);
+ elems[count] = Expr{ .loc = arg.loc, .data = .{ .spread = .{ .loc = arg.loc, .arg = arg } } };
+ count += 1;
+ if (!self.match(.comma)) break;
+ } else {
+ const expr = try self.parseExpr(0);
+ elems[count] = expr.*;
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+
+ try self.expect(.rbracket);
+ return self.arena.exprPtr(.{ .array = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .elems = elems[0..count] } }, .{ .start = start, .end = self.tok().loc.start });
+ }
+
+ fn parseTemplateLit(self: *@This()) ParseError!ast.TemplateLit {
+ const start = self.tok().loc.start;
+ var quasis: [64]ast.TemplateElem = undefined;
+ var exprs: [64]Expr = undefined;
+ var q_count: usize = 0;
+ var e_count: usize = 0;
+
+ while (true) {
+ switch (self.peek()) {
+ .no_sub_template => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = true };
+ q_count += 1;
+ break;
+ },
+ .template_head => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
+ q_count += 1;
+ const expr = try self.parseExpr(0);
+ exprs[e_count] = expr.*;
+ e_count += 1;
+ try self.expect(.rbrace);
+ },
+ .template_middle => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = false };
+ q_count += 1;
+ const expr = try self.parseExpr(0);
+ exprs[e_count] = expr.*;
+ e_count += 1;
+ try self.expect(.rbrace);
+ },
+ .template_tail => {
+ const t = self.advance();
+ const slice = self.tokenSlice(t);
+ quasis[q_count] = .{ .loc = .{ .start = t.loc.start, .end = t.loc.end }, .value = slice, .tail = true };
+ q_count += 1;
+ break;
+ },
+ else => break,
+ }
+ }
+
+ return .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .quasis = quasis[0..q_count], .exprs = exprs[0..e_count] };
+ }
+
+ fn parsePropKey(self: *@This()) ParseError!ast.PropKey {
+ switch (self.peek()) {
+ .identifier, .let, .static, .get, .set, .async => {
+ const t = self.advance();
+ return ast.PropKey{ .ident = self.tokenSlice(t) };
+ },
+ .string => {
+ const t = self.advance();
+ return ast.PropKey{ .string = self.tokenSlice(t) };
+ },
+ .number => {
+ const t = self.advance();
+ const val = std.fmt.parseFloat(f64, self.tokenSlice(t)) catch 0.0;
+ return ast.PropKey{ .number = val };
+ },
+ .lbracket => {
+ self.advance();
+ const expr = try self.parseExpr(0);
+ try self.expect(.rbracket);
+ return ast.PropKey{ .computed = expr };
+ },
+ .private_identifier => {
+ const t = self.advance();
+ return ast.PropKey{ .private = self.tokenSlice(t) };
+ },
+ else => return ParseError.ExpectedPropertyName,
+ }
+ }
+
+ fn parseBindingPattern(self: *@This()) ParseError!Pat {
+ const start = self.tok().loc.start;
+
+ switch (self.peek()) {
+ .identifier, .await, .yield => {
+ const t = self.advance();
+ const name = self.tokenSlice(t);
+ return Pat{ .loc = .{ .start = start, .end = t.loc.end }, .data = .{ .ident = name } };
+ },
+ .lbrace => {
+ self.advance();
+ var props: [128]ast.PatProp = undefined;
+ var count: usize = 0;
+ var rest: ?*const Pat = null;
+
+ while (self.peek() != .rbrace and self.peek() != .eof) {
+ if (self.peek() == .@"...") {
+ self.advance();
+ const pat = try self.parseBindingPattern();
+ rest = self.arena.patPtr(pat.data, pat.loc);
+ break;
+ }
+ if (self.peek() == .identifier) {
+ const name_tok = self.tok();
+ const name = self.tokenSlice(name_tok);
+ self.advance();
+ if (self.peek() == .@":") {
+ self.advance();
+ const val_pat = try self.parseBindingPattern();
+ props[count] = .{ .key_value = .{ .key = .{ .ident = name }, .value = self.arena.patPtr(val_pat.data, val_pat.loc) } };
+ count += 1;
+ } else {
+ props[count] = .{ .shorthand = name };
+ count += 1;
+ }
+ } else if (self.peek() == .lbracket) {
+ const key = try self.parsePropKey();
+ try self.expect(.@":");
+ const val_pat = try self.parseBindingPattern();
+ props[count] = .{ .key_value = .{ .key = key, .value = self.arena.patPtr(val_pat.data, val_pat.loc) } };
+ count += 1;
+ } else {
+ break;
+ }
+ if (!self.match(.comma)) break;
+ }
+
+ try self.expect(.rbrace);
+ return Pat{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .object = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .props = props[0..count], .rest = rest } } };
+ },
+ .lbracket => {
+ self.advance();
+ var elems: [128]?Pat = undefined;
+ var count: usize = 0;
+ var rest: ?*const Pat = null;
+
+ while (self.peek() != .rbracket and self.peek() != .eof) {
+ if (self.match(.comma)) {
+ elems[count] = null;
+ count += 1;
+ } else if (self.peek() == .@"...") {
+ self.advance();
+ const pat = try self.parseBindingPattern();
+ rest = self.arena.patPtr(pat.data, pat.loc);
+ break;
+ } else {
+ const pat = try self.parseBindingPattern();
+ elems[count] = pat;
+ count += 1;
+ if (!self.match(.comma)) break;
+ }
+ }
+
+ try self.expect(.rbracket);
+ return Pat{ .loc = .{ .start = start, .end = self.tok().loc.start }, .data = .{ .array = .{ .loc = .{ .start = start, .end = self.tok().loc.start }, .elems = elems[0..count], .rest = rest } } };
+ },
+ else => return ParseError.ExpectedParameterName,
+ }
+ }
+
+ fn bp(self: *@This(), kind: TokenType) u32 {
+ return switch (kind) {
+ .comma => BP.comma,
+ .@"=", .@"+=", .@"-=", .@"*=", .@"/=", .@"%=", .@"**=", .@"<<=", .@">>=", .@">>>=", .@"&=", .@"|=", .@"^=", .@"&&=", .@"||=", .@"??=" => BP.assign,
+ .@"?" => BP.conditional,
+ .@"??" => BP.coalesce,
+ .@"||" => BP.log_or,
+ .@"&&" => BP.log_and,
+ .@"|" => BP.bit_or,
+ .@"^" => BP.bit_xor,
+ .@"&" => BP.bit_and,
+ .@"==", .@"!=", .@"===", .@"!==" => BP.equality,
+ .@"<", .@">", .@"<=", .@">=", .in, .instanceof => BP.relational,
+ .@"<<", .@">>", .@">>>" => BP.shift,
+ .@"+", .@"-" => BP.additive,
+ .@"*", .@"/", .@"%" => BP.multiplicative,
+ .@"**" => BP.exponentiation,
+ .lparen => BP.call,
+ .@"[", .@".", .@"?." => BP.member,
+ .@"++", .@"--" => BP.update,
+ .template_head, .no_sub_template => BP.member,
+ else => 0,
+ };
+ }
+
+ fn unaryOpFromToken(self: *@This()) ast.UnaryOp {
+ return switch (self.tok().kind) {
+ .@"+" => .@"+",
+ .@"-" => .@"-",
+ .@"!" => .@"!",
+ .@"~" => .@"~",
+ .typeof => .typeof,
+ .void => .void,
+ .delete => .delete,
+ else => .@"+",
+ };
+ }
+
+ fn assignOpFromToken(self: *@This()) ast.AssignOp {
+ return switch (self.tokens[self.pos - 1].kind) {
+ .@"=" => .@"=",
+ .@"+=" => .@"+=",
+ .@"-=" => .@"-=",
+ .@"*=" => .@"*=",
+ .@"/=" => .@"/=",
+ .@"%=" => .@"%=",
+ .@"**=" => .@"**=",
+ .@"<<=" => .@"<<=",
+ .@">>=" => .@">>=",
+ .@">>>=" => .@">>>=",
+ .@"&=" => .@"&=",
+ .@"|=" => .@"|=",
+ .@"^=" => .@"^=",
+ .@"&&=" => .@"&&=",
+ .@"||=" => .@"||=",
+ .@"??=" => .@"??=",
+ else => .@"=",
+ };
+ }
+
+ fn semicolon(self: *@This()) void {
+ if (self.peek() == .semicolon) {
+ self.advance();
+ }
+ }
+
+ fn statementPointer(self: *@This()) ParseError!*const Stmt {
+ const s = try self.parseStatement();
+ return self.arena.stmtPtr(s.data, s.loc);
+ }
+};
+
+pub fn parse(comptime source: []const u8) ParseError!ast.Program {
+ comptime {
+ const tokens = lex.Lexer.tokenize(source);
+ var arena: NodeArena = .{};
+ var parser = Parser.init(tokens, source, &arena);
+ const result = try parser.parseProgram();
+ return result;
+ }
+}
+
+pub fn parseTokens(comptime source: []const u8) ParseError![]const Token {
+ comptime {
+ return lex.Lexer.tokenize(source);
+ }
+}
+
+pub fn tokenType(kind: TokenType) []const u8 {
+ return @tagName(kind);
+}
diff --git a/src/z/z.js b/src/z/z.js
index ba07b9e..e279f42 100644
--- a/src/z/z.js
+++ b/src/z/z.js
@@ -1,10 +1,14 @@
class ZComponent extends HTMLElement {
+ static observedAttributes = [ 'type' ];
+
constructor() {
super();
this.attachShadow({ mode: 'open' });
+ this.onconnected = () => {};
+ this.ondisconnected = () => {};
}
- connectedCallback() {
+ build() {
const type = this.getAttribute('type');
if (type == null) {
@@ -22,6 +26,21 @@ class ZComponent extends HTMLElement {
Zenv.constructors[`component__${type}`](new Zenv(this));
}
+
+ attributeChangedCallback(name, previous, current) {
+ if (name == 'type') {
+ this.shadowRoot.innerHTML = '';
+ this.build()
+ }
+ }
+
+ connectedCallback() {
+ this.onconnected();
+ }
+
+ disconnectedCallback() {
+ this.ondisconnected();
+ }
}
class Zenv {