Namespace resolution on unknown command

In comp.lang.tcl, Larry Smith proposed on 09 Nov 2000 that

 foo do-something

should be resolved, in case of no command foo being defined, like

 namespace eval foo do-something

A generalized approach, using whitespace as namespace separators, was proposed in http://people.manchester.ac.uk/~zzcgudf/tcl/future.html

Reinhard Max commented: A few weeks ago, I implemented parts of that concept in pure Tcl. Maybe it's a good starting point for a more sophisticated implementation:

 #
 # by Reinhard Max <[email protected]>
 #
 if {[info commands _unknown] eq ""} {

    rename unknown _unknown

    proc unknown {args} {

        # get the namespace of the caller
        # and set it to "" if it was the global namespace
        set ns [uplevel 1 namespace current]
        if {[string equal $ns ::]} {set ns ""} else {append ns ::}
        append ns [lindex $args 0]

        if {[catch {namespace children $ns}]} {

            # The namespace doesn't exist,
            # call the default [unknown] proc.
            set ret [catch {uplevel 1 _unknown $args} val]
            return -code $ret $val
        }

        # The namespace exists. Create a proc with the same name
        # for executing subcommands in this namespace ...
        eval [subst -nocommands {

            proc $ns {command args} {
                    
                set code [catch {
                    uplevel 1 ${ns}::[set command] [set args]
                } value]
                
                return -code [set code] [set value]
            }
        }]

        # ... and call it.
        set ret [catch {uplevel 1 $ns [lrange $args 1 end]} val]
        return -code $ret $val
    }

    package provide nslist 0.42
 }

Usage examples and test:

 tclsh8.3 > package require nslist 
 0.42
 tclsh8.3 > namespace eval a { namespace eval b { proc c {} {puts c}}}
 tclsh8.3 > a b c
 c
 tclsh8.3 > a
 no value given for parameter "command" to "a"
 while evaluating a
 tclsh8.3 > a b
 no value given for parameter "command" to "a::b"
 while evaluating {a b}

RS: It would be even more symmetric if we could write

 proc {a b c} {} {puts c}

instead of

 namespace eval a { namespace eval b { proc c {} {puts c}}}

or

 proc a::b::c {} {puts c}

e.g. by overloading the proc command to substitute spaces in the proc name with ::


RM: Something like that?

 #
 # Wrapper around [proc] that allows commands inside namespaces
 # to be provided as {a b c} instead of a::b::c .
 #
 proc nproc {args} {

    set name [lindex $args 0]
    set ns [join [lrange $name 0 end-1] ::]
    set name [lindex $name end]
    set args [lreplace $args 0 0 _proc_ $name]

    set code [catch {namespace eval $ns $args} val]
    regsub _proc_ $val proc val
    
    return -code $code $val
 }

 rename proc _proc_
 rename nproc proc

RS: I think [llength name] should be checked first: if ==1, calling the renamed _proc_ (ex proc) is sufficient.


RM: OK, here is a new version, that takes care about that. In addition, it chages the syntax to be:

 proc a b c {arg1 arg2} {body}

I think this fits better to the subcommand style.

 proc nproc {args} {

    if {[llength $args] <= 3} {

        set code [catch [linsert $args 0 _proc_] val]
        regsub _proc_ $val proc val
        return -code $code $val
    }
    
    set ns [join [lrange $args 0 end-3] ::]
    set args [lreplace $args 0 end-3 _proc_]

    set code [catch {namespace eval $ns $args} val]
    regsub _proc_ $val proc val
    
    return -code $code $val
 }