Permalink
Browse files

Add support for lightweight namespaces

See README.namespaces

Signed-off-by: Steve Bennett <steveb@workware.net.au>
  • Loading branch information...
1 parent 1f0d4b7 commit 7f383c6726fd71c23d622753152faf749124ca22 @msteveb committed Dec 1, 2011
Showing with 1,475 additions and 113 deletions.
  1. +191 −0 README.namespaces
  2. +4 −0 auto.def
  3. +29 −34 examples/dns.tcl
  4. +333 −0 jim-namespace.c
  5. +288 −77 jim.c
  6. +11 −0 jim.h
  7. +124 −0 nshelper.tcl
  8. +1 −1 tests/alias.test
  9. +493 −0 tests/namespace.test
  10. +1 −1 utf8.h
View
@@ -0,0 +1,191 @@
+Lightweight Namespaces for Jim Tcl
+==================================
+
+There are two broad requirements for namespace support in Jim Tcl.
+
+1. To allow code from multiple sources while reducing the chance of name clashes
+2. To simplify porting existing Tcl code which uses namespaces
+
+This proposal addresses both of these requirements, with the following
+additional requirements imposed by Jim Tcl.
+
+3. Support for namespaces should be optional, with the space and time overhead
+ when namespaces are disabled as close to zero as possible.
+4. The implementation should be small and reasonably efficient.
+
+To further expand on requirement (2), the goal is not to be able to run
+any Tcl scripts using namespaces with no changes. Rather, scripts
+which use namespaces in a straightforward manner, should be easily
+ported with changes which are compatible with Tcl.
+
+Implicit namespaces
+-------------------
+Rather than supporting explicit namespaces as Tcl does, Jim Tcl
+supports implicit namespaces. Any procedure or variable which
+is defined with a name containing ::, is implicitly scoped within
+a namespace.
+
+For example, the following procedure and variable are created
+in the namespace 'test'
+
+proc ::test::myproc {} {
+ puts "I am in namespace [namespace current]"
+}
+set ::test::myvar 3
+
+This approach allows much of the existing variable and command
+resolution machinery to be used with little change. It also means
+that it is possible to simply define a namespace-scoped variable
+or procedure without first creating the namespace, and similarly,
+namespaces "disappear" when all variables and procedures defined
+with the namespace scope are deleted.
+
+Namespaces, procedures and call frames
+--------------------------------------
+When namespace support is enabled (at build time), each procedure has an associated
+namespace (based on the procedure name). When the procedure is evaluated,
+the namespace for the created call frame is set to the namespace associated
+with the procedure.
+
+Command resolution is based on the namespace of the current call frame.
+An unscoped command name will first be looked up in the current namespace,
+and then in the global namespace.
+
+This also means that commands which do not create a call frame (such as commands
+implemented in C) do not have an associated namespace.
+
+Similarly to Tcl, namespace eval introduces a temporary, anonymous
+call frame with the associated namespace. For example, the following
+will return "::test,1".
+
+namespace eval test {
+ puts [namespace current],[info level]
+}
+
+Variable resolution
+-------------------
+The variable command in Jim Tcl has the same syntax as Tcl, but is closer in behaviour to the global command.
+The variable command creates a link from a local variable to a namespace variable, possibly initialising it.
+
+For example, the following procedure uses 'variable' to initialse and access myvar.
+
+proc ::test::myproc {} {
+ variable myvar 4
+ incr myvar
+}
+
+Note that there is no automatic resolution of namespace variables.
+For example, the following will *not* work.
+
+namespace eval ::test {
+ variable myvar 4
+}
+namespace eval ::test {
+ # This will increment a local variable, not ::test::myvar
+ incr myvar
+}
+
+And similarly, the following will only access local variables
+
+set x 3
+namespace eval ::test {
+ # This will incremement a local variable, not ::x
+ incr x
+ # This will also increment a local variable
+ incr abc::def
+}
+
+In the same way that variable resolution does not "fall back" to
+global variables, it also does not "fall back" to namespace variables.
+
+This approach allows name resolution to be simpler and more efficient
+since it uses the same variable linking mechanism as upvar/global
+and it allows namespaces to be implicit. It also solves the "creative
+writing" problem where a variable may be created in an unintentional
+scope.
+
+The namespace command
+---------------------
+Currently, the following namespace commands are supported.
+
+* current - returns the current, fully-qualified namespace
+* eval - evaluates a script in a namespace (introduces a call frame)
+* qualifiers, tail, parent - note that these do not check for existence
+* code, inscope - implemented
+* delete - deletes all variables and commands with the namespace prefix
+* which - implemented
+* upvar - implemented
+
+namespace children, exists, path
+--------------------------------
+With implicit namespaces, the namespace exists and namespace children commands
+are expensive to implement and are of limited use. Checking the existence
+of a namespace can be better done by checking for the existence of a known procedure
+or variable in the namespace.
+
+Command resolution is always done by first looking in the namespace and then
+at the global scope, so namespace path is not required.
+
+namespace ensemble
+------------------
+The namespace ensemble command is not currently supported. A future version
+of Jim Tcl will have a general-purpose ensemble creation and manipulation
+mechanism and namespace ensemble will be implemented in terms of that mechanism.
+
+namespace import, export, forget, origin
+----------------------------------------
+Since Jim Tcl namespaces are implicit, there is no location to store export patterns.
+Therefore the namespace export command is a dummy command which does nothing.
+All procedures in a namespace are considered to be exported.
+
+The namespace import command works by creating aliases to the target namespace
+procedures.
+
+namespace forget is not implemented.
+
+namespace origin understands aliases created by namespace import
+and can return the original command.
+
+namespace unknown
+-----------------
+If an undefined command is invoked, the "unknown" command is invoked.
+The same namespace resolution rules apply for the unknown command.
+This means that in the following example, test::unknown will be invoked
+for the missing command rather than the global ::unknown.
+
+proc unknown {args} {
+ puts "global unknown"
+}
+
+proc test::unknown {args} {
+ puts "test unknown"
+}
+
+namespace eval test {
+ bogus
+}
+
+This approach requires no special support and provides enough flexibility that
+the namespace unknown command is not implemented.
+
+Porting namespace code from Tcl to Jim Tcl
+------------------------------------------
+For most code, the following changes will be sufficient to port code.
+
+1. Canonicalise namespace names. For example, ::ns:: should be written
+ as ::ns or ns as appropriate, and excess colons should be removed.
+ For example ::ns:::blah should be written as ::ns::blah
+ (Note that the only "excess colon" case supported is ::::abc
+ in order to support [namespace current]::abc in the global namespace)
+
+2. The variable command should be used within namespace eval to link
+ to namespace variables, and access to variables in other namespaces
+ should be fully qualified
+
+Changes in the core Jim Tcl
+---------------------------
+Previously Jim Tcl performed no scoping of command names. i.e. The
+::format command was considered different from the format command.
+
+Even if namespace support is disabled, the command resolution will
+recognised global scoping of commands and treat these as identical.
View
@@ -197,6 +197,8 @@ dict set extdb attrs {
history {}
load { static }
mk { cpp optional }
+ namespace { static }
+ nshelper { tcl optional }
oo { tcl }
pack {}
package { static }
@@ -226,10 +228,12 @@ dict set extdb info {
glob { dep readdir }
load { check {[have-feature dlopen-compat] || [cc-check-function-in-lib dlopen dl]} libdep lib_dlopen }
mk { check {[check-metakit]} libdep lib_mk }
+ namespace { dep nshelper }
posix { check {[have-feature waitpid]} }
readdir { check {[have-feature opendir]} }
readline { check {[cc-check-function-in-lib readline readline]} }
rlprompt { dep readline }
+ tree { dep oo }
sdl { check {[cc-check-function-in-lib SDL_SetVideoMode SDL] && [cc-check-function-in-lib rectangleRGBA SDL_gfx]}
libdep {lib_SDL_SetVideoMode lib_rectangleRGBA}
}
Oops, something went wrong.

0 comments on commit 7f383c6

Please sign in to comment.