{ ----------------------------------------------------------------------------- -- | -- Module : Language.Python.Version3.Parser.Parser -- Copyright : (c) 2009 Bernie Pope -- License : BSD-style -- Maintainer : bjpop@csse.unimelb.edu.au -- Stability : experimental -- Portability : ghc -- -- Implementation of the Python version 3 parser. Generated by happy. ----------------------------------------------------------------------------- module Language.Python.Version3.Parser.Parser (parseFileInput, parseSingleInput, parseEval) where import Data.Char (isSpace, isAlpha, isDigit) import Language.Python.Version3.Parser.Lexer import Language.Python.Version3.Parser.Token hiding (True, False) import qualified Language.Python.Version3.Parser.Token as Token import Language.Python.Version3.Syntax.AST as AST import Language.Python.Version3.Parser.ParserUtils import Language.Python.Version3.Parser.ParserMonad import Language.Python.Data.SrcLocation import Data.List (foldl') import qualified Data.ByteString.Char8 as BS (ByteString) } %name parseFileInput FileInput %name parseSingleInput SingleInput %name parseEval EvalInput %tokentype { Token } %error { parseError } %monad { P } { thenP } { returnP } %lexer { lexCont } { Token.EOF } %token import { Token.Import _ } ident { Token.Identifier _ $$ } string { Token.String _ $$ } bytestring { Token.ByteString _ $$ } integer { Token.Integer _ $$ } float { Token.Float _ $$ } imaginary { Token.Imaginary _ $$ } '=' { Token.Assign _ } '(' { Token.LeftRoundBracket _ } ')' { Token.RightRoundBracket _ } '[' { Token.LeftSquareBracket _ } ']' { Token.RightSquareBracket _ } '{' { Token.LeftBrace _ } '}' { Token.RightBrace _ } ',' { Token.Comma _ } ';' { Token.SemiColon _ } ':' { Token.Colon _ } def { Token.Def _ } class { Token.Class _ } while { Token.While _ } for { Token.For _ } if { Token.If _ } with { Token.With _ } as { Token.As _ } true { Token.True _ } false { Token.False _ } none { Token.None _ } return { Token.Return _ } yield { Token.Yield _ } indent { Token.Indent _ } dedent { Token.Dedent _ } newline { Token.Newline _ } try { Token.Try _ } except { Token.Except _ } finally { Token.Finally _ } raise { Token.Raise _ } '+' { Token.Plus _ } '-' { Token.Minus _ } '*' { Token.Mult _ } '/' { Token.Div _ } '>' { Token.GreaterThan _ } '<' { Token.LessThan _ } '==' { Token.Equality _ } '>=' { Token.GreaterThanEquals _ } '<=' { Token.LessThanEquals _ } and { Token.And _ } or { Token.Or _ } '**' { Token.Exponent _ } pass { Token.Pass _ } break { Token.Break _ } continue { Token.Continue _ } del { Token.Delete _ } else { Token.Else _ } elif { Token.Elif _ } not { Token.Not _ } '|' { Token.BinaryOr _ } '^' { Token.Xor _ } '&' { Token.BinaryAnd _ } '>>' { Token.ShiftLeft _ } '<<' { Token.ShiftRight _ } '%' { Token.Modulo _ } floordiv { Token.FloorDiv _ } '~' { Token.Tilde _ } '!=' { Token.NotEquals _ } in { Token.In _ } is { Token.Is _ } lambda { Token.Lambda _ } '.' { Token.Dot _ } '...' { Token.Ellipsis _ } '+=' { Token.PlusAssign _ } '-=' { Token.MinusAssign _ } '*=' { Token.MultAssign _ } '/=' { Token.DivAssign _ } '%=' { Token.ModAssign _ } '**=' { Token.PowAssign _ } '&=' { Token.BinAndAssign _ } '|=' { Token.BinOrAssign _ } '^=' { Token.BinXorAssign _ } '<<=' { Token.LeftShiftAssign _ } '>>=' { Token.RightShiftAssign _ } '//=' { Token.FloorDivAssign _ } '@' { Token.At _ } '->' { Token.RightArrow _ } from { Token.From _ } global { Token.Global _ } nonlocal { Token.NonLocal _ } assert { Token.Assert _ } eof { Token.EOF } %% {- Note: newline tokens in the grammar: It seems there are some dubious uses of NEWLINE in the grammar. This is corroborated by this posting: http://mail.python.org/pipermail/python-dev/2005-October/057014.html The general idea is that the lexer does not generate NEWLINE tokens for lines which contain only spaces or comments. However, the grammar sometimes suggests that such tokens may exist. -} -- single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE {- Complete: but we don't support the newline at the end of a compound stmt because the lexer would not produce a newline there. It seems like a weirdness in the way the interactive input works. -} SingleInput :: { [Statement] } SingleInput : newline { [] } | SimpleStmt { $1 } | CompoundStmt {- No newline here! -} { [$1] } -- file_input: (NEWLINE | stmt)* ENDMARKER -- Complete: there is no need to mention the ENDMARKER, happy takes care of that. FileInput :: { Module } FileInput : ManyStmtOrNewline {- No ENDMARKER here! -} { Module $1 } ManyStmtOrNewline :: { [Statement] } ManyStmtOrNewline : ManyStmtOrNewlineRec { concat (reverse $1) } ManyStmtOrNewlineRec :: { [[Statement]] } ManyStmtOrNewlineRec : {- empty -} { [] } | ManyStmtOrNewlineRec NewLineOrStmt { $2 : $1 } NewLineOrStmt :: { [Statement] } NewLineOrStmt : newline { [] } | Stmt { $1 } -- eval_input: testlist NEWLINE* ENDMARKER -- Complete. EvalInput :: { Expr } EvalInput : TestList ZeroOrMoreNewline { $1 } ZeroOrMoreNewline :: { () } ZeroOrMoreNewline : {- empty -} { () } | ZeroOrMoreNewline newline { () } {- decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE decorators: decorator+ decorated: decorators (classdef | funcdef) -} -- Complete Decorator :: { Decorator } Decorator : '@' DottedName OptionalArgList newline { Decorator { decorator_name = $2, decorator_args = $3 } } Decorators :: { [Decorator] } Decorators : DecoratorsRev { reverse $1 } DecoratorsRev :: { [Decorator] } DecoratorsRev : Decorator { [$1] } | DecoratorsRev Decorator { $2 : $1 } Decorated :: { Statement } Decorated : Decorators ClassOrFunction { Decorated { decorated_decorators = $1, decorated_def = $2 } } ClassOrFunction :: { Statement } ClassOrFunction : ClassDef { $1 } | FuncDef { $1 } -- funcdef: 'def' NAME parameters ['->' test] ':' suite -- Complete FuncDef :: { Statement } FuncDef : def Name Parameters OptionalResultAnnotation ':' Suite { Fun { fun_name = $2 , fun_args = $3, fun_result_annotation = $4, fun_body = $6 } } OptionalResultAnnotation :: { Maybe Expr } OptionalResultAnnotation : {- empty -} { Nothing } | '->' Test { Just $2 } -- parameters: '(' [typedargslist] ')' Parameters :: { [Parameter] } Parameters : '(' TypedArgsList ')' { $2 } {- typedargslist: ((tfpdef ['=' test] ',')* ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) -} -- Complete. {- Note the grammar allows an optional trailing comma, but only after the positional arguments. If varargs are used (the star forms) then the optional comma is not allowed. Why is this so? I don't know. The code below uses right recursion extensively. The Happy docs say that there can be problems with this: http://www.haskell.org/happy/doc/html/sec-sequences.html#sec-separators "right-recursive rules require stack space proportional to the length of the list being parsed. This can be extremely important where long sequences are involved, for instance in automatically generated output." At the moment it seems easier to write using right recursion, but we may want to re-visit the use of right recursion at some point. -} TypedArgsList :: { [Parameter] } TypedArgsList : Params { $1 } Params :: { [Parameter] } Params : {- empty -} { [] } | Star { [$1] } | StarStar { [$1] } | Param { [$1] } | Param ',' Params { $1 : $3 } | Star ',' StarParams { $1 : $3 } StarParams :: { [Parameter] } StarParams : Param { [$1] } | Param ',' StarParams { $1 : $3 } | StarStar { [$1] } -- tfpdef: NAME [':' test] -- Complete TfpDef :: { (Ident, Maybe Expr) } TfpDef : Name OptionalColonTest { ($1, $2) } OptionalColonTest :: { Maybe Expr } OptionalColonTest : {- empty -} { Nothing } | ':' Test { Just $2 } OptionalDefault :: { Maybe Expr } OptionalDefault : {- empty -} { Nothing } | '=' Test { Just $2 } Param :: { Parameter } Param : TfpDef OptionalDefault { makeParam $1 $2 } Star :: { Parameter } Star : '*' OptionalTfpDef { makeStarParam $2 } OptionalTfpDef :: { Maybe (Ident, Maybe Expr) } OptionalTfpDef : {- empty -} { Nothing } | TfpDef { Just $1 } StarStar :: { Parameter } StarStar : '**' TfpDef { makeStarStarParam $2 } {- varargslist: ((vfpdef ['=' test] ',')* ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) vfpdef: NAME -} -- Complete {- There is some tedious similarity in these rules to the ones for TypedArgsList. VarArgsList is used for lambda functions, and they do not have parentheses around them (unlike function definitions). Therefore lambda parameters cannot have the optional annotations that normal functions can, because the annotations are introduced using a colon. This would cause ambibguity with the colon that marks the end of the lambda parameter list! See the remarks about right recursion in the comments for TypedArgsList. -} VarArgsList :: { [Parameter] } VarArgsList : VParams { $1 } VParams :: { [Parameter] } VParams : {- empty -} { [] } | VStar { [$1] } | VStarStar { [$1] } | VParam { [$1] } | VParam ',' VParams { $1 : $3 } | VStar ',' VStarParams { $1 : $3 } VStarParams :: { [Parameter] } VStarParams : VParam { [$1] } | VParam ',' VStarParams { $1 : $3 } | VStarStar { [$1] } VParam :: { Parameter } VParam : VfpDef OptionalDefault { makeParam ($1, Nothing) $2 } VStar :: { Parameter } VStar : '*' OptionalVfpDef { makeStarParam $2 } OptionalVfpDef :: { Maybe (Ident, Maybe Expr) } OptionalVfpDef : {- empty -} { Nothing } | VfpDef { Just ($1, Nothing) } VStarStar :: { Parameter } VStarStar : '**' VfpDef { makeStarStarParam ($2, Nothing) } VfpDef :: { Ident } VfpDef : ident { Ident $1 } Name :: { Ident } Name : ident { Ident $1 } -- stmt: simple_stmt | compound_stmt -- Complete Stmt :: { [Statement] } Stmt : SimpleStmt { $1 } | CompoundStmt { [$1] } -- simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE -- Complete SimpleStmt :: { [Statement] } SimpleStmt : SmallStmts OptionalSemiColon newline { reverse $1 } OptionalSemiColon :: { () } OptionalSemiColon : {- empty -} { () } | ';' { () } SmallStmts :: { [Statement] } SmallStmts : SmallStmt { [$1] } | SmallStmts ';' SmallStmt { $3 : $1 } {- small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt) -} -- Complete SmallStmt :: { Statement } SmallStmt : ExprStmt { $1 } | DelStmt { $1 } | PassStmt { $1 } | FlowStmt { $1 } | ImportStmt { $1 } | GlobalStmt { $1 } | NonLocalStmt { $1 } | AssertStmt { $1 } -- expr_stmt: testlist (augassign (yield_expr|testlist) | ('=' (yield_expr|testlist))*) -- Complete ExprStmt :: { Statement } ExprStmt : TestList Assignment { makeAssignmentOrExpr $1 $2 } Assignment :: { Either [Expr] (AssignOp, Expr) } Assignment : NormalAssign { Left $1 } | AugAssign { Right $1 } NormalAssign :: { [Expr] } NormalAssign : ZeroOrMoreAssignRev { reverse $1 } ZeroOrMoreAssignRev :: { [Expr] } ZeroOrMoreAssignRev : {- empty -} { [] } | ZeroOrMoreAssignRev '=' YieldOrTestList { $3 : $1 } YieldOrTestList :: { Expr } YieldOrTestList : YieldExpr { $1 } | TestList { $1 } {- augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=') -} -- Complete AugAssign :: { (AssignOp, Expr) } AugAssign : AugAssignOp YieldOrTestList { ($1, $2) } AugAssignOp :: { AssignOp } AugAssignOp : '+=' { AST.PlusAssign } | '-=' { AST.MinusAssign } | '*=' { AST.MultAssign } | '/=' { AST.DivAssign } | '%=' { AST.ModAssign } | '**=' { AST.PowAssign } | '&=' { AST.BinAndAssign } | '|=' { AST.BinOrAssign } | '^=' { AST.BinXorAssign } | '<<=' { AST.LeftShiftAssign } | '>>=' { AST.RightShiftAssign } | '//=' { AST.FloorDivAssign } -- del_stmt: 'del' exprlist -- Complete DelStmt :: { Statement } DelStmt : del ExprList { AST.Delete { del_exprs = $2 } } -- pass_stmt: 'pass' -- Complete PassStmt :: { Statement } PassStmt : pass { AST.Pass } -- flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt -- Complete FlowStmt :: { Statement } FlowStmt : BreakStmt { $1 } | ContinueStmt { $1 } | ReturnStmt { $1 } | RaiseStmt { $1 } | YieldStmt { $1 } -- break_stmt: 'break' -- Complete BreakStmt :: { Statement } BreakStmt : break { AST.Break } -- continue_stmt: 'continue' -- Complete ContinueStmt :: { Statement } ContinueStmt : continue { AST.Continue } -- return_stmt: 'return' [testlist] -- Complete ReturnStmt :: { Statement } ReturnStmt : return OptionalTestList { AST.Return { return_expr = $2 }} -- yield_stmt: yield_expr -- Complete YieldStmt :: { Statement } YieldStmt : YieldExpr { StmtExpr { stmt_expr = $1 } } -- raise_stmt: 'raise' [test ['from' test]] -- Complete RaiseStmt :: { Statement } RaiseStmt : raise OptionalTestFrom { AST.Raise { raise_expr = $2 }} OptionalTestFrom :: { Maybe (Expr, Maybe Expr) } OptionalTestFrom : {- empty -} { Nothing } | Test OptionalFrom { Just ($1, $2) } OptionalFrom :: { Maybe Expr } OptionalFrom : {- empty -} { Nothing } | from Test { Just $2 } -- import_stmt: import_name | import_from -- Complete ImportStmt :: { Statement } ImportStmt : ImportName { $1 } | ImportFrom { $1 } {- # note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+) 'import' ('*' | '(' import_as_names ')' | import_as_names)) -} ImportFrom :: { Statement } ImportFrom : from ImportModule import StarOrAsNames { FromImport { from_module = $2, from_items = $4 }} StarOrAsNames :: { FromItems } StarOrAsNames : '*' { ImportEverything } | '(' ImportAsNames ')' { $2 } | ImportAsNames { $1 } ImportModule :: { ImportModule } ImportModule : '.' { ImportDot } | '...' { ImportRelative (ImportRelative ImportDot) } | DottedName { ImportName $1 } | '.' ImportModule { ImportRelative $2 } | '...' ImportModule { ImportRelative (ImportRelative (ImportRelative $2)) } -- import_as_name: NAME ['as' NAME] ImportAsName :: { FromItem } ImportAsName : Name OptionalAsName { FromItem { from_item_name = $1, from_as_name = $2 }} -- import_as_names: import_as_name (',' import_as_name)* [','] ImportAsNames :: { FromItems } ImportAsNames : ImportAsNamesRev OptionalComma { FromItems (reverse $1) } ImportAsNamesRev :: { [FromItem] } ImportAsNamesRev : ImportAsName { [$1] } | ImportAsNamesRev ',' ImportAsName { $3 : $1 } -- import_name: 'import' dotted_as_names ImportName :: { Statement } ImportName : import DottedAsNames { AST.Import { import_items = $2 }} -- dotted_as_names: dotted_as_name (',' dotted_as_name)* DottedAsNames :: { [ImportItem] } DottedAsNames : OneOrMoreDottedAsNamesRev { reverse $1 } OneOrMoreDottedAsNamesRev :: { [ImportItem] } OneOrMoreDottedAsNamesRev : DottedAsName { [$1] } | OneOrMoreDottedAsNamesRev ',' DottedAsName { $3 : $1 } -- dotted_as_name: dotted_name ['as' NAME] DottedAsName :: { ImportItem } DottedAsName : DottedName OptionalAsName { ImportItem { import_item_name = $1, import_as_name = $2 }} -- dotted_name: NAME ('.' NAME)* -- Complete DottedName :: { DottedName } DottedName : Name DotNames { $1 : reverse $2 } DotNames :: { DottedName } DotNames : {- empty -} { [] } | DotNames '.' Name { $3 : $1 } -- global_stmt: 'global' NAME (',' NAME)* -- Complete GlobalStmt :: { Statement } GlobalStmt : global OneOrMoreNames { AST.Global { global_vars = $2 }} OneOrMoreNames :: { [Ident] } OneOrMoreNames : OneOrMoreNamesRev { reverse $1 } OneOrMoreNamesRev :: { [Ident] } OneOrMoreNamesRev : Name { [$1] } | OneOrMoreNamesRev ',' Name { $3 : $1 } -- nonlocal_stmt: 'nonlocal' NAME (',' NAME)* NonLocalStmt :: { Statement } NonLocalStmt : nonlocal OneOrMoreNames { AST.NonLocal { nonLocal_vars = $2 }} -- assert_stmt: 'assert' test [',' test] AssertStmt :: { Statement } AssertStmt : assert TestListRev { AST.Assert { assert_exprs = reverse $2 }} -- compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated -- Complete CompoundStmt :: { Statement } CompoundStmt : IfStmt { $1 } | WhileStmt { $1 } | ForStmt { $1 } | TryStmt { $1 } | WithStmt { $1 } | FuncDef { $1 } | ClassDef { $1 } | Decorated { $1 } -- if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] -- Complete IfStmt :: { Statement } IfStmt : IfConditionals OptionalElse { Conditional { cond_guards = $1, cond_else = $2 } } IfConditionals :: { [(Expr,[Statement])] } IfConditionals : If ZeroOrMoreElifs { $1 : $2 } If :: { (Expr, [Statement]) } If : if Test ':' Suite { ($2, $4) } ZeroOrMoreElifs :: { [(Expr, [Statement])]} ZeroOrMoreElifs : ZeroOrMoreElifsRev { reverse $1 } ZeroOrMoreElifsRev :: { [(Expr, [Statement])]} ZeroOrMoreElifsRev : {- empty -} { [] } | ZeroOrMoreElifsRev elif Test ':' Suite { ($3, $5) : $1 } OptionalElse :: { [Statement] } OptionalElse : {- empty -} { [] } | else ':' Suite { $3 } -- while_stmt: 'while' test ':' suite ['else' ':' suite] -- Complete WhileStmt :: { Statement } WhileStmt : while Test ':' Suite OptionalElse { AST.While { while_cond = $2 , while_body = $4, while_else = $5 } } -- for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] -- Complete ForStmt :: { Statement } ForStmt : for ExprList in TestList ':' Suite OptionalElse { AST.For { for_targets = $2, for_generator = $4, for_body = $6, for_else = $7 } } {- try_stmt: ('try' ':' suite ((except_clause ':' suite)+ ['else' ':' suite] ['finally' ':' suite] | 'finally' ':' suite)) -} -- Complete TryStmt :: { Statement } TryStmt : try ':' Suite Handlers { makeTry $3 $4 } Handlers :: { ([Handler], [Statement], [Statement]) } Handlers : OneOrMoreExceptClauses OptionalElse OptionalFinally { ($1, $2, $3) } | finally ':' Suite { ([], [], $3) } OptionalFinally :: { [Statement] } OptionalFinally : {- empty -} { [] } | finally ':' Suite { $3 } OneOrMoreExceptClauses :: { [Handler] } OneOrMoreExceptClauses : OneOrMoreExceptClausesRev { reverse $1 } OneOrMoreExceptClausesRev :: { [Handler] } OneOrMoreExceptClausesRev : Handler { [$1] } | OneOrMoreExceptClausesRev Handler { $2 : $1 } Handler :: { Handler } Handler : ExceptClause ':' Suite { ($1, $3) } {- with_stmt: 'with' test [ with_var ] ':' suite with_var: 'as' expr -} -- Complete WithStmt :: { Statement } WithStmt : with Test OptionalAs ':' Suite { AST.With { with_context = $2, with_as = $3, with_body = $5 } } OptionalAs :: { Maybe Expr } OptionalAs : {- empty -} { Nothing } | as Expr { Just $2 } -- except_clause: 'except' [test ['as' NAME]] -- Complete ExceptClause :: { ExceptClause } ExceptClause : except ExceptExpr { $2 } ExceptExpr :: { ExceptClause } ExceptExpr : {- empty -} { Nothing } | Test OptionalAsName { Just ($1, $2) } OptionalAsName :: { Maybe Ident } OptionalAsName : {- empty -} { Nothing } | as Name { Just $2 } -- suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT -- Complete, but we don't have a newline before indent b/c it is redundant Suite :: { [Statement] } Suite : SimpleStmt { $1 } | {- no newline here! -} indent OneOrMoreStmts dedent { $2 } OneOrMoreStmts :: { [Statement] } OneOrMoreStmts : OneOrMoreStmtsRec { reverse (concat $1) } OneOrMoreStmtsRec :: { [[Statement]] } OneOrMoreStmtsRec : Stmt { [$1] } | OneOrMoreStmtsRec Stmt { $2 : $1 } -- test: or_test ['if' or_test 'else' test] | lambdef -- Complete Test :: { Expr } Test : OrTest TestCond { makeConditionalExpr $1 $2 } | LambDef { $1 } TestCond :: { Maybe (Expr, Expr) } TestCond : {- empty -} { Nothing } | if OrTest else Test { Just ($2, $4) } -- test_nocond: or_test | lambdef_nocond -- Complete TestNoCond :: { Expr } TestNoCond : OrTest { $1 } | LambDefNoCond { $1 } -- lambdef: 'lambda' [varargslist] ':' test -- Complete LambDef :: { Expr } LambDef : lambda VarArgsList ':' Test { AST.Lambda $2 $4 } -- lambdef_nocond: 'lambda' [varargslist] ':' test_nocond -- Complete LambDefNoCond :: { Expr } LambDefNoCond : lambda VarArgsList ':' TestNoCond { AST.Lambda $2 $4 } -- or_test: and_test ('or' and_test)* -- Complete OrTest :: { Expr } OrTest : AndTest OrSequence { makeBinOp $1 $2 } OrSequence :: { [(Op, Expr)] } OrSequence : {- empty -} { [] } | OrSequence or AndTest { (AST.Or, $3) : $1 } -- and_test: not_test ('and' not_test)* -- Complete AndTest :: { Expr } AndTest : NotTest AndSequence { makeBinOp $1 $2 } AndSequence :: { [(Op, Expr)] } AndSequence : {- empty -} { [] } | AndSequence and NotTest { (AST.And, $3) : $1 } -- not_test: 'not' not_test | comparison -- Complete NotTest :: { Expr } NotTest : not NotTest { UnaryOp {operator = AST.Not, op_arg = $2} } | Comparison { $1 } -- comparison: star_expr (comp_op star_expr)* -- Complete Comparison :: { Expr } Comparison : StarExpr CompSequence { makeBinOp $1 $2 } CompSequence :: { [(Op, Expr)] } CompSequence : {- empty -} { [] } | CompSequence CompOp StarExpr { ($2, $3) : $1 } -- comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not' -- Complete CompOp :: { Op } CompOp : '<' { AST.LessThan } | '>' { AST.GreaterThan } | '==' { AST.Equality } | '>=' { AST.GreaterThanEquals } | '<=' { AST.LessThanEquals } | '!=' { AST.NotEquals } | in { AST.In } | not in { AST.NotIn } | IsOp { $1 } IsOp :: { Op } IsOp : is NotPart { $2 } NotPart :: { Op } NotPart : {- empty -} { AST.Is } | not { AST.IsNot } -- star_expr: ['*'] expr -- Incomplete {- XXX The grammar grossly over-states the places where a starred expression can occur. It leads to an ambiguity because of the starred argument lists. I think this is a bug in the grammar, and it will need more investigation to see if it can be fixed. -} StarExpr :: { Expr } StarExpr : Expr { $1 } {- : '*' Expr { Starred { starred_expr = $2 }} | Expr { $1 } -} -- expr: xor_expr ('|' xor_expr)* -- Complete Expr :: { Expr } Expr : XorExpr BinaryOrSequence { makeBinOp $1 $2 } BinaryOrSequence :: { [(Op, Expr)] } BinaryOrSequence : {- empty -} { [] } | BinaryOrSequence '|' XorExpr { (AST.BinaryOr, $3) : $1 } -- xor_expr: and_expr ('^' and_expr)* -- Complete XorExpr :: { Expr } XorExpr : AndExpr XorSequence { makeBinOp $1 $2 } XorSequence :: { [(Op, Expr)] } XorSequence : {- empty -} { [] } | XorSequence '^' AndExpr { (AST.Xor, $3) : $1 } -- and_expr: shift_expr ('&' shift_expr)* -- Complete AndExpr :: { Expr } AndExpr : ShiftExpr BinaryAndSequence { makeBinOp $1 $2 } BinaryAndSequence :: { [(Op, Expr)] } BinaryAndSequence : {- empty -} { [] } | BinaryAndSequence '&' ShiftExpr { (AST.BinaryAnd, $3) : $1 } -- shift_expr: arith_expr (('<<'|'>>') arith_expr)* -- Complete ShiftExpr :: { Expr } ShiftExpr : ArithExpr ShiftSequence { makeBinOp $1 $2 } ShiftSequence :: { [(Op, Expr)] } ShiftSequence : {- empty -} { [] } | ShiftSequence ShiftOp ArithExpr { ($2, $3) : $1 } ShiftOp :: { Op } ShiftOp : '<<' { AST.ShiftLeft } | '>>' { AST.ShiftRight } -- arith_expr: term (('+'|'-') term)* -- Complete ArithExpr :: { Expr } ArithExpr : Term TermSequence { makeBinOp $1 $2 } TermSequence :: { [(Op, Expr)] } TermSequence : {- empty -} { [] } | TermSequence ArithOp Term { ($2, $3) : $1 } ArithOp :: { Op } ArithOp : '+' { AST.Plus } | '-' { AST.Minus } -- term: factor (('*'|'/'|'%'|'//') factor)* -- Complete Term :: { Expr } Term : Factor FactorSequence { makeBinOp $1 $2 } FactorSequence :: { [(Op, Expr)] } FactorSequence : {- empty -} { [] } | FactorSequence MultDivOp Factor { ($2, $3) : $1 } MultDivOp :: { Op } MultDivOP : '*' { AST.Multiply } | '/' { AST.Divide } | '%' { AST.Modulo } | floordiv { AST.FloorDivide } -- factor: ('+'|'-'|'~') factor | power -- Complete Factor :: { Expr } Factor : '+' Factor { UnaryOp { operator = AST.Plus, op_arg = $2 } } | '-' Factor { UnaryOp { operator = AST.Minus, op_arg = $2 } } | '~' Factor { UnaryOp { operator = AST.Invert, op_arg = $2 } } | Power { $1 } -- power: atom trailer* ['**' factor] -- Complete, but maybe we should factor out the common prefix? Power :: { Expr } Power : Atom ZeroOrMoreTrailer { addTrailer $1 $2 } | Atom ZeroOrMoreTrailer '**' Factor { makeBinOp (addTrailer $1 $2) [(AST.Exponent, $4)] } ZeroOrMoreTrailer :: { [Trailer] } ZeroOrMoreTrailer : ZeroOrMoreTrailerRev { reverse $1 } ZeroOrMoreTrailerRev :: { [Trailer] } ZeroOrMoreTrailerRev : {- empty -} { [] } | ZeroOrMoreTrailerRev Trailer { $2 : $1 } {- atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False') -} -- Incomplete Atom :: { Expr } Atom : ParenForm { $1 } | ListForm { $1 } | DictOrSetForm { $1 } | Name { AST.Var $1 } | integer { AST.Int $1 } | float { AST.Float $1 } | imaginary { AST.Imaginary { imaginary_value = $1 }} | OneOrMoreStrings { AST.Strings (reverse $1) } | OneOrMoreByteStrings { AST.ByteStrings (reverse $1) } | '...' { AST.Ellipsis } | none { AST.None } | true { AST.Bool Prelude.True } | false { AST.Bool Prelude.False } ParenForm :: { Expr } ParenForm : '(' YieldOrTestListComp ')' { $2 } ListForm :: { Expr } ListForm : '[' ']' { List { list_exprs = [] } } | '[' TestListComp ']' { makeListForm $2 } DictOrSetForm :: { Expr } DictOrSetForm : '{' '}' { Dictionary { dict_mappings = [] }} | '{' DictOrSetMaker '}' { $2 } YieldOrTestListComp :: { Expr } YieldOrTestListComp : {- empty -} { Tuple { tuple_exprs = [] } } | YieldExpr { $1 } | TestListComp { either id (\c -> Generator { gen_comprehension = c }) $1 } OneOrMoreStrings :: { [String] } OneOrMoreStrings : string { [$1] } | OneOrMoreStrings string { $2 : $1 } OneOrMoreByteStrings :: { [BS.ByteString] } OneOrMoreByteStrings : bytestring { [$1] } | OneOrMoreByteStrings bytestring { $2 : $1 } -- testlist_comp: test ( comp_for | (',' test)* [','] ) -- Complete TestListComp :: { Either Expr (Comprehension Expr) } TestListComp : TestList { Left $1 } | Test CompFor { Right (makeComprehension $1 $2) } -- trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME -- Complete Trailer :: { Trailer } Trailer : '(' ArgList ')' { TrailerCall $2 } | '[' SubscriptList ']' { TrailerSubscript $2 } | '.' Name { TrailerDot $2 } -- subscriptlist: subscript (',' subscript)* [','] SubscriptList :: { [Subscript] } SubscriptList : OneOrMoreSubsRev OptionalComma { reverse $1 } OneOrMoreSubsRev :: { [Subscript] } OneOrMoreSubsRev : Subscript { [$1] } | OneOrMoreSubsRev ',' Subscript { $3 : $1 } -- subscript: test | [test] ':' [test] [sliceop] Subscript :: { Subscript } Subscript : Test { SubscriptExpr $1 } | OptionalTest ':' OptionalTest OptionalSliceOp { SubscriptSlice $1 $3 $4 } OptionalTest :: { Maybe Expr } OptionalTest : {- empty -} { Nothing } | Test { Just $1 } OptionalSliceOp :: { Maybe (Maybe Expr) } OptionalSliceOp : {- empty -} { Nothing } | SliceOp { Just $1 } -- sliceop: ':' [test] SliceOp :: { Maybe Expr } SliceOp : ':' OptionalTest { $2 } -- exprlist: star_expr (',' star_expr)* [','] -- Complete ExprList :: { [Expr] } ExprList : ExprListRev OptionalComma { reverse $1 } OptionalComma :: { Bool } OptionalComma : {- empty -} { False } | ',' { True } ExprListRev :: { [Expr] } ExprListRev : StarExpr { [$1] } | ExprListRev ',' StarExpr { $3 : $1 } -- testlist: test (',' test)* [','] -- Complete -- Some trickery here because the of the optional trailing comma, which -- could turn a normal expression into a tuple. -- Very occasionally, TestList is used to generate something which is not -- a tuple (such as the square bracket notation in list literals). Therefore -- it would seem like a good idea to not return a tuple in this case, but -- a list of expressions. However this would complicate a lot of code -- since we would have to carry around the optional comma information. -- I've decided to leave it as a tuple, and in special cases, unpack the -- tuple and pull out the list of expressions. TestList :: { Expr } TestList : TestListRev OptionalComma { makeTupleOrExpr (reverse $1) $2 } TestListRev :: { [Expr] } TestListRev : Test { [$1] } | TestListRev ',' Test { $3 : $1 } {- dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | (test (comp_for | (',' test)* [','])) ) -} DictOrSetMaker :: { Expr } DictOrSetMaker : Test ':' Test DictRest { makeDictionary ($1, $3) $4 } | Test SetRest { makeSet $1 $2 } DictRest :: { Either CompFor [(Expr, Expr)] } DictRest : CompFor { Left $1 } | ZeroOrMoreDictMappings OptionalComma { Right (reverse $1) } ZeroOrMoreDictMappings :: { [(Expr, Expr)] } ZeroOrMoreDictMappings : {- empty -} { [] } | ZeroOrMoreDictMappings ',' Test ':' Test { ($3,$5) : $1 } SetRest :: { Either CompFor [Expr] } SetRest : CompFor { Left $1 } | ZeroOrMoreCommaTest OptionalComma { Right (reverse $1) } ZeroOrMoreCommaTest :: { [Expr] } ZeroOrMoreCommaTest : {- empty -} { [] } | ZeroOrMoreCommaTest ',' Test { $3 : $1 } -- classdef: 'class' NAME ['(' [arglist] ')'] ':' suite -- Complete ClassDef :: { Statement } ClassDef : class Name OptionalArgList ':' Suite { AST.Class { class_name = $2, class_args = $3, class_body = $5 }} OptionalArgList :: { [Argument] } OptionalArgList : {- empty -} { [] } | '(' ArgList ')' { $2 } {- arglist: (argument ',')* (argument [','] |'*' test (',' argument)* [',' '**' test] |'**' test) -} {- Deviates slightly from the grammar because we allow empty arg lists. The grammar allows for this by making arg lists non-empty but optional. Works out the same in the end. -} ArgList :: { [Argument] } ArgList : {- empty -} { [] } | '*' Test { [ArgVarArgsPos { arg_expr = $2 }] } | '**' Test { [ArgVarArgsKeyword { arg_expr = $2 }] } | Argument { [$1] } | Argument ',' ArgList { $1 : $3 } | '*' Test ',' StarArgs { ArgVarArgsPos { arg_expr = $2 } : $4 } StarArgs :: { [Argument] } StarArgs : Argument { [$1] } | Argument ',' StarArgs { $1 : $3 } | '**' Test { [ArgVarArgsKeyword { arg_expr = $2 }] } -- argument: test [comp_for] | test '=' test # Really [keyword '='] test Argument :: { Argument } Argument : Name '=' Test { ArgKeyword { arg_keyword = $1, arg_expr = $3 }} | Test { ArgExpr { arg_expr = $1 }} | Test CompFor { ArgExpr { arg_expr = Generator { gen_comprehension = makeComprehension $1 $2 }}} -- comp_iter: comp_for | comp_if -- Complete CompIter :: { CompIter } CompIter : CompFor { IterFor $1 } | CompIf { IterIf $1 } -- comp_for: 'for' exprlist 'in' or_test [comp_iter] CompFor :: { CompFor } CompFor : for ExprList in OrTest OptionalCompIter { CompFor { comp_for_exprs = $2, comp_in_expr = $4, comp_for_iter = $5 }} OptionalCompIter :: { Maybe CompIter } OptionalCompIter : {- empty -} { Nothing } | CompIter { Just $1 } -- comp_if: 'if' test_nocond [comp_iter] CompIf :: { CompIf } CompIf : if TestNoCond OptionalCompIter { CompIf { comp_if = $2, comp_if_iter = $3 } } -- testlist1: test (',' test)* -- Not used in the rest of the grammar! -- encoding_decl: NAME -- Not used in the rest of the grammqr! -- yield_expr: 'yield' [testlist] -- Complete YieldExpr :: { Expr } YieldExpr : yield OptionalTestList { AST.Yield { yield_expr = $2 } } OptionalTestList :: { Maybe Expr } OptionalTestList : {- empty -} { Nothing } | TestList { Just $1 } { -- Put additional Haskell code in here if needed. }