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
32000Scott 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