See
Operator precedence expression parser, example# This code is an operator precedence parser which uses an operator table and a variation
# on the shunting yard parser algorithm to convert an infix expression to a series of
# prefix operator callbacks.
#
#
namespace eval expression {
# This is the operator precedence table for C++. The dot operator is effectively
# commented out by "renaming" it ".###". The lexical "analyser" is too primitive
# to distinguish floating point numbers and a real . operator usage.
#
# The conditional, "?:" and sequence operator, "," are missing.
#
# precedence values are spaced out by multiplying the nominal
# C++ precedence by 10. This should allow applications to add other
# operators as needed at any preceduce (???) required.
#
# The unary * has been changes to nulary to support tna
#
# The index [] operator is nary and treated similarly to a function call to "indx" instead
# of "call".
#
# dolar "$" is added for eventual support of tcl variables in tna.
#
set optable {
:: { 10 2 left name }
++ { 20 1 left inc }
-- { 20 1 left dec }
.### { 20 2 left dot }
-> { 20 2 left arrow }
++u { 30 1 right uinc }
--u { 30 1 right udec }
-u { 30 1 right usub }
+u { 30 1 right uadd }
*u { 30 0 right deref }
$u { 30 1 right dolar }
&u { 30 1 right refer }
* { 50 2 left mul }
/ { 50 2 left div }
% { 50 2 left mod }
+ { 60 2 left add }
- { 60 2 left sub }
<< { 70 2 left shl }
>> { 70 2 left shr }
> { 80 2 left gt }
< { 80 2 left lt }
<= { 80 2 left lte }
>= { 80 2 left gte }
== { 90 2 left equ }
!= { 90 2 left neq }
& { 100 2 left band }
^ { 110 2 left bxor }
| { 120 2 left bor }
&& { 130 2 left land }
|| { 140 2 left lor }
? { 150 2 right hook }
= { 160 2 right assign }
+= { 160 2 right addasn }
-= { 160 2 right subasn }
*= { 160 2 right mulasn }
/= { 160 2 right divasn }
%= { 160 2 right modasn }
<<= { 160 2 right shlasn }
>>= { 160 2 right shrasn }
&= { 160 2 right bndasn }
^= { 160 2 right bxrasn }
|= { 160 2 right borasn }
[ { 1000 0 {} indx }
] { 1000 0 {} none }
( { 1000 0 {} call }
) { 1000 0 {} none }
, { 1000 0 {} none }
; { 1000 0 {} semi }
}
# A little helper to sort a precedence table in to a [string map] mapping that will be used
# for "lexical analysis".
#
proc prep-tokens { tokens } {
variable opers
foreach token [dict keys $tokens] {
set opers([lindex [dict get $tokens $token] 3]) $token
set token [string map { u {} } $token]
lappend map $token " $token "
}
lappend map "\n" " ; "
lsort -stride 2 -command lencmp [lsort -stride 2 -u $map]
}
proc lencmp { a b } {
return [expr [string len $b] - [string len $a]]
}
# Accessors for the operator table data structure.
#
proc prec { tok } { # Return the precedence of an operator.
upvar optable optable
set reply 0
catch { set reply [lindex [dict get $optable $tok] 0] }
return $reply
}
proc arity { tok } { # Return the arity of an operator.
upvar optable optable
set reply 0
catch { set reply [lindex [dict get $optable $tok] 1] }
return $reply
}
proc assoc { tok } { # Return the assiciativity of an operator.
upvar optable optable
set reply 0
catch { set reply [lindex [dict get $optable $tok] 2] }
return $reply
}
proc name { tok } { # Return the name of an operator.
upvar optable optable
set reply 0
catch { set reply [lindex [dict get $optable $tok] 3] } reply; # puts $reply
return $reply
}
# Stack operators.
#
proc push { stkName value } { upvar $stkName stk; lappend stk $value }
proc pop { stkName { n 1 } } { upvar $stkName stk;
set x [expr $n-1]
set top [lrange $stk end-$x end]
set stk [lrange $stk 0 end-$n]
if { $n == 1 } {
return [join $top]
} else {
return $top
}
}
proc top { stkName } { upvar $stkName stk; return [lindex $stk end] }
proc pull { stkName } { upvar $stkName stk; set stk [lassign $stk top]; return $top }
proc next { stkName } { upvar $stkName stk; return [lindex $stk 0] }
# Here is the parser. You pass in the input string, the token map, the operator precedence table and
# a script prefix with will be called as each sub-expression in the input is recognized.
#
proc parse { input tokenmap optable prefix } {
set operator {} ; # Stacks
set operands {}
set parens {}
set prv {} ; # The previous token.
set input [string map $tokenmap $input] ; # Lexing done by mapping spaces around the operators!
#puts $input
try {
while { [llength $input] } {
set tok [pull input]
if { $tok eq ";" } {
while { [top operator] ne {} } {
push operands [{*}$prefix [name [set op [pop operator]]] {*}[pop operands [arity $op]]]
}
{*}$prefix [name ";"]
# Reset everyone
#
set operator {} ; # Stacks
set operands {}
set parens {}
set prv {} ; # The previous token.
continue
}
if { [prec ${tok}u] && $prv ne ")" && ($prv eq {} || [prec $prv]) } {
set tok ${tok}u
}
#puts "$tok [prec $tok], op: $operator args: $operands"
if { ![prec $tok] } { ; # Push operand
if { $prv ne {} && ![prec $prv] } { error "unexpected token : $prv _@_ $tok" }
push operands $tok
} elseif { $tok eq "("
|| $tok eq {[}
|| [top operator] eq {}
|| [prec $tok] < [prec [top operator]]
|| ([prec $tok] <= [prec [top operator]]
&& [assoc $tok] eq "right" ) } {
if { $tok eq "(" || $tok eq {[} } {
if { $tok eq {[} || ($prv ne {} && ![prec $prv]) } {
if { [next input] eq ")" } { push parens 0 ; # Function call or Index
} else { push parens 1 }
} else { push parens "(" } ; # Normal expression paren
} else {
if { [prec ${tok}u] && ($prv eq {} || $prv eq "," || $prv eq "(" || $prv eq {[}) } {
set tok ${tok}u
}
}
push operator $tok
} elseif { $tok eq ")" || $tok eq {]} || $tok eq "," } { ; # Function call, Index or comma
switch [top parens] {
{} { error "unexpected \")\" : $prv $tok [next input]" }
"(" { ; # Close paren on normal expression paren
while { [top operator] ne "(" } {
push operands [{*}$prefix [name [set op [pop operator]]] {*}[pop operands [arity $op]]]
}
pop operator
pop parens
}
default { ; # Function call, Index or comma
if { $tok eq "," } { push parens [expr [pop parens]+1] } ; # Incr function nargs
while { [top operator] ne "(" && [top operator] ne {[} } { ; # Output function arg
push operands [{*}$prefix [name [set op [pop operator]]] {*}[pop operands [arity $op]]]
}
if { $tok eq ")" || $tok eq "]" } { ; # Output function call or Index
push operands [{*}$prefix [name [pop operator]] {*}[pop operands [expr [pop parens]+1]]]
pop parens
}
}
}
} else {
while { [top operator] ne {} && [prec $tok] >= [prec [top operator]] } {
push operands [{*}$prefix [name [set op [pop operator]]] {*}[pop operands [arity $op]]]
}
push operator $tok
}
set prv $tok
}
if { $parens ne {} } { error "parens not balanced" }
while { [top operator] ne {} } {
push operands [{*}$prefix [name [set op [pop operator]]] {*}[pop operands [arity $op]]]
}
} on error message {
puts $::errorInfo
error "parse error at: $prv _@_ $tok [next input] : $message"
}
pop operands
}
}