George Peter Staplin: July 10, 2004 - It sometimes amazes me how beautiful some aspects of Tcl are. I've been using proc unknown args {expr $args} for a while for doing calculations and tests when designing software. Today it occured to me that I could make life easier and avoid so many parentheses by extending that simple unknown procedure to this:
proc unknown args {set ::that [expr $args]}
Now I can do:
$ echo "proc unknown args {set ::that [expr \$args]}" > .tclshrc $ tclsh8.4 % 1000 * 256 256000 % $that / 8 32000
Scott Nichols Really cool. Would there be any issues adding your calculator proc to existing Tcl applications? This would be helpful and save typing time for programmers.
George Peter Staplin: Error handling and auto-loading/lazy-loading might not work the same. You could do it this way:
rename unknown _unknown proc unknown args { global that if {[catch {expr $args} that]} { uplevel 1 [concat _unknown $args] } }
MG That introduces some bugs for me on Win XP; when you enter a command that can't be parsed by expr, the $that variable is overwritten with an error message. Also, math commands handled by unknown no longer return the answer. Changing it like this works for me. . .
rename unknown _unknown proc unknown {args} { global that if { [catch {expr $args} temp] } { uplevel 1 [concat _unknown $args] } else { set that $temp } }
George Peter Staplin: That is better! :)
Should the that variable move into a namespace to protect it from accidental overwrites?
GN what was the idea of "that"? A slightly improved version:
rename unknown _unknown proc unknown {args} { if {! [catch {set result [expr $args]}] } { return $result } uplevel 1 [concat _unknown $args] }
MG - GPS explained it up the top; it's so you can continue calculations. For instance, and very basically,
% 1 + 2 3 % $that * 2 6
Also, in yours, the 'set result ..' isn't necessary, because of the way catch works, that line could've been
if { ![catch {expr $args} result] } {
with the call to catch automatically setting $result for you, with the return value of the expr call.
GN sure, but without the second arg its less magic for a novice and the overwriting of that with the errormsg can't happen; prepend two ":" before the result string (twice) to get a gobal result variable. To save results, one can use as well:
set 3pi [acos(-1) * 3]
RS Here's a simple one-liner macro for zsh (on bash it didn't work) to start a short-lived tclsh as calculator:
$ tc () { echo puts '[expr' $@ ']' | tclsh }
Testing:
$ tc acos(-1)*2 6.28318530718
HolgerJ This is a version for bash:
tc() { echo "puts [expr "$@" ] " | tclsh; }
RS Note however that bash has different ideas what * means. Here's an interactive testing session:
~ $ tc 7*6 42 ~ $ tc 7 * 6 expected integer but got "0810" (looks like invalid octal number) ~ $ tc 7 \* 6 expected integer but got "0810" (looks like invalid octal number) ~ $ tc '7 \* 6' 42 ~ $ tc '7 * 6' expected integer but got "0810" (looks like invalid octal number) ~ $
The last test surprised me - I thought single quotes would prevent interpretation of the "*", similar to Tcl's braces... Maybe some double evaluation going on. before and inside the call to tc(). The old rule of thumb applies: for each round of evaluation, escape once more :^) See:
~ $ tc 7 \\\* 6 42
Of course this gets ugly. And depending on what files you have, more trouble may be in store:
~ $ touch 7by6 ~ $ tc 7*6 syntax error in expression "7by6": extra tokens at end of expression
awk is safer that way, but needs more syntax:
~ $ awk 'BEGIN{print 7*6}' 42 ~ $ awk 'BEGIN{print 7 * 6}' 42