# $Id: /mirror/trunk/languages/cardinal/src/parser/grammar.pg 32786 2008-11-17T04:48:01.587092Z dmknopp  $

=begin overview

This is the grammar for cardinal written as a sequence of Perl 6 rules.

Originally taken (partly) from:
http://www.math.hokudai.ac.jp/~gotoken/ruby/man/yacc.html

and parse.y from the ruby source

=end overview

grammar cardinal::Grammar is PCT::Grammar;

token TOP {
    <comp_stmt>
    [ $ || <panic: Syntax error> ]
    {*}
}

token comp_stmt {
    {*}                        #= open
    <stmts>
    {*}                        #= close
}

rule stmts {
    <.term>?[ <stmt> [<.term>+ | <.before <end_block>> | $ | <panic: unterminated statement>] ]* {*}
}

token term { \n | ';' }
token end_block { <.ws> [ 'end' | '}' ] }

token basic_stmt {
    | <alias> {*}           #= alias
    | <classdef> {*}        #= classdef
    | <functiondef> {*}     #= functiondef
    | <if_stmt> {*}         #= if_stmt
    | <while_stmt> {*}      #= while_stmt
    | <for_stmt> {*}        #= for_stmt
    | <unless_stmt> {*}     #= unless_stmt
    | <module> {*}          #= module
    | <begin_end> {*}       #= begin_end
    | <indexed_assignment> {*}      #= indexed_assignment
    | <member_assignment> {*}      #= member_assignment
    | <assignment> {*}      #= assignment
    | <return_stmt> {*}     #= return_stmt
    | <expr> {*}            #= expr
    | <begin> {*}           #= begin
    | <end> {*}             #= end
}

token return_stmt {
    'return' <.ws> <call_args> {*}
}

rule alias {
    'alias' <fname> <fname>
    {*}
}

token stmt {
    <basic_stmt> <.ws> <stmt_mod>*
    {*}
}

token stmt_mod {
    $<sym>=[if|while|unless|until] <.ws> <expr>
    {*}
}

rule expr {
    [$<not>=['!'|'not']]? <arg> [$<op>=['and'|'or'] <expr>]?
    {*}
}

rule begin {
    'BEGIN' '{' <comp_stmt> '}'
    {*}
}

rule end {
    'END' '{' <comp_stmt> '}'
    {*}
}

token indexed_assignment {
    <basic_primary> '[' <key=arg> ']' <.ws> '=' <.ws> <rhs=arg>
    {*}
}

token member_assignment {
    <basic_primary> '.' <key=identifier> <.ws> '=' <.ws> <rhs=arg>
    {*}
}

rule assignment {
    <mlhs=lhs> '=' <mrhs=arg>       #XXX need to figure out multiple assignment
    {*}
}

rule mlhs {
    | <lhs> {*}               #= lhs
    | '(' <mlhs> ')' {*}      #= mlhs
}

token lhs {
    | <basic_primary> {*}            #= basic_primary
}

token indexed {
    '[' <args>? ']'
    {*}
}

token member_variable {
    <primary> '.' <identifier>
    {*}
}

token methodcall {
    $<dot>='.'
    <operation> <call_args>? <do_block>?
    {*}
}

rule do_block {
    | 'do' <do_args>? <.term>? <.before <stmt>><comp_stmt> 'end' {*}
    | '{' <do_args>? <.term>? <.before <stmt>><comp_stmt> '}' {*}
}

rule super_call {
    'super' <call_args>
    {*}
}

token operation {
    'class'|
    'nil?' |
    'next' |
    'begin'|
    'end'  |
    '`'    |
    <.identifier> ('!'|'?')?
}

#XXX UGLY!  Refactor into <args> maybe?
token call_args {
    | '()' [<.ws> <do_block>]? {*}
    | [ <.after \s|\)> | <.before \s> ] <args> [<.ws> <do_block>]? {*}
    | '(' <.ws> <args> <.ws> ')' [<.ws> <do_block>]? {*}
}

rule do_args {
    '|' <block_signature> '|'
}

rule sig_identifier {
                      #XXX Should this be basic_primary or expr or what?
    <identifier>[ '=' <default=basic_primary>]? {*}
}

rule block_signature {
    [
    | <sig_identifier> [',' <sig_identifier>]* [',' <slurpy_param>]? [',' <block_param>]?
    | <slurpy_param> [',' <block_param>]?
    | <block_param>?
    ] {*}
}

token variable {
    | <varname> {*}      #= varname
    | 'nil'     {*}      #= nil
    | 'self'    {*}      #= self
}

