[Richard Suchenwirth] 2001-12-17 - To extend Tcl, i.e. to make it understand and do things that before raised an error, the easiest way is to write a [proc]. Any proc must however be called in compliance with Tcl's fundamental syntax: first word is the command name, then the arguments separated by whitespace. Deeper changes are possible with the [unknown] command, which is called if a command name is, well, unknown, and in the standard version tries to call executables, to auto-load scripts, or do other helpful things (see the file init.tcl). One could edit that file (not recommended), or rename ''unknown'' to something else and provide one's own unknown handler, that falls through to the original proc if unsuccessful, as shown in [Radical language modification]. Here is a simpler way that allows to extend [unknown] "in place" and incrementally: We let [unknown] "know" what action it shall take under what conditions. The ''know'' command is called with a '''condition''' that should result in an integer when given to [expr], and a '''body''' that will be executed if ''cond'' results in nonzero, returning the last result if not terminated with an explicit [return]. In both ''cond'' and ''body'' you may use the variable ''args'' that holds the problem command ''unknown'' was invoked with. proc know {cond body} { proc unknown {args} [string map [list @c@ $cond @b@ $body] { if {![catch {expr {@c@}} res] && $res} { return [eval {@b@}] } }][info body unknown] } if 0 {''Condition'' and ''body'' are wrapped together and prepended to the previous [unknown] body. This means that subsequent calls to ''know'' stack up, last condition being tried first, so if you have several conditions that fire on the same input, let them be "known" from generic to specific. Here's a little debugging helper, if "know" conditions don't fire: } proc know? {} {puts [string range [info body unknown] 0 511]} if 0 {Now testing what new magic this handful of code allows us to do. This simple example invokes [expr] if the "command" is digestible for it: know {[expr $args] || 1} {expr $args} % 1+2 * 3 7 The "||(or) 1" appended in the condition lets this fire even if the expression would result in zero... I started these experiments because I wanted to have an infix index vector constructor (cf. [Playing Haskell]), so [[1..4]] returns {1 2 3 4}: know {[regexp {^([0-9]+)\.\.([0-9]+)$} [lindex $args 0] -> from to]} { set res {} while {$from<=$to} {lappend res $from; incr from} set res } % puts [1..5] 1 2 3 4 5 Here's a weird experiment with a "reverse Polish" command (see [RPN in Tcl]): % know {[lindex $args end]=="say"} {puts [lrange $args 0 end-1]} % hello world say hello world And another variation on infix assignment:} know {[lindex $args 1] == "="} { set name [lindex $args 0] set value [lrange $args 2 end] if [catch {uplevel 1 set $name [expr $value]} res] { uplevel 1 set $name [list $value] } else {set res} } puts [i = 17] puts [j = $i + 4] But as in [radical language modification], this works only for variable names that are unknown as commands... ---- This is pure and utter evil. I ''like'' it! ---- I have wondered how hard it would be to connect unknown to a database or even a socket that would serve as the source for unknown procedures. I was thinking about how a local client can be customized to match a remote server. In the two cases I am considering, the code that defines the procedures for editing data in a data base could be in the data base itself. When the client runs and needs to access a table that is defined (in the data base) but not previously accessed it would go to a data base table of tcl code (accessed using the table name as the key) and ''somehow'' get the code into the interpreter. I was looking at the pkgIndex file and was wondering about what would happen if the code [list source .....] was replaced with something besides source that would load code. Then I wondered, what ''is'' the equivalent of '''source'''? That is, if I have a string containing tcl code that is equivalent to the contents of a file of tcl code, what is the proper way to load it? In the second case, instead of having the source code in a data base, the source would reside on a remote system. I would want to dynamically load it through a socket. (This sounds similar to the interface between [Erlang] and Tk.) So ''somehow'' unknown would need to do these steps: 1. Inquire through a socket whether the remote system had a definition for a proc. 1. If it did, then transfer it through the socket (from the server to the client). 1. If it was received successfully, load it and execute it (on the client). Is this kind of behavior a ''solved problem'' somewhere? ''[escargo] 5 Nov 2002'' ''[schlenk] 17 Aug 2005'' - something like this is done for [PostgreSQL] stored procedures in PL/Tcl [http://www.postgresql.org/docs/8.0/interactive/pltcl-unknown.html] ''[escargo] 17 Aug 2005'' - Are stored procedures executed on the server or on the client? My question above had the client executing the downloaded code (and I've revised my earlier statements to make that more clear). ---- [RS]: To evaluate a string, use [eval] of course ;-) ''source $file'' does basically: set fp [open $file] set data [read $fp] close $fp eval $data So if you get your code over a network, just [eval] it (but maybe in a safe interpreter...) [escargo]: I was sure it was something simple like that, but [eval] is not mentioned on the [source] page (although ''evaluate'' is). [RS]: Thanks for the hint, I put it there - but Wiki pages don't claim to be the authoritative reference, that would rather be the man pages. [escargo]: The discussion on [source] is now much more detailed from my point of view, and helps clarify my options for the future. Thank you. [DKF]: [Source], because it is a C command, uses ''Tcl_Eval'' internally. Well, really it uses ''Tcl_EvalFile'' but that's just a trivial wrapper round ''Tcl_Eval''. There's only one minor subtlety, and that is that it stores the name of the file in the interpreter for retrieval with [[info script]]. Luckily, this mechanism is also accessable to Tcl scripters, making the [[source]] command completely replaceable with the following (except for error handling, which is an exercise): proc new_source {filename} { set fd [open $filename] set script [read $fd] close $fd info script $filename uplevel 1 $script } ---- See [Toot as toot can] for a [simple]r variation of ''know'': proc know what {proc unknown args $what\n[info body unknown]} [DKF] rightly pointed, out, on 2005-03-29 in the [Tcl chatroom], that this is insecure if ''what'' is not a complete command (or sequence thereof) - the body of [unknown] is uncritically extended, but execution of [unknown] runs into an error and basically blocks the road. So, despite my love for one-liners, I now recommend the safer version proc know what { if ![info complete $what] {error "incomplete command(s) $what"} proc unknown args $what\n[info body unknown] } ;# RS Usage example (let [expr] language be known): % know {if {![catch {expr $args} res]} {return $res}} % 3+4 7 ---- [CMcC] Following from [jenglish]'s comment: why learn to use a monster HTML-template library when you can, with a few lines of code, extend the base language so you can write things like [[ href "/home.html" { ! "Home" }]] ... I wrote the following to do just that using unknown. know {[string match <*> [lindex $args 0]]} { set tag [string trim [lindex $args 0] "<>"] ::proc ::<$tag> {args} [string map [list @T $tag] { set result {} foreach {n v} [lrange $args 0 end-1] { set v [string map {& & ' ' " " < < > >} $v] lappend result "$n='$v'" } return "<@T [join ${result}]>[lindex $args end]" }] return [eval $args] } ---- [NEM] TIP 181 [http://tip.tcl.tk/181] introduces a ''namespace unknown'' command into Tcl 8.5 that allows per-namespace unknown command handling. By default this works exactly as now, but it also allows a more controlled manner of dealing with unknown commands. For instance, here is how you would add [RS]'s numeric ranges code: % proc range {next args} { if {[regexp {^([0-9]+)\.\.([0-9]+)$} [lindex $args 0] -> from to]} { set res {} while {$from<=$to} { lappend res $from; incr from } return $res } else { uplevel 1 $next $args } } % # Install this behaviour for the current namespace % namespace unknown [list range [namespace unknown]] range ::unknown % puts [1..5] 1 2 3 4 5 Notice how we can use any named proc as part of our unknown handler instead of having to redefine "::unknown". Also note that with the introspection we can handily capture the old unknown handler and pass it as an argument to our new handler, allowing for easy chaining of functionality. For instance, if I now add auto-expansion of leading word to command lookup, it is easy to accomplish: % proc expand {next cmd args} { if {[llength $cmd] > 1} { uplevel 1 $cmd $args } else { uplevel 1 $next [linsert $args 0 $cmd] } } % namespace unknown [list expand [namespace unknown]] expand {range ::unknown} % puts [1..5] 1 2 3 4 5 % set chop {string range} string range % $chop "Hello, World!" 0 4 Hello % foo invalid command name "foo" Here we have a chain of unknown handlers: expand -> range -> unknown (Tcl's default unknown handler). ---- [RS] 2006-08-10: Here's another thing to "know", bringing slice syntax to [list]s: know { if {[regexp {(.+)\((.+)\)$} [lindex $args 0] -> _var ind]} { upvar 1 $_var var foreach {from to} [split $ind :] break if {$to eq ""} {set to $from} if {[lindex $args 1] eq "="} { return [set var [eval [list lreplace $var $from $to] [lrange $args 2 end]]] } else {return [lrange $var $from $to]} } } % set try {a b c d e f g} a b c d e f g % try(0) a % try(0:5) a b c d e f % try(end-2:end) e f g % try(2:3) = foo a b foo e f g % try(2:3) = foo bar a b foo bar f g ---- hmm the know? helper above is a little nasty given that it hard codes the amount to output, might i suggest the following? ====== proc known {} { set val "" regexp {(.*)\#original} [info body unknown] -> val set val } proc unknown {args} " #original unknown follows [info body unknown]" ====== ---- [unknown] - [Arts and crafts of Tcl-Tk programming]