# Copyright (C) 2005-2008, The Perl Foundation.
# $Id: /mirror/trunk/languages/punie/src/parser/grammar.pg 31826 2008-10-08T19:12:02.028807Z pmichaud  $

grammar Punie::Grammar is PCT::Grammar;

token TOP    {
    <lineseq> [ $ || <panic: 'parse error'> ] {*}
}

token ws {
    || \v+
    || \h+
    || '#' \N*
}

token block { \{ <lineseq> \} {*} }


rule compblock {
    $<block>=<block>
    ['elsif' \( <expr> \) <block> ]*
    [['continue'|'else'] $<continue>=<block>]?
    {*}
}

rule lineseq { <line>*  {*} }

token line {
    | <loop> {*}                                   #= loop
    | <label>
      [ <cond> {*}                                 #= cond
      | <decl> {*}                                 #= decl
      | <sideff> ';' {*}                           #= sideff
      | ';' {*}                                    #= label
      ] \s*
}

rule sideff {
    <expr> [$<modifier>=<condmod>|<loopmod>]? {*}
}

rule condmod { $<sym>=[if|unless] <expr> {*} }

rule loopmod { $<sym>=[while|until] <expr> {*} }

rule label { [<word>\:]? {*} }

token word { [\w|_][\w|\d|_]* }

rule decl {
    | <subroutine> {*} #= subroutine
    #| <format> {*}     #= format
}

rule subroutine { sub <word> <block> {*} }

rule cond {
    $<sym>=[if|unless]
    \( <expr> \) <block>
    [ 'elsif' \( <expr> \) <block> ]*
    [ 'else' $<else>=<block> ]?
    {*}
}

rule loop {
    <label>
    [ <while_loop> {*}                          #= while_loop
    | <until_loop> {*}                          #= until_loop
    | <for_loop> {*}                            #= for_loop
    | <compblock> {*}                           #= compblock
    ]
}

rule while_loop {
    while <while_cond> <compblock> {*}
}

rule while_cond {
    | \( <texpr> \) {*}         #= texpr
    | <block> {*}               #= block
}

rule until_loop {
    until <until_cond> <compblock> {*}
}

rule until_cond {
    | \( <expr> \) {*}          #= expr
    | <block> {*}               #= block
}

rule for_loop {
    for \( [$<init>=<sideff>]? ';' <texpr> ';' [$<step>=<sideff>]? \)
    <block>
    {*}
}

rule texpr { <expr>? {*}  }

rule expr {
    | <gprint> {*}                               #= gprint
    | <oexpr> {*}                                #= oexpr
}

rule gprint { $<op>=['print'|'printf'|'chown'|'system'|'unlink'|'exec'|'kill'] <expr> {*} }

rule 'oexpr' is optable { ... }


token term {
    | <list> {*}                                 #= list
    | \( <expr> \) {*}                           #= expr
    | <number> {*}                               #= number
    | <integer> {*}                              #= integer
    | <stringdouble> {*}                         #= stringdouble
    | <stringsingle> {*}                         #= stringsingle
    | <stringbacktick> {*}                       #= stringbacktick
    | do <.ws> <block> {*}                       #= block
    | <opcall> {*}                               #= opcall
    | <subcall> {*}                              #= subcall
    | <do_chop> {*}                              #= do_chop
    | <do_pop> {*}                               #= do_pop
    | <do_push> {*}                              #= do_push
    | <do_shift> {*}                             #= do_shift
    | <func0> {*}                                #= func0
    | <func1> {*}                                #= func1
    | <func2> {*}                                #= func2
    | <func3> {*}                                #= func3
    | <stabfun> {*}                              #= stabfun
    | <variable> {*}                             #= variable
    | <do_split> {*}                             #= split
    | <do_join> {*}                              #= join
}

rule func0 { <fun0> {*} }

token fun0 { 'fork'|'time'|'times' }

rule func1 { <fun1>  \( <expr> \) {*} }

token fun1 { 'exp'|'gmtime'|'hex'|'int'|'length'|'localtime'|'log'|'ord'|'oct'|'sqrt'|'umask' }

rule func2 { <fun2>  \( <oexpr> ',' <expr> \) {*} }

token fun2 { 'crypt'|'index'|'link'|'rename' }

rule func3 { <fun3>  \( <oexpr> ',' <oexpr> ',' <expr> \) {*} }

token fun3 { 'substr' }

rule stabfun { <sfun> \( <word> \) {*} }

token sfun { 'each'|'keys'|'values' }

rule list {
    \( \)  {*}
}

## XXX add "pattern" to lexical rules.
rule do_split {
    'split' [\( <variable> [',' <variable> [',' <expr>]? ]? \)]?
}

## XXX complete this and implement
rule do_join {
    'join' [\( <oexpr> ',' <expr> \)]?
}

rule do_chop {
    'chop' [\( <expr> \)]?   {*}
}

rule do_pop {
    'pop'
    [ <arrayarg>
    | <wordarg>
    ]
    {*}
}

rule do_push {
    $<op>=['push'|'unshift'] \( <arrayvar> ',' <expr> \)  {*}
}