token varname {
    <!reserved_word>
    [ <global> {*}             #= global
    | <class_variable> {*}     #= class_variable
    | <instance_variable> {*}  #= instance_variable
    | <local_variable> {*}     #= local_variable
    | <constant_variable> {*}  #= constant_variable
    ]
}

token funcall {
    <!reserved_word> <local_variable> <.before \s|'('> <.before <call_args>> {*}
}

token mrhs {
    <args> {*}
}

rule args {
    <arg> [',' <arg>]*
    {*}
}

rule 'arg' is optable { ... }

proto 'infix:=' is precedence('1') is pasttype('copy') is lvalue(1)     { ... }



token basic_primary {
    | <literal> {*}                         #= literal
    | <funcall> {*}                         #= funcall
    | <variable> {*}                        #= variable
    | <ahash> {*}                           #= ahash
    | <regex> {*}                           #= regex
    | <do_block> {*}                        #= do_block
    | <quote_string> {*}                    #= quote_string
    | <warray> {*}                          #= warray
    | <array> {*}                           #= array
    | <pcomp_stmt> {*}                      #= pcomp_stmt
    | <yield> {*}                           #= yield
    | <control_command> {*}                 #= control_command
}

token primary {
    <basic_primary> <post_primary_expr>*
    {*}
}

token post_primary_expr {
    | <indexed> {*}            #= indexed
    | <call_args> {*}          #= call_args
    | <methodcall> {*}         #= methodcall
    | '[' <args>? ']' {*}      #= args
}

token pcomp_stmt {
    '(' <comp_stmt> ')'
    {*}
}


rule if_stmt {
    'if' <expr> <.then>
    [<comp_stmt>
    ['elsif' <expr> <.then>
    <comp_stmt>]*
    <else>?
    'end'
    |<panic: syntax error in if statement>]
    {*}
}

token then { ':' | 'then' | <term> ['then']? }

rule while_stmt {
    $<sym>=['while'|'until'] <expr> <.do>
    <comp_stmt>
    'end'
    {*}
}

rule for_stmt {
    'for' <variable> 'in' <expr> <.do>
    <comp_stmt>
    'end'
    {*}
}

token do { ':' | 'do' | <term> ['do']? }

rule unless_stmt {
    'unless' <expr> <.then> <comp_stmt>
    <else>?
    'end'
    {*}
}

token else {
    'else' <.ws> <comp_stmt>
    {*}
}

token ensure {
    'ensure' <.ws> <comp_stmt>
    {*}
}

rule rescue {
    # XXX check <args>
    ['rescue' <args> <.then> <comp_stmt>]+
    {*}
}

token control_command {
    | 'next'  {*}                   #= next
    | 'break' {*}                   #= break
    | 'redo'  {*}                   #= redo
}

token yield {
    'yield' <call_args> {*}
}

rule module {
    'module' <module_identifier>
    <comp_stmt>
    'end'
    {*}
}

rule classdef {
    'class' <module_identifier> {*}  #= open
    <comp_stmt>
    'end'                       {*}  #= block
}

rule functiondef {
    'def' <fname> <argdecl>
    <comp_stmt>
    'end'
    {*}
}

rule bodystmt {
    <comp_stmt>
    <rescue>?
    <else>?
    <ensure>?
}

rule argdecl {
    ['('
    <block_signature>
    ')']?
}

token slurpy_param {
    '*' <identifier>
    {*}
}

token block_param {
    '&' <identifier>
    {*}
}

rule begin_end {
    'begin'
    <comp_stmt>
    ['rescue' <args>? <.do> <comp_stmt>]+
    ['else' <comp_stmt>]?
    ['ensure' <comp_stmt>]?
    'end'
    {*}
}

token fname {
    <.identifier> <[=!?]>?
}

