# Copyright (C) 2006-2007, The Perl Foundation.
# $Id: /mirror/trunk/languages/lua/src/nqp/grammar.pg 26750 2008-04-04T14:35:41.158485Z fperrad  $

grammar Lua::Grammar is PCT::Grammar;

token TOP {
    <.shebang>?
    <block>
    [$ | <.panic: 'eof' expected>]
    {*}
}

rule block {
    {*}                                   #= open
    [ <statement> ';'? ]*
    [ <last_statement> ';'? ]?
    {*}                                   #= close
}

rule statement {
    | <expression_stat> {*}               #= expression_stat
    | <if_stat> {*}                       #= if_stat
    | <while_stat> {*}                    #= while_stat
    | <do_block> {*}                      #= do_block
    | <for_stat> {*}                      #= for_stat
    | <repeat_stat> {*}                   #= repeat_stat
    | <function_stat> {*}                 #= function_stat
    | <local_stat> {*}                    #= local_stat
}

rule last_statement {
    | <return_stat> {*}                   #= return_stat
    | <break_stat> {*}                    #= break_stat
}

rule expression_stat {
    <primary_expression> [ [',' <primary_expression>]* '=' <expression_list> ]?
    {*}
}

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

rule if_stat {
    'if' <expression> 'then' <block>
    ['elseif' <expression> 'then' <block>]*
    ['else' $<else>=<block>]?
    'end'
    {*}
}

rule while_stat {
    'while' <expression> 'do' <block> 'end'
    {*}
}

rule do_block {
    'do' <block> 'end'
    {*}
}

rule for_stat {
    'for'
    [
    | <fornum> {*}                       #= fornum
    | <forlist> {*}                      #= forlist
    ]
    'end'
}

rule fornum {
    <Name> '=' $<var>=<expression> ',' $<limit>=<expression> [ ',' $<step>=<expression> ]?
    'do' <block>
    {*}
}

rule forlist {
    <name_list> 'in' <expression_list> 'do' <block>
    {*}
}

rule repeat_stat {
    'repeat' <block> 'until' <expression>
    {*}
}

rule function_stat {
    'function' <function_name> <function_body>
    {*}
}

rule function_name {
    <Name> [ '.' <Name> ]* [ ':' <Name> ]?
    {*}
}

rule function_body {
    '(' <parameter_list> ')' <block> 'end'
    {*}
}

rule parameter_list {
    [ <name_list> [ ',' <vararg> ]?
    | <vararg>?
    ]
    {*}
}

rule name_list {
    <Name> [ ',' <Name> ]*
    {*}
}

rule local_stat {
    'local'
    [ <local_function> {*}               #= local_function
    | <local_declaration> {*}            #= local_declaration
    ]
}

rule local_function {
    'function' <Name> <function_body>
    {*}
}

rule local_declaration {
    <Name> [ ',' <Name> ]* [ '=' <expression_list> ]?
    {*}
}

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

token break_stat {
    'break'
    {*}
}

rule constructor {
    '{' <tablefieldlist>? '}'
    {*}
}

rule tablefieldlist {
    <tablefield> [ <[,;]> <tablefield> ]* <[,;]>?
    {*}
}

rule tablefield {
    | <record_field> {*}               #= record_field
    | <expr_field> {*}                 #= expr_field
}

rule expr_field {
    <expression>
    {*}
}

rule record_field {
    <field> '=' <expression>
    {*}
}

rule field {
    | <field_name> {*}                 #= field_name
    | <index> {*}                      #= index
}

rule index {
    '[' <expression> ']'
    {*}
}

rule primary_expression {
    <prefix_expression> <slice_expression>*
    {*}
}

rule prefix_expression {
    | '(' <expression> ')' {*}         #= expression
    | <Name> {*}                       #= Name
}

rule slice_expression {
    | '.' <field_name> {*}             #= field_name
    | <index> {*}                      #= index
    | <method_call> {*}                #= method_call
    | <function_args> {*}              #= function_args
}

rule field_name {
    <Name>
    {*}
}

rule method_call {
    ':' <Name> <function_args>
    {*}
}

rule function_args {
    | <arguments> {*}                  #= arguments
    | <constructor> {*}                #= constructor
    | <string_argument> {*}            #= string_argument
}

token string_argument {
    <quote> {*}
}

rule arguments {
    '(' <expression_list>? ')'
    {*}
}

rule simple_expression {
    | <primary_expression> {*}         #= primary_expression
    | <number> {*}                     #= number
    | <quote> {*}                      #= quote
    | <nil> {*}                        #= nil
    | <true> {*}                       #= true
    | <false> {*}                      #= false
    | <vararg> {*}                     #= vararg
    | <constructor> {*}                #= constructor
    | 'function' <function_body> {*}   #= function_body
}

token Name {
    <!keyword> <[a..z]>\w*
    {*}
}

token keyword {
    [and|break|do|elseif|else|end|false|for|function
    |if|in|local|nil|not|or|repeat|then|true|until|while]>>
}


token quote {
    [ \" <string_literal: "> \"
    | \' <string_literal: '> \'
    #| <long_string>
    ]
    {*}
}

token number {
    [ \d+ ['.' \d*]?
    | \d* '.' \d+ [ [eE] ['-']? \d+ ]?
    | '0x' \x+
    ]
    {*}
}

token nil { 'nil' {*} }

token true { 'true' {*} }

token false { 'false' {*} }

token vararg { '...' }

token ws {
    [ \s+
    | '--' \N*
    #| '--' <long_comment>
    ]*
}


token shebang {
    '#' \N*
}


token 'expression'  is optable { ... }

proto 'term:'       is precedence('=')
                    is parsed(&simple_expression)               { ... }

proto 'infix:^'     is looser('term:')      is pirop('pow')
                    is assoc('right')                           { ... }

proto 'prefix:not'  is looser('infix:^')    is pirop('not')     { ... }
proto 'prefix:#'    is equiv('prefix:not')  is past('len')      { ... }
proto 'prefix:-'    is equiv('prefix:not')  is pirop('neg')     { ... }

proto 'infix:*'     is looser('prefix:not') is pirop('n_mul')     { ... }
proto 'infix:/'     is equiv('infix:*')     is pirop('n_div')     { ... }
proto 'infix:%'     is equiv('infix:*')     is pirop('n_mod')     { ... }

proto 'infix:+'     is looser('infix:*')    is pirop('n_add')     { ... }
proto 'infix:-'     is equiv('infix:+')     is pirop('n_sub')     { ... }

proto 'infix:..'    is looser('infix:+')    is pirop('n_concat')
                    is assoc('right')                           { ... }

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:<')         { ... }

proto 'infix:and'   is looser('infix:<')    is pasttype('if')      { ... }

proto 'infix:or'    is looser('infix:and')  is pasttype('unless')       { ... }




# Local Variables:
#   mode: cperl
#   cperl-indent-level: 4
#   fill-column: 100
# End:
# vim: expandtab shiftwidth=4:

