grammar TclExpr::Grammar;

# RT#40688: This began as an [expr] only grammar, and has since grown to
# encompass the language itself. Either rename the whole thing, or,
# preferentially, split out the non-expr specific bits into another
# grammar that this one can then refer to.

token ws { \h* }

token program {
   | <[;\n\r\t\ ]>* <command> [ <?ws> <[\r\n;]> <?ws> <command>? ]*
   | $<empty>:=[^\s*$]
}

token command {
    | \# \N*
    | [ $<word>:=( <expand>?
          [ <quoted_word> [ <before [<[;\n\r\t\ ]>|$]>
                          | <error: extra characters after close-quote> ]
          | <braced_word> [ <before [<[;\n\r\t\ ]>|$]>
                          | <error: extra characters after close-brace> ]
          | $<chunk>:=( <substitution> | \$ | <-[[$\\;\n\r\t\ ]>+ )+
          ]
        ) \h*
      ]+
}

token substitution {
    | <command_substitution>
    | <variable_substitution>
    | <backslash_substitution>
}

token quoted_word {
    " $<chunk>:=( <substitution> | \$ | <-[[$\\"]>+ )* [ " | <error: missing "> ]
}

token braced_word {
    <PGE::Text::bracketed: {}>
  | \{ <error: missing close-brace>
}

token command_substitution {
    \[ \]
  | \[ <[;\n\r\t\ ]>* <subcommand> [ <?ws> <[\r\n;]> <?ws> <subcommand>? ]* \]
  | \[ <error: missing close-bracket>
}

token subcommand {
    | \# \N*
    | [ $<word>:=( <expand>?
          [ <quoted_word> [ <before [<[\];\n\r\t\ ]>|$]>
                          | <error: extra characters after close-quote> ]
          | <braced_word> [ <before [<[\];\n\r\t\ ]>|$]>
                          | <error: extra characters after close-brace> ]
          | $<chunk>:=( <substitution> | \$ | <-[[$\]\\;\n\r\t\ ]>+ )+
          ]
        ) \h*
      ]+
}

token variable_substitution {
    \$ [ \{  ( <-[}]>+ )  \}
       | ([\:\:]?<?name>) [ \( $<index>:=( <substitution> | \$ | <-[[$\\)]>+ )+ \) ]?
       ]
}

token backslash_substitution {
    \\ [ x <[0..9a..fA..F]>+
       | u <[0..9a..fA..F]>**{1..4}
       | <[0..7]>**{1..3}
       | .
       ]
}

token expand {
      \{expand\}<!before:\s>
    | \{\*\}<!before:\s>
}

# this is so we can use these rules in [subst]
token subst_backslash { <backslash_substitution> }
token subst_command   { <command_substitution> }
token subst_variable  { <variable_substitution> }

# Actual [expr] items

token term {
      <substitution>
    | <quoted_word>
    | <braced_word>
    | <number>
    | <mathfunc>
    | <nested>
    | <boolean>
    | <[a..zA..Z]> <syntax_error_variable_or_function>
}

## used by term

rule boolean {
    (:i true  | tru  | tr  | t
      | false | fals | fal | fa | f
      | yes   | ye   | y
      | no    | n
      | on
      | off   | of )
    <?wb>
}

rule nested { \( <expression> \) }

token number { <scientific> | <float> | <integer> }

token float  { (<[+\-]>)? [ <[0..9]>+\.<[0..9]>* | \.<[0..9]>+ ] }

token scientific { (<float> | <integer>) <[Ee]> (<[+\-]>? <[0..9]>+) }

token decimal { [<[1..9]><[0..9]>*] | 0 }

token binary  { 0<[bB]> ( <[01]>+ ) }
token octal   { 0<[oO]>? ([ <[0..7]> | <[89]><[0..9]>* <invalid_octal> ]+) }
token raw_hex  { <[0..9A..Fa..f]> + }
token hex     { 0<[xX]> <raw_hex> }
token integer { (<[+\-]>)? [ <binary> | <hex> | <octal> | <decimal>] }

token nullary_function { rand }

token unary_function { 
 abs    | acos   | asin  | atan   | bool      | ceil   | cosh?  | double |
 entier | exp    | floor | int    | log[10]?  | round  | sinh?  | sqrt   |
 srand  | tanh?  | wide
}

token binary_function { atan2 | fmod | hypot | pow }

token nary_function { max | min }

# XXX This should actually be a PIR rule so we can add more rules.
token mathfunc {
  [ <nary_function> | <binary_function> | <unary_function> | <nullary_function> 
  | ( <[a..zA..Z]>\w* )  \(  <unknown_math_function>  ]
        \( [<expression> [, <expression>]*]? \)
}

rule 'expression' is optable {...}

proto 'term:' is precedence('16=')
    is parsed(&term) {...}
 
proto 'prefix:-' is precedence('15=') {...}
proto 'prefix:+' is precedence('15=') {...}
proto 'prefix:~' is precedence('15=') {...}
proto 'prefix:!' is precedence('15=') {...}

proto 'infix:**' is precedence('14=') {...}

proto 'infix:*'  is precedence('13=') {...}
proto 'infix:/'  is precedence('13=') {...}
proto 'infix:%'  is precedence('13=') {...}

proto 'infix:+'  is precedence('12=') {...}
proto 'infix:-'  is precedence('12=') {...}

proto 'infix:<<' is precedence('11=') {...}
proto 'infix:>>' is precedence('11=') {...}

proto 'infix:<'  is precedence('10=') {...}
proto 'infix:>'  is precedence('10=') {...}
proto 'infix:<=' is precedence('10=') {...}
proto 'infix:>=' is precedence('10=') {...}

proto 'infix:==' is precedence('09=') {...}
proto 'infix:!=' is precedence('09=') {...}

proto 'infix:eq' is precedence('08=') {...}
proto 'infix:ne' is precedence('08=') {...}

proto 'infix:in' is precedence('07=') {...}
proto 'infix:ni' is precedence('07=') {...}

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

proto 'infix:^'  is precedence('05=') {...}

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

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

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

proto 'ternary:? :' is precedence('01=') {...}