token quote_string {
    ['%q'|'%Q'] <.before <[<[_|({]>> <quote_expression: :qq>
    {*}
}

token warray {
    '%w' <.before <[<[({]>> <quote_expression: :w :q>
    {*}
}

rule array {
    '[' [ <args> [',']? ]? ']'
    {*}
}

rule ahash {
    '{' [ <assocs> [',']? ]? '}'
    {*}
}

rule assocs {
    <assoc> [',' <assoc>]*
    {*}
}

rule assoc {
    <arg> '=>' <arg>
    {*}
}

token identifier {
    <!reserved_word> <ident> {*}
}

token module_identifier {
    <.before <[A..Z]>> <ident>
    {*}
}

token global {
    '$' <ident>
    {*}
}

token instance_variable {
    '@' <ident>
    {*}
}

token class_variable {
    '@@' <ident>
    {*}
}

token local_variable {
    [<ns=ident> '::']* [ <before <[a..z_]>> | <after '::'> ] <ident>
    {*}
}

token constant_variable {
    <.before <[A..Z]>> <.ident>
    {*}
}

token literal {
    | <float> {*}          #= float
    | <integer> {*}        #= integer
    | <string> {*}         #= string
}

token float {
    '-'? \d* '.' \d+
    {*}
}

token integer {
    '-'? \d+
    {*}
}

token string {
    [ \' <string_literal: "'"> \' | \" <string_literal: '"'> \" ]
    {*}
}

token regex {
    <.before '/'> [<quote_expression: :regex> $<modifiers>=[<alpha>]*
                  |<panic: problem parsing regex>]
    {*}
}

token reserved_word {
    [alias|and|BEGIN|begin|break|case
    |class|def|defined|do|else|elsif
    |END|end|ensure|false|for|if
    |in|module|next|nil|not|or
    |redo|rescue|retry|return|self|super
    |then|true|undef|unless|until|when
    |while|yield|__FILE__|__LINE__]>>
}

token ws {
    | '\\' \n                      ## a backslash at end of line
    | <after [','|'='|'+']> \n     ## a newline after a comma or operator is ignored
    | \h* ['#' \N* \n* <ws>]?
}


proto 'infix:=' is precedence('1') is pasttype('copy') is lvalue(1)     { ... }

proto 'prefix:defined?' is looser('infix:=') { ... }

proto 'infix:+=' is equiv('infix:=')
                 { ... }

proto 'infix:-=' is equiv('infix:=')
                 { ... }

proto 'infix:/=' is equiv('infix:=')
                 is pirop('div')        { ... }

proto 'infix:*=' is equiv('infix:=')
                 is pirop('mul')        { ... }

proto 'infix:%=' is equiv('infix:=')
                 is pirop('mul')        { ... }

proto 'infix:|=' is equiv('infix:=')    { ... }

proto 'infix:&=' is equiv('infix:=')    { ... }

proto 'infix:~=' is equiv('infix:=')    { ... }

proto infix:«>>=» is equiv('infix:=')
                  is pirop('rsh')       { ... }

proto infix:«<<=» is equiv('infix:=')
                  is pirop('lsh')       { ... }

proto 'infix:&&=' is equiv('infix:=')
                  is pirop('and')       { ... }

proto 'infix:**=' is equiv('infix:=')
                  is pirop('pow')       { ... }

proto 'ternary:? :' is tighter('infix:=')
                    is pirop('if')      { ... }

proto 'infix:..' is tighter('ternary:? :') { ... }
                #is parsed(&primary)     { ... }
                 #is pirop('add')      { ... }

proto 'infix:...' is equiv('infix:..') { ... }

proto 'infix:||' is tighter('infix:..')
                 is pasttype('unless')      { ... }

proto 'infix:&&' is tighter('infix:||')
                 is pasttype('if')          { ... }


proto 'infix:=='      is tighter('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:«>»  is tighter('infix:===') { ... }
proto infix:«<»  is tighter('infix:===') { ... }
proto infix:«<=»  is tighter('infix:===') { ... }
proto infix:«>=» is tighter('infix:===') { ... }

proto 'infix:|' is tighter('infix:<=')  { ... }
proto 'infix:^' is equiv('infix:|')  { ... }

proto 'infix:&' is tighter('infix:|')  { ... }

proto infix:«<<»  is tighter('infix:&') { ... }
proto infix:«>>»  is equiv(infix:«<<») { ... }

proto 'infix:+' is tighter(infix:«<<»)  { ... }

proto 'infix:-' is equiv('infix:+') { ... }
                #is pirop('sub')       { ... }

proto 'infix:*' is tighter('infix:+') { ... }
                #is pirop('mul')       { ... }

proto 'infix:/' is equiv('infix:*')  { ... }
                #is pirop('div')       { ... }

proto 'infix:%' is equiv('infix:*')
                is pirop('mod')       { ... }

proto 'postfix:++' is tighter('infix:*')
		is pirop('n_add')  { ... }

proto 'postfix:--' is tighter('infix:*')
		is pirop('n_sub')  { ... }
#
#proto 'prefix:+' is tighter('infix:*')  { ... }
#proto 'prefix:-' is equiv('prefix:+')  { ... }
#proto 'prefix:!' is equiv('prefix:+')  { ... }
#proto 'prefix:~' is equiv('prefix:+')  { ... }

proto 'term:'   is tighter('infix:*')
                is parsed(&primary)     { ... }
