{ module Language.JavaScript.Parser.Grammar ( parseProgram , parseLiteral , parsePrimaryExpression , parseStatement ) where import Control.Monad.Error.Class (throwError) import Data.Char import Language.JavaScript.Parser.Lexer import Language.JavaScript.Parser.ParserMonad import Language.JavaScript.Parser.SrcLocation import qualified Language.JavaScript.Parser.AST as AST } -- The name of the generated function to be exported from the module %name parseProgram Program %name parseLiteral Literal %name parsePrimaryExpression PrimaryExpression %name parseStatement Statement %tokentype { Token } %error { parseError } %monad { P } { thenP } { returnP } %lexer { lexCont } { EOFToken {} } %token ';' { SemiColonToken {} } ',' { CommaToken {} } '?' { HookToken {} } ':' { ColonToken {} } '||' { OrToken {} } '&&' { AndToken {} } '|' { BitwiseOrToken {} } '^' { BitwiseXorToken {} } '&' { BitwiseAndToken {} } '===' { StrictEqToken {} } '==' { EqToken {} } '=' { SimpleAssignToken {} } '!==' { StrictNeToken {} } '!=' { NeToken {} } '<<' { LshToken {} } '<=' { LeToken {} } '<' { LtToken {} } '>>>' { UrshToken {} } '>>' { RshToken {} } '>=' { GeToken {} } '>' { GtToken {} } '++' { IncrementToken {} } '--' { DecrementToken {} } '+' { PlusToken {} } '-' { MinusToken {} } '*' { MulToken {} } '/' { DivToken {} } '%' { ModToken {} } '!' { NotToken {} } '~' { BitwiseNotToken {} } '.' { DotToken {} } '[' { LeftBracketToken {} } ']' { RightBracketToken {} } '{' { LeftCurlyToken {} } '}' { RightCurlyToken {} } '(' { LeftParenToken {} } ')' { RightParenToken {} } '@*/' { CondcommentEndToken {} } 'break' { BreakToken {} } 'case' { CaseToken {} } 'catch' { CatchToken {} } 'const' { ConstToken {} } 'continue' { ContinueToken {} } 'debugger' { DebuggerToken {} } 'default' { DefaultToken {} } 'delete' { DeleteToken {} } 'do' { DoToken {} } 'else' { ElseToken {} } 'enum' { EnumToken {} } 'false' { FalseToken {} } 'finally' { FinallyToken {} } 'for' { ForToken {} } 'function' { FunctionToken {} } 'if' { IfToken {} } 'in' { InToken {} } 'instanceof' { InstanceofToken {} } 'new' { NewToken {} } 'null' { NullToken {} } 'return' { ReturnToken {} } 'switch' { SwitchToken {} } 'this' { ThisToken {} } 'throw' { ThrowToken {} } 'true' { TrueToken {} } 'try' { TryToken {} } 'typeof' { TypeofToken {} } 'var' { VarToken {} } 'void' { VoidToken {} } 'while' { WhileToken {} } 'with' { WithToken {} } 'ident' { IdentifierToken {} } 'decimal' { DecimalToken {} } 'hexinteger' { HexIntegerToken {} } 'string' { StringToken {} } 'regex' { RegExToken {} } 'assign' { AssignToken {} } %% -- -------------------------------------------------------------------- -- Start of GOLD Grammar for Javascript, used as a base -- -------------------------------------------------------------------- -- "Name" = 'JavaScript Grammar' -- "Author" = 'M.Schnoor-Matriciani' -- "Version" = '0.9' -- "About" = 'JavaScript Grammar, Subset of ECMA Edition 3' -- "Start Symbol" = -- "Case Sensitive" = 'True' -- ! ------------------------------------------------- Sets -- {ID Head} = {Letter} + [_] + [$] -- {ID Tail} = {Alphanumeric} + [_] + [$] -- {String Chars1} = {Printable} + {HT} - ["\] -- {String Chars2} = {Printable} + {HT} - [\''] -- {Hex Digit} = {Digit} + [ABCDEF] + [abcdef] -- {RegExp Chars} = {Letter}+{Digit}+['^']+['$']+['*']+['+']+['?']+['{']+['}']+['|']+['-']+['.']+[',']+['#']+['[']+[']']+['_']+['<']+['>'] -- {Non Terminator} = {String Chars1} - {CR} - {LF} -- {Non Zero Digits}={Digit}-[0] -- ! ------------------------------------------------- Terminals -- Identifier = {ID Head}{ID Tail}* -- StringLiteral = '"' ( {String Chars1} | '\' {Printable} )* '"' | '' ( {String Chars2} | '\' {Printable} )* '' -- HexIntegerLiteral = '0x' {Hex Digit}+ -- RegExp = '/' ({RegExp Chars} | '\' {Non Terminator})+ '/' ( 'g' | 'i' | 'm' )* -- DecimalLiteral= {Non Zero Digits}+ '.' {Digit}* ('e' | 'E' ) {Non Zero Digits}+ {Digit}* | {Non Zero Digits}+ '.' {Digit}* | '0' '.' {Digit}+ ('e' | 'E' ) {Non Zero Digits}+ {Digit}* | {Non Zero Digits}+ {Digit}* | '0' | '0' '.' {Digit}+ -- Comment Start = '/*' -- Comment End = '*/' -- Comment Line = '//' -- ! ------------------------------------------------- Rules -- --------------------------------------------------------------------- -- Sort out automatically inserted semi-colons AutoSemi : ';' { AST.JSLiteral ";"} | { AST.JSLiteral ""} -- --------------------------------------------------------------------- -- ::= -- | -- | -- | StringLiteral Literal : NullLiteral {$1} | BooleanLiteral {$1} | NumericLiteral {$1} | StringLiteral {$1} NullLiteral : 'null' { AST.JSLiteral "null" } BooleanLiteral : 'true' { AST.JSLiteral "true" } | 'false' { AST.JSLiteral "false" } -- ::= DecimalLiteral -- | HexIntegerLiteral NumericLiteral : 'decimal' { AST.JSDecimal (token_literal $1)} | 'hexinteger' { AST.JSHexInteger (token_literal $1)} StringLiteral : 'string' {AST.JSStringLiteral (token_delimiter $1) (token_literal $1)} -- ::= RegExp RegularExpressionLiteral : 'regex' {AST.JSRegEx (token_literal $1)} -- ::= 'this' -- | Identifier -- | -- | -- | -- | '(' ')' -- | PrimaryExpression :: { AST.JSNode } PrimaryExpression : 'this' { AST.JSLiteral "this" } | Identifier { $1 {- PrimaryExpression1 -}} | Literal { $1 {- PrimaryExpression2 -}} | ArrayLiteral { $1 {- PrimaryExpression3 -}} | ObjectLiteral { $1 {- PrimaryExpression4 -}} | '(' Expression ')' { AST.JSExpressionParen $2 } | RegularExpressionLiteral { $1 {- PrimaryExpression5 -}} Identifier : 'ident' { AST.JSIdentifier (token_literal $1) } -- ::= '[' ']' -- | '[' ']' -- | '[' ']' -- | '[' ',' ']' ArrayLiteral : '[' ']' { AST.JSArrayLiteral [] } | '[' Elision ']' { AST.JSArrayLiteral $2 } | '[' ElementList ']' { AST.JSArrayLiteral $2 } | '[' ElementList ',' Elision ']' { AST.JSArrayLiteral ($2++$4) } -- ::= ',' -- | ',' Elision : ',' { [(AST.JSElision [])] } | Elision ',' { $1 ++ [(AST.JSElision [])] } -- ::= -- | ',' -- | ',' -- | ElementList : Elision AssignmentExpression { ($1++$2) {- ElementList -}} | ElementList ',' Elision AssignmentExpression { ($1++[(AST.JSElision [])]++$3++$4) {- ElementList -}} | ElementList ',' AssignmentExpression { ($1++[(AST.JSElision [])]++$3) {- ElementList -}} | AssignmentExpression { $1 {- ElementList -}} -- ::= '{' '}' ObjectLiteral :: { AST.JSNode } ObjectLiteral : '{' PropertyNameandValueList '}' { AST.JSObjectLiteral $2 } -- ::= ':' -- | ',' ':' -- Seems we can have function declarations in the value part too -- TODO: And can end with a comma PropertyNameandValueList :: { [ AST.JSNode ] } PropertyNameandValueList : PropertyName ':' AssignmentExpression { [(AST.JSPropertyNameandValue $1 $3)] } | PropertyName ':' FunctionDeclaration { [(AST.JSPropertyNameandValue $1 [$3])] } | PropertyNameandValueList ',' PropertyName ':' AssignmentExpression { ($1 ++ [(AST.JSPropertyNameandValue $3 $5)]) } | PropertyNameandValueList ',' PropertyName ':' FunctionDeclaration { ($1 ++ [(AST.JSPropertyNameandValue $3 [$5])]) } | PropertyNameandValueList ',' { ($1 ++ [(AST.JSLiteral ",")]) } | { [] } -- ::= Identifier -- | StringLiteral -- | PropertyName : Identifier { $1 {- PropertyName1 -}} | StringLiteral { $1 {- PropertyName2 -}} | NumericLiteral { $1 {- PropertyName3 -}} -- ::= -- | -- | '[' ']' -- | '.' Identifier -- | 'new' MemberExpression :: { [AST.JSNode] } MemberExpression : PrimaryExpression { [$1] {- MemberExpression -}} | FunctionExpression { [$1] {- MemberExpression -}} | MemberExpression '[' Expression ']' { [AST.JSMemberSquare $1 $3] } | MemberExpression '.' Identifier { [AST.JSMemberDot $1 $3] } | 'new' MemberExpression Arguments { (((AST.JSLiteral "new "):$2)++[$3])} -- ::= -- | new NewExpression : MemberExpression {$1 {- NewExpression -}} | 'new' NewExpression { (AST.JSLiteral "new "):$2 } -- ::= -- | -- | '[' ']' -- | '.' Identifier CallExpression :: { [AST.JSNode] } CallExpression : MemberExpression Arguments { $1++[$2] {- CallExpression -} } | CallExpression Arguments { ($1++[(AST.JSCallExpression "()" [$2])]) } | CallExpression '[' Expression ']' { ($1++[(AST.JSCallExpression "[]" [$3])]) } | CallExpression '.' Identifier { ($1++[(AST.JSCallExpression "." [$3])]) } -- ::= '(' ')' -- | '(' ')' Arguments : '(' ')' { (AST.JSArguments []) } | '(' ArgumentList ')' { (AST.JSArguments $2) } -- ::= -- | ',' ArgumentList :: { [[AST.JSNode]] } ArgumentList : AssignmentExpression { [$1] {- ArgumentList -}} | ArgumentList ',' AssignmentExpression { $1++[$3] {- ArgumentList2 -} } -- ::= -- | LeftHandSideExpression : NewExpression { $1 {- LeftHandSideExpression1 -}} | CallExpression { $1 {- LeftHandSideExpression12 -}} -- ::= -- | '++' -- | '--' PostfixExpression : LeftHandSideExpression { $1 {- PostfixExpression -} } | PostfixExpression '++' {[(AST.JSExpressionPostfix "++" $1)]} | PostfixExpression '--' {[(AST.JSExpressionPostfix "--" $1)]} -- ::= -- | 'delete' -- | 'void' -- | 'typeof' -- | '++' -- | '--' -- | '+' -- | '-' -- | '~' -- | '!' UnaryExpression :: { [AST.JSNode] } UnaryExpression : PostfixExpression { $1 {- UnaryExpression -} } | 'delete' UnaryExpression { ((AST.JSUnary "delete "):$2)} | 'void' UnaryExpression { ((AST.JSUnary "void "):$2)} | 'typeof' UnaryExpression { ((AST.JSUnary "typeof "):$2)} | '++' UnaryExpression { ((AST.JSUnary "++"):$2) } | '--' UnaryExpression { ((AST.JSUnary "--"):$2)} | '+' UnaryExpression { ((AST.JSUnary "+"):$2)} | '-' UnaryExpression { ((AST.JSUnary "-"):$2)} | '~' UnaryExpression { ((AST.JSUnary "~"):$2)} | '!' UnaryExpression { ((AST.JSUnary "!"):$2)} -- ::= -- | '*' -- | '/' -- | '%' MultiplicativeExpression :: { [AST.JSNode] } MultiplicativeExpression : UnaryExpression { $1 {- MultiplicativeExpression -}} | UnaryExpression '*' MultiplicativeExpression { [(AST.JSExpressionBinary "*" $1 $3)]} | UnaryExpression '/' MultiplicativeExpression { [(AST.JSExpressionBinary "/" $1 $3)]} | UnaryExpression '%' MultiplicativeExpression { [(AST.JSExpressionBinary "%" $1 $3)]} -- ::= '+' -- | '-' -- | AdditiveExpression :: { [AST.JSNode] } AdditiveExpression : AdditiveExpression '+' MultiplicativeExpression { [(AST.JSExpressionBinary "+" $1 $3)]} | AdditiveExpression '-' MultiplicativeExpression { [(AST.JSExpressionBinary "-" $1 $3)]} | MultiplicativeExpression { $1 {- (goRegExp $1)-} {- AdditiveExpression -} } -- ::= '<<' -- | '>>' -- | '>>>' -- | ShiftExpression :: { [AST.JSNode] } ShiftExpression : ShiftExpression '<<' AdditiveExpression { [(AST.JSExpressionBinary "<<" $1 $3)]} | ShiftExpression '>>' AdditiveExpression { [(AST.JSExpressionBinary ">>" $1 $3)]} | ShiftExpression '>>>' AdditiveExpression { [(AST.JSExpressionBinary ">>>" $1 $3)]} | AdditiveExpression { $1 {- ShiftExpression -}} -- ::= -- | '<' -- | '>' -- | '<=' -- | '>=' -- | 'instanceof' RelationalExpression :: { [AST.JSNode] } RelationalExpression : ShiftExpression { $1 {- RelationalExpression -}} | RelationalExpression '<' ShiftExpression { [(AST.JSExpressionBinary "<" $1 $3)]} | RelationalExpression '>' ShiftExpression { [(AST.JSExpressionBinary ">" $1 $3)]} | RelationalExpression '<=' ShiftExpression { [(AST.JSExpressionBinary "<=" $1 $3)]} | RelationalExpression '>=' ShiftExpression { [(AST.JSExpressionBinary ">=" $1 $3)]} | RelationalExpression 'instanceof' ShiftExpression { [(AST.JSExpressionBinary " instanceof " $1 $3)]} -- Strictly speaking should have all the NoIn variants of expressions, -- but we assume syntax is checked so no problem. Cross fingers. | RelationalExpression 'in' ShiftExpression { [(AST.JSExpressionBinary " in " $1 $3)]} -- ::= -- | '==' -- | '!=' -- | '===' -- | '!==' EqualityExpression :: { [AST.JSNode] } EqualityExpression : RelationalExpression { $1 {- EqualityExpression -} } | EqualityExpression '==' RelationalExpression { [(AST.JSExpressionBinary "==" $1 $3)]} | EqualityExpression '!=' RelationalExpression { [(AST.JSExpressionBinary "!=" $1 $3)]} | EqualityExpression '===' RelationalExpression { [(AST.JSExpressionBinary "===" $1 $3)]} | EqualityExpression '!==' RelationalExpression { [(AST.JSExpressionBinary "!==" $1 $3)]} -- ::= -- | '&' BitwiseAndExpression :: { [AST.JSNode] } BitwiseAndExpression : EqualityExpression { $1 {- BitwiseAndExpression -} } | BitwiseAndExpression '&' EqualityExpression { [(AST.JSExpressionBinary "&" $1 $3)]} -- ::= -- | '^' BitwiseXOrExpression :: { [AST.JSNode] } BitwiseXOrExpression : BitwiseAndExpression { $1 {- BitwiseXOrExpression -} } | BitwiseXOrExpression '^' BitwiseAndExpression { [(AST.JSExpressionBinary "^" $1 $3)]} -- ::= -- | '|' BitwiseOrExpression :: { [AST.JSNode] } BitwiseOrExpression : BitwiseXOrExpression { $1 {- BitwiseOrExpression -} } | BitwiseOrExpression '|' BitwiseXOrExpression { [(AST.JSExpressionBinary "|" $1 $3)]} -- ::= -- | '&&' LogicalAndExpression :: { [AST.JSNode] } LogicalAndExpression : BitwiseOrExpression { $1 {- LogicalAndExpression -} } | LogicalAndExpression '&&' BitwiseOrExpression { [(AST.JSExpressionBinary "&&" $1 $3)]} -- ::= -- | '||' LogicalOrExpression :: { [AST.JSNode] } LogicalOrExpression : LogicalAndExpression { $1 {- LogicalOrExpression -} } | LogicalOrExpression '||' LogicalAndExpression { [(AST.JSExpressionBinary "||" $1 $3)]} -- ::= -- | '?' ':' ConditionalExpression :: { [AST.JSNode] } ConditionalExpression : LogicalOrExpression { $1 {- ConditionalExpression -} } | LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression { [AST.JSExpressionTernary $1 $3 $5] } -- ::= -- | AssignmentExpression :: { [AST.JSNode] } AssignmentExpression : ConditionalExpression { $1 {- AssignmentExpression -}} | LeftHandSideExpression AssignmentOperator AssignmentExpression { ($1++[$2]++$3) } -- ::= '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=' AssignmentOperator :: { AST.JSNode } AssignmentOperator : 'assign' { AST.JSOperator (token_literal $1) } | '=' { AST.JSOperator "=" } -- ::= -- | ',' Expression :: { AST.JSNode } Expression : AssignmentExpression { AST.JSExpression $1 {- Expression -} } | Expression ',' AssignmentExpression { flattenExpression $1 $3 } ExpressionOpt :: { [AST.JSNode] } ExpressionOpt : Expression { [$1] {- ExpressionOpt -}} | { [] {- ExpressionOpt -}} -- ::= -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | Statement :: { AST.JSNode } Statement : StatementNoEmpty { $1 {- Statement1 -}} | EmptyStatement { $1 {- Statement3 -}} StatementNoEmpty :: { AST.JSNode } StatementNoEmpty : StatementBlock { $1 {- StatementNoEmpty1 -}} | VariableStatement { $1 {- StatementNoEmpty2 -}} -- | EmptyStatement { $1 {- StatementNoEmpty3 -}} -- | IfStatement { $1 {- StatementNoEmpty4 -}} | IfElseStatement { $1 {- StatementNoEmpty5 -}} | IterationStatement { $1 {- StatementNoEmpty6 -}} | ContinueStatement { $1 {- StatementNoEmpty7 -}} | BreakStatement { $1 {- StatementNoEmpty8 -}} | ReturnStatement { $1 {- StatementNoEmpty9 -}} | WithStatement { $1 {- StatementNoEmpty10 -}} | LabelledStatement { $1 {- StatementNoEmpty11 -}} | SwitchStatement { $1 {- StatementNoEmpty12 -}} | ThrowStatement { $1 {- StatementNoEmpty13 -}} | TryStatement { $1 {- StatementNoEmpty14 -}} | Expression { $1 {- StatementNoEmpty15 -}} StatementBlock : '{' '}' { (AST.JSLiteral ";") } | '{' StatementList '}' { (if ($2 == AST.JSStatementList [AST.JSLiteral ";"]) then (AST.JSLiteral ";") else (AST.JSBlock $2)) } Block : '{' '}' { (AST.JSBlock (AST.JSStatementList [])) } | '{' StatementList '}' { (AST.JSBlock $2) } StatementList :: { AST.JSNode } StatementList : Statement { (AST.JSStatementList [$1]) } | StatementList Statement { (combineStatements $1 $2) } -- ::= var ';' VariableStatement : 'var' VariableDeclarationList AutoSemi { AST.JSVariables "var" $2 } | 'const' VariableDeclarationList AutoSemi { AST.JSVariables "const" $2 } -- ::= -- | ',' VariableDeclarationList :: { [AST.JSNode] } VariableDeclarationList : VariableDeclaration { [$1] {- VariableDeclarationList -}} | VariableDeclarationList ',' VariableDeclaration { ($1 ++ [$3]) {- VariableDeclarationList -}} VariableDeclaration :: { AST.JSNode } VariableDeclaration : Identifier { (AST.JSVarDecl $1 [])} | Identifier Initializer { (AST.JSVarDecl $1 $2)} -- ::= '=' Initializer : '=' AssignmentExpression { $2 {- Initializer -}} EmptyStatement : ';' { (AST.JSLiteral ";") } {- -- ::= 'if' '(' ')' IfStatement : 'if' '(' Expression ')' Statement { (AST.JSIf $3 $5) } -- ::= 'if' '(' ')' 'else' IfElseStatement : 'if' '(' Expression ')' StatementSemi 'else' Statement { (AST.JSIfElse $3 $5 $7) } -} {- IfElseStatement : 'if' '(' Expression ')' Statement ';' 'else' Statement { (AST.JSIfElse $3 (AST.JSBlock (AST.JSStatementList [$5])) $8) } | 'if' '(' Expression ')' Statement 'else' Statement { (AST.JSIfElse $3 $5 $7) } | 'if' '(' Expression ')' Statement { (AST.JSIf $3 $5) } -} IfElseStatement :: { AST.JSNode } IfElseStatement : 'if' '(' Expression ')' StatementSemi IfElseRest { (if ($6 /= []) then (if (length $6 == 1) then (AST.JSIfElse $3 $5 (head $6)) else (AST.JSIfElse $3 (AST.JSBlock (AST.JSStatementList [$5])) (last $6))) else (AST.JSIf $3 $5)) } IfElseRest :: { [AST.JSNode] } IfElseRest : -- ';' 'else' Statement { [$3,$3] } -- Horrible, but a type-compliant signal nevertheless {- | -} 'else' Statement { [$2] } | { [] } {- ElsePart : 'else' { 1 } | ';' 'else' { 2 } -} StatementSemi : StatementNoEmpty ';' { (AST.JSBlock (AST.JSStatementList [$1])) } | StatementNoEmpty { $1 {- StatementSemi -}} | ';' { AST.JSLiteral ";" } -- ::= 'do' 'while' '(' ')' ';' -- | 'while' '(' ')' -- | 'for' '(' ';' ';' ')' -- | 'for' '(' 'var' ';' ';' ')' -- | 'for' '(' in ')' -- | 'for' '(' 'var' in ')' IterationStatement :: { AST.JSNode } IterationStatement : 'do' Statement 'while' '(' Expression ')' AutoSemi { (AST.JSDoWhile $2 $5 $7) } | 'while' '(' Expression ')' Statement { (AST.JSWhile $3 $5) } | 'for' '(' ExpressionOpt ';' ExpressionOpt ';' ExpressionOpt ')' Statement { (AST.JSFor $3 $5 $7 $9) } | 'for' '(' 'var' VariableDeclarationList ';' ExpressionOpt ';' ExpressionOpt ')' Statement { (AST.JSForVar $4 $6 $8 $10) } | 'for' '(' LeftHandSideExpression 'in' Expression ')' Statement { (AST.JSForIn $3 $5 $7) } | 'for' '(' 'var' VariableDeclaration 'in' Expression ')' Statement { (AST.JSForVarIn $4 $6 $8) } -- ::= 'continue' ';' -- | 'continue' Identifier ';' ContinueStatement : 'continue' AutoSemi { (AST.JSContinue [$2]) } | 'continue' Identifier AutoSemi { (AST.JSContinue [$2,$3]) } -- ::= 'break' ';' -- | 'break' Identifier ';' BreakStatement : 'break' AutoSemi { (AST.JSBreak [] [$2]) } | 'break' Identifier AutoSemi { (AST.JSBreak [$2] [$3]) } -- ::= 'return' ';' -- | 'return' ';' ReturnStatement : 'return' AutoSemi { (AST.JSReturn [$2]) } | 'return' Expression AutoSemi { (AST.JSReturn [$2,$3]) } -- ::= 'with' '(' ')' ';' WithStatement : 'with' '(' Expression ')' Statement AutoSemi { (AST.JSWith $3 [$5,$6]) } -- ::= 'switch' '(' ')' SwitchStatement : 'switch' '(' Expression ')' CaseBlock { (AST.JSSwitch $3 $5) } -- ::= '{' '}' -- | '{' '}' -- | '{' '}' -- | '{' '}' -- | '{' '}' -- | '{' '}' CaseBlock :: { [AST.JSNode] } CaseBlock : '{' '}' { [] } | '{' CaseClauses '}' { $2 {- CaseBlock2 -}} | '{' CaseClauses DefaultClause '}' { ($2++[$3]) {- CaseBlock3 -}} | '{' CaseClauses DefaultClause CaseClauses '}' { ($2++($3:$4)) {- CaseBlock4 -}} | '{' DefaultClause CaseClauses '}' { ($2:$3) {- CaseBlock5 -}} | '{' DefaultClause '}' { [$2] {- CaseBlock6 -}} -- ::= -- | CaseClauses :: { [AST.JSNode] } CaseClauses : CaseClause { [$1] {- CaseClauses1 -}} | CaseClauses CaseClause { ($1++[$2]) {- CaseClauses2 -}} -- ::= 'case' ':' -- | 'case' ':' CaseClause :: { AST.JSNode } CaseClause : 'case' Expression ':' StatementList { (AST.JSCase $2 $4) } | 'case' Expression ':' { (AST.JSCase $2 (AST.JSStatementList [])) } -- ::= 'default' ':' -- | 'default' ':' DefaultClause :: { AST.JSNode } DefaultClause : 'default' ':' { (AST.JSDefault (AST.JSStatementList [])) } | 'default' ':' StatementList { (AST.JSDefault $3) } -- ::= Identifier ':' LabelledStatement : Identifier ':' Statement { (AST.JSLabelled $1 $3) } -- ::= 'throw' ThrowStatement : 'throw' Expression { (AST.JSThrow $2) } -- Note: worked in updated syntax as per https://developer.mozilla.org/en/JavaScript/Reference/Statements/try...catch -- i.e., 0 or more catches, then an optional finally -- ::= 'try' -- | 'try' -- | 'try' TryStatement : 'try' Block Catches { (AST.JSTry $2 $3) {- TryStatement1 -} } | 'try' Block Finally { (AST.JSTry $2 [$3]) {- TryStatement2 -} } | 'try' Block Catches Finally { (AST.JSTry $2 ($3++[$4])) {- TryStatement3 -} } Catches :: { [AST.JSNode] } Catches : Catch { [$1] {- Catches 1 -} } | Catches Catch { ($1++[$2]) {- Catches 2 -} } -- Note: worked in updated syntax as per https://developer.mozilla.org/en/JavaScript/Reference/Statements/try...catch -- ::= 'catch' '(' Identifier ')' -- becomes -- ::= 'catch' '(' Identifier ')' -- | 'catch' '(' Identifier 'if' ConditionalExpression ')' Catch : 'catch' '(' Identifier ')' Block { (AST.JSCatch $3 [] $5) } | 'catch' '(' Identifier 'if' ConditionalExpression ')' Block { (AST.JSCatch $3 $5 $7) } -- ::= 'finally' Finally : 'finally' Block { (AST.JSFinally $2) } -- ::= 'function' Identifier '(' ')' '{' '}' -- | 'function' Identifier '(' ')' '{' '}' FunctionDeclaration :: { AST.JSNode } FunctionDeclaration : 'function' Identifier '(' FormalParameterList ')' '{' FunctionBody '}' { (AST.JSFunction $2 $4 $7) } | 'function' Identifier '(' ')' '{' FunctionBody '}' { (AST.JSFunction $2 [] $6) } -- ::= 'function' '(' ')' '{' '}' -- | 'function' '(' ')' '{' '}' FunctionExpression :: { AST.JSNode } FunctionExpression : 'function' IdentifierOpt '(' ')' '{' FunctionBody '}' { (AST.JSFunctionExpression $2 [] $6) } | 'function' IdentifierOpt '(' FormalParameterList ')' '{' FunctionBody '}' { (AST.JSFunctionExpression $2 $4 $7) } IdentifierOpt :: { [AST.JSNode] } IdentifierOpt : Identifier { [$1] {- IdentifierOpt -}} | { [] {- IdentifierOpt -}} -- ::= Identifier -- | ',' Identifier FormalParameterList :: { [AST.JSNode] } FormalParameterList : Identifier { [$1] {- FormalParameterList -}} | FormalParameterList ',' Identifier { ($1++[$3]) } -- ::= -- | FunctionBody :: { AST.JSNode } FunctionBody : SourceElements { (AST.JSFunctionBody [$1]) } | { (AST.JSFunctionBody []) } -- ::= Program : SourceElementsTop { $1 {- Program -}} -- ::= -- | SourceElements :: { AST.JSNode } SourceElements : SourceElement { (AST.JSSourceElements [$1]) } | SourceElements SourceElement { (combineSourceElements $1 $2) } SourceElementsTop :: { AST.JSNode } SourceElementsTop : SourceElement { (AST.JSSourceElementsTop [$1]) } | SourceElementsTop SourceElement { (combineSourceElementsTop $1 $2) } -- ::= -- | SourceElement :: { AST.JSNode } SourceElement : Statement { $1 {- SourceElement1 -} } | FunctionDeclaration { $1 {- SourceElement2 -} } { combineSourceElements :: AST.JSNode -> AST.JSNode -> AST.JSNode combineSourceElements (AST.JSSourceElements xs) x = (AST.JSSourceElements (xs++[x]) ) combineSourceElementsTop :: AST.JSNode -> AST.JSNode -> AST.JSNode combineSourceElementsTop (AST.JSSourceElementsTop xs) x = (AST.JSSourceElementsTop (xs++[x]) ) combineStatements :: AST.JSNode -> AST.JSNode -> AST.JSNode combineStatements (AST.JSStatementList xs) (AST.JSStatementList ys) = (AST.JSStatementList (xs++ys) ) combineStatements (AST.JSStatementList xs) y = (AST.JSStatementList (xs++[y]) ) parseError :: Token -> P a parseError = throwError . UnexpectedToken flattenExpression :: AST.JSNode -> [AST.JSNode] -> AST.JSNode flattenExpression (AST.JSExpression xs) e = AST.JSExpression (xs++litComma++e) where litComma :: [AST.JSNode] litComma = [(AST.JSLiteral ",")] } -- Set emacs mode -- Local Variables: -- mode:haskell -- End: