[Richard Suchenwirth] 1999-07-14 - My very first Wiki page was introduced by [DKF]: You can do some mind-bogglingly fancy stuff with modifications to the [[unknown]] command (the handler for everything that [Tcl] doesn't know what to do with). This page includes some of the more fun ones.... As always, additions, comments and corrections are utterly welcome. - '''DKF''' ---- And, as you're interested in alternatives to [expr], here's my unknown proc, introducing '''infix notation for assignment''' (a = b+c) into the strictly Polish Tcl language: if {[info proc _unknown]==""} {rename unknown _unknown} ;# keep the original proc unknown {args} { if [regexp (.+):$ [lindex $args 0] -> name] { set args [lreplace $args 0 0 $name =] } ;# allow REBOL-style assignments (foo: bar; bar: 17+4) if {[lindex $args 1]=="="} { # maybe an assignment like "x = 3+4" ? (Blanks matter!) upvar [lindex $args 0] _x set rest [lrange $args 2 end] if [llength [info commands [lindex $args 2]]] { return [set _x [uplevel eval $rest]] } set _x $rest ;# this should always work... catch {set _x [expr $rest]} ;# ...but maybe expr is happy return $_x } elseif {[regexp {^([^ ]+)\+\+$} $args -> vname]} { uplevel [list incr $vname] ;# allow things like "i++" ... } elseif {[regexp {^([^ ]+)--$} $args -> vname]} { uplevel [list incr $vname -1] ;# ... or "j--" } elseif {[regexp {^[-+/\*\.0-9 ()]+$} $args]} { return [expr $args] ;# pure expression? "(17+4)/3" } else {uplevel 1 _unknown $args} ;# let old "unknown" do it } For calls like the following sample usages (from an imaginary session): % v = info tclversion ;# maybe assign a proc result (no []!!) 8.1 % s = this is a string ;# default: shrink-wrapped string (no ""!) this is a string % j = sqrt(2)*3 ;# if expr agrees, the result (no [expr..]!) 4.24264068712 Yes, this is still Tcl, and no, it's not like in the book. (Adapted from an article [http://www.deja.com/getdoc.xp?AN=498519170] ---- '''Known problem:''' The unknown handler is (of course) only called for something unknown. So, if you want to assign to a variable that happens to be also the name of a command/proc, "unknown" would not get called: info = Something to tell bad option "=": must be args, body, cmdcount, commands, ... and this will not be too rare because of many common-sense command/proc names in Tcl. This is where the [REBOL] style ("i: 5", blank after but not before the colon) comes in handy. info: Something to tell would only miss the unknown if you had a proc named "info:", which may not occur that often. BTW: how's for {i: 0} {$i<$max} {i++} {...} For a cleaner, unknown-free approach to "natural assignments" see [Gadgets]. ---- Another use of unknown, now for '''indexing lists and arrays''' in half-natural syntax: if {![llength [info commands _unknown]]} {rename unknown _unknown} proc unknown {args} { if {[lindex $args 1]=="(" && [lindex $args 3]==")"} { upvar [lindex $args 0] _x if [array exists _x] {return $_x([lindex $args 2])} ;## return [lindex $_x [lindex $args 2]] } else { eval _unknown $args } This again only works if no proc has the same name as the list (or array). The line marked with ## shields the distinction between list and array, so you can say set foo {a b c d e f}; foo ( 2 ) => c array set bar {cat Katze dog Hund}; bar ( cat ) => Katze Blanks must however be written round the brackets (this is a potential error reason). This indexing handler can be combined with the infix-expression handler above. [Richard Suchenwirth] Tcl is the best invention since even before sliced bread !-) ---- [Bryan Oakley] proposed (without recommending) another use, so you can '''generate a list of integers''' by just putting .. between them, e.g. set i [5 .. 12] ==> {5 6 7 8 9 10 11 12}: if {![llength [info commands _unknown]]} {rename unknown _unknown} proc unknown args { if {[lindex $args 1]==".."} { set res {} for {set i [lindex $args 0]} {$i<=[lindex $args 2]} {incr i} { lappend res $i } return $res } else { eval _unknown $args } } RS - See also [Let unknown know] [Larry Smith]: You can get arbitrarily fancy with [run]. ---- [FW]: This hack allows for you to perform ''expr'' math operations "freeform" without actually needing to call expr. Similar to RS's expr functionality in infix notation for assignment at the top of the page. Don't try anything with strings, though - this is only for numbers. Since it captures all procedure names that contain a number or an operator, don't go naming any procedures things like "go!" and you'll be fine. Nor can you use the conditional (?:) operator. if {[info proc _unknown] == ""} {rename unknown _unknown} proc unknown {args} { if {[regexp {[0-9+-~!*/%<>|&().]} [lindex $args 0]]} { return [expr $args] } else { eval _unknown $args } } ;# FW Example: puts [sqrt(2) * 3] [LES]: no, it doesn't work for me [MG] Would something along these lines be easier? This works for all valid [expr] expressions. if {[info proc _unknown] == ""} {rename unknown _unknown} proc unknown {args} { if { [catch {expr $args} ans] } { return [eval _unknown $args]; } return $ans; } ---- [RS] Very much so - except that five years later, I prefer to just augment the [unknown] body, see [Let unknown know] - with a reusable ''know'' command: proc know what {proc unknown args $what\n[info body unknown]} know {if ![catch {expr $args} res] {return $res} ---- [Category Concept]