## $Id: /mirror/trunk/languages/pynie/src/parser/Grammar.pg 35865 2009-01-20T18:52:51.878540Z pmichaud  $

## TITLE
##     Pynie::Grammar -- a grammar for parsing Python
##
## DESCRIPTION
##
## These are a set of rules for parsing programs written in Python.
## Many of the rules are derived from the "Python Language Reference
## Manual", version 2.5 by Guido van Rossum (Fred L. Drake, Jr., Editor).
## Available online at http://docs.python.org/ref/ and
## http://docs.python.org/ref/grammar.txt .

grammar Pynie::Grammar is PCT::Grammar;

token TOP {
    <.indent_zero>
    <file_input>
    {*}
}

token newline { [ <.ws> \n ]+ }

token ws {
    [ ^^
      [ \h* '#' \N* ]? ]
    ||
    [ \h*   [ '#' \N* ]? ]
}

token file_input {
    ^ [ <.newline> | <statement> ]*
    [ $ || <.panic: 'syntax_error'> ]
    {*}
}

token suite {
    | <suite1> {*}               #= suite1
    | <suite2> {*}               #= suite2
}

token suite1 {
    <stmt_list> <.newline> {*}
}

token suite2 {
    <.newline> <.indent> <statement>
    [ <.indent_same> <statement> ]*
    [ <.dedent> | <.panic: 'IndentationError: unindent does not match any outer indentation level'> ]
    {*}
}

token statement {
    | <compound_stmt> {*}                        #= compound_stmt
    | <stmt_list> <.newline> {*}                 #= stmt_list
}

token stmt_list {
    <simple_stmt> [ ';' <simple_stmt> ]* ';'?
    {*}
}

token compound_stmt {
    | <if_stmt> {*}                              #= if_stmt
    | <while_stmt> {*}                           #= while_stmt
    | <for_stmt> {*}                             #= for_stmt
    | <try_stmt> {*}                             #= try_stmt
    | <with_stmt>
    | <funcdef> {*}                              #= funcdef
    | <classdef> {*}                             #= classdef
}

rule if_stmt {
    'if' <expression> ':' <suite>
    [ 'elif' <expression> ':' <suite> ]*
    [ 'else' ':' $<else>=<suite> ]?
    {*}
}

rule while_stmt {
    'while' <expression> ':' <suite>
    [ 'else' ':' <else=suite> ]?
    {*}
}

rule for_stmt {
    'for' <target> 'in' <expression_list> ':' <suite>
#    'for' <target_list> 'in' <expression_list> ':' <suite>
#    [ 'else' ':' <suite> ]?
    {*}
}

rule try_stmt {
    | <try1_stmt> {*}                   #= try1_stmt
    | <try2_stmt>
}

rule try1_stmt {
    'try' ':' <try=suite>
    'except' ':' <except=suite>
#    'except' [ <expression> [ ',' <target> ]? ]? ':' <suite>
#    [ 'else' ':' <suite> ]?
#    [ 'finally' ':' <suite> ]?
    {*}
}

rule try2_stmt {
    'try' ':' <suite>
    'finally' ':' <suite>
}

rule with_stmt {
    'with' <expression> [ 'as' <target> ]? ':' <suite>
}

rule funcdef {
    <decorators>? 'def' <funcname> '(' <parameter_list>? ')'
    ':' <suite>
    {*}
}

rule decorators {
    <decorator>+
}

rule decorator {
    '@' <dotted_name> [ '(' [ <argument_list> ','? ]? ')' ]? <.newline>
}

rule argument_list {
    [
      [ <keyword_arguments>
      | <positional_arguments> ',' <keyword_arguments>
      | <positional_arguments>
      ]
        [ ',' <excess_positional_argument> ]?
        [ ',' <excess_keyword_argument> ]?
    | <keyword_arguments>
        [ ',' <excess_positional_argument> ]?
        [ ',' <excess_keyword_argument> ]?
    | <excess_positional_argument>
        [ ',' <excess_keyword_argument> ]?
    | <excess_keyword_argument>
    ]
    {*}
}


rule excess_keyword_argument {
    '**' <expression>
}

rule excess_positional_argument {
    '*' <expression>
}

rule positional_arguments {
    <expression> [ ',' <expression> ]*
    {*}
}

rule keyword_arguments {
    <keyword_item> [ ',' <keyword_item> ]*
    {*}
}

rule keyword_item {
    <identifier> '=' <expression>
    {*}
}

rule dotted_name {
    <identifier> [ '.' <identifier> ]*
}

rule funcname {
    <identifier> {*}
}

rule parameter_list {
    [ <defparameter> ',' ]*
    [ <excess_positional_parameter> ',' <excess_keyword_parameter>
    | <excess_positional_parameter>
    | <excess_keyword_parameter>
    | <defparameter> ','?
    ]
    {*}
}

rule excess_positional_parameter {
    '*' <identifier>
    {*}
}

rule excess_keyword_parameter {
    '**' <identifier>
    {*}
}

rule defparameter {
    <parameter> [ '=' <expression> ]?
    {*}
}

rule sublist {
    <parameter> [ ',' <parameter> ]* ','?
}

rule parameter {
    | <identifier> {*}             #= identifier
    | '(' <sublist> ')'            #= sublist
}


rule classdef {
    'class' <classname> <inheritance>? ':' <suite>
    {*}
}

rule classname {
    <identifier> {*}
}

rule inheritance {
    '(' <expression_list>? ')'
}


rule lambda_form {
    'lambda' <parameter_list>? ':' <expression>
    {*}
}

token simple_stmt {
    | <assert_stmt> {*}                          #= assert_stmt
    | <assignment_stmt> {*}                      #= assignment_stmt
    | <augmented_assignment_stmt> {*}            #= augmented_assignment_stmt
    | <expression_stmt> {*}                      #= expression_stmt
    | <pass_stmt> {*}                            #= pass_stmt
    | <del_stmt> {*}                             #= del_stmt
    | <print_stmt> {*}                           #= print_stmt
    | <return_stmt> {*}                          #= return_stmt
    | <yield_stmt>
    | <raise_stmt> {*}                           #= raise_stmt
    | <break_stmt>
    | <continue_stmt>
    | <import_stmt>
    | <global_stmt> {*}                          #= global_stmt
    | <exec_stmt>
}

rule expression_stmt {
    <expression_list> {*}
}

rule assert_stmt {
    'assert' <exp1=expression> [ ',' <exp2=expression> ]?
    {*}
}

rule assignment_stmt {
    <target_list> '=' <expression_list>
    {*}
}

rule augmented_assignment_stmt {
    <target> <augop> <expression>
    {*}
}

rule target_list {
    <target> [ ',' <target> ]* ','?
    {*}
}

rule target {
    | '(' <target_list> ')'
    | '[' <target_list> ']'
    | <identifier> {*}                           #= identifier
    #| <primary> <attributeref>
    #| <primary> <subscription>
    #| <primary> <slicing>
}

rule call {
    '(' <argument_list>? ')' {*}                                  #= argument_list
    #| '(' [ <argument_list>? ','? ]? ')' {*}
    #| '(' [ <argument_list> ','? | <test> <genexpr_for> ] ')'
}

rule attributeref {
    '.' <identifier>
    {*}
}

rule methodcall {
    '.' <identifier> <call>
    {*}
}

rule subscription {
    '[' <tuple_or_scalar> ']' {*}
}

rule slicing {
    | <simple_slicing>
    | <extended_slicing>
}

rule simple_slicing {
    '[' <short_slice> ']'
}

rule extended_slicing {
    '[' <slice_list> ']'
}

rule slice_list {
    <slice_item> [ ',' <slice_item> ]* ','?
}

rule slice_item {
    <expression> | <proper_slice> | '...'
}

rule proper_slice {
    | <short_slice>
    | <long_slice>
}

rule short_slice {
    [<start=expression>]? ':' [<end=expression>]?
}

rule long_slice {
    <short_slice> ':' <expression>?
}

token identifier { <!reserved> [ <.alpha> | '_' ] \w* {*} }

token name { <!reserved> <[a..z]> [ <alpha> | '_' ]* }

rule print_stmt {
    'print' [ <expression> [ ',' <expression> ]* ( ',' )? ]?
    {*}
}

rule pass_stmt {
    'pass' {*}
}

rule del_stmt {
    'del' <target_list> {*}
}

rule return_stmt {
    'return' <expression_list>?
    {*}
}

rule yield_stmt {
    'yield' <expression_list>
}

rule break_stmt {
    'break'
}

rule continue_stmt {
    'continue'
}

rule raise_stmt {
    'raise' [ <expression>
              [ ',' <expression>
                [ ',' <expression> ]?
              ]?
            ]?
    {*}
}

rule global_stmt {
    'global' <identifier> [ ',' <identifier> ]*
    {*}
}

rule exec_stmt {
    'exec' <expression>
    [ 'in' <expression> [ ',' <expression> ]? ]?
}

rule import_stmt {
    | 'import' <module> <import_alias>?
      [ ',' <module> <import_alias>? ]*
    | <import_module> <identifier> <import_alias>?
      [ ',' <identifier> <import_alias>? ]*
    | <import_module> '(' <identifier> <import_alias>?
      [ ',' <identifier> <import_alias>? ]* ','? ')'
    | <import_module> '*'
}

rule import_module {
    'from' <module> 'import'
}

rule import_alias {
    'as' <name>
}

rule module {
    [ <identifier> '.' ]* <identifier>
}

token literal {
    | <stringliteral> {*}                        #= stringliteral
    | <imagnumber>
    | <floatnumber> {*}                          #= floatnumber
    | <longinteger>
    | <integer> {*}                              #= integer
}

token integer {
    [
    | '0' <[0..7]>+
    | '0' <[xX]> [ \d | <[a..f]> | <[A..F]> ]+
    | <[1..9]> \d* | '0'
    ]
    {*}
}

token intpart { \d+ }

token longinteger { <integer> <[lL]> }

token imagnumber { [ <floatnumber> | <intpart> ] <[jJ]> }

token floatnumber {
    [ \d* '.' \d+ [ <[eE]> <[+\-]>? \d+ ]?
    | \d+ '.' [ <[eE]> <[+\-]>? \d+ ]?
    | \d+ <[eE]> <[+\-]>? \d+
    ]
    {*}
}

token stringliteral {
    <stringprefix>?
    [ <longstring> {*}                           #= longstring
    | <shortstring> {*}                          #= shortstring
    ]
}

token shortstring {
    [
    | \" ( [ \\ . | <-["\n]> ]* ) \"
    | \' ( [ \\ . | <-['\n]> ]* ) \'
    ]
    {*}
}

regex longstring {
    | \"\"\" ( [ \\ . | . ]* ) \"\"\"
    | \'\'\' ( [ \\ . | . ]* ) \'\'\'
}


token stringprefix {
    $<prefix>=['r'|'u'|'ur'|'R'|'U'|'UR'|'Ur'|'uR']
}

rule expression_list {
     <expression> [',' <expression> ]* ','? {*}
}

rule tuple_or_scalar {
    | <tuple_constructor> {*} #= tuple_constructor
    | <expression> {*}        #= expression
}

rule tuple_constructor {
      <expression> ',' [ <expression> [ ',' <expression> ]* ','? ]? {*}
}

rule list_display {
    '['
        [ <list_literal> {*}       #= list_literal
        # | <list_comprehension> {*} #= list_comprehension
        ]
     ']'
}

rule list_literal {
     [ <expression> [',' <expression> ]* ','? ]?
     {*}
}

rule list_comprehension {
     <expression> <list_iter>+
}

rule list_iter {
    | <list_for> {*}           #= list_for
    | <list_if> {*}            #= list_if
}

rule list_for {
    'for' <expression_list> 'in' <testlist>
    <list_iter>?
}

rule list_if {
    'if' <test>
    <list_iter>?
}

rule test {
    | <or_test> {*}             #= or_test
    | <lambda_form> {*}         #= lambda_form
}

rule testlist {
    <test> [ ',' <test> ]* ','?
}

token augop {
    | '+='  {*}         #= add
    | '-='  {*}         #= sub
    | '*='  {*}         #= mul
    | '/='  {*}         #= div
    | '\%=' {*}         #= mod
#    | '**=' {*}         #= pow
    | '>>=' {*}         #= shr
    | '<<=' {*}         #= shl
    | '&='  {*}         #= band
    | '^='  {*}         #= bxor
    | '|='  {*}         #= bor
}

rule primary {
    <atom> <postop>*
    {*}
}

rule postop {
    | <methodcall> {*}         #= methodcall
    | <attributeref> {*}       #= attributeref
    | <subscription> {*}       #= subscription
    #| <slicing>
    | <call> {*}               #= call
}


rule atom {
    | <identifier> {*}                           #= identifier
    | <literal> {*}                              #= literal
    | <parenth_form> {*}                         #= parenth_form
    | <list_display> {*}                         #= list_display
   #| <generator_expression>           #   ""
    | <dict_display> {*}                         #= dict_display
   #| <string_conversion>              #   ""
}

rule parenth_form {
    '(' <tuple_or_scalar>? ')'
    {*}
}

rule generator_expression {
    '(' <test> <genexpr_for> ')'
}

rule genexpr_for {
    'for' <expression_list> 'in' <test>
    <genexpr_iter>?
}

rule genexpr_iter {
    | <genexpr_for>
    | <genexpr_if>
}

rule genexpr_if {
    'if' <test> <genexpr_iter>?
}

rule dict_display {
    '{' <key_datum_list>? '}'
    {*}
}

rule key_datum_list {
    <key_datum> [ ',' <key_datum> ]* ','?
    {*}
}

rule key_datum {
    <key=expression> ':' <value=expression>
    {*}
}

rule string_conversion {
    '`' <expression_list> '`'
}

# Evaluation order of expressions is based on the table as provided in
# http://docs.python.org/ref/summary.html
#
#
rule expression {
    | <or_test> [ 'if' <or_test> 'else' <test> ]?  {*}     #= or_test
    | <lambda_form> {*}                                    #= lambda_form
}

rule or_test {
    <and_test> [ 'or' <and_test> ]*
    {*}
}

rule and_test {
    <not_test> [ 'and' <not_test> ]*
    {*}
}

rule not_test {
    [$<nots>='not']* <in_test> {*}
}

rule in_test {
    <is_test> [ ['not']? 'in' <is_test> ]*
    {*}
}

rule is_test {
    <comparison> [ 'is' ['not']? <comparison> ]*
    {*}
}

token 'comparison' is optable { ... }

proto 'term:'      is precedence('=')    is parsed(&primary) { ... }
proto infix:<**>   is looser('term:')        { ... }
proto prefix:<~>   is looser(infix:<**>)     { ... }
proto prefix:<+>   is looser(prefix:<~>)     { ... }
proto prefix:<->   is equiv(prefix:<+>)  is pirop('neg')     { ... }
proto infix:<*>    is looser(prefix:<+>) is pirop('mul')     { ... }
proto infix:</>    is equiv(infix:<*>)   is pirop('div')     { ... }
proto infix:<//>   is equiv(infix:<*>)   is pirop('fdiv')    { ... }
proto infix:<%>    is equiv(infix:<*>)   is pirop('mod')     { ... }
proto infix:<+>    is looser(infix:<*>)  is pirop('add')     { ... }
proto infix:<->    is equiv(infix:<+>)   is pirop('sub')     { ... }

proto infix:«<<»   is looser(infix:<+>)  is pirop('shl')     { ... }
proto infix:«>>»   is equiv(infix:«<<»)  is pirop('shr')     { ... }
proto infix:<&>    is looser(infix:«<<»)     { ... }
proto infix:<^>    is looser(infix:<&>)      { ... }
proto infix:<|>    is looser(infix:<^>)      { ... }

proto infix:<==>   is looser(infix:<|>)      { ... }
proto infix:<!=>   is equiv(infix:<==>)      { ... }
proto infix:«<=»   is equiv(infix:<==>)      { ... }
proto infix:«>=»   is equiv(infix:<==>)      { ... }
proto infix:«<»    is equiv(infix:<==>)      { ... }
proto infix:«>»    is equiv(infix:<==>)      { ... }


## Python reserved words and keywords

token reserved {
    <keyword> | 'None'
}

token keyword {
    [ 'and'   | 'assert' | 'break'  | 'class'  | 'continue' | 'def'
    | 'del'   | 'elif'   | 'else'   | 'except' | 'exec'     | 'finally'
    | 'for'   | 'from'   | 'global' | 'if'     | 'import'   | 'in'
    | 'is'    | 'lambda' | 'not'    | 'or'     | 'pass'     | 'print'
    | 'raise' | 'return' | 'try'    | 'while'  | 'with'     | 'yield' ] >>
}

## vim: expandtab sw=4
