[Richard Suchenwirth] - There are exactly three ways of horizontally placing a binary operator (e.g. +) relative to its operands (call them A and B): before, between, or behind. The "between" style (infix notation, A + B) we're most familiar with, from arithmetics in daily life, C, and Tcl's expr command. Here you need operator precedence rules and/or parens to indicate the intended execution sequence. (1 + 2) * (3 + 4) -- C, tcl/expr, ... The "before" style (prefix or "Polish" notation, + A B) is unambiguous, but execution sequence goes not necessarily from left to right. Lisp and Tcl (mostly) use this pattern, but still need parens or brackets. (* (+ 1 2) (+ 3 4)) -- LISP The "behind" style (postfix or "Reverse Polish (RPN)" notation, A B +) is used in Forth (see [Trying FORTH in Tcl]), PostScript and many pocket calculators. No parens are needed at all, so both languages reuse them for different purposes: Forth for comments, PostScript as string delimiters. When you meet an operator in an RPN language, all its operands are already set up, so it's easiest implemented. That's why I picked it for this weekend's fun project... an interpreter for a tiny subset of Forth - but you could adjust this with few changes for Postscript, maybe except for all the graphics rendering engine ;-) 1 2 + 3 4 + * -- Forth RPN languages are heavily stack-centered, so I create a global list Stack where a push appends (possibly more than one element) to the end, as is the habit in Forth display, while a pop returns the last element, assigns it to a var if specified, and truncates the list by 1: proc push args {eval lappend ::Stack $args} proc pop {args} { global Stack if ![llength $Stack] {error "stack underflow"} set res [lindex $Stack end] set Stack [lrange $Stack 0 [expr [llength $Stack]-2]] if [llength $args]==1 {upvar 1 $args there; set there $res} set res } Initially, the stack and an array RPN for word definitions are set up (in Forth, word defs are also in a stack, but this was weekend after all), and operands from expr and some Forth basics are defined in Tcl - note that I use small integers as variable names in order not to conflict with other user code. Finally, some word definitions are done in Forth in their peculiar : name words that make up the body ; syntax - in fact Forth "commands" could be called by just substituting their body for the name, but since RPN holds Tcl definitions, I call ''rpn'' with the specified word sequence. proc rpn:init {} { global RPN Stack set RPN(_mode) normal ;# may also be comment or code set Stack [list] ######### importing binary operators from expr... foreach op {+ - * / % == != > >= < <= && ||} { regsub @op {pop 1; pop 2; push [expr $2 @op $1]} $op RPN($op) } ######### defining some elementaries in Tcl... foreach {op code} { . {puts -nonewline "[pop] "; flush stdout} .r {puts -nonewline [format "%[pop]d " [pop]]} .s {puts $Stack} abs {push [expr abs([pop])]} cr {puts ""} drop {pop} emit {puts -nonewline [format %c [pop]]} pick {push [lindex $Stack [expr -[pop]+[llength $Stack]-1]]} roll { set 1 [expr [llength $Stack]-[pop]-2] set 2 [lindex $Stack $1] set Stack [lreplace $Stack $1 $1] push $2 } : {set RPN(_mode) code} "(" {set RPN(_mode) comment} +debug {set RPN(_mode) debug} -debug {set RPN(_mode) normal} } {set RPN($op) $code} ######## defining more elementaries in sub-Forth itself... rpn { : dup 0 pick ; : mod % ; : over 1 pick ; : rot 2 roll ; : swap 1 roll ; } } And this is the main RPN "engine" that executes this kind of code. You can pass it a list of words or, convenient for interactive use, just omit the braces and let ''args'' pick its arguments up (except in word definitions, where it's better to hide the final semicolon from Tcl's parser): rpn {1 2 + 3 4 + *} --> 21 rpn 1 2 + 3 4 + * --> 21 Each call to ''rpn'' returns the top of stack without modifying that, so for calculation tasks, you're almost set. You can see the whole stack from Tcl with ''set ::Stack'', or from sub-Forth with ''.s''. For testing, I added the words ''+debug'' and ''-debug'' that turn on/off a display of ''(word) stack'' for each word processed. Without arguments, you can call ''rpn'' like an interactive Forth system, and leave it with the word ''bye''. proc rpn args { global RPN Stack if ![info exists Stack] rpn:init if {$args==""} { while {[lindex $Stack end]!="bye"} { puts -nonewline "ok "; flush stdout gets stdin line if [catch {rpn $line} res] {puts "ERROR: $res"} } puts [pop] return } if [llength $args]==1 {set args [lindex $args 0]} foreach word $args { switch -- $RPN(_mode) { normal - debug { if [info exists RPN($word)] { eval $RPN($word) } else { push $word } if {$RPN(_mode)=="debug"} {puts "($word) $Stack"} } comment {if {$word==")"} {set RPN(_mode) normal}} code { if {$word==";"} { set RPN(_mode) normal set c $RPN(_code) set RPN([lindex $c 0]) [concat rpn [lrange $c 1 end]] set RPN(_code) {} } else { lappend RPN(_code) $word} } } } lindex $Stack end } A real Forth system stacks shorts/longs or other fixed-size stuff, but since a Tcl list can hold any string as elements, I'm not hurried to model the more complex string handling in Forth. Instead, all words not known as commands are pushed, and it's up to the popper what he does with them, so Forth's ." hello, world " (dot-quote and quote being words that cause special handling of their content) is now simply "hello, world" . - the dot, in Forth only for printing integers, here prints any string it finds on top of stack... ---- What's completely missing yet are control structures like ... (cond) IF (this) ELSE (that) THEN ... To add those, more lookahead modes in the interpreter are needed. Maybe sometime later... ---- FORTH handles these with a separate stack I understand. Is there a way of doing that in Tcl? -[BMA] ---- [Minimal RPN] - [Tcl and other languages] - [Arts and crafts of Tcl-Tk programming]