# Copyright (C) 2006-2008, The Perl Foundation.
# $Id: /mirror/trunk/languages/lua/src/lua51.pg 34462 2008-12-27T17:23:27.572097Z fperrad  $

=head1 NAME

lua.pg - a Lua 5.1 grammar in PGE

=head1 DESCRIPTION

=head2 GRAMMAR RULES

The grammar rules are according to the original Lua parser as defined lparser.c
in the lua distribution.

=cut

grammar Lua::Grammar;

token TOP {
    ^ <shebang>? <block> <eof>
}

rule block {
    [ <statement> ';'? ]*
}

rule statement {
    | <expression_stat>
    | <if_stat>
    | <while_stat>
    | <do_block>
    | <for_stat>
    | <repeat_stat>
    | <function_stat>
    | <local_stat>
    | <return_statement>
    | <break_statement>
    | <unexpected>
}


rule expression_stat {
    <primary_expression> <assignment>?
}

rule assignment {
    | ',' <primary_expression> <assignment>
    | '=' <expression_list>
}

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


rule if_stat {
    'if' <expression> <.then> <block> <elseif_block>* <else_block>? <.end>
}

rule elseif_block {
    'elseif' <expression> <.then> <block>
}

rule else_block {
    'else' <block>
}


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


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


rule for_stat {
    'for' [ <fornum> | <forlist> ] <for_body>
}

rule fornum {
    <Name> '=' <expression> <.comma> <expression> [ ',' <expression> ]?
}

rule forlist {
    <name_list> <.in> <expression_list>
}

rule for_body {
    <.do> <block> <.end>
}


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


rule function_stat {
    'function' <functionname> <function_body>
}

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

rule function_body {
    '(' <parameter_list> <.closeparen> <block> <.end>
}

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

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


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

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

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


rule return_statement {
    'return' <expression_list>?
}


token break_statement {
    'break'
}


rule constructor {
    '{' <tablefieldlist>? <.closebrace>
}

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

rule tablefield {
    <record_field> | <expression>
}

rule record_field {
    [ <Name> | <index> ] '=' <expression>
}

rule index {
    '[' <expression> <.closebracket>
}


rule primary_expression {
    <prefix_expression> <slice_expression>*
}

rule prefix_expression {
    | <parenthesed_expression>
    | <Name>
}

rule parenthesed_expression {
    '(' <expression> <.closeparen>
}

rule slice_expression {
    | '.' <Name>
    | <index>
    | ':' <Name> <function_args>
    | <function_args>
}

rule function_args {
    | '(' <expression_list>? <.closeparen>
    | <constructor>
    | <string>
}

rule simple_expression {
    | <number>
    | <string>
    | <nil>
    | <true>
    | <false>
    | <vararg>
    | <constructor>
    | 'function' <function_body>
    | <primary_expression>
}


=head2 HELPER RULES

Helper rules will match a specific token, otherwise a syntax error is generated.
These rules make the grammar more readable, so the calls to syntaxerror() are
not all over the grammar, but only in these rules.

=cut

rule do {
    'do' | <syntaxerror: "'do' expected">
}

rule then {
    'then' | <syntaxerror: "'then' expected">
}

rule end {
    'end' | <syntaxerror: "'end' expected">
}

rule until {
    'until' | <syntaxerror: "'until' expected">
}

rule comma {
    ',' | <syntaxerror: "',' expected">
}

rule in {
    'in' | <syntaxerror: "'in' expected">
}

rule closebracket {
    ']'  | <syntaxerror: "']' expected">
}

rule closebrace {
    '}' | <syntaxerror: "'}' expected">
}

rule closeparen {
    ')' | <syntaxerror: "')' expected">
}


=head2 TOKENS

Tokens not defined here (in PGE) are written in PIR in
F<languages/lua/src/lua51.pir>.

=cut

token string {
    | \" <quoted_literal: "\""> \"
    | \' <quoted_literal: "'"> \'
    | <long_string>
}

token nil {
    'nil'
}

token true {
    'true'
}

token false {
    'false'
}

token vararg {
    '...'
}

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

token eof {
    $ | <syntaxerror: "'eof' expected">
}

token shebang {
    '#' \N*
}

=head2 EXPRESSIONS

Operator precedence is implemented using an optable.

=cut

rule '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('mul')     { ... }
proto 'infix:/'     is equiv('infix:*')     is pirop('div')     { ... }
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('concat')
                    is assoc('right')                           { ... }

proto 'infix:<'     is looser('infix:..')   is pirop('islt')    { ... }
proto 'infix:>'     is equiv('infix:<')     is pirop('isgt')    { ... }
proto 'infix:<='    is equiv('infix:<')     is pirop('isle')    { ... }
proto 'infix:>='    is equiv('infix:<')     is pirop('isge')    { ... }
proto 'infix:~='    is equiv('infix:<')     is pirop('isne')    { ... }
proto 'infix:=='    is equiv('infix:<')     is pirop('iseq')    { ... }

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

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