rule do_shift {
    'shift' [$<arg>=<arrayarg>|<wordarg>]?  {*}
}

rule wordarg { \( <word> \) | <word>  {*} }

rule arrayarg { \( <arrayvar> \) | <arrayvar>  {*}  }

rule variable {
    | <sigil>? <word> <key>? {*}
    | <special_variable> <key>? {*}
}

token arrayvar { '@' <word>  {*}  }

token special_variable { '$_' | '$#' | '$ ' '_' }

token sigil { '$#' | <[$@%]> {*} }
token key {
    | $<KEY>=[\[] <.ws> <expr> <.ws> $<KEY_CLOSE>=[\]]
    | $<KEY>=[\{] <.ws> <expr> <.ws> $<KEY_CLOSE>=[\}]
    {*}
}
token integer { \d+ {*} }
token number { \d+\.\d+ {*} }
token stringdouble { '"' <string_literal: '"' > '"' {*} }
token stringsingle { \' <string_literal: '\'' > \' {*} }
token stringbacktick { \` <string_literal: '`'> \` {*} }
rule subcall { do $<sym>=<word> \( <expr>? \) {*} }
rule opcall  { $<sym>=<op> \( <expr>? \) {*} }

token op {
    | open
    | die
}

token ws {
    [ \s+
    | \# \N*
    ]*
    {*}
}

proto infix:<+> is assoc('left') is precedence('=')
    is pirop('add')
    {...}

proto infix:<-> is assoc('left') is equiv(infix:<+>)
    is pirop('sub')
    {...}

proto infix:<.> is assoc('left')  is equiv(infix:<+>)
    is pirop('concat')
    {...}

proto infix:<*> is assoc('left') is tighter(infix:<+>)
    is pirop('mul')
    {...}

proto infix:</> is assoc('left') is equiv(infix:<*>)
    is pirop('div')
    {...}

proto infix:<%> is assoc('left') is equiv(infix:<*>)
    is pirop('mod')
    {...}

proto infix:<x> is assoc('left') is equiv(infix:<*>)
    {...}

proto prefix:<-> is tighter(infix:<*>) is assoc('right') { ... }
proto prefix:<~> is equiv(prefix:<->) is assoc('right') { ... }
proto prefix:<!> is equiv(prefix:<->) is assoc('right') { ... }

proto postfix:<++> is tighter(prefix:<->) is assoc('non') is lvalue(1) { ... }
proto postfix:<--> is equiv(postfix:<++>)is assoc('non') is lvalue(1) { ... }
proto prefix:<++> is equiv(postfix:<++>) is assoc('non') is lvalue(1) { ... }
proto prefix:<--> is equiv(postfix:<++>) is assoc('non') is lvalue(1) { ... }


proto infix:«<<» is looser(infix:<+>) is assoc('left')
#    is pirop('shl') # XXX pirop()s with non PREG destinations don't work yet
    {...}

proto infix:«>>» is equiv(infix:«<<») is assoc('left')
#    is pirop('shr') # XXX pirop()s with non PREG destinations don't work yet
    {...}

proto infix:«<»  is looser(infix:«<<») is assoc('non') {...}
proto infix:«>»  is equiv(infix:«<»)   is assoc('non') {...}
proto infix:«<=» is equiv(infix:«<»)   is assoc('non') {...}
proto infix:«>=» is equiv(infix:«<»)   is assoc('non') {...}
proto infix:<lt> is equiv(infix:«<»)   is assoc('non') {...}
proto infix:<gt> is equiv(infix:«<»)   is assoc('non') {...}
proto infix:<le> is equiv(infix:«<»)   is assoc('non') {...}
proto infix:<ge> is equiv(infix:«<»)   is assoc('non') {...}

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

proto infix:<eq> is equiv(infix:<==>) is assoc('non')
    {...}
proto infix:<ne> is equiv(infix:<==>) is assoc('non')
    {...}

proto infix:<&> is looser(infix:<==>) is assoc('left')
#    is pirop('band') # XXX pirop()s with non PREG destinations don't work yet
    {...}

proto infix:<|> is looser(infix:<&>) is assoc('left')
#    is pirop('bor') # XXX pirop()s with non PREG destinations don't work yet
    {...}

proto infix:<^> is equiv(infix:<|>)   is assoc('left')
#    is pirop('bxor') # XXX pirop()s with non PREG destinations don't work yet
    {...}

proto infix:<&&> is looser(infix:<|>)  is assoc('left')
    is pasttype('if')
    {...}
proto infix:<||> is looser(infix:<&&>) is assoc('left')
    is pasttype('unless')
    {...}

proto infix:<..> is looser(infix:<||>) is assoc('non')
    {...}

proto ternary:<? :> is looser(infix:<..>) is assoc('right')
    is pasttype('if')
    {...}

proto infix:<=> is looser(ternary:<? :>)
    is pirop('assign')
    is lvalue(1)
    {...}

proto infix:<,> is looser(infix:<=>) is assoc('list')
    { ... }

proto term: is tighter(infix:<+>) is parsed(&term) { ... }




