commit a copy of snit
[scpubgit/TenDotTcl.git] / snit / main1_83.tcl
diff --git a/snit/main1_83.tcl b/snit/main1_83.tcl
new file mode 100644 (file)
index 0000000..d8b16f6
--- /dev/null
@@ -0,0 +1,4011 @@
+#-----------------------------------------------------------------------
+# TITLE:
+#      main1_83.tcl
+#
+# AUTHOR:
+#      Will Duquette
+#
+# DESCRIPTION:
+#       Snit's Not Incr Tcl, a simple object system in Pure Tcl.
+#
+#       Snit 1.x Compiler and Run-Time Library, Tcl 8.3 and later
+#
+#       Copyright (C) 2003-2006 by William H. Duquette
+#       This code is licensed as described in license.txt.
+#
+#-----------------------------------------------------------------------
+# Back-port to Tcl8.3 by Kenneth Green (kmg)
+# Modified by Andreas Kupries.
+# Further modified by Will Duquette 12 Aug 2006
+#
+# Local changes marked with "#kmg-tcl83"
+#
+# Global changes:
+#  " trace add variable " -> "trace variable "
+#  " write " -> "w" in all calls to 'trace variable'
+#  " unset -nocomplain "  -> "::snit83::unset -nocomplain"
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# Namespace
+
+namespace eval ::snit:: {
+    namespace export \
+        compile type widget widgetadaptor typemethod method macro
+}
+
+#-----------------------------------------------------------------------
+# Some Snit variables
+
+namespace eval ::snit:: {
+    variable reservedArgs {type selfns win self}
+
+    # Widget classes which can be hulls (must have -class)
+    variable hulltypes {
+       toplevel tk::toplevel
+       frame tk::frame ttk::frame
+       labelframe tk::labelframe ttk::labelframe
+    }
+}
+
+#-----------------------------------------------------------------------
+# Snit Type Implementation template
+
+namespace eval ::snit:: {
+    # Template type definition: All internal and user-visible Snit
+    # implementation code.
+    #
+    # The following placeholders will automatically be replaced with
+    # the client's code, in two passes:
+    #
+    # First pass:
+    # %COMPILEDDEFS%  The compiled type definition.
+    #
+    # Second pass:
+    # %TYPE%          The fully qualified type name.
+    # %IVARDECS%      Instance variable declarations
+    # %TVARDECS%      Type variable declarations
+    # %TCONSTBODY%    Type constructor body
+    # %INSTANCEVARS%  The compiled instance variable initialization code.
+    # %TYPEVARS%      The compiled type variable initialization code.
+
+    # This is the overall type template.
+    variable typeTemplate
+
+    # This is the normal type proc
+    variable nominalTypeProc
+
+    # This is the "-hastypemethods no" type proc
+    variable simpleTypeProc
+}
+
+set ::snit::typeTemplate {
+
+    #-------------------------------------------------------------------
+    # The type's namespace definition and the user's type variables
+
+    namespace eval %TYPE% {%TYPEVARS%
+    }
+
+    #----------------------------------------------------------------
+    # Commands for use in methods, typemethods, etc.
+    #
+    # These are implemented as aliases into the Snit runtime library.
+
+    interp alias {} %TYPE%::installhull  {} ::snit::RT.installhull %TYPE%
+    interp alias {} %TYPE%::install      {} ::snit::RT.install %TYPE%
+    interp alias {} %TYPE%::typevariable {} ::variable
+    interp alias {} %TYPE%::variable     {} ::snit::RT.variable
+    interp alias {} %TYPE%::mytypevar    {} ::snit::RT.mytypevar %TYPE%
+    interp alias {} %TYPE%::typevarname  {} ::snit::RT.mytypevar %TYPE%
+    interp alias {} %TYPE%::myvar        {} ::snit::RT.myvar
+    interp alias {} %TYPE%::varname      {} ::snit::RT.myvar
+    interp alias {} %TYPE%::codename     {} ::snit::RT.codename %TYPE%
+    interp alias {} %TYPE%::myproc       {} ::snit::RT.myproc %TYPE%
+    interp alias {} %TYPE%::mymethod     {} ::snit::RT.mymethod
+    interp alias {} %TYPE%::mytypemethod {} ::snit::RT.mytypemethod %TYPE%
+    interp alias {} %TYPE%::from         {} ::snit::RT.from %TYPE%
+
+    #-------------------------------------------------------------------
+    # Snit's internal variables
+
+    namespace eval %TYPE% {
+        # Array: General Snit Info
+        #
+        # ns:                The type's namespace
+        # hasinstances:      T or F, from pragma -hasinstances.
+        # simpledispatch:    T or F, from pragma -hasinstances.
+        # canreplace:        T or F, from pragma -canreplace.
+        # counter:           Count of instances created so far.
+        # widgetclass:       Set by widgetclass statement.
+        # hulltype:          Hull type (frame or toplevel) for widgets only.
+        # exceptmethods:     Methods explicitly not delegated to *
+        # excepttypemethods: Methods explicitly not delegated to *
+        # tvardecs:          Type variable declarations--for dynamic methods
+        # ivardecs:          Instance variable declarations--for dyn. methods
+        typevariable Snit_info
+        set Snit_info(ns)      %TYPE%::
+        set Snit_info(hasinstances) 1
+        set Snit_info(simpledispatch) 0
+        set Snit_info(canreplace) 0
+        set Snit_info(counter) 0
+        set Snit_info(widgetclass) {}
+        set Snit_info(hulltype) frame
+        set Snit_info(exceptmethods) {}
+        set Snit_info(excepttypemethods) {}
+        set Snit_info(tvardecs) {%TVARDECS%}
+        set Snit_info(ivardecs) {%IVARDECS%}
+
+        # Array: Public methods of this type.
+        # The index is the method name, or "*".
+        # The value is [list $pattern $componentName], where
+        # $componentName is "" for normal methods.
+        typevariable Snit_typemethodInfo
+        array unset Snit_typemethodInfo
+
+        # Array: Public methods of instances of this type.
+        # The index is the method name, or "*".
+        # The value is [list $pattern $componentName], where
+        # $componentName is "" for normal methods.
+        typevariable Snit_methodInfo
+        array unset Snit_methodInfo
+
+        # Array: option information.  See dictionary.txt.
+        typevariable Snit_optionInfo
+        array unset Snit_optionInfo
+        set Snit_optionInfo(local)     {}
+        set Snit_optionInfo(delegated) {}
+        set Snit_optionInfo(starcomp)  {}
+        set Snit_optionInfo(except)    {}
+    }
+
+    #----------------------------------------------------------------
+    # Compiled Procs
+    #
+    # These commands are created or replaced during compilation:
+
+
+    # Snit_instanceVars selfns
+    #
+    # Initializes the instance variables, if any.  Called during
+    # instance creation.
+
+    proc %TYPE%::Snit_instanceVars {selfns} {
+        %INSTANCEVARS%
+    }
+
+    # Type Constructor
+    proc %TYPE%::Snit_typeconstructor {type} {
+        %TVARDECS%
+        %TCONSTBODY%
+    }
+
+    #----------------------------------------------------------------
+    # Default Procs
+    #
+    # These commands might be replaced during compilation:
+
+    # Snit_destructor type selfns win self
+    #
+    # Default destructor for the type.  By default, it does
+    # nothing.  It's replaced by any user destructor.
+    # For types, it's called by method destroy; for widgettypes,
+    # it's called by a destroy event handler.
+
+    proc %TYPE%::Snit_destructor {type selfns win self} { }
+
+    #----------------------------------------------------------
+    # Compiled Definitions
+
+    %COMPILEDDEFS%
+
+    #----------------------------------------------------------
+    # Finally, call the Type Constructor
+
+    %TYPE%::Snit_typeconstructor %TYPE%
+}
+
+#-----------------------------------------------------------------------
+# Type procs
+#
+# These procs expect the fully-qualified type name to be
+# substituted in for %TYPE%.
+
+# This is the nominal type proc.  It supports typemethods and
+# delegated typemethods.
+set ::snit::nominalTypeProc {
+    # Type dispatcher function.  Note: This function lives
+    # in the parent of the %TYPE% namespace!  All accesses to
+    # %TYPE% variables and methods must be qualified!
+    proc %TYPE% {{method ""} args} {
+        # First, if there's no method, and no args, and there's a create
+        # method, and this isn't a widget, then method is "create" and
+        # "args" is %AUTO%.
+        if {"" == $method && [llength $args] == 0} {
+            ::variable %TYPE%::Snit_info
+
+            if {$Snit_info(hasinstances) && !$Snit_info(isWidget)} {
+                set method create
+                lappend args %AUTO%
+            } else {
+                error "wrong \# args: should be \"%TYPE% method args\""
+            }
+        }
+
+        # Next, retrieve the command.
+       variable %TYPE%::Snit_typemethodCache
+        while 1 {
+            if {[catch {set Snit_typemethodCache($method)} commandRec]} {
+                set commandRec [::snit::RT.CacheTypemethodCommand %TYPE% $method]
+
+                if {[llength $commandRec] == 0} {
+                    return -code error  "\"%TYPE% $method\" is not defined"
+                }
+            }
+
+            # If we've got a real command, break.
+            if {[lindex $commandRec 0] == 0} {
+                break
+            }
+
+            # Otherwise, we need to look up again...if we can.
+            if {[llength $args] == 0} {
+                return -code error \
+                 "wrong number args: should be \"%TYPE% $method method args\""
+            }
+
+            lappend method [lindex $args 0]
+            set args [lrange $args 1 end]
+        }
+
+        set command [lindex $commandRec 1]
+
+        # Pass along the return code unchanged.
+        set retval [catch {uplevel 1 $command $args} result]
+
+        if {$retval} {
+            if {$retval == 1} {
+                global errorInfo
+                global errorCode
+                return -code error -errorinfo $errorInfo \
+                    -errorcode $errorCode $result
+            } else {
+                return -code $retval $result
+            }
+        }
+
+        return $result
+    }
+}
+
+# This is the simplified type proc for when there are no typemethods
+# except create.  In this case, it doesn't take a method argument;
+# the method is always "create".
+set ::snit::simpleTypeProc {
+    # Type dispatcher function.  Note: This function lives
+    # in the parent of the %TYPE% namespace!  All accesses to
+    # %TYPE% variables and methods must be qualified!
+    proc %TYPE% {args} {
+        ::variable %TYPE%::Snit_info
+
+        # FIRST, if the are no args, the single arg is %AUTO%
+        if {[llength $args] == 0} {
+            if {$Snit_info(isWidget)} {
+                error "wrong \# args: should be \"%TYPE% name args\""
+            }
+
+            lappend args %AUTO%
+        }
+
+        # NEXT, we're going to call the create method.
+        # Pass along the return code unchanged.
+        if {$Snit_info(isWidget)} {
+            set command [list ::snit::RT.widget.typemethod.create %TYPE%]
+        } else {
+            set command [list ::snit::RT.type.typemethod.create %TYPE%]
+        }
+
+        set retval [catch {uplevel 1 $command $args} result]
+
+        if {$retval} {
+            if {$retval == 1} {
+                global errorInfo
+                global errorCode
+                return -code error -errorinfo $errorInfo \
+                    -errorcode $errorCode $result
+            } else {
+                return -code $retval $result
+            }
+        }
+
+        return $result
+    }
+}
+
+#-----------------------------------------------------------------------
+# Instance procs
+#
+# The following must be substituted into these proc bodies:
+#
+# %SELFNS%       The instance namespace
+# %WIN%          The original instance name
+# %TYPE%         The fully-qualified type name
+#
+
+# Nominal instance proc body: supports method caching and delegation.
+#
+# proc $instanceName {method args} ....
+set ::snit::nominalInstanceProc {
+    set self [set %SELFNS%::Snit_instance]
+
+    while {1} {
+        if {[catch {set %SELFNS%::Snit_methodCache($method)} commandRec]} {
+            set commandRec [snit::RT.CacheMethodCommand %TYPE% %SELFNS% %WIN% $self $method]
+
+            if {[llength $commandRec] == 0} {
+                return -code error \
+                    "\"$self $method\" is not defined"
+            }
+        }
+
+        # If we've got a real command, break.
+        if {[lindex $commandRec 0] == 0} {
+            break
+        }
+
+        # Otherwise, we need to look up again...if we can.
+        if {[llength $args] == 0} {
+            return -code error \
+                "wrong number args: should be \"$self $method method args\""
+        }
+
+        lappend method [lindex $args 0]
+        set args [lrange $args 1 end]
+    }
+
+    set command [lindex $commandRec 1]
+
+    # Pass along the return code unchanged.
+    set retval [catch {uplevel 1 $command $args} result]
+
+    if {$retval} {
+        if {$retval == 1} {
+            global errorInfo
+            global errorCode
+            return -code error -errorinfo $errorInfo \
+                -errorcode $errorCode $result
+        } else {
+            return -code $retval $result
+        }
+    }
+
+    return $result
+}
+
+# Simplified method proc body: No delegation allowed; no support for
+# upvar or exotic return codes or hierarchical methods.  Designed for
+# max speed for simple types.
+#
+# proc $instanceName {method args} ....
+
+set ::snit::simpleInstanceProc {
+    set self [set %SELFNS%::Snit_instance]
+
+    if {[lsearch -exact ${%TYPE%::Snit_methods} $method] == -1} {
+       set optlist [join ${%TYPE%::Snit_methods} ", "]
+       set optlist [linsert $optlist "end-1" "or"]
+       error "bad option \"$method\": must be $optlist"
+    }
+
+    eval [linsert $args 0 \
+              %TYPE%::Snit_method$method %TYPE% %SELFNS% %WIN% $self]
+}
+
+
+#=======================================================================
+# Snit Type Definition
+#
+# These are the procs used to define Snit types, widgets, and
+# widgetadaptors.
+
+
+#-----------------------------------------------------------------------
+# Snit Compilation Variables
+#
+# The following variables are used while Snit is compiling a type,
+# and are disposed afterwards.
+
+namespace eval ::snit:: {
+    # The compiler variable contains the name of the slave interpreter
+    # used to compile type definitions.
+    variable compiler ""
+
+    # The compile array accumulates information about the type or
+    # widgettype being compiled.  It is cleared before and after each
+    # compilation.  It has these indices:
+    #
+    # type:                  The name of the type being compiled, for use
+    #                        in compilation procs.
+    # defs:                  Compiled definitions, both standard and client.
+    # which:                 type, widget, widgetadaptor
+    # instancevars:          Instance variable definitions and initializations.
+    # ivprocdec:             Instance variable proc declarations.
+    # tvprocdec:             Type variable proc declarations.
+    # typeconstructor:       Type constructor body.
+    # widgetclass:           The widgetclass, for snit::widgets, only
+    # hasoptions:            False, initially; set to true when first
+    #                        option is defined.
+    # localoptions:          Names of local options.
+    # delegatedoptions:      Names of delegated options.
+    # localmethods:          Names of locally defined methods.
+    # delegatesmethods:      no if no delegated methods, yes otherwise.
+    # hashierarchic       :  no if no hierarchic methods, yes otherwise.
+    # components:            Names of defined components.
+    # typecomponents:        Names of defined typecomponents.
+    # typevars:              Typevariable definitions and initializations.
+    # varnames:              Names of instance variables
+    # typevarnames           Names of type variables
+    # hasconstructor         False, initially; true when constructor is
+    #                        defined.
+    # resource-$opt          The option's resource name
+    # class-$opt             The option's class
+    # -default-$opt          The option's default value
+    # -validatemethod-$opt   The option's validate method
+    # -configuremethod-$opt  The option's configure method
+    # -cgetmethod-$opt       The option's cget method.
+    # -hastypeinfo           The -hastypeinfo pragma
+    # -hastypedestroy        The -hastypedestroy pragma
+    # -hastypemethods        The -hastypemethods pragma
+    # -hasinfo               The -hasinfo pragma
+    # -hasinstances          The -hasinstances pragma
+    # -simpledispatch        The -simpledispatch pragma
+    # -canreplace            The -canreplace pragma
+    variable compile
+
+    # This variable accumulates method dispatch information; it has
+    # the same structure as the %TYPE%::Snit_methodInfo array, and is
+    # used to initialize it.
+    variable methodInfo
+
+    # This variable accumulates typemethod dispatch information; it has
+    # the same structure as the %TYPE%::Snit_typemethodInfo array, and is
+    # used to initialize it.
+    variable typemethodInfo
+
+    # The following variable lists the reserved type definition statement
+    # names, e.g., the names you can't use as macros.  It's built at
+    # compiler definition time using "info commands".
+    variable reservedwords {}
+}
+
+#-----------------------------------------------------------------------
+# type compilation commands
+#
+# The type and widgettype commands use a slave interpreter to compile
+# the type definition.  These are the procs
+# that are aliased into it.
+
+# Initialize the compiler
+proc ::snit::Comp.Init {} {
+    variable compiler
+    variable reservedwords
+
+    if {"" == $compiler} {
+        # Create the compiler's interpreter
+        set compiler [interp create]
+
+        # Initialize the interpreter
+       $compiler eval {
+           catch {close stdout}
+           catch {close stderr}
+           catch {close stdin}
+
+            # Load package information
+            # TBD: see if this can be moved outside.
+           # @mdgen NODEP: ::snit::__does_not_exist__
+            catch {package require ::snit::__does_not_exist__}
+
+            # Protect some Tcl commands our type definitions
+            # will shadow.
+            rename proc _proc
+            rename variable _variable
+        }
+
+        # Define compilation aliases.
+        $compiler alias pragma          ::snit::Comp.statement.pragma
+        $compiler alias widgetclass     ::snit::Comp.statement.widgetclass
+        $compiler alias hulltype        ::snit::Comp.statement.hulltype
+        $compiler alias constructor     ::snit::Comp.statement.constructor
+        $compiler alias destructor      ::snit::Comp.statement.destructor
+        $compiler alias option          ::snit::Comp.statement.option
+        $compiler alias oncget          ::snit::Comp.statement.oncget
+        $compiler alias onconfigure     ::snit::Comp.statement.onconfigure
+        $compiler alias method          ::snit::Comp.statement.method
+        $compiler alias typemethod      ::snit::Comp.statement.typemethod
+        $compiler alias typeconstructor ::snit::Comp.statement.typeconstructor
+        $compiler alias proc            ::snit::Comp.statement.proc
+        $compiler alias typevariable    ::snit::Comp.statement.typevariable
+        $compiler alias variable        ::snit::Comp.statement.variable
+        $compiler alias typecomponent   ::snit::Comp.statement.typecomponent
+        $compiler alias component       ::snit::Comp.statement.component
+        $compiler alias delegate        ::snit::Comp.statement.delegate
+        $compiler alias expose          ::snit::Comp.statement.expose
+
+        # Get the list of reserved words
+        set reservedwords [$compiler eval {info commands}]
+    }
+}
+
+# Compile a type definition, and return the results as a list of two
+# items: the fully-qualified type name, and a script that will define
+# the type when executed.
+#
+# which                type, widget, or widgetadaptor
+# type          the type name
+# body          the type definition
+proc ::snit::Comp.Compile {which type body} {
+    variable typeTemplate
+    variable nominalTypeProc
+    variable simpleTypeProc
+    variable compile
+    variable compiler
+    variable methodInfo
+    variable typemethodInfo
+
+    # FIRST, qualify the name.
+    if {![string match "::*" $type]} {
+        # Get caller's namespace;
+        # append :: if not global namespace.
+        set ns [uplevel 2 [list namespace current]]
+        if {"::" != $ns} {
+            append ns "::"
+        }
+
+        set type "$ns$type"
+    }
+
+    # NEXT, create and initialize the compiler, if needed.
+    Comp.Init
+
+    # NEXT, initialize the class data
+    array unset methodInfo
+    array unset typemethodInfo
+
+    array unset compile
+    set compile(type) $type
+    set compile(defs) {}
+    set compile(which) $which
+    set compile(hasoptions) no
+    set compile(localoptions) {}
+    set compile(instancevars) {}
+    set compile(typevars) {}
+    set compile(delegatedoptions) {}
+    set compile(ivprocdec) {}
+    set compile(tvprocdec) {}
+    set compile(typeconstructor) {}
+    set compile(widgetclass) {}
+    set compile(hulltype) {}
+    set compile(localmethods) {}
+    set compile(delegatesmethods) no
+    set compile(hashierarchic) no
+    set compile(components) {}
+    set compile(typecomponents) {}
+    set compile(varnames) {}
+    set compile(typevarnames) {}
+    set compile(hasconstructor) no
+    set compile(-hastypedestroy) yes
+    set compile(-hastypeinfo) yes
+    set compile(-hastypemethods) yes
+    set compile(-hasinfo) yes
+    set compile(-hasinstances) yes
+    set compile(-simpledispatch) no
+    set compile(-canreplace) no
+
+    set isWidget [string match widget* $which]
+    set isWidgetAdaptor [string match widgetadaptor $which]
+
+    # NEXT, Evaluate the type's definition in the class interpreter.
+    $compiler eval $body
+
+    # NEXT, Add the standard definitions
+    append compile(defs) \
+        "\nset %TYPE%::Snit_info(isWidget) $isWidget\n"
+
+    append compile(defs) \
+        "\nset %TYPE%::Snit_info(isWidgetAdaptor) $isWidgetAdaptor\n"
+
+    # Indicate whether the type can create instances that replace
+    # existing commands.
+    append compile(defs) "\nset %TYPE%::Snit_info(canreplace) $compile(-canreplace)\n"
+
+
+    # Check pragmas for conflict.
+
+    if {!$compile(-hastypemethods) && !$compile(-hasinstances)} {
+        error "$which $type has neither typemethods nor instances"
+    }
+
+    if {$compile(-simpledispatch) && $compile(delegatesmethods)} {
+        error "$which $type requests -simpledispatch but delegates methods."
+    }
+
+    if {$compile(-simpledispatch) && $compile(hashierarchic)} {
+        error "$which $type requests -simpledispatch but defines hierarchical methods."
+    }
+
+    # If there are typemethods, define the standard typemethods and
+    # the nominal type proc.  Otherwise define the simple type proc.
+    if {$compile(-hastypemethods)} {
+        # Add the info typemethod unless the pragma forbids it.
+        if {$compile(-hastypeinfo)} {
+            Comp.statement.delegate typemethod info \
+                using {::snit::RT.typemethod.info %t}
+        }
+
+        # Add the destroy typemethod unless the pragma forbids it.
+        if {$compile(-hastypedestroy)} {
+            Comp.statement.delegate typemethod destroy \
+                using {::snit::RT.typemethod.destroy %t}
+        }
+
+        # Add the nominal type proc.
+        append compile(defs) $nominalTypeProc
+    } else {
+        # Add the simple type proc.
+        append compile(defs) $simpleTypeProc
+    }
+
+    # Add standard methods/typemethods that only make sense if the
+    # type has instances.
+    if {$compile(-hasinstances)} {
+        # If we're using simple dispatch, remember that.
+        if {$compile(-simpledispatch)} {
+            append compile(defs) "\nset %TYPE%::Snit_info(simpledispatch) 1\n"
+        }
+
+        # Add the info method unless the pragma forbids it.
+        if {$compile(-hasinfo)} {
+            if {!$compile(-simpledispatch)} {
+                Comp.statement.delegate method info \
+                    using {::snit::RT.method.info %t %n %w %s}
+            } else {
+                Comp.statement.method info {args} {
+                    eval [linsert $args 0 \
+                              ::snit::RT.method.info $type $selfns $win $self]
+                }
+            }
+        }
+
+        # Add the option handling stuff if there are any options.
+        if {$compile(hasoptions)} {
+            Comp.statement.variable options
+
+            if {!$compile(-simpledispatch)} {
+                Comp.statement.delegate method cget \
+                    using {::snit::RT.method.cget %t %n %w %s}
+                Comp.statement.delegate method configurelist \
+                    using {::snit::RT.method.configurelist %t %n %w %s}
+                Comp.statement.delegate method configure \
+                    using {::snit::RT.method.configure %t %n %w %s}
+            } else {
+                Comp.statement.method cget {args} {
+                    eval [linsert $args 0 \
+                              ::snit::RT.method.cget $type $selfns $win $self]
+                }
+                Comp.statement.method configurelist {args} {
+                    eval [linsert $args 0 \
+                              ::snit::RT.method.configurelist $type $selfns $win $self]
+                }
+                Comp.statement.method configure {args} {
+                    eval [linsert $args 0 \
+                              ::snit::RT.method.configure $type $selfns $win $self]
+                }
+            }
+        }
+
+        # Add a default constructor, if they haven't already defined one.
+        # If there are options, it will configure args; otherwise it
+        # will do nothing.
+        if {!$compile(hasconstructor)} {
+            if {$compile(hasoptions)} {
+                Comp.statement.constructor {args} {
+                    $self configurelist $args
+                }
+            } else {
+                Comp.statement.constructor {} {}
+            }
+        }
+
+        if {!$isWidget} {
+            if {!$compile(-simpledispatch)} {
+                Comp.statement.delegate method destroy \
+                    using {::snit::RT.method.destroy %t %n %w %s}
+            } else {
+                Comp.statement.method destroy {args} {
+                    eval [linsert $args 0 \
+                              ::snit::RT.method.destroy $type $selfns $win $self]
+                }
+            }
+
+            Comp.statement.delegate typemethod create \
+                using {::snit::RT.type.typemethod.create %t}
+        } else {
+            Comp.statement.delegate typemethod create \
+                using {::snit::RT.widget.typemethod.create %t}
+        }
+
+        # Save the list of method names, for -simpledispatch; otherwise,
+        # save the method info.
+        if {$compile(-simpledispatch)} {
+            append compile(defs) \
+                "\nset %TYPE%::Snit_methods [list $compile(localmethods)]\n"
+        } else {
+            append compile(defs) \
+                "\narray set %TYPE%::Snit_methodInfo [list [array get methodInfo]]\n"
+        }
+
+    } else {
+        append compile(defs) "\nset %TYPE%::Snit_info(hasinstances) 0\n"
+    }
+
+    # NEXT, compiling the type definition built up a set of information
+    # about the type's locally defined options; add this information to
+    # the compiled definition.
+    Comp.SaveOptionInfo
+
+    # NEXT, compiling the type definition built up a set of information
+    # about the typemethods; save the typemethod info.
+    append compile(defs) \
+        "\narray set %TYPE%::Snit_typemethodInfo [list [array get typemethodInfo]]\n"
+
+    # NEXT, if this is a widget define the hull component if it isn't
+    # already defined.
+    if {$isWidget} {
+        Comp.DefineComponent hull
+    }
+
+    # NEXT, substitute the compiled definition into the type template
+    # to get the type definition script.
+    set defscript [Expand $typeTemplate \
+                       %COMPILEDDEFS% $compile(defs)]
+
+    # NEXT, substitute the defined macros into the type definition script.
+    # This is done as a separate step so that the compile(defs) can
+    # contain the macros defined below.
+
+    set defscript [Expand $defscript \
+                       %TYPE%         $type \
+                       %IVARDECS%     $compile(ivprocdec) \
+                       %TVARDECS%     $compile(tvprocdec) \
+                       %TCONSTBODY%   $compile(typeconstructor) \
+                       %INSTANCEVARS% $compile(instancevars) \
+                       %TYPEVARS%     $compile(typevars) \
+                      ]
+
+    array unset compile
+
+    return [list $type $defscript]
+}
+
+# Information about locally-defined options is accumulated during
+# compilation, but not added to the compiled definition--the option
+# statement can appear multiple times, so it's easier this way.
+# This proc fills in Snit_optionInfo with the accumulated information.
+#
+# It also computes the option's resource and class names if needed.
+#
+# Note that the information for delegated options was put in
+# Snit_optionInfo during compilation.
+
+proc ::snit::Comp.SaveOptionInfo {} {
+    variable compile
+
+    foreach option $compile(localoptions) {
+        if {"" == $compile(resource-$option)} {
+            set compile(resource-$option) [string range $option 1 end]
+        }
+
+        if {"" == $compile(class-$option)} {
+            set compile(class-$option) [Capitalize $compile(resource-$option)]
+        }
+
+        # NOTE: Don't verify that the validate, configure, and cget
+        # values name real methods; the methods might be defined outside
+        # the typedefinition using snit::method.
+
+        Mappend compile(defs) {
+            # Option %OPTION%
+            lappend %TYPE%::Snit_optionInfo(local) %OPTION%
+
+            set %TYPE%::Snit_optionInfo(islocal-%OPTION%)   1
+            set %TYPE%::Snit_optionInfo(resource-%OPTION%)  %RESOURCE%
+            set %TYPE%::Snit_optionInfo(class-%OPTION%)     %CLASS%
+            set %TYPE%::Snit_optionInfo(default-%OPTION%)   %DEFAULT%
+            set %TYPE%::Snit_optionInfo(validate-%OPTION%)  %VALIDATE%
+            set %TYPE%::Snit_optionInfo(configure-%OPTION%) %CONFIGURE%
+            set %TYPE%::Snit_optionInfo(cget-%OPTION%)      %CGET%
+            set %TYPE%::Snit_optionInfo(readonly-%OPTION%)  %READONLY%
+            set %TYPE%::Snit_optionInfo(typespec-%OPTION%)  %TYPESPEC%
+        }   %OPTION%    $option                                   \
+            %RESOURCE%  $compile(resource-$option)                \
+            %CLASS%     $compile(class-$option)                   \
+            %DEFAULT%   [list $compile(-default-$option)]         \
+            %VALIDATE%  [list $compile(-validatemethod-$option)]  \
+            %CONFIGURE% [list $compile(-configuremethod-$option)] \
+            %CGET%      [list $compile(-cgetmethod-$option)]      \
+            %READONLY%  $compile(-readonly-$option)               \
+            %TYPESPEC%  [list $compile(-type-$option)]
+    }
+}
+
+
+# Evaluates a compiled type definition, thus making the type available.
+proc ::snit::Comp.Define {compResult} {
+    # The compilation result is a list containing the fully qualified
+    # type name and a script to evaluate to define the type.
+    set type [lindex $compResult 0]
+    set defscript [lindex $compResult 1]
+
+    # Execute the type definition script.
+    # Consider using namespace eval %TYPE%.  See if it's faster.
+    if {[catch {eval $defscript} result]} {
+        namespace delete $type
+        catch {rename $type ""}
+        error $result
+    }
+
+    return $type
+}
+
+# Sets pragma options which control how the type is defined.
+proc ::snit::Comp.statement.pragma {args} {
+    variable compile
+
+    set errRoot "Error in \"pragma...\""
+
+    foreach {opt val} $args {
+        switch -exact -- $opt {
+            -hastypeinfo    -
+            -hastypedestroy -
+            -hastypemethods -
+            -hasinstances   -
+            -simpledispatch -
+            -hasinfo        -
+            -canreplace     {
+                if {![string is boolean -strict $val]} {
+                    error "$errRoot, \"$opt\" requires a boolean value"
+                }
+                set compile($opt) $val
+            }
+            default {
+                error "$errRoot, unknown pragma"
+            }
+        }
+    }
+}
+
+# Defines a widget's option class name.
+# This statement is only available for snit::widgets,
+# not for snit::types or snit::widgetadaptors.
+proc ::snit::Comp.statement.widgetclass {name} {
+    variable compile
+
+    # First, widgetclass can only be set for true widgets
+    if {"widget" != $compile(which)} {
+        error "widgetclass cannot be set for snit::$compile(which)s"
+    }
+
+    # Next, validate the option name.  We'll require that it begin
+    # with an uppercase letter.
+    set initial [string index $name 0]
+    if {![string is upper $initial]} {
+        error "widgetclass \"$name\" does not begin with an uppercase letter"
+    }
+
+    if {"" != $compile(widgetclass)} {
+        error "too many widgetclass statements"
+    }
+
+    # Next, save it.
+    Mappend compile(defs) {
+        set  %TYPE%::Snit_info(widgetclass) %WIDGETCLASS%
+    } %WIDGETCLASS% [list $name]
+
+    set compile(widgetclass) $name
+}
+
+# Defines a widget's hull type.
+# This statement is only available for snit::widgets,
+# not for snit::types or snit::widgetadaptors.
+proc ::snit::Comp.statement.hulltype {name} {
+    variable compile
+    variable hulltypes
+
+    # First, hulltype can only be set for true widgets
+    if {"widget" != $compile(which)} {
+        error "hulltype cannot be set for snit::$compile(which)s"
+    }
+
+    # Next, it must be one of the valid hulltypes (frame, toplevel, ...)
+    if {[lsearch -exact $hulltypes [string trimleft $name :]] == -1} {
+        error "invalid hulltype \"$name\", should be one of\
+               [join $hulltypes {, }]"
+    }
+
+    if {"" != $compile(hulltype)} {
+        error "too many hulltype statements"
+    }
+
+    # Next, save it.
+    Mappend compile(defs) {
+        set  %TYPE%::Snit_info(hulltype) %HULLTYPE%
+    } %HULLTYPE% $name
+
+    set compile(hulltype) $name
+}
+
+# Defines a constructor.
+proc ::snit::Comp.statement.constructor {arglist body} {
+    variable compile
+
+    CheckArgs "constructor" $arglist
+
+    # Next, add a magic reference to self.
+    set arglist [concat type selfns win self $arglist]
+
+    # Next, add variable declarations to body:
+    set body "%TVARDECS%%IVARDECS%\n$body"
+
+    set compile(hasconstructor) yes
+    append compile(defs) "proc %TYPE%::Snit_constructor [list $arglist] [list $body]\n"
+}
+
+# Defines a destructor.
+proc ::snit::Comp.statement.destructor {body} {
+    variable compile
+
+    # Next, add variable declarations to body:
+    set body "%TVARDECS%%IVARDECS%\n$body"
+
+    append compile(defs) "proc %TYPE%::Snit_destructor {type selfns win self} [list $body]\n\n"
+}
+
+# Defines a type option.  The option value can be a triple, specifying
+# the option's -name, resource name, and class name.
+proc ::snit::Comp.statement.option {optionDef args} {
+    variable compile
+
+    # First, get the three option names.
+    set option [lindex $optionDef 0]
+    set resourceName [lindex $optionDef 1]
+    set className [lindex $optionDef 2]
+
+    set errRoot "Error in \"option [list $optionDef]...\""
+
+    # Next, validate the option name.
+    if {![Comp.OptionNameIsValid $option]} {
+        error "$errRoot, badly named option \"$option\""
+    }
+
+    if {[Contains $option $compile(delegatedoptions)]} {
+        error "$errRoot, cannot define \"$option\" locally, it has been delegated"
+    }
+
+    if {![Contains $option $compile(localoptions)]} {
+        # Remember that we've seen this one.
+        set compile(hasoptions) yes
+        lappend compile(localoptions) $option
+
+        # Initialize compilation info for this option.
+        set compile(resource-$option)         ""
+        set compile(class-$option)            ""
+        set compile(-default-$option)         ""
+        set compile(-validatemethod-$option)  ""
+        set compile(-configuremethod-$option) ""
+        set compile(-cgetmethod-$option)      ""
+        set compile(-readonly-$option)        0
+        set compile(-type-$option)            ""
+    }
+
+    # NEXT, see if we have a resource name.  If so, make sure it
+    # isn't being redefined differently.
+    if {"" != $resourceName} {
+        if {"" == $compile(resource-$option)} {
+            # If it's undefined, just save the value.
+            set compile(resource-$option) $resourceName
+        } elseif {![string equal $resourceName $compile(resource-$option)]} {
+            # It's been redefined differently.
+            error "$errRoot, resource name redefined from \"$compile(resource-$option)\" to \"$resourceName\""
+        }
+    }
+
+    # NEXT, see if we have a class name.  If so, make sure it
+    # isn't being redefined differently.
+    if {"" != $className} {
+        if {"" == $compile(class-$option)} {
+            # If it's undefined, just save the value.
+            set compile(class-$option) $className
+        } elseif {![string equal $className $compile(class-$option)]} {
+            # It's been redefined differently.
+            error "$errRoot, class name redefined from \"$compile(class-$option)\" to \"$className\""
+        }
+    }
+
+    # NEXT, handle the args; it's not an error to redefine these.
+    if {[llength $args] == 1} {
+        set compile(-default-$option) [lindex $args 0]
+    } else {
+        foreach {optopt val} $args {
+            switch -exact -- $optopt {
+                -default         -
+                -validatemethod  -
+                -configuremethod -
+                -cgetmethod      {
+                    set compile($optopt-$option) $val
+                }
+                -type {
+                    set compile($optopt-$option) $val
+                    
+                    if {[llength $val] == 1} {
+                        # The type spec *is* the validation object
+                        append compile(defs) \
+                            "\nset %TYPE%::Snit_optionInfo(typeobj-$option) [list $val]\n"
+                    } else {
+                        # Compilation the creation of the validation object
+                        set cmd [linsert $val 1 %TYPE%::Snit_TypeObj_%AUTO%]
+                        append compile(defs) \
+                            "\nset %TYPE%::Snit_optionInfo(typeobj-$option) \[$cmd\]\n"
+                    }
+                }
+                -readonly        {
+                    if {![string is boolean -strict $val]} {
+                        error "$errRoot, -readonly requires a boolean, got \"$val\""
+                    }
+                    set compile($optopt-$option) $val
+                }
+                default {
+                    error "$errRoot, unknown option definition option \"$optopt\""
+                }
+            }
+        }
+    }
+}
+
+# 1 if the option name is valid, 0 otherwise.
+proc ::snit::Comp.OptionNameIsValid {option} {
+    if {![string match {-*} $option] || [string match {*[A-Z ]*} $option]} {
+        return 0
+    }
+
+    return 1
+}
+
+# Defines an option's cget handler
+proc ::snit::Comp.statement.oncget {option body} {
+    variable compile
+
+    set errRoot "Error in \"oncget $option...\""
+
+    if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
+        return -code error "$errRoot, option \"$option\" is delegated"
+    }
+
+    if {[lsearch -exact $compile(localoptions) $option] == -1} {
+        return -code error "$errRoot, option \"$option\" unknown"
+    }
+
+    Comp.statement.method _cget$option {_option} $body
+    Comp.statement.option $option -cgetmethod _cget$option
+}
+
+# Defines an option's configure handler.
+proc ::snit::Comp.statement.onconfigure {option arglist body} {
+    variable compile
+
+    if {[lsearch -exact $compile(delegatedoptions) $option] != -1} {
+        return -code error "onconfigure $option: option \"$option\" is delegated"
+    }
+
+    if {[lsearch -exact $compile(localoptions) $option] == -1} {
+        return -code error "onconfigure $option: option \"$option\" unknown"
+    }
+
+    if {[llength $arglist] != 1} {
+        error \
+       "onconfigure $option handler should have one argument, got \"$arglist\""
+    }
+
+    CheckArgs "onconfigure $option" $arglist
+
+    # Next, add a magic reference to the option name
+    set arglist [concat _option $arglist]
+
+    Comp.statement.method _configure$option $arglist $body
+    Comp.statement.option $option -configuremethod _configure$option
+}
+
+# Defines an instance method.
+proc ::snit::Comp.statement.method {method arglist body} {
+    variable compile
+    variable methodInfo
+
+    # FIRST, check the method name against previously defined
+    # methods.
+    Comp.CheckMethodName $method 0 ::snit::methodInfo \
+        "Error in \"method [list $method]...\""
+
+    if {[llength $method] > 1} {
+        set compile(hashierarchic) yes
+    }
+
+    # Remeber this method
+    lappend compile(localmethods) $method
+
+    CheckArgs "method [list $method]" $arglist
+
+    # Next, add magic references to type and self.
+    set arglist [concat type selfns win self $arglist]
+
+    # Next, add variable declarations to body:
+    set body "%TVARDECS%%IVARDECS%\n# END snit method prolog\n$body"
+
+    # Next, save the definition script.
+    if {[llength $method] == 1} {
+        set methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
+        Mappend compile(defs) {
+            proc %TYPE%::Snit_method%METHOD% %ARGLIST% %BODY%
+        } %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
+    } else {
+        set methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
+
+        Mappend compile(defs) {
+            proc %TYPE%::Snit_hmethod%JMETHOD% %ARGLIST% %BODY%
+        } %JMETHOD% [join $method _] %ARGLIST% [list $arglist] \
+            %BODY% [list $body]
+    }
+}
+
+# Check for name collisions; save prefix information.
+#
+# method       The name of the method or typemethod.
+# delFlag       1 if delegated, 0 otherwise.
+# infoVar       The fully qualified name of the array containing
+#               information about the defined methods.
+# errRoot       The root string for any error messages.
+
+proc ::snit::Comp.CheckMethodName {method delFlag infoVar errRoot} {
+    upvar $infoVar methodInfo
+
+    # FIRST, make sure the method name is a valid Tcl list.
+    if {[catch {lindex $method 0}]} {
+        error "$errRoot, the name \"$method\" must have list syntax."
+    }
+
+    # NEXT, check whether we can define it.
+    if {![catch {set methodInfo($method)} data]} {
+        # We can't redefine methods with submethods.
+        if {[lindex $data 0] == 1} {
+            error "$errRoot, \"$method\" has submethods."
+        }
+
+        # You can't delegate a method that's defined locally,
+        # and you can't define a method locally if it's been delegated.
+        if {$delFlag && "" == [lindex $data 2]} {
+            error "$errRoot, \"$method\" has been defined locally."
+        } elseif {!$delFlag && "" != [lindex $data 2]} {
+            error "$errRoot, \"$method\" has been delegated"
+        }
+    }
+
+    # Handle hierarchical case.
+    if {[llength $method] > 1} {
+        set prefix {}
+        set tokens $method
+        while {[llength $tokens] > 1} {
+            lappend prefix [lindex $tokens 0]
+            set tokens [lrange $tokens 1 end]
+
+            if {![catch {set methodInfo($prefix)} result]} {
+                # Prefix is known.  If it's not a prefix, throw an
+                # error.
+                if {[lindex $result 0] == 0} {
+                    error "$errRoot, \"$prefix\" has no submethods."
+                }
+            }
+
+            set methodInfo($prefix) [list 1]
+        }
+    }
+}
+
+# Defines a typemethod method.
+proc ::snit::Comp.statement.typemethod {method arglist body} {
+    variable compile
+    variable typemethodInfo
+
+    # FIRST, check the typemethod name against previously defined
+    # typemethods.
+    Comp.CheckMethodName $method 0 ::snit::typemethodInfo \
+        "Error in \"typemethod [list $method]...\""
+
+    CheckArgs "typemethod $method" $arglist
+
+    # First, add magic reference to type.
+    set arglist [concat type $arglist]
+
+    # Next, add typevariable declarations to body:
+    set body "%TVARDECS%\n# END snit method prolog\n$body"
+
+    # Next, save the definition script
+    if {[llength $method] == 1} {
+        set typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
+
+        Mappend compile(defs) {
+            proc %TYPE%::Snit_typemethod%METHOD% %ARGLIST% %BODY%
+        } %METHOD% $method %ARGLIST% [list $arglist] %BODY% [list $body]
+    } else {
+        set typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
+
+        Mappend compile(defs) {
+            proc %TYPE%::Snit_htypemethod%JMETHOD% %ARGLIST% %BODY%
+        } %JMETHOD% [join $method _] \
+            %ARGLIST% [list $arglist] %BODY% [list $body]
+    }
+}
+
+
+# Defines a type constructor.
+proc ::snit::Comp.statement.typeconstructor {body} {
+    variable compile
+
+    if {"" != $compile(typeconstructor)} {
+        error "too many typeconstructors"
+    }
+
+    set compile(typeconstructor) $body
+}
+
+# Defines a static proc in the type's namespace.
+proc ::snit::Comp.statement.proc {proc arglist body} {
+    variable compile
+
+    # If "ns" is defined, the proc can see instance variables.
+    if {[lsearch -exact $arglist selfns] != -1} {
+        # Next, add instance variable declarations to body:
+        set body "%IVARDECS%\n$body"
+    }
+
+    # The proc can always see typevariables.
+    set body "%TVARDECS%\n$body"
+
+    append compile(defs) "
+
+        # Proc $proc
+        proc [list %TYPE%::$proc $arglist $body]
+    "
+}
+
+# Defines a static variable in the type's namespace.
+proc ::snit::Comp.statement.typevariable {name args} {
+    variable compile
+
+    set errRoot "Error in \"typevariable $name...\""
+
+    set len [llength $args]
+
+    if {$len > 2 ||
+        ($len == 2 && "-array" != [lindex $args 0])} {
+        error "$errRoot, too many initializers"
+    }
+
+    if {[lsearch -exact $compile(varnames) $name] != -1} {
+        error "$errRoot, \"$name\" is already an instance variable"
+    }
+
+    lappend compile(typevarnames) $name
+
+    if {$len == 1} {
+        append compile(typevars) \
+               "\n\t    [list ::variable $name [lindex $args 0]]"
+    } elseif {$len == 2} {
+        append compile(typevars) \
+            "\n\t    [list ::variable $name]"
+        append compile(typevars) \
+            "\n\t    [list array set $name [lindex $args 1]]"
+    } else {
+        append compile(typevars) \
+               "\n\t    [list ::variable $name]"
+    }
+
+    append compile(tvprocdec) "\n\t    typevariable ${name}"
+}
+
+# Defines an instance variable; the definition will go in the
+# type's create typemethod.
+proc ::snit::Comp.statement.variable {name args} {
+    variable compile
+
+    set errRoot "Error in \"variable $name...\""
+
+    set len [llength $args]
+
+    if {$len > 2 ||
+        ($len == 2 && "-array" != [lindex $args 0])} {
+        error "$errRoot, too many initializers"
+    }
+
+    if {[lsearch -exact $compile(typevarnames) $name] != -1} {
+        error "$errRoot, \"$name\" is already a typevariable"
+    }
+
+    lappend compile(varnames) $name
+
+    if {$len == 1} {
+        append compile(instancevars) \
+            "\nset \${selfns}::$name [list [lindex $args 0]]\n"
+    } elseif {$len == 2} {
+        append compile(instancevars) \
+            "\narray set \${selfns}::$name [list [lindex $args 1]]\n"
+    }
+
+    append  compile(ivprocdec) "\n\t    "
+    Mappend compile(ivprocdec) {::variable ${selfns}::%N} %N $name
+}
+
+# Defines a typecomponent, and handles component options.
+#
+# component     The logical name of the delegate
+# args          options.
+
+proc ::snit::Comp.statement.typecomponent {component args} {
+    variable compile
+
+    set errRoot "Error in \"typecomponent $component...\""
+
+    # FIRST, define the component
+    Comp.DefineTypecomponent $component $errRoot
+
+    # NEXT, handle the options.
+    set publicMethod ""
+    set inheritFlag 0
+
+    foreach {opt val} $args {
+        switch -exact -- $opt {
+            -public {
+                set publicMethod $val
+            }
+            -inherit {
+                set inheritFlag $val
+                if {![string is boolean $inheritFlag]} {
+    error "typecomponent $component -inherit: expected boolean value, got \"$val\""
+                }
+            }
+            default {
+                error "typecomponent $component: Invalid option \"$opt\""
+            }
+        }
+    }
+
+    # NEXT, if -public specified, define the method.
+    if {"" != $publicMethod} {
+        Comp.statement.delegate typemethod [list $publicMethod *] to $component
+    }
+
+    # NEXT, if "-inherit 1" is specified, delegate typemethod * to
+    # this component.
+    if {$inheritFlag} {
+        Comp.statement.delegate typemethod "*" to $component
+    }
+
+}
+
+
+# Defines a name to be a typecomponent
+#
+# The name becomes a typevariable; in addition, it gets a
+# write trace so that when it is set, all of the component mechanisms
+# get updated.
+#
+# component     The component name
+
+proc ::snit::Comp.DefineTypecomponent {component {errRoot "Error"}} {
+    variable compile
+
+    if {[lsearch -exact $compile(varnames) $component] != -1} {
+        error "$errRoot, \"$component\" is already an instance variable"
+    }
+
+    if {[lsearch -exact $compile(typecomponents) $component] == -1} {
+        # Remember we've done this.
+        lappend compile(typecomponents) $component
+
+        # Make it a type variable with no initial value
+        Comp.statement.typevariable $component ""
+
+        # Add a write trace to do the component thing.
+        Mappend compile(typevars) {
+            trace variable %COMP% w \
+                [list ::snit::RT.TypecomponentTrace [list %TYPE%] %COMP%]
+        } %TYPE% $compile(type) %COMP% $component
+    }
+}
+
+# Defines a component, and handles component options.
+#
+# component     The logical name of the delegate
+# args          options.
+#
+# TBD: Ideally, it should be possible to call this statement multiple
+# times, possibly changing the option values.  To do that, I'd need
+# to cache the option values and not act on them until *after* I'd
+# read the entire type definition.
+
+proc ::snit::Comp.statement.component {component args} {
+    variable compile
+
+    set errRoot "Error in \"component $component...\""
+
+    # FIRST, define the component
+    Comp.DefineComponent $component $errRoot
+
+    # NEXT, handle the options.
+    set publicMethod ""
+    set inheritFlag 0
+
+    foreach {opt val} $args {
+        switch -exact -- $opt {
+            -public {
+                set publicMethod $val
+            }
+            -inherit {
+                set inheritFlag $val
+                if {![string is boolean $inheritFlag]} {
+    error "component $component -inherit: expected boolean value, got \"$val\""
+                }
+            }
+            default {
+                error "component $component: Invalid option \"$opt\""
+            }
+        }
+    }
+
+    # NEXT, if -public specified, define the method.
+    if {"" != $publicMethod} {
+        Comp.statement.delegate method [list $publicMethod *] to $component
+    }
+
+    # NEXT, if -inherit is specified, delegate method/option * to
+    # this component.
+    if {$inheritFlag} {
+        Comp.statement.delegate method "*" to $component
+        Comp.statement.delegate option "*" to $component
+    }
+}
+
+
+# Defines a name to be a component
+#
+# The name becomes an instance variable; in addition, it gets a
+# write trace so that when it is set, all of the component mechanisms
+# get updated.
+#
+# component     The component name
+
+proc ::snit::Comp.DefineComponent {component {errRoot "Error"}} {
+    variable compile
+
+    if {[lsearch -exact $compile(typevarnames) $component] != -1} {
+        error "$errRoot, \"$component\" is already a typevariable"
+    }
+
+    if {[lsearch -exact $compile(components) $component] == -1} {
+        # Remember we've done this.
+        lappend compile(components) $component
+
+        # Make it an instance variable with no initial value
+        Comp.statement.variable $component ""
+
+        # Add a write trace to do the component thing.
+        Mappend compile(instancevars) {
+            trace variable ${selfns}::%COMP% w \
+                [list ::snit::RT.ComponentTrace [list %TYPE%] $selfns %COMP%]
+        } %TYPE% $compile(type) %COMP% $component
+    }
+}
+
+# Creates a delegated method, typemethod, or option.
+proc ::snit::Comp.statement.delegate {what name args} {
+    # FIRST, dispatch to correct handler.
+    switch $what {
+        typemethod { Comp.DelegatedTypemethod $name $args }
+        method     { Comp.DelegatedMethod     $name $args }
+        option     { Comp.DelegatedOption     $name $args }
+        default {
+            error "Error in \"delegate $what $name...\", \"$what\"?"
+        }
+    }
+
+    if {([llength $args] % 2) != 0} {
+        error "Error in \"delegate $what $name...\", invalid syntax"
+    }
+}
+
+# Creates a delegated typemethod delegating it to a particular
+# typecomponent or an arbitrary command.
+#
+# method    The name of the method
+# arglist       Delegation options
+
+proc ::snit::Comp.DelegatedTypemethod {method arglist} {
+    variable compile
+    variable typemethodInfo
+
+    set errRoot "Error in \"delegate typemethod [list $method]...\""
+
+    # Next, parse the delegation options.
+    set component ""
+    set target ""
+    set exceptions {}
+    set pattern ""
+    set methodTail [lindex $method end]
+
+    foreach {opt value} $arglist {
+        switch -exact $opt {
+            to     { set component $value  }
+            as     { set target $value     }
+            except { set exceptions $value }
+            using  { set pattern $value    }
+            default {
+                error "$errRoot, unknown delegation option \"$opt\""
+            }
+        }
+    }
+
+    if {"" == $component && "" == $pattern} {
+        error "$errRoot, missing \"to\""
+    }
+
+    if {"*" == $methodTail && "" != $target} {
+        error "$errRoot, cannot specify \"as\" with \"*\""
+    }
+
+    if {"*" != $methodTail && "" != $exceptions} {
+        error "$errRoot, can only specify \"except\" with \"*\""
+    }
+
+    if {"" != $pattern && "" != $target} {
+        error "$errRoot, cannot specify both \"as\" and \"using\""
+    }
+
+    foreach token [lrange $method 1 end-1] {
+        if {"*" == $token} {
+            error "$errRoot, \"*\" must be the last token."
+        }
+    }
+
+    # NEXT, define the component
+    if {"" != $component} {
+        Comp.DefineTypecomponent $component $errRoot
+    }
+
+    # NEXT, define the pattern.
+    if {"" == $pattern} {
+        if {"*" == $methodTail} {
+            set pattern "%c %m"
+        } elseif {"" != $target} {
+            set pattern "%c $target"
+        } else {
+            set pattern "%c %m"
+        }
+    }
+
+    # Make sure the pattern is a valid list.
+    if {[catch {lindex $pattern 0} result]} {
+        error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
+    }
+
+    # NEXT, check the method name against previously defined
+    # methods.
+    Comp.CheckMethodName $method 1 ::snit::typemethodInfo $errRoot
+
+    set typemethodInfo($method) [list 0 $pattern $component]
+
+    if {[string equal $methodTail "*"]} {
+        Mappend compile(defs) {
+            set %TYPE%::Snit_info(excepttypemethods) %EXCEPT%
+        } %EXCEPT% [list $exceptions]
+    }
+}
+
+
+# Creates a delegated method delegating it to a particular
+# component or command.
+#
+# method        The name of the method
+# arglist       Delegation options.
+
+proc ::snit::Comp.DelegatedMethod {method arglist} {
+    variable compile
+    variable methodInfo
+
+    set errRoot "Error in \"delegate method [list $method]...\""
+
+    # Next, parse the delegation options.
+    set component ""
+    set target ""
+    set exceptions {}
+    set pattern ""
+    set methodTail [lindex $method end]
+
+    foreach {opt value} $arglist {
+        switch -exact $opt {
+            to     { set component $value  }
+            as     { set target $value     }
+            except { set exceptions $value }
+            using  { set pattern $value    }
+            default {
+                error "$errRoot, unknown delegation option \"$opt\""
+            }
+        }
+    }
+
+    if {"" == $component && "" == $pattern} {
+        error "$errRoot, missing \"to\""
+    }
+
+    if {"*" == $methodTail && "" != $target} {
+        error "$errRoot, cannot specify \"as\" with \"*\""
+    }
+
+    if {"*" != $methodTail && "" != $exceptions} {
+        error "$errRoot, can only specify \"except\" with \"*\""
+    }
+
+    if {"" != $pattern && "" != $target} {
+        error "$errRoot, cannot specify both \"as\" and \"using\""
+    }
+
+    foreach token [lrange $method 1 end-1] {
+        if {"*" == $token} {
+            error "$errRoot, \"*\" must be the last token."
+        }
+    }
+
+    # NEXT, we delegate some methods
+    set compile(delegatesmethods) yes
+
+    # NEXT, define the component.  Allow typecomponents.
+    if {"" != $component} {
+        if {[lsearch -exact $compile(typecomponents) $component] == -1} {
+            Comp.DefineComponent $component $errRoot
+        }
+    }
+
+    # NEXT, define the pattern.
+    if {"" == $pattern} {
+        if {"*" == $methodTail} {
+            set pattern "%c %m"
+        } elseif {"" != $target} {
+            set pattern "%c $target"
+        } else {
+            set pattern "%c %m"
+        }
+    }
+
+    # Make sure the pattern is a valid list.
+    if {[catch {lindex $pattern 0} result]} {
+        error "$errRoot, the using pattern, \"$pattern\", is not a valid list"
+    }
+
+    # NEXT, check the method name against previously defined
+    # methods.
+    Comp.CheckMethodName $method 1 ::snit::methodInfo $errRoot
+
+    # NEXT, save the method info.
+    set methodInfo($method) [list 0 $pattern $component]
+
+    if {[string equal $methodTail "*"]} {
+        Mappend compile(defs) {
+            set %TYPE%::Snit_info(exceptmethods) %EXCEPT%
+        } %EXCEPT% [list $exceptions]
+    }
+}
+
+# Creates a delegated option, delegating it to a particular
+# component and, optionally, to a particular option of that
+# component.
+#
+# optionDef     The option definition
+# args          definition arguments.
+
+proc ::snit::Comp.DelegatedOption {optionDef arglist} {
+    variable compile
+
+    # First, get the three option names.
+    set option [lindex $optionDef 0]
+    set resourceName [lindex $optionDef 1]
+    set className [lindex $optionDef 2]
+
+    set errRoot "Error in \"delegate option [list $optionDef]...\""
+
+    # Next, parse the delegation options.
+    set component ""
+    set target ""
+    set exceptions {}
+
+    foreach {opt value} $arglist {
+        switch -exact $opt {
+            to     { set component $value  }
+            as     { set target $value     }
+            except { set exceptions $value }
+            default {
+                error "$errRoot, unknown delegation option \"$opt\""
+            }
+        }
+    }
+
+    if {"" == $component} {
+        error "$errRoot, missing \"to\""
+    }
+
+    if {"*" == $option && "" != $target} {
+        error "$errRoot, cannot specify \"as\" with \"delegate option *\""
+    }
+
+    if {"*" != $option && "" != $exceptions} {
+        error "$errRoot, can only specify \"except\" with \"delegate option *\""
+    }
+
+    # Next, validate the option name
+
+    if {"*" != $option} {
+        if {![Comp.OptionNameIsValid $option]} {
+            error "$errRoot, badly named option \"$option\""
+        }
+    }
+
+    if {[Contains $option $compile(localoptions)]} {
+        error "$errRoot, \"$option\" has been defined locally"
+    }
+
+    if {[Contains $option $compile(delegatedoptions)]} {
+        error "$errRoot, \"$option\" is multiply delegated"
+    }
+
+    # NEXT, define the component
+    Comp.DefineComponent $component $errRoot
+
+    # Next, define the target option, if not specified.
+    if {![string equal $option "*"] &&
+        [string equal $target ""]} {
+        set target $option
+    }
+
+    # NEXT, save the delegation data.
+    set compile(hasoptions) yes
+
+    if {![string equal $option "*"]} {
+        lappend compile(delegatedoptions) $option
+
+        # Next, compute the resource and class names, if they aren't
+        # already defined.
+
+        if {"" == $resourceName} {
+            set resourceName [string range $option 1 end]
+        }
+
+        if {"" == $className} {
+            set className [Capitalize $resourceName]
+        }
+
+        Mappend  compile(defs) {
+            set %TYPE%::Snit_optionInfo(islocal-%OPTION%) 0
+            set %TYPE%::Snit_optionInfo(resource-%OPTION%) %RES%
+            set %TYPE%::Snit_optionInfo(class-%OPTION%) %CLASS%
+            lappend %TYPE%::Snit_optionInfo(delegated) %OPTION%
+            set %TYPE%::Snit_optionInfo(target-%OPTION%) [list %COMP% %TARGET%]
+            lappend %TYPE%::Snit_optionInfo(delegated-%COMP%) %OPTION%
+        }   %OPTION% $option \
+            %COMP% $component \
+            %TARGET% $target \
+            %RES% $resourceName \
+            %CLASS% $className
+    } else {
+        Mappend  compile(defs) {
+            set %TYPE%::Snit_optionInfo(starcomp) %COMP%
+            set %TYPE%::Snit_optionInfo(except) %EXCEPT%
+        } %COMP% $component %EXCEPT% [list $exceptions]
+    }
+}
+
+# Exposes a component, effectively making the component's command an
+# instance method.
+#
+# component     The logical name of the delegate
+# "as"          sugar; if not "", must be "as"
+# methodname    The desired method name for the component's command, or ""
+
+proc ::snit::Comp.statement.expose {component {"as" ""} {methodname ""}} {
+    variable compile
+
+
+    # FIRST, define the component
+    Comp.DefineComponent $component
+
+    # NEXT, define the method just as though it were in the type
+    # definition.
+    if {[string equal $methodname ""]} {
+        set methodname $component
+    }
+
+    Comp.statement.method $methodname args [Expand {
+        if {[llength $args] == 0} {
+            return $%COMPONENT%
+        }
+
+        if {[string equal $%COMPONENT% ""]} {
+            error "undefined component \"%COMPONENT%\""
+        }
+
+
+        set cmd [linsert $args 0 $%COMPONENT%]
+        return [uplevel 1 $cmd]
+    } %COMPONENT% $component]
+}
+
+
+
+#-----------------------------------------------------------------------
+# Public commands
+
+# Compile a type definition, and return the results as a list of two
+# items: the fully-qualified type name, and a script that will define
+# the type when executed.
+#
+# which                type, widget, or widgetadaptor
+# type          the type name
+# body          the type definition
+proc ::snit::compile {which type body} {
+    return [Comp.Compile $which $type $body]
+}
+
+proc ::snit::type {type body} {
+    return [Comp.Define [Comp.Compile type $type $body]]
+}
+
+proc ::snit::widget {type body} {
+    return [Comp.Define [Comp.Compile widget $type $body]]
+}
+
+proc ::snit::widgetadaptor {type body} {
+    return [Comp.Define [Comp.Compile widgetadaptor $type $body]]
+}
+
+proc ::snit::typemethod {type method arglist body} {
+    # Make sure the type exists.
+    if {![info exists ${type}::Snit_info]} {
+        error "no such type: \"$type\""
+    }
+
+    upvar ${type}::Snit_info           Snit_info
+    upvar ${type}::Snit_typemethodInfo Snit_typemethodInfo
+
+    # FIRST, check the typemethod name against previously defined
+    # typemethods.
+    Comp.CheckMethodName $method 0 ${type}::Snit_typemethodInfo \
+        "Cannot define \"$method\""
+
+    # NEXT, check the arguments
+    CheckArgs "snit::typemethod $type $method" $arglist
+
+    # Next, add magic reference to type.
+    set arglist [concat type $arglist]
+
+    # Next, add typevariable declarations to body:
+    set body "$Snit_info(tvardecs)\n$body"
+
+    # Next, define it.
+    if {[llength $method] == 1} {
+        set Snit_typemethodInfo($method) {0 "%t::Snit_typemethod%m %t" ""}
+        uplevel 1 [list proc ${type}::Snit_typemethod$method $arglist $body]
+    } else {
+        set Snit_typemethodInfo($method) {0 "%t::Snit_htypemethod%j %t" ""}
+        set suffix [join $method _]
+        uplevel 1 [list proc ${type}::Snit_htypemethod$suffix $arglist $body]
+    }
+}
+
+proc ::snit::method {type method arglist body} {
+    # Make sure the type exists.
+    if {![info exists ${type}::Snit_info]} {
+        error "no such type: \"$type\""
+    }
+
+    upvar ${type}::Snit_methodInfo  Snit_methodInfo
+    upvar ${type}::Snit_info        Snit_info
+
+    # FIRST, check the method name against previously defined
+    # methods.
+    Comp.CheckMethodName $method 0 ${type}::Snit_methodInfo \
+        "Cannot define \"$method\""
+
+    # NEXT, check the arguments
+    CheckArgs "snit::method $type $method" $arglist
+
+    # Next, add magic references to type and self.
+    set arglist [concat type selfns win self $arglist]
+
+    # Next, add variable declarations to body:
+    set body "$Snit_info(tvardecs)$Snit_info(ivardecs)\n$body"
+
+    # Next, define it.
+    if {[llength $method] == 1} {
+        set Snit_methodInfo($method) {0 "%t::Snit_method%m %t %n %w %s" ""}
+        uplevel 1 [list proc ${type}::Snit_method$method $arglist $body]
+    } else {
+        set Snit_methodInfo($method) {0 "%t::Snit_hmethod%j %t %n %w %s" ""}
+
+        set suffix [join $method _]
+        uplevel 1 [list proc ${type}::Snit_hmethod$suffix $arglist $body]
+    }
+}
+
+# Defines a proc within the compiler; this proc can call other
+# type definition statements, and thus can be used for meta-programming.
+proc ::snit::macro {name arglist body} {
+    variable compiler
+    variable reservedwords
+
+    # FIRST, make sure the compiler is defined.
+    Comp.Init
+
+    # NEXT, check the macro name against the reserved words
+    if {[lsearch -exact $reservedwords $name] != -1} {
+        error "invalid macro name \"$name\""
+    }
+
+    # NEXT, see if the name has a namespace; if it does, define the
+    # namespace.
+    set ns [namespace qualifiers $name]
+
+    if {"" != $ns} {
+        $compiler eval "namespace eval $ns {}"
+    }
+
+    # NEXT, define the macro
+    $compiler eval [list _proc $name $arglist $body]
+}
+
+#-----------------------------------------------------------------------
+# Utility Functions
+#
+# These are utility functions used while compiling Snit types.
+
+# Builds a template from a tagged list of text blocks, then substitutes
+# all symbols in the mapTable, returning the expanded template.
+proc ::snit::Expand {template args} {
+    return [string map $args $template]
+}
+
+# Expands a template and appends it to a variable.
+proc ::snit::Mappend {varname template args} {
+    upvar $varname myvar
+
+    append myvar [string map $args $template]
+}
+
+# Checks argument list against reserved args
+proc ::snit::CheckArgs {which arglist} {
+    variable reservedArgs
+
+    foreach name $reservedArgs {
+        if {[Contains $name $arglist]} {
+            error "$which's arglist may not contain \"$name\" explicitly"
+        }
+    }
+}
+
+# Returns 1 if a value is in a list, and 0 otherwise.
+proc ::snit::Contains {value list} {
+    if {[lsearch -exact $list $value] != -1} {
+        return 1
+    } else {
+        return 0
+    }
+}
+
+# Capitalizes the first letter of a string.
+proc ::snit::Capitalize {text} {
+    set first [string index $text 0]
+    set rest [string range $text 1 end]
+    return "[string toupper $first]$rest"
+}
+
+# Converts an arbitrary white-space-delimited string into a list
+# by splitting on white-space and deleting empty tokens.
+
+proc ::snit::Listify {str} {
+    set result {}
+    foreach token [split [string trim $str]] {
+        if {[string length $token] > 0} {
+            lappend result $token
+        }
+    }
+
+    return $result
+}
+
+
+#=======================================================================
+# Snit Runtime Library
+#
+# These are procs used by Snit types and widgets at runtime.
+
+#-----------------------------------------------------------------------
+# Object Creation
+
+# Creates a new instance of the snit::type given its name and the args.
+#
+# type         The snit::type
+# name         The instance name
+# args         Args to pass to the constructor
+
+proc ::snit::RT.type.typemethod.create {type name args} {
+    variable ${type}::Snit_info
+    variable ${type}::Snit_optionInfo
+
+    # FIRST, qualify the name.
+    if {![string match "::*" $name]} {
+        # Get caller's namespace;
+        # append :: if not global namespace.
+        set ns [uplevel 1 [list namespace current]]
+        if {"::" != $ns} {
+            append ns "::"
+        }
+
+        set name "$ns$name"
+    }
+
+    # NEXT, if %AUTO% appears in the name, generate a unique
+    # command name.  Otherwise, ensure that the name isn't in use.
+    if {[string match "*%AUTO%*" $name]} {
+        set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
+    } elseif {$Snit_info(canreplace) && [llength [info commands $name]]} {
+
+       #kmg-tcl83
+       #
+       # Had to add this elseif branch to pass test rename-1.5
+       #
+        # Allowed to replace so must first destroy the prior instance
+
+        $name destroy
+    } elseif {!$Snit_info(canreplace) && [llength [info commands $name]]} {
+        error "command \"$name\" already exists"
+    }
+
+    # NEXT, create the instance's namespace.
+    set selfns \
+        [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
+    namespace eval $selfns {}
+
+    # NEXT, install the dispatcher
+    RT.MakeInstanceCommand $type $selfns $name
+
+    # Initialize the options to their defaults.
+    upvar ${selfns}::options options
+    foreach opt $Snit_optionInfo(local) {
+        set options($opt) $Snit_optionInfo(default-$opt)
+    }
+
+    # Initialize the instance vars to their defaults.
+    # selfns must be defined, as it is used implicitly.
+    ${type}::Snit_instanceVars $selfns
+
+    # Execute the type's constructor.
+    set errcode [catch {
+        RT.ConstructInstance $type $selfns $name $args
+    } result]
+
+    if {$errcode} {
+        global errorInfo
+        global errorCode
+
+        set theInfo $errorInfo
+        set theCode $errorCode
+        ::snit::RT.DestroyObject $type $selfns $name
+        error "Error in constructor: $result" $theInfo $theCode
+    }
+
+    # NEXT, return the object's name.
+    return $name
+}
+
+# Creates a new instance of the snit::widget or snit::widgetadaptor
+# given its name and the args.
+#
+# type         The snit::widget or snit::widgetadaptor
+# name         The instance name
+# args         Args to pass to the constructor
+
+proc ::snit::RT.widget.typemethod.create {type name args} {
+    variable ${type}::Snit_info
+    variable ${type}::Snit_optionInfo
+
+    # FIRST, if %AUTO% appears in the name, generate a unique
+    # command name.
+    if {[string match "*%AUTO%*" $name]} {
+        set name [::snit::RT.UniqueName Snit_info(counter) $type $name]
+    }
+
+    # NEXT, create the instance's namespace.
+    set selfns \
+        [::snit::RT.UniqueInstanceNamespace Snit_info(counter) $type]
+    namespace eval $selfns { }
+
+    # NEXT, Initialize the widget's own options to their defaults.
+    upvar ${selfns}::options options
+    foreach opt $Snit_optionInfo(local) {
+        set options($opt) $Snit_optionInfo(default-$opt)
+    }
+
+    # Initialize the instance vars to their defaults.
+    ${type}::Snit_instanceVars $selfns
+
+    # NEXT, if this is a normal widget (not a widget adaptor) then create a
+    # frame as its hull.  We set the frame's -class to the user's widgetclass,
+    # or, if none, search for -class in the args list, otherwise default to
+    # the basename of the $type with an initial upper case letter.
+    if {!$Snit_info(isWidgetAdaptor)} {
+        # FIRST, determine the class name
+       set wclass $Snit_info(widgetclass)
+        if {$Snit_info(widgetclass) == ""} {
+           set idx [lsearch -exact $args -class]
+           if {$idx >= 0 && ($idx%2 == 0)} {
+               # -class exists and is in the -option position
+               set wclass [lindex $args [expr {$idx+1}]]
+               set args [lreplace $args $idx [expr {$idx+1}]]
+           } else {
+               set wclass [::snit::Capitalize [namespace tail $type]]
+           }
+       }
+
+        # NEXT, create the widget
+        set self $name
+        package require Tk
+        ${type}::installhull using $Snit_info(hulltype) -class $wclass
+
+        # NEXT, let's query the option database for our
+        # widget, now that we know that it exists.
+        foreach opt $Snit_optionInfo(local) {
+            set dbval [RT.OptionDbGet $type $name $opt]
+
+            if {"" != $dbval} {
+                set options($opt) $dbval
+            }
+        }
+    }
+
+    # Execute the type's constructor, and verify that it
+    # has a hull.
+    set errcode [catch {
+        RT.ConstructInstance $type $selfns $name $args
+
+        ::snit::RT.Component $type $selfns hull
+
+        # Prepare to call the object's destructor when the
+        # <Destroy> event is received.  Use a Snit-specific bindtag
+        # so that the widget name's tag is unencumbered.
+
+        bind Snit$type$name <Destroy> [::snit::Expand {
+            ::snit::RT.DestroyObject %TYPE% %NS% %W
+        } %TYPE% $type %NS% $selfns]
+
+        # Insert the bindtag into the list of bindtags right
+        # after the widget name.
+        set taglist [bindtags $name]
+        set ndx [lsearch -exact $taglist $name]
+        incr ndx
+        bindtags $name [linsert $taglist $ndx Snit$type$name]
+    } result]
+
+    if {$errcode} {
+        global errorInfo
+        global errorCode
+
+        set theInfo $errorInfo
+        set theCode $errorCode
+        ::snit::RT.DestroyObject $type $selfns $name
+        error "Error in constructor: $result" $theInfo $theCode
+    }
+
+    # NEXT, return the object's name.
+    return $name
+}
+
+
+# RT.MakeInstanceCommand type selfns instance
+#
+# type        The object type
+# selfns      The instance namespace
+# instance    The instance name
+#
+# Creates the instance proc.
+
+proc ::snit::RT.MakeInstanceCommand {type selfns instance} {
+    variable ${type}::Snit_info
+
+    # FIRST, remember the instance name.  The Snit_instance variable
+    # allows the instance to figure out its current name given the
+    # instance namespace.
+    upvar ${selfns}::Snit_instance Snit_instance
+    set Snit_instance $instance
+
+    # NEXT, qualify the proc name if it's a widget.
+    if {$Snit_info(isWidget)} {
+        set procname ::$instance
+    } else {
+        set procname $instance
+    }
+
+    # NEXT, install the new proc
+    if {!$Snit_info(simpledispatch)} {
+        set instanceProc $::snit::nominalInstanceProc
+    } else {
+        set instanceProc $::snit::simpleInstanceProc
+    }
+
+    proc $procname {method args} \
+        [string map \
+             [list %SELFNS% $selfns %WIN% $instance %TYPE% $type] \
+             $instanceProc]
+
+    #kmg-tcl83
+    # NEXT, add the trace.
+    ::snit83::traceAddCommand $procname {rename delete} \
+        [list ::snit::RT.InstanceTrace $type $selfns $instance]
+}
+
+# This proc is called when the instance command is renamed.
+# If op is delete, then new will always be "", so op is redundant.
+#
+# type         The fully-qualified type name
+# selfns       The instance namespace
+# win          The original instance/tk window name.
+# old          old instance command name
+# new          new instance command name
+# op           rename or delete
+#
+# If the op is delete, we need to clean up the object; otherwise,
+# we need to track the change.
+#
+# NOTE: In Tcl 8.4.2 there's a bug: errors in rename and delete
+# traces aren't propagated correctly.  Instead, they silently
+# vanish.  Add a catch to output any error message.
+
+proc ::snit::RT.InstanceTrace {type selfns win old new op} {
+    variable ${type}::Snit_info
+
+    # Note to developers ...
+    # For Tcl 8.4.0, errors thrown in trace handlers vanish silently.
+    # Therefore we catch them here and create some output to help in
+    # debugging such problems.
+
+    if {[catch {
+        # FIRST, clean up if necessary
+        if {"" == $new} {
+            if {$Snit_info(isWidget)} {
+                destroy $win
+            } else {
+                ::snit::RT.DestroyObject $type $selfns $win
+            }
+        } else {
+            # Otherwise, track the change.
+            variable ${selfns}::Snit_instance
+            set Snit_instance [uplevel 1 [list namespace which -command $new]]
+
+            # Also, clear the instance caches, as many cached commands
+            # might be invalid.
+            RT.ClearInstanceCaches $selfns
+        }
+    } result]} {
+        global errorInfo
+        # Pop up the console on Windows wish, to enable stdout.
+        # This clobbers errorInfo on unix, so save it so we can print it.
+        set ei $errorInfo
+        catch {console show}
+        puts "Error in ::snit::RT.InstanceTrace $type $selfns $win $old $new $op:"
+        puts $ei
+    }
+}
+
+# Calls the instance constructor and handles related housekeeping.
+proc ::snit::RT.ConstructInstance {type selfns instance arglist} {
+    variable ${type}::Snit_optionInfo
+    variable ${selfns}::Snit_iinfo
+
+    # Track whether we are constructed or not.
+    set Snit_iinfo(constructed) 0
+
+    # Call the user's constructor
+    eval [linsert $arglist 0 \
+              ${type}::Snit_constructor $type $selfns $instance $instance]
+
+    set Snit_iinfo(constructed) 1
+
+    # Validate the initial set of options (including defaults)
+    foreach option $Snit_optionInfo(local) {
+        set value [set ${selfns}::options($option)]
+
+        if {"" != $Snit_optionInfo(typespec-$option)} {
+            if {[catch {
+                $Snit_optionInfo(typeobj-$option) validate $value
+            } result]} {
+                return -code error "invalid $option default: $result"
+            }
+        }
+    }
+
+    # Unset the configure cache for all -readonly options.
+    # This ensures that the next time anyone tries to
+    # configure it, an error is thrown.
+    foreach opt $Snit_optionInfo(local) {
+        if {$Snit_optionInfo(readonly-$opt)} {
+            ::snit83::unset -nocomplain ${selfns}::Snit_configureCache($opt)
+        }
+    }
+
+    return
+}
+
+# Returns a unique command name.
+#
+# REQUIRE: type is a fully qualified name.
+# REQUIRE: name contains "%AUTO%"
+# PROMISE: the returned command name is unused.
+proc ::snit::RT.UniqueName {countervar type name} {
+    upvar $countervar counter
+    while 1 {
+        # FIRST, bump the counter and define the %AUTO% instance name;
+        # then substitute it into the specified name.  Wrap around at
+        # 2^31 - 2 to prevent overflow problems.
+        incr counter
+        if {$counter > 2147483646} {
+            set counter 0
+        }
+        set auto "[namespace tail $type]$counter"
+        set candidate [Expand $name %AUTO% $auto]
+        if {![llength [info commands $candidate]]} {
+            return $candidate
+        }
+    }
+}
+
+# Returns a unique instance namespace, fully qualified.
+#
+# countervar     The name of a counter variable
+# type           The instance's type
+#
+# REQUIRE: type is fully qualified
+# PROMISE: The returned namespace name is unused.
+
+proc ::snit::RT.UniqueInstanceNamespace {countervar type} {
+    upvar $countervar counter
+    while 1 {
+        # FIRST, bump the counter and define the namespace name.
+        # Then see if it already exists.  Wrap around at
+        # 2^31 - 2 to prevent overflow problems.
+        incr counter
+        if {$counter > 2147483646} {
+            set counter 0
+        }
+        set ins "${type}::Snit_inst${counter}"
+        if {![namespace exists $ins]} {
+            return $ins
+        }
+    }
+}
+
+# Retrieves an option's value from the option database.
+# Returns "" if no value is found.
+proc ::snit::RT.OptionDbGet {type self opt} {
+    variable ${type}::Snit_optionInfo
+
+    return [option get $self \
+                $Snit_optionInfo(resource-$opt) \
+                $Snit_optionInfo(class-$opt)]
+}
+
+#-----------------------------------------------------------------------
+# Object Destruction
+
+# Implements the standard "destroy" method
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+
+proc ::snit::RT.method.destroy {type selfns win self} {
+    variable ${selfns}::Snit_iinfo
+
+    # Can't destroy the object if it isn't complete constructed.
+    if {!$Snit_iinfo(constructed)} {
+        return -code error "Called 'destroy' method in constructor"
+    }
+
+    # Calls Snit_cleanup, which (among other things) calls the
+    # user's destructor.
+    ::snit::RT.DestroyObject $type $selfns $win
+}
+
+# This is the function that really cleans up; it's automatically
+# called when any instance is destroyed, e.g., by "$object destroy"
+# for types, and by the <Destroy> event for widgets.
+#
+# type         The fully-qualified type name.
+# selfns       The instance namespace
+# win          The original instance command name.
+
+proc ::snit::RT.DestroyObject {type selfns win} {
+    variable ${type}::Snit_info
+
+    # If the variable Snit_instance doesn't exist then there's no
+    # instance command for this object -- it's most likely a
+    # widgetadaptor. Consequently, there are some things that
+    # we don't need to do.
+    if {[info exists ${selfns}::Snit_instance]} {
+        upvar ${selfns}::Snit_instance instance
+
+        # First, remove the trace on the instance name, so that we
+        # don't call RT.DestroyObject recursively.
+        RT.RemoveInstanceTrace $type $selfns $win $instance
+
+        # Next, call the user's destructor
+        ${type}::Snit_destructor $type $selfns $win $instance
+
+        # Next, if this isn't a widget, delete the instance command.
+        # If it is a widget, get the hull component's name, and rename
+        # it back to the widget name
+
+        # Next, delete the hull component's instance command,
+        # if there is one.
+        if {$Snit_info(isWidget)} {
+            set hullcmd [::snit::RT.Component $type $selfns hull]
+
+            catch {rename $instance ""}
+
+            # Clear the bind event
+            bind Snit$type$win <Destroy> ""
+
+            if {[llength [info commands $hullcmd]]} {
+                # FIRST, rename the hull back to its original name.
+                # If the hull is itself a megawidget, it will have its
+                # own cleanup to do, and it might not do it properly
+                # if it doesn't have the right name.
+                rename $hullcmd ::$instance
+
+                # NEXT, destroy it.
+                destroy $instance
+            }
+        } else {
+            catch {rename $instance ""}
+        }
+    }
+
+    # Next, delete the instance's namespace.  This kills any
+    # instance variables.
+    namespace delete $selfns
+
+    return
+}
+
+# Remove instance trace
+#
+# type           The fully qualified type name
+# selfns         The instance namespace
+# win            The original instance name/Tk window name
+# instance       The current instance name
+
+proc ::snit::RT.RemoveInstanceTrace {type selfns win instance} {
+    variable ${type}::Snit_info
+
+    if {$Snit_info(isWidget)} {
+        set procname ::$instance
+    } else {
+        set procname $instance
+    }
+
+    # NEXT, remove any trace on this name
+    catch {
+       #kmg-tcl83
+        ::snit83::traceRemoveCommand $procname {rename delete} \
+            [list ::snit::RT.InstanceTrace $type $selfns $win]
+    }
+}
+
+#-----------------------------------------------------------------------
+# Typecomponent Management and Method Caching
+
+# Typecomponent trace; used for write trace on typecomponent
+# variables.  Saves the new component object name, provided
+# that certain conditions are met.  Also clears the typemethod
+# cache.
+
+proc ::snit::RT.TypecomponentTrace {type component n1 n2 op} {
+    upvar ${type}::Snit_info Snit_info
+    upvar ${type}::${component} cvar
+    upvar ${type}::Snit_typecomponents Snit_typecomponents
+
+    # Save the new component value.
+    set Snit_typecomponents($component) $cvar
+
+    # Clear the typemethod cache.
+    # TBD: can we unset just the elements related to
+    # this component?
+    ::snit83::unset -nocomplain -- ${type}::Snit_typemethodCache
+}
+
+# Generates and caches the command for a typemethod.
+#
+# type         The type
+# method       The name of the typemethod to call.
+#
+# The return value is one of the following lists:
+#
+#    {}              There's no such method.
+#    {1}             The method has submethods; look again.
+#    {0 <command>}   Here's the command to execute.
+
+proc snit::RT.CacheTypemethodCommand {type method} {
+    upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
+    upvar ${type}::Snit_typecomponents  Snit_typecomponents
+    upvar ${type}::Snit_typemethodCache Snit_typemethodCache
+    upvar ${type}::Snit_info            Snit_info
+
+    # FIRST, get the pattern data and the typecomponent name.
+    set implicitCreate 0
+    set instanceName ""
+
+    set starredMethod [lreplace $method end end *]
+    set methodTail [lindex $method end]
+
+    if {[info exists Snit_typemethodInfo($method)]} {
+        set key $method
+    } elseif {[info exists Snit_typemethodInfo($starredMethod)]} {
+        if {[lsearch -exact $Snit_info(excepttypemethods) $methodTail] == -1} {
+            set key $starredMethod
+        } else {
+            return [list ]
+        }
+    } elseif {[llength $method] > 1} {
+       return [list ]
+    } elseif {$Snit_info(hasinstances)} {
+        # Assume the unknown name is an instance name to create, unless
+        # this is a widget and the style of the name is wrong, or the
+        # name mimics a standard typemethod.
+
+        if {[set ${type}::Snit_info(isWidget)] &&
+            ![string match ".*" $method]} {
+            return [list ]
+        }
+
+        # Without this check, the call "$type info" will redefine the
+        # standard "::info" command, with disastrous results.  Since it's
+        # a likely thing to do if !-typeinfo, put in an explicit check.
+        if {"info" == $method || "destroy" == $method} {
+            return [list ]
+        }
+
+        set implicitCreate 1
+        set instanceName $method
+        set key create
+        set method create
+    } else {
+        return [list ]
+    }
+
+    foreach {flag pattern compName} $Snit_typemethodInfo($key) {}
+
+    if {$flag == 1} {
+        return [list 1]
+    }
+
+    # NEXT, build the substitution list
+    set subList [list \
+                     %% % \
+                     %t $type \
+                     %M $method \
+                     %m [lindex $method end] \
+                     %j [join $method _]]
+
+    if {"" != $compName} {
+        if {![info exists Snit_typecomponents($compName)]} {
+            error "$type delegates typemethod \"$method\" to undefined typecomponent \"$compName\""
+        }
+
+        lappend subList %c [list $Snit_typecomponents($compName)]
+    }
+
+    set command {}
+
+    foreach subpattern $pattern {
+        lappend command [string map $subList $subpattern]
+    }
+
+    if {$implicitCreate} {
+        # In this case, $method is the name of the instance to
+        # create.  Don't cache, as we usually won't do this one
+        # again.
+        lappend command $instanceName
+    } else {
+        set Snit_typemethodCache($method) [list 0 $command]
+    }
+
+    return [list 0 $command]
+}
+
+
+#-----------------------------------------------------------------------
+# Component Management and Method Caching
+
+# Retrieves the object name given the component name.
+proc ::snit::RT.Component {type selfns name} {
+    variable ${selfns}::Snit_components
+
+    if {[catch {set Snit_components($name)} result]} {
+        variable ${selfns}::Snit_instance
+
+        error "component \"$name\" is undefined in $type $Snit_instance"
+    }
+
+    return $result
+}
+
+# Component trace; used for write trace on component instance
+# variables.  Saves the new component object name, provided
+# that certain conditions are met.  Also clears the method
+# cache.
+
+proc ::snit::RT.ComponentTrace {type selfns component n1 n2 op} {
+    upvar ${type}::Snit_info Snit_info
+    upvar ${selfns}::${component} cvar
+    upvar ${selfns}::Snit_components Snit_components
+
+    # If they try to redefine the hull component after
+    # it's been defined, that's an error--but only if
+    # this is a widget or widget adaptor.
+    if {"hull" == $component &&
+        $Snit_info(isWidget) &&
+        [info exists Snit_components($component)]} {
+        set cvar $Snit_components($component)
+        error "The hull component cannot be redefined"
+    }
+
+    # Save the new component value.
+    set Snit_components($component) $cvar
+
+    # Clear the instance caches.
+    # TBD: can we unset just the elements related to
+    # this component?
+    RT.ClearInstanceCaches $selfns
+}
+
+# Generates and caches the command for a method.
+#
+# type:                The instance's type
+# selfns:      The instance's private namespace
+# win:          The instance's original name (a Tk widget name, for
+#               snit::widgets.
+# self:         The instance's current name.
+# method:      The name of the method to call.
+#
+# The return value is one of the following lists:
+#
+#    {}              There's no such method.
+#    {1}             The method has submethods; look again.
+#    {0 <command>}   Here's the command to execute.
+
+proc ::snit::RT.CacheMethodCommand {type selfns win self method} {
+    variable ${type}::Snit_info
+    variable ${type}::Snit_methodInfo
+    variable ${type}::Snit_typecomponents
+    variable ${selfns}::Snit_components
+    variable ${selfns}::Snit_methodCache
+
+    # FIRST, get the pattern data and the component name.
+    set starredMethod [lreplace $method end end *]
+    set methodTail [lindex $method end]
+
+    if {[info exists Snit_methodInfo($method)]} {
+        set key $method
+    } elseif {[info exists Snit_methodInfo($starredMethod)] &&
+              [lsearch -exact $Snit_info(exceptmethods) $methodTail] == -1} {
+        set key $starredMethod
+    } else {
+        return [list ]
+    }
+
+    foreach {flag pattern compName} $Snit_methodInfo($key) {}
+
+    if {$flag == 1} {
+        return [list 1]
+    }
+
+    # NEXT, build the substitution list
+    set subList [list \
+                     %% % \
+                     %t $type \
+                     %M $method \
+                     %m [lindex $method end] \
+                     %j [join $method _] \
+                     %n [list $selfns] \
+                     %w [list $win] \
+                     %s [list $self]]
+
+    if {"" != $compName} {
+        if {[info exists Snit_components($compName)]} {
+            set compCmd $Snit_components($compName)
+        } elseif {[info exists Snit_typecomponents($compName)]} {
+            set compCmd $Snit_typecomponents($compName)
+        } else {
+            error "$type $self delegates method \"$method\" to undefined component \"$compName\""
+        }
+
+        lappend subList %c [list $compCmd]
+    }
+
+    # Note: The cached command will executed faster if it's
+    # already a list.
+    set command {}
+
+    foreach subpattern $pattern {
+        lappend command [string map $subList $subpattern]
+    }
+
+    set commandRec [list 0 $command]
+
+    set Snit_methodCache($method) $commandRec
+
+    return $commandRec
+}
+
+
+# Looks up a method's command.
+#
+# type:                The instance's type
+# selfns:      The instance's private namespace
+# win:          The instance's original name (a Tk widget name, for
+#               snit::widgets.
+# self:         The instance's current name.
+# method:      The name of the method to call.
+# errPrefix:    Prefix for any error method
+proc ::snit::RT.LookupMethodCommand {type selfns win self method errPrefix} {
+    set commandRec [snit::RT.CacheMethodCommand \
+                        $type $selfns $win $self \
+                        $method]
+
+
+    if {[llength $commandRec] == 0} {
+        return -code error \
+            "$errPrefix, \"$self $method\" is not defined"
+    } elseif {[lindex $commandRec 0] == 1} {
+        return -code error \
+            "$errPrefix, wrong number args: should be \"$self\" $method method args"
+    }
+
+    return  [lindex $commandRec 1]
+}
+
+
+# Clears all instance command caches
+proc ::snit::RT.ClearInstanceCaches {selfns} {
+    ::snit83::unset -nocomplain -- ${selfns}::Snit_methodCache
+    ::snit83::unset -nocomplain -- ${selfns}::Snit_cgetCache
+    ::snit83::unset -nocomplain -- ${selfns}::Snit_configureCache
+    ::snit83::unset -nocomplain -- ${selfns}::Snit_validateCache
+}
+
+
+#-----------------------------------------------------------------------
+# Component Installation
+
+# Implements %TYPE%::installhull.  The variables self and selfns
+# must be defined in the caller's context.
+#
+# Installs the named widget as the hull of a
+# widgetadaptor.  Once the widget is hijacked, its new name
+# is assigned to the hull component.
+
+proc ::snit::RT.installhull {type {using "using"} {widgetType ""} args} {
+    variable ${type}::Snit_info
+    variable ${type}::Snit_optionInfo
+    upvar self self
+    upvar selfns selfns
+    upvar ${selfns}::hull hull
+    upvar ${selfns}::options options
+
+    # FIRST, make sure we can do it.
+    if {!$Snit_info(isWidget)} {
+        error "installhull is valid only for snit::widgetadaptors"
+    }
+
+    if {[info exists ${selfns}::Snit_instance]} {
+        error "hull already installed for $type $self"
+    }
+
+    # NEXT, has it been created yet?  If not, create it using
+    # the specified arguments.
+    if {"using" == $using} {
+        # FIRST, create the widget
+        set cmd [linsert $args 0 $widgetType $self]
+        set obj [uplevel 1 $cmd]
+
+        # NEXT, for each option explicitly delegated to the hull
+        # that doesn't appear in the usedOpts list, get the
+        # option database value and apply it--provided that the
+        # real option name and the target option name are different.
+        # (If they are the same, then the option database was
+        # already queried as part of the normal widget creation.)
+        #
+        # Also, we don't need to worry about implicitly delegated
+        # options, as the option and target option names must be
+        # the same.
+        if {[info exists Snit_optionInfo(delegated-hull)]} {
+
+            # FIRST, extract all option names from args
+            set usedOpts {}
+            set ndx [lsearch -glob $args "-*"]
+            foreach {opt val} [lrange $args $ndx end] {
+                lappend usedOpts $opt
+            }
+
+            foreach opt $Snit_optionInfo(delegated-hull) {
+                set target [lindex $Snit_optionInfo(target-$opt) 1]
+
+                if {"$target" == $opt} {
+                    continue
+                }
+
+                set result [lsearch -exact $usedOpts $target]
+
+                if {$result != -1} {
+                    continue
+                }
+
+                set dbval [RT.OptionDbGet $type $self $opt]
+                $obj configure $target $dbval
+            }
+        }
+    } else {
+        set obj $using
+
+        if {![string equal $obj $self]} {
+            error \
+                "hull name mismatch: \"$obj\" != \"$self\""
+        }
+    }
+
+    # NEXT, get the local option defaults.
+    foreach opt $Snit_optionInfo(local) {
+        set dbval [RT.OptionDbGet $type $self $opt]
+
+        if {"" != $dbval} {
+            set options($opt) $dbval
+        }
+    }
+
+
+    # NEXT, do the magic
+    set i 0
+    while 1 {
+        incr i
+        set newName "::hull${i}$self"
+        if {![llength [info commands $newName]]} {
+            break
+        }
+    }
+
+    rename ::$self $newName
+    RT.MakeInstanceCommand $type $selfns $self
+
+    # Note: this relies on RT.ComponentTrace to do the dirty work.
+    set hull $newName
+
+    return
+}
+
+# Implements %TYPE%::install.
+#
+# Creates a widget and installs it as the named component.
+# It expects self and selfns to be defined in the caller's context.
+
+proc ::snit::RT.install {type compName "using" widgetType winPath args} {
+    variable ${type}::Snit_optionInfo
+    variable ${type}::Snit_info
+    upvar self self
+    upvar selfns selfns
+    upvar ${selfns}::$compName comp
+    upvar ${selfns}::hull hull
+
+    # We do the magic option database stuff only if $self is
+    # a widget.
+    if {$Snit_info(isWidget)} {
+        if {"" == $hull} {
+            error "tried to install \"$compName\" before the hull exists"
+        }
+
+        # FIRST, query the option database and save the results
+        # into args.  Insert them before the first option in the
+        # list, in case there are any non-standard parameters.
+        #
+        # Note: there might not be any delegated options; if so,
+        # don't bother.
+
+        if {[info exists Snit_optionInfo(delegated-$compName)]} {
+            set ndx [lsearch -glob $args "-*"]
+
+            foreach opt $Snit_optionInfo(delegated-$compName) {
+                set dbval [RT.OptionDbGet $type $self $opt]
+
+                if {"" != $dbval} {
+                    set target [lindex $Snit_optionInfo(target-$opt) 1]
+                    set args [linsert $args $ndx $target $dbval]
+                }
+            }
+        }
+    }
+
+    # NEXT, create the component and save it.
+    set cmd [concat [list $widgetType $winPath] $args]
+    set comp [uplevel 1 $cmd]
+
+    # NEXT, handle the option database for "delegate option *",
+    # in widgets only.
+    if {$Snit_info(isWidget) && [string equal $Snit_optionInfo(starcomp) $compName]} {
+        # FIRST, get the list of option specs from the widget.
+        # If configure doesn't work, skip it.
+        if {[catch {$comp configure} specs]} {
+            return
+        }
+
+        # NEXT, get the set of explicitly used options from args
+        set usedOpts {}
+        set ndx [lsearch -glob $args "-*"]
+        foreach {opt val} [lrange $args $ndx end] {
+            lappend usedOpts $opt
+        }
+
+        # NEXT, "delegate option *" matches all options defined
+        # by this widget that aren't defined by the widget as a whole,
+        # and that aren't excepted.  Plus, we skip usedOpts.  So build
+        # a list of the options it can't match.
+        set skiplist [concat \
+                          $usedOpts \
+                          $Snit_optionInfo(except) \
+                          $Snit_optionInfo(local) \
+                          $Snit_optionInfo(delegated)]
+
+        # NEXT, loop over all of the component's options, and set
+        # any not in the skip list for which there is an option
+        # database value.
+        foreach spec $specs {
+            # Skip aliases
+            if {[llength $spec] != 5} {
+                continue
+            }
+
+            set opt [lindex $spec 0]
+
+            if {[lsearch -exact $skiplist $opt] != -1} {
+                continue
+            }
+
+            set res [lindex $spec 1]
+            set cls [lindex $spec 2]
+
+            set dbvalue [option get $self $res $cls]
+
+            if {"" != $dbvalue} {
+                $comp configure $opt $dbvalue
+            }
+        }
+    }
+
+    return
+}
+
+
+#-----------------------------------------------------------------------
+# Method/Variable Name Qualification
+
+# Implements %TYPE%::variable.  Requires selfns.
+proc ::snit::RT.variable {varname} {
+    upvar selfns selfns
+
+    if {![string match "::*" $varname]} {
+        uplevel 1 [list upvar 1 ${selfns}::$varname $varname]
+    } else {
+        # varname is fully qualified; let the standard
+        # "variable" command handle it.
+        uplevel 1 [list ::variable $varname]
+    }
+}
+
+# Fully qualifies a typevariable name.
+#
+# This is used to implement the mytypevar command.
+
+proc ::snit::RT.mytypevar {type name} {
+    return ${type}::$name
+}
+
+# Fully qualifies an instance variable name.
+#
+# This is used to implement the myvar command.
+proc ::snit::RT.myvar {name} {
+    upvar selfns selfns
+    return ${selfns}::$name
+}
+
+# Use this like "list" to convert a proc call into a command
+# string to pass to another object (e.g., as a -command).
+# Qualifies the proc name properly.
+#
+# This is used to implement the "myproc" command.
+
+proc ::snit::RT.myproc {type procname args} {
+    set procname "${type}::$procname"
+    return [linsert $args 0 $procname]
+}
+
+# DEPRECATED
+proc ::snit::RT.codename {type name} {
+    return "${type}::$name"
+}
+
+# Use this like "list" to convert a typemethod call into a command
+# string to pass to another object (e.g., as a -command).
+# Inserts the type command at the beginning.
+#
+# This is used to implement the "mytypemethod" command.
+
+proc ::snit::RT.mytypemethod {type args} {
+    return [linsert $args 0 $type]
+}
+
+# Use this like "list" to convert a method call into a command
+# string to pass to another object (e.g., as a -command).
+# Inserts the code at the beginning to call the right object, even if
+# the object's name has changed.  Requires that selfns be defined
+# in the calling context, eg. can only be called in instance
+# code.
+#
+# This is used to implement the "mymethod" command.
+
+proc ::snit::RT.mymethod {args} {
+    upvar selfns selfns
+    return [linsert $args 0 ::snit::RT.CallInstance ${selfns}]
+}
+
+# Calls an instance method for an object given its
+# instance namespace and remaining arguments (the first of which
+# will be the method name.
+#
+# selfns               The instance namespace
+# args                 The arguments
+#
+# Uses the selfns to determine $self, and calls the method
+# in the normal way.
+#
+# This is used to implement the "mymethod" command.
+
+proc ::snit::RT.CallInstance {selfns args} {
+    upvar ${selfns}::Snit_instance self
+
+    set retval [catch {uplevel 1 [linsert $args 0 $self]} result]
+
+    if {$retval} {
+        if {$retval == 1} {
+            global errorInfo
+            global errorCode
+            return -code error -errorinfo $errorInfo \
+                -errorcode $errorCode $result
+        } else {
+            return -code $retval $result
+        }
+    }
+
+    return $result
+}
+
+# Looks for the named option in the named variable.  If found,
+# it and its value are removed from the list, and the value
+# is returned.  Otherwise, the default value is returned.
+# If the option is undelegated, it's own default value will be
+# used if none is specified.
+#
+# Implements the "from" command.
+
+proc ::snit::RT.from {type argvName option {defvalue ""}} {
+    variable ${type}::Snit_optionInfo
+    upvar $argvName argv
+
+    set ioption [lsearch -exact $argv $option]
+
+    if {$ioption == -1} {
+        if {"" == $defvalue &&
+            [info exists Snit_optionInfo(default-$option)]} {
+            return $Snit_optionInfo(default-$option)
+        } else {
+            return $defvalue
+        }
+    }
+
+    set ivalue [expr {$ioption + 1}]
+    set value [lindex $argv $ivalue]
+
+    set argv [lreplace $argv $ioption $ivalue]
+
+    return $value
+}
+
+#-----------------------------------------------------------------------
+# Type Destruction
+
+# Implements the standard "destroy" typemethod:
+# Destroys a type completely.
+#
+# type         The snit type
+
+proc ::snit::RT.typemethod.destroy {type} {
+    variable ${type}::Snit_info
+
+    # FIRST, destroy all instances
+    foreach selfns [namespace children $type] {
+        if {![namespace exists $selfns]} {
+            continue
+        }
+        upvar ${selfns}::Snit_instance obj
+
+        if {$Snit_info(isWidget)} {
+            destroy $obj
+        } else {
+            if {[llength [info commands $obj]]} {
+                $obj destroy
+            }
+        }
+    }
+
+    # NEXT, destroy the type's data.
+    namespace delete $type
+
+    # NEXT, get rid of the type command.
+    rename $type ""
+}
+
+
+
+#-----------------------------------------------------------------------
+# Option Handling
+
+# Implements the standard "cget" method
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# option        The name of the option
+
+proc ::snit::RT.method.cget {type selfns win self option} {
+    if {[catch {set ${selfns}::Snit_cgetCache($option)} command]} {
+        set command [snit::RT.CacheCgetCommand $type $selfns $win $self $option]
+
+        if {[llength $command] == 0} {
+            return -code error "unknown option \"$option\""
+        }
+    }
+
+    uplevel 1 $command
+}
+
+# Retrieves and caches the command that implements "cget" for the
+# specified option.
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# option        The name of the option
+
+proc ::snit::RT.CacheCgetCommand {type selfns win self option} {
+    variable ${type}::Snit_optionInfo
+    variable ${selfns}::Snit_cgetCache
+
+    if {[info exists Snit_optionInfo(islocal-$option)]} {
+        # We know the item; it's either local, or explicitly delegated.
+        if {$Snit_optionInfo(islocal-$option)} {
+            # It's a local option.  If it has a cget method defined,
+            # use it; otherwise just return the value.
+
+            if {"" == $Snit_optionInfo(cget-$option)} {
+                set command [list set ${selfns}::options($option)]
+            } else {
+                set command [snit::RT.LookupMethodCommand \
+                                 $type $selfns $win $self \
+                                 $Snit_optionInfo(cget-$option) \
+                                 "can't cget $option"]
+
+                lappend command $option
+            }
+
+            set Snit_cgetCache($option) $command
+            return $command
+        }
+
+        # Explicitly delegated option; get target
+        set comp [lindex $Snit_optionInfo(target-$option) 0]
+        set target [lindex $Snit_optionInfo(target-$option) 1]
+    } elseif {"" != $Snit_optionInfo(starcomp) &&
+              [lsearch -exact $Snit_optionInfo(except) $option] == -1} {
+        # Unknown option, but unknowns are delegated; get target.
+        set comp $Snit_optionInfo(starcomp)
+        set target $option
+    } else {
+        return ""
+    }
+
+    # Get the component's object.
+    set obj [RT.Component $type $selfns $comp]
+
+    set command [list $obj cget $target]
+    set Snit_cgetCache($option) $command
+
+    return $command
+}
+
+# Implements the standard "configurelist" method
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# optionlist    A list of options and their values.
+
+proc ::snit::RT.method.configurelist {type selfns win self optionlist} {
+    variable ${type}::Snit_optionInfo
+
+    foreach {option value} $optionlist {
+        # FIRST, get the configure command, caching it if need be.
+        if {[catch {set ${selfns}::Snit_configureCache($option)} command]} {
+            set command [snit::RT.CacheConfigureCommand \
+                             $type $selfns $win $self $option]
+
+            if {[llength $command] == 0} {
+                return -code error "unknown option \"$option\""
+            }
+        }
+
+        # NEXT, if we have a type-validation object, use it.
+        # TBD: Should test (islocal-$option) here, but islocal
+        # isn't defined for implicitly delegated options.
+        if {[info exists Snit_optionInfo(typeobj-$option)]
+            && "" != $Snit_optionInfo(typeobj-$option)} {
+            if {[catch {
+                $Snit_optionInfo(typeobj-$option) validate $value
+            } result]} {
+                return -code error "invalid $option value: $result"
+            }
+        }
+
+        # NEXT, the caching the configure command also cached the
+        # validate command, if any.  If we have one, run it.
+        set valcommand [set ${selfns}::Snit_validateCache($option)]
+
+        if {[llength $valcommand]} {
+            lappend valcommand $value
+            uplevel 1 $valcommand
+        }
+
+        # NEXT, configure the option with the value.
+        lappend command $value
+        uplevel 1 $command
+    }
+
+    return
+}
+
+# Retrieves and caches the command that stores the named option.
+# Also stores the command that validates the name option if any;
+# If none, the validate command is "", so that the cache is always
+# populated.
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# option        An option name
+
+proc ::snit::RT.CacheConfigureCommand {type selfns win self option} {
+    variable ${type}::Snit_optionInfo
+    variable ${selfns}::Snit_configureCache
+    variable ${selfns}::Snit_validateCache
+
+    if {[info exist Snit_optionInfo(islocal-$option)]} {
+        # We know the item; it's either local, or explicitly delegated.
+
+        if {$Snit_optionInfo(islocal-$option)} {
+            # It's a local option.
+
+            # If it's readonly, it throws an error if we're already
+            # constructed.
+            if {$Snit_optionInfo(readonly-$option)} {
+                if {[set ${selfns}::Snit_iinfo(constructed)]} {
+                    error "option $option can only be set at instance creation"
+                }
+            }
+
+            # If it has a validate method, cache that for later.
+            if {"" != $Snit_optionInfo(validate-$option)} {
+                set command [snit::RT.LookupMethodCommand \
+                                 $type $selfns $win $self \
+                                 $Snit_optionInfo(validate-$option) \
+                                 "can't validate $option"]
+
+                lappend command $option
+                set Snit_validateCache($option) $command
+            } else {
+                set Snit_validateCache($option) ""
+            }
+
+            # If it has a configure method defined,
+            # cache it; otherwise, just set the value.
+
+            if {"" == $Snit_optionInfo(configure-$option)} {
+                set command [list set ${selfns}::options($option)]
+            } else {
+                set command [snit::RT.LookupMethodCommand \
+                                 $type $selfns $win $self \
+                                 $Snit_optionInfo(configure-$option) \
+                                 "can't configure $option"]
+
+                lappend command $option
+            }
+
+            set Snit_configureCache($option) $command
+            return $command
+        }
+
+        # Delegated option: get target.
+        set comp [lindex $Snit_optionInfo(target-$option) 0]
+        set target [lindex $Snit_optionInfo(target-$option) 1]
+    } elseif {$Snit_optionInfo(starcomp) != "" &&
+              [lsearch -exact $Snit_optionInfo(except) $option] == -1} {
+        # Unknown option, but unknowns are delegated.
+        set comp $Snit_optionInfo(starcomp)
+        set target $option
+    } else {
+        return ""
+    }
+
+    # There is no validate command in this case; save an empty string.
+    set Snit_validateCache($option) ""
+
+    # Get the component's object
+    set obj [RT.Component $type $selfns $comp]
+
+    set command [list $obj configure $target]
+    set Snit_configureCache($option) $command
+
+    return $command
+}
+
+# Implements the standard "configure" method
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# args          A list of options and their values, possibly empty.
+
+proc ::snit::RT.method.configure {type selfns win self args} {
+    # If two or more arguments, set values as usual.
+    if {[llength $args] >= 2} {
+        ::snit::RT.method.configurelist $type $selfns $win $self $args
+        return
+    }
+
+    # If zero arguments, acquire data for each known option
+    # and return the list
+    if {[llength $args] == 0} {
+        set result {}
+        foreach opt [RT.method.info.options $type $selfns $win $self] {
+            # Refactor this, so that we don't need to call via $self.
+            lappend result [RT.GetOptionDbSpec \
+                                $type $selfns $win $self $opt]
+        }
+
+        return $result
+    }
+
+    # They want it for just one.
+    set opt [lindex $args 0]
+
+    return [RT.GetOptionDbSpec $type $selfns $win $self $opt]
+}
+
+
+# Retrieves the option database spec for a single option.
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# option        The name of an option
+#
+# TBD: This is a bad name.  What it's returning is the
+# result of the configure query.
+
+proc ::snit::RT.GetOptionDbSpec {type selfns win self opt} {
+    variable ${type}::Snit_optionInfo
+
+    upvar ${selfns}::Snit_components Snit_components
+    upvar ${selfns}::options         options
+
+    if {[info exists options($opt)]} {
+        # This is a locally-defined option.  Just build the
+        # list and return it.
+        set res $Snit_optionInfo(resource-$opt)
+        set cls $Snit_optionInfo(class-$opt)
+        set def $Snit_optionInfo(default-$opt)
+
+        return [list $opt $res $cls $def \
+                    [RT.method.cget $type $selfns $win $self $opt]]
+    } elseif {[info exists Snit_optionInfo(target-$opt)]} {
+        # This is an explicitly delegated option.  The only
+        # thing we don't have is the default.
+        set res $Snit_optionInfo(resource-$opt)
+        set cls $Snit_optionInfo(class-$opt)
+
+        # Get the default
+        set logicalName [lindex $Snit_optionInfo(target-$opt) 0]
+        set comp $Snit_components($logicalName)
+        set target [lindex $Snit_optionInfo(target-$opt) 1]
+
+        if {[catch {$comp configure $target} result]} {
+            set defValue {}
+        } else {
+            set defValue [lindex $result 3]
+        }
+
+        return [list $opt $res $cls $defValue [$self cget $opt]]
+    } elseif {"" != $Snit_optionInfo(starcomp) &&
+              [lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
+        set logicalName $Snit_optionInfo(starcomp)
+        set target $opt
+        set comp $Snit_components($logicalName)
+
+        if {[catch {set value [$comp cget $target]} result]} {
+            error "unknown option \"$opt\""
+        }
+
+        if {![catch {$comp configure $target} result]} {
+            # Replace the delegated option name with the local name.
+            return [::snit::Expand $result $target $opt]
+        }
+
+        # configure didn't work; return simple form.
+        return [list $opt "" "" "" $value]
+    } else {
+        error "unknown option \"$opt\""
+    }
+}
+
+#-----------------------------------------------------------------------
+# Type Introspection
+
+# Implements the standard "info" typemethod.
+#
+# type         The snit type
+# command       The info subcommand
+# args          All other arguments.
+
+proc ::snit::RT.typemethod.info {type command args} {
+    global errorInfo
+    global errorCode
+
+    switch -exact $command {
+       args        -
+       body        -
+       default     -
+        typevars    -
+        typemethods -
+        instances {
+            # TBD: it should be possible to delete this error
+            # handling.
+            set errflag [catch {
+                uplevel 1 [linsert $args 0 \
+                              ::snit::RT.typemethod.info.$command $type]
+            } result]
+
+            if {$errflag} {
+                return -code error -errorinfo $errorInfo \
+                    -errorcode $errorCode $result
+            } else {
+                return $result
+            }
+        }
+        default {
+            error "\"$type info $command\" is not defined"
+        }
+    }
+}
+
+
+# Returns a list of the type's typevariables whose names match a
+# pattern, excluding Snit internal variables.
+#
+# type         A Snit type
+# pattern       Optional.  The glob pattern to match.  Defaults
+#               to *.
+
+proc ::snit::RT.typemethod.info.typevars {type {pattern *}} {
+    set result {}
+    foreach name [info vars "${type}::$pattern"] {
+        set tail [namespace tail $name]
+        if {![string match "Snit_*" $tail]} {
+            lappend result $name
+        }
+    }
+
+    return $result
+}
+
+# Returns a list of the type's methods whose names match a
+# pattern.  If "delegate typemethod *" is used, the list may
+# not be complete.
+#
+# type         A Snit type
+# pattern       Optional.  The glob pattern to match.  Defaults
+#               to *.
+
+proc ::snit::RT.typemethod.info.typemethods {type {pattern *}} {
+    variable ${type}::Snit_typemethodInfo
+    variable ${type}::Snit_typemethodCache
+
+    # FIRST, get the explicit names, skipping prefixes.
+    set result {}
+
+    foreach name [array names Snit_typemethodInfo $pattern] {
+        if {[lindex $Snit_typemethodInfo($name) 0] != 1} {
+            lappend result $name
+        }
+    }
+
+    # NEXT, add any from the cache that aren't explicit.
+    if {[info exists Snit_typemethodInfo(*)]} {
+        # First, remove "*" from the list.
+        set ndx [lsearch -exact $result "*"]
+        if {$ndx != -1} {
+            set result [lreplace $result $ndx $ndx]
+        }
+
+        foreach name [array names Snit_typemethodCache $pattern] {
+            if {[lsearch -exact $result $name] == -1} {
+                lappend result $name
+            }
+        }
+    }
+
+    return $result
+}
+
+# $type info args
+#
+# Returns a method's list of arguments. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.typemethod.info.args {type method} {
+    upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    #parray Snit_typemethodInfo
+
+    if {![info exists Snit_typemethodInfo($method)]} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    foreach {flag cmd component} $Snit_typemethodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated typemethod \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [lrange [::info args $theproc] 1 end]
+}
+
+# $type info body
+#
+# Returns a method's body. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.typemethod.info.body {type method} {
+    upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    #parray Snit_typemethodInfo
+
+    if {![info exists Snit_typemethodInfo($method)]} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    foreach {flag cmd component} $Snit_typemethodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated typemethod \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [RT.body [::info body $theproc]]
+}
+
+# $type info default
+#
+# Returns a method's list of arguments. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.typemethod.info.default {type method aname dvar} {
+    upvar 1 $dvar def
+    upvar ${type}::Snit_typemethodInfo  Snit_typemethodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    #parray Snit_methodInfo
+
+    if {![info exists Snit_typemethodInfo($method)]} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    foreach {flag cmd component} $Snit_typemethodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown typemethod \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated typemethod \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [::info default $theproc $aname def]
+}
+
+# Returns a list of the type's instances whose names match
+# a pattern.
+#
+# type         A Snit type
+# pattern       Optional.  The glob pattern to match
+#               Defaults to *
+#
+# REQUIRE: type is fully qualified.
+
+proc ::snit::RT.typemethod.info.instances {type {pattern *}} {
+    set result {}
+
+    foreach selfns [namespace children $type] {
+        upvar ${selfns}::Snit_instance instance
+
+        if {[string match $pattern $instance]} {
+            lappend result $instance
+        }
+    }
+
+    return $result
+}
+
+#-----------------------------------------------------------------------
+# Instance Introspection
+
+# Implements the standard "info" method.
+#
+# type         The snit type
+# selfns        The instance's instance namespace
+# win           The instance's original name
+# self          The instance's current name
+# command       The info subcommand
+# args          All other arguments.
+
+proc ::snit::RT.method.info {type selfns win self command args} {
+    switch -exact $command {
+       args        -
+       body        -
+       default     -
+        type        -
+        vars        -
+        options     -
+        methods     -
+        typevars    -
+        typemethods {
+            set errflag [catch {
+                uplevel 1 [linsert $args 0 ::snit::RT.method.info.$command \
+                              $type $selfns $win $self]
+            } result]
+
+            if {$errflag} {
+                global errorInfo
+                return -code error -errorinfo $errorInfo $result
+            } else {
+                return $result
+            }
+        }
+        default {
+            # error "\"$self info $command\" is not defined"
+            return -code error "\"$self info $command\" is not defined"
+        }
+    }
+}
+
+# $self info type
+#
+# Returns the instance's type
+proc ::snit::RT.method.info.type {type selfns win self} {
+    return $type
+}
+
+# $self info typevars
+#
+# Returns the instance's type's typevariables
+proc ::snit::RT.method.info.typevars {type selfns win self {pattern *}} {
+    return [RT.typemethod.info.typevars $type $pattern]
+}
+
+# $self info typemethods
+#
+# Returns the instance's type's typemethods
+proc ::snit::RT.method.info.typemethods {type selfns win self {pattern *}} {
+    return [RT.typemethod.info.typemethods $type $pattern]
+}
+
+# Returns a list of the instance's methods whose names match a
+# pattern.  If "delegate method *" is used, the list may
+# not be complete.
+#
+# type         A Snit type
+# selfns        The instance namespace
+# win          The original instance name
+# self          The current instance name
+# pattern       Optional.  The glob pattern to match.  Defaults
+#               to *.
+
+proc ::snit::RT.method.info.methods {type selfns win self {pattern *}} {
+    variable ${type}::Snit_methodInfo
+    variable ${selfns}::Snit_methodCache
+
+    # FIRST, get the explicit names, skipping prefixes.
+    set result {}
+
+    foreach name [array names Snit_methodInfo $pattern] {
+        if {[lindex $Snit_methodInfo($name) 0] != 1} {
+            lappend result $name
+        }
+    }
+
+    # NEXT, add any from the cache that aren't explicit.
+    if {[info exists Snit_methodInfo(*)]} {
+        # First, remove "*" from the list.
+        set ndx [lsearch -exact $result "*"]
+        if {$ndx != -1} {
+            set result [lreplace $result $ndx $ndx]
+        }
+
+        foreach name [array names Snit_methodCache $pattern] {
+            if {[lsearch -exact $result $name] == -1} {
+                lappend result $name
+            }
+        }
+    }
+
+    return $result
+}
+
+# $self info args
+#
+# Returns a method's list of arguments. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.method.info.args {type selfns win self method} {
+
+    upvar ${type}::Snit_methodInfo  Snit_methodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    #parray Snit_methodInfo
+
+    if {![info exists Snit_methodInfo($method)]} {
+       return -code error "Unknown method \"$method\""
+    }
+    foreach {flag cmd component} $Snit_methodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown method \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated method \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [lrange [::info args $theproc] 4 end]
+}
+
+# $self info body
+#
+# Returns a method's body. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.method.info.body {type selfns win self method} {
+
+    upvar ${type}::Snit_methodInfo  Snit_methodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    #parray Snit_methodInfo
+
+    if {![info exists Snit_methodInfo($method)]} {
+       return -code error "Unknown method \"$method\""
+    }
+    foreach {flag cmd component} $Snit_methodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown method \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated method \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [RT.body [::info body $theproc]]
+}
+
+# $self info default
+#
+# Returns a method's list of arguments. does not work for delegated
+# methods, nor for the internal dispatch methods of multi-word
+# methods.
+
+proc ::snit::RT.method.info.default {type selfns win self method aname dvar} {
+    upvar 1 $dvar def
+    upvar ${type}::Snit_methodInfo  Snit_methodInfo
+
+    # Snit_methodInfo: method -> list (flag cmd component)
+
+    # flag      : 1 -> internal dispatcher for multi-word method.
+    #             0 -> regular method
+    #
+    # cmd       : template mapping from method to command prefix, may
+    #             contain placeholders for various pieces of information.
+    #
+    # component : is empty for normal methods.
+
+    if {![info exists Snit_methodInfo($method)]} {
+       return -code error "Unknown method \"$method\""
+    }
+    foreach {flag cmd component} $Snit_methodInfo($method) break
+    if {$flag} {
+       return -code error "Unknown method \"$method\""
+    }
+    if {$component != ""} {
+       return -code error "Delegated method \"$method\""
+    }
+
+    set map     [list %m $method %j [join $method _] %t $type %n $selfns %w $win %s $self]
+    set theproc [lindex [string map $map $cmd] 0]
+    return [::info default $theproc $aname def]
+}
+
+# $self info vars
+#
+# Returns the instance's instance variables
+proc ::snit::RT.method.info.vars {type selfns win self {pattern *}} {
+    set result {}
+    foreach name [info vars "${selfns}::$pattern"] {
+        set tail [namespace tail $name]
+        if {![string match "Snit_*" $tail]} {
+            lappend result $name
+        }
+    }
+
+    return $result
+}
+
+# $self info options
+#
+# Returns a list of the names of the instance's options
+proc ::snit::RT.method.info.options {type selfns win self {pattern *}} {
+    variable ${type}::Snit_optionInfo
+
+    # First, get the local and explicitly delegated options
+    set result [concat $Snit_optionInfo(local) $Snit_optionInfo(delegated)]
+
+    # If "configure" works as for Tk widgets, add the resulting
+    # options to the list.  Skip excepted options
+    if {"" != $Snit_optionInfo(starcomp)} {
+        upvar ${selfns}::Snit_components Snit_components
+        set logicalName $Snit_optionInfo(starcomp)
+        set comp $Snit_components($logicalName)
+
+        if {![catch {$comp configure} records]} {
+            foreach record $records {
+                set opt [lindex $record 0]
+                if {[lsearch -exact $result $opt] == -1 &&
+                    [lsearch -exact $Snit_optionInfo(except) $opt] == -1} {
+                    lappend result $opt
+                }
+            }
+        }
+    }
+
+    # Next, apply the pattern
+    set names {}
+
+    foreach name $result {
+        if {[string match $pattern $name]} {
+            lappend names $name
+        }
+    }
+
+    return $names
+}
+
+proc ::snit::RT.body {body} {
+    regsub -all ".*# END snit method prolog\n" $body {} body
+    return $body
+}