From 85bc26eee0a60b1c3191ebed28d4754001caca57 Mon Sep 17 00:00:00 2001 From: alanz Date: Fri, 7 Jan 2011 16:16:46 +0200 Subject: [PATCH] Preparing to work in the grammar from ECMA262 edition 5. --- .gitignore | 1 + language-javascript.cabal | 1 + src/Language/JavaScript/Parser/Grammar5.y | 739 ++++++++++++++++++++++ src/Language/JavaScript/Parser/Parser.hs | 4 +- 4 files changed, 743 insertions(+), 2 deletions(-) create mode 100644 src/Language/JavaScript/Parser/Grammar5.y diff --git a/.gitignore b/.gitignore index 7abda3c..5351597 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ /unicode/uc-nd.htm /unicode/uc-pc.htm /ecmascript3.txt~ +/src/Language/JavaScript/Parser/Grammar5.y~ diff --git a/language-javascript.cabal b/language-javascript.cabal index d007bd1..3b5c355 100644 --- a/language-javascript.cabal +++ b/language-javascript.cabal @@ -34,6 +34,7 @@ Library Language.JavaScript.Parser.Parser Language.JavaScript.Parser.Lexer Language.JavaScript.Parser.Grammar + Language.JavaScript.Parser.Grammar5 Language.JavaScript.Parser.AST Other-modules: Language.JavaScript.Parser.LexerUtils Language.JavaScript.Parser.ParseError diff --git a/src/Language/JavaScript/Parser/Grammar5.y b/src/Language/JavaScript/Parser/Grammar5.y new file mode 100644 index 0000000..c1b720f --- /dev/null +++ b/src/Language/JavaScript/Parser/Grammar5.y @@ -0,0 +1,739 @@ +{ +module Language.JavaScript.Parser.Grammar5 ( + 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 -}} -- Not in ECMA ed 5? + +Identifier : 'ident' { AST.JSIdentifier (token_literal $1) } + +-- ::= '[' ']' +-- | '[' ']' +-- | '[' ']' +-- | '[' ',' ']' +ArrayLiteral : '[' ']' { AST.JSArrayLiteral [] } + | '[' Elision ']' { AST.JSArrayLiteral $2 } + | '[' ElementList ']' { AST.JSArrayLiteral $2 } + | '[' ElementList ',' Elision ']' { AST.JSArrayLiteral ($2++$4) } + | '[' ElementList ',' ']' { AST.JSArrayLiteral ($2++[AST.JSLiteral ","]) } + +-- ::= ',' +-- | ',' +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: diff --git a/src/Language/JavaScript/Parser/Parser.hs b/src/Language/JavaScript/Parser/Parser.hs index 471e5cb..558fc58 100644 --- a/src/Language/JavaScript/Parser/Parser.hs +++ b/src/Language/JavaScript/Parser/Parser.hs @@ -9,10 +9,10 @@ module Language.JavaScript.Parser.Parser ( ) where import Language.JavaScript.Parser.ParseError -import Language.JavaScript.Parser.Grammar +--import Language.JavaScript.Parser.Grammar +import Language.JavaScript.Parser.Grammar5 import Language.JavaScript.Parser.Lexer import Language.JavaScript.Parser.ParserMonad ---import Language.JavaScript.Parser.SrcLocation import qualified Language.JavaScript.Parser.AST as AST -- | Parse one compound statement, or a sequence of simple statements.