grammar PunieGrammar;

token apply    { ^<lineseq>$ }

rule block { \s* \{ <lineseq> \} \s* }

rule lineseq { <line>* }

token line { 
    <label>
    [ <cond> 
    | <subroutine>
    | <expr>;
    ] \s* 
}

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

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

rule subroutine { sub <word> <block> }

rule else {
    (else) <block>
    | (elsif) \( <expr> \)
              <block>
              <else>?
}

rule cond {
    (if|unless) \( <expr> \)
              <block>
              <else>?
} 

rule expr    { <gprint> | <oexpr> }

rule gprint  { (print) <expr> }

regex term {
      \( <expr> \)
    | <variable>
    | <number>
    | <integer>
    | <stringdouble>
    | <stringsingle>
    | do <block>
    | <subcall>
}
rule variable { <sigil> <word> }
token sigil { <[$@%]> }
token integer { \d+ }
token number { \d+\.\d+ }
regex stringdouble { <PGE::Text::bracketed: "> }
regex stringsingle { <PGE::Text::bracketed: '> }
rule subcall { do <word> \( <expr>? \) }

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

proto infix:+ is precedence('=') 
    is pirop('n_add')
    {...}

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

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

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

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

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

proto infix:x is equiv('infix:*')
    is pirop('repeat')
    {...}

proto infix:<< is looser('infix:+')
    is pirop('shl')
    {...}

proto infix:>> is equiv('infix:<<')
    is pirop('shr')
    {...}

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')
    is past('chain')
    {...}
proto infix:ne is equiv('infix:==') is assoc('non')
    is past('chain')
    {...}

proto infix:& is looser('infix:==') is assoc('left')
    is pirop('band')
    {...}

proto infix:| is looser('infix:&') is assoc('left')
    is pirop('bor')
    {...}

proto infix:^ is equiv('infix:|')   is assoc('left')
    is pirop('bxor')
    {...}

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

proto infix:= is looser('infix:||')
    is past('assign')
    {...}

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

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