[FM] This is an experimentation to parse with coroutine. It uses the [typedlist] package. ====== #! /bin/sh # this is a -*-Tcl-*- file package require typedlist typedlist create BNF::nombre coroutine nombre apply {{} { while 1 { if {[regexp {[0-9]} [set n [yield]]]} { if {[info exist ::String($::Instruction)] && \ [[lindex $::String($::Instruction) end] type] eq "nombre"} { [lindex $::String($::Instruction) end] lappend $n } else { lappend ::String($::Instruction) [BNF::nombre $n] } } } }} typedlist create BNF::mot coroutine mot apply {{} { while 1 { set n [yield] if {[regexp {[a-zA-Z]} $n]} { if {[info exist ::String($::Instruction)]} { if {[[lindex $::String($::Instruction) end] type] eq "mot"} { [lindex $::String($::Instruction) end] lappend $n } elseif {[[lindex $::String($::Instruction) end] type] eq "var"} { [lindex $::String($::Instruction) end] lappend $n } else { lappend ::String($::Instruction) [BNF::mot $n] } } else { lappend ::String($::Instruction) [BNF::mot $n] } } elseif {[regexp {[0-9]} $n]} { if {[info exist ::String($::Instruction)]} { if {[[lindex $::String($::Instruction) end] type] eq "mot"} { [lindex $::String($::Instruction) end] lappend $n } elseif {[[lindex $::String($::Instruction) end] type] eq "var"} { [lindex $::String($::Instruction) end] lappend $n } } } } }} typedlist create BNF::égal coroutine égal apply {{} { while 1 { if {[regexp {=} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::égal $n] } } }} typedlist create BNF::plus coroutine plus apply {{} { while 1 { if {[regexp {\+} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::plus $n] } } }} typedlist create BNF::fois coroutine fois apply {{} { while 1 { if {[regexp -- {\*} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::fois $n] } } }} typedlist create BNF::moins coroutine moins apply {{} { while 1 { if {[regexp -- {-} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::moins $n] } } }} typedlist create BNF::rapport coroutine rapport apply {{} { while 1 { if {[regexp -- {/} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::rapport $n] } } }} typedlist create BNF::ParenthèseOuverte coroutine ParenthèseOuverte apply {{} { while 1 { if {[regexp -- {\(} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::ParenthèseOuverte $n] } } }} typedlist create BNF::ParenthèseFermée coroutine ParenthèseFermée apply {{} { while 1 { if {[regexp -- {\)} [set n [yield]]]} { lappend ::String($::Instruction) [BNF::ParenthèseFermée $n] } } }} typedlist create BNF::var coroutine var apply {{} { while 1 { set n [yield] if {[regexp {\$} $n]} { lappend ::String($::Instruction) [BNF::var $n] } } }} typedlist create BNF::FinDeInstruction coroutine FinDeInstruction apply {{} { while 1 { set n [yield] if {[regexp {\;} $n] || [regexp {\n} $n]} { incr ::Instruction } } }} typedlist create BNF::point coroutine point apply {{} { while 1 { set n [yield] if {[regexp {\.} $n]} { if {[info exist ::String($::Instruction)] && \ [[lindex $::String($::Instruction) end] type] eq "nombre"} { [lindex $::String($::Instruction) end] lappend $n } else { lappend ::String($::Instruction) [BNF::nombre $n] } } } }} typedlist create BNF::sousScript coroutine sousScript apply {{} { while 1 { set n [yield] if {[regexp {\[} $n]} { if {$::sousScript == 0} { lappend ::String($::Instruction) [BNF::sousScript $n] } else { [lindex $::String($::Instruction) end] lappend $n } incr ::sousScript } elseif {[regexp {\]} $n]} { [lindex $::String($::Instruction) end] lappend $n incr ::sousScript -1 } elseif {$::sousScript >= 1} { ToutVenant $n } } }} typedlist create BNF::FinDeMot coroutine FinDeMot apply {{} { while 1 { set n [yield] if {[regexp {[ \t]} $n]} { lappend ::String($::Instruction) [BNF::FinDeMot $n] } } }} typedlist create BNF::ToutVenant coroutine ToutVenant apply {{} { set i 0 set N {} set ::SousExpression 0 while 1 { set n [yield] if {$n eq " " && $i == 0} { lappend N " " incr i } elseif {$n eq "\{" && $i == 1} { lappend N \{ incr i } elseif {$n eq "=" && $i == 2} { lappend N = incr i } elseif {$n eq "\}" && $i == 3} { lappend N \} incr i } elseif {$n eq "(" && $i == 4} { set ::SousExpression 1 set ::Parenthèse 1 [lindex $::String($::Instruction) end] lappend \[ e x p r { } \{ $n set N {} set i 5 } elseif {$::SousExpression} { sousExpression $n if {!$::SousExpression} { set i 1 } } else { [lindex $::String($::Instruction) end] lappend {*}$N $n set N {} } } }} typedlist create BNF::sousExpression coroutine sousExpression apply {{} { set i 0 while 1 { set n [yield] if {$n eq ")" && ${::Parenthèse} == 1} { [lindex $::String($::Instruction) end] lappend $n \} \] set ::SousExpression 0 } elseif {$n eq ")" && ${::Parenthèse} > 1} { [lindex $::String($::Instruction) end] lappend $n incr ::Parenthèse -1 } elseif {$n eq "("} { [lindex $::String($::Instruction) end] lappend $n incr ::Parenthèse } else { [lindex $::String($::Instruction) end] lappend $n } } }} proc lexpr {sz} { catch {array unset ::String} array set ::String {} set ::Instruction 0 set ::sousScript 0 # lexer foreach c [split $sz ""] { sousScript $c if {$::sousScript} continue mot $c égal $c var $c nombre $c plus $c fois $c moins $c rapport $c point $c ParenthèseOuverte $c ParenthèseFermée $c FinDeInstruction $c } set Res [] # interpreteur for {set i 0} {$i <= $::Instruction} {incr i} { set res [] set var [] set Silencieux 0 if {![info exist ::String($i)]} continue if {[set range [lsearch $::String($i) *::égal:*]] ne {}} { foreach e [lrange $::String($i) 0 ${range}-1] { append var [$e get] } set ::String($i) [lrange $::String($i) ${range}+1 end] if {$range == 0} { set Silencieux 1 } } foreach e $::String($i) { append res [join [$e get] ""] } if {$var ne {}} { set $var [expr $res] } elseif {$Silencieux} { eval subst $res } else { lappend Res [expr $res] } } return $Res } # Test lexpr { =[wm withdraw . toplevel [set t .top_tmp_] wm geom $t +100+100 wm attributes $t -alpha 0.0 wm state $t zoomed update idletask lassign [split [winfo geometry $t] x+] W H X Y] w=[winfo width .]; h=[winfo height .] =[wm geom . +{=}(($W-$w)/2+$X)+{=}(($H-$h)/2+$Y) wm deiconify . destroy $t update idletask] } ====== the `lexpr` proc here do : * if the line begin with `v =`, `v` is seen as a variable and so is set to the value calculate after the equal sign (variable is only one letter long for the moment) * if the line begin with `=`, the action isn't return as result of the command (else it will be appended to a list which will be return) * what is between `[[` and `]]` is a seen as a subscript to be evaluated * the construct `{=}( ... )` is seen as an expression to be calculated in a subscript. <>Enter Category Here