summaryrefslogtreecommitdiffstats
path: root/tools/tclOOScript.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tools/tclOOScript.tcl')
-rw-r--r--tools/tclOOScript.tcl798
1 files changed, 0 insertions, 798 deletions
diff --git a/tools/tclOOScript.tcl b/tools/tclOOScript.tcl
deleted file mode 100644
index 0b75882..0000000
--- a/tools/tclOOScript.tcl
+++ /dev/null
@@ -1,798 +0,0 @@
-# tclOOScript.h --
-#
-# This file contains support scripts for TclOO. They are defined here so
-# that the code can be definitely run even in safe interpreters; TclOO's
-# core setup is safe.
-#
-# Copyright © 2012-2019 Donal K. Fellows
-# Copyright © 2013 Andreas Kupries
-# Copyright © 2017 Gerald Lester
-#
-# See the file "license.terms" for information on usage and redistribution of
-# this file, and for a DISCLAIMER OF ALL WARRANTIES.
-
-::namespace eval ::oo {
- ::namespace path {}
-
- #
- # Commands that are made available to objects by default.
- #
- namespace eval Helpers {
- namespace path {}
-
- # ------------------------------------------------------------------
- #
- # callback, mymethod --
- #
- # Create a script prefix that calls a method on the current
- # object. Same operation, two names.
- #
- # ------------------------------------------------------------------
-
- proc callback {method args} {
- list [uplevel 1 {::namespace which my}] $method {*}$args
- }
-
- # Make the [callback] command appear as [mymethod] too.
- namespace export callback
- namespace eval tmp {namespace import ::oo::Helpers::callback}
- namespace export -clear
- rename tmp::callback mymethod
- namespace delete tmp
-
- # ------------------------------------------------------------------
- #
- # classvariable --
- #
- # Link to a variable in the class of the current object.
- #
- # ------------------------------------------------------------------
-
- proc classvariable {name args} {
- # Get a reference to the class's namespace
- set ns [info object namespace [uplevel 1 {self class}]]
- # Double up the list of variable names
- foreach v [list $name {*}$args] {
- if {[string match *(*) $v]} {
- set reason "can't create a scalar variable that looks like an array element"
- return -code error -errorcode {TCL UPVAR LOCAL_ELEMENT} \
- [format {bad variable name "%s": %s} $v $reason]
- }
- if {[string match *::* $v]} {
- set reason "can't create a local variable with a namespace separator in it"
- return -code error -errorcode {TCL UPVAR INVERTED} \
- [format {bad variable name "%s": %s} $v $reason]
- }
- lappend vs $v $v
- }
- # Lastly, link the caller's local variables to the class's variables
- tailcall namespace upvar $ns {*}$vs
- }
-
- # ------------------------------------------------------------------
- #
- # link --
- #
- # Make a command that invokes a method on the current object.
- # The name of the command and the name of the method match by
- # default.
- #
- # ------------------------------------------------------------------
-
- proc link {args} {
- set ns [uplevel 1 {::namespace current}]
- foreach link $args {
- if {[llength $link] == 2} {
- lassign $link src dst
- } elseif {[llength $link] == 1} {
- lassign $link src
- set dst $src
- } else {
- return -code error -errorcode {TCL OO CMDLINK_FORMAT} \
- "bad link description; must only have one or two elements"
- }
- if {![string match ::* $src]} {
- set src [string cat $ns :: $src]
- }
- interp alias {} $src {} ${ns}::my $dst
- trace add command ${ns}::my delete [list \
- ::oo::UnlinkLinkedCommand $src]
- }
- return
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # UnlinkLinkedCommand --
- #
- # Callback used to remove linked command when the underlying mechanism
- # that supports it is deleted.
- #
- # ----------------------------------------------------------------------
-
- proc UnlinkLinkedCommand {cmd args} {
- if {[namespace which $cmd] ne {}} {
- rename $cmd {}
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # DelegateName --
- #
- # Utility that gets the name of the class delegate for a class. It's
- # trivial, but makes working with them much easier as delegate names are
- # intentionally hard to create by accident.
- #
- # ----------------------------------------------------------------------
-
- proc DelegateName {class} {
- string cat [info object namespace $class] {:: oo ::delegate}
- }
-
- # ----------------------------------------------------------------------
- #
- # MixinClassDelegates --
- #
- # Support code called *after* [oo::define] inside the constructor of a
- # class that patches in the appropriate class delegates.
- #
- # ----------------------------------------------------------------------
-
- proc MixinClassDelegates {class} {
- if {![info object isa class $class]} {
- return
- }
- set delegate [DelegateName $class]
- if {![info object isa class $delegate]} {
- return
- }
- foreach c [info class superclass $class] {
- set d [DelegateName $c]
- if {![info object isa class $d]} {
- continue
- }
- define $delegate ::oo::define::superclass -appendifnew $d
- }
- objdefine $class ::oo::objdefine::mixin -appendifnew $delegate
- }
-
- # ----------------------------------------------------------------------
- #
- # UpdateClassDelegatesAfterClone --
- #
- # Support code that is like [MixinClassDelegates] except for when a
- # class is cloned.
- #
- # ----------------------------------------------------------------------
-
- proc UpdateClassDelegatesAfterClone {originObject targetObject} {
- # Rebuild the class inheritance delegation class
- set originDelegate [DelegateName $originObject]
- set targetDelegate [DelegateName $targetObject]
- if {
- [info object isa class $originDelegate]
- && ![info object isa class $targetDelegate]
- } then {
- copy $originDelegate $targetDelegate
- objdefine $targetObject ::oo::objdefine::mixin -set \
- {*}[lmap c [info object mixin $targetObject] {
- if {$c eq $originDelegate} {set targetDelegate} {set c}
- }]
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::define::classmethod --
- #
- # Defines a class method. See define(n) for details.
- #
- # Note that the ::oo::define namespace is semi-public and a bit weird
- # anyway, so we don't regard the namespace path as being under control:
- # fully qualified names are used for everything.
- #
- # ----------------------------------------------------------------------
-
- proc define::classmethod {name args} {
- # Create the method on the class if the caller gave arguments and body
- ::set argc [::llength [::info level 0]]
- ::if {$argc == 3} {
- ::return -code error -errorcode {TCL WRONGARGS} [::format \
- {wrong # args: should be "%s name ?args body?"} \
- [::lindex [::info level 0] 0]]
- }
- ::set cls [::uplevel 1 self]
- ::if {$argc == 4} {
- ::oo::define [::oo::DelegateName $cls] method $name {*}$args
- }
- # Make the connection by forwarding
- ::tailcall forward $name myclass $name
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::define::initialise, oo::define::initialize --
- #
- # Do specific initialisation for a class. See define(n) for details.
- #
- # Note that the ::oo::define namespace is semi-public and a bit weird
- # anyway, so we don't regard the namespace path as being under control:
- # fully qualified names are used for everything.
- #
- # ----------------------------------------------------------------------
-
- proc define::initialise {body} {
- ::set clsns [::info object namespace [::uplevel 1 self]]
- ::tailcall apply [::list {} $body $clsns]
- }
-
- # Make the [initialise] definition appear as [initialize] too
- namespace eval define {
- ::namespace export initialise
- ::namespace eval tmp {::namespace import ::oo::define::initialise}
- ::namespace export -clear
- ::rename tmp::initialise initialize
- ::namespace delete tmp
- }
-
- # ----------------------------------------------------------------------
- #
- # Slot --
- #
- # The class of slot operations, which are basically lists at the low
- # level of TclOO; this provides a more consistent interface to them.
- #
- # ----------------------------------------------------------------------
-
- define Slot {
- # ------------------------------------------------------------------
- #
- # Slot Get --
- #
- # Basic slot getter. Retrieves the contents of the slot.
- # Particular slots must provide concrete non-erroring
- # implementation.
- #
- # ------------------------------------------------------------------
-
- method Get -unexport {} {
- return -code error -errorcode {TCL OO ABSTRACT_SLOT} "unimplemented"
- }
-
- # ------------------------------------------------------------------
- #
- # Slot Set --
- #
- # Basic slot setter. Sets the contents of the slot. Particular
- # slots must provide concrete non-erroring implementation.
- #
- # ------------------------------------------------------------------
-
- method Set -unexport list {
- return -code error -errorcode {TCL OO ABSTRACT_SLOT} "unimplemented"
- }
-
- # ------------------------------------------------------------------
- #
- # Slot Resolve --
- #
- # Helper that lets a slot convert a list of arguments of a
- # particular type to their canonical forms. Defaults to doing
- # nothing (suitable for simple strings).
- #
- # ------------------------------------------------------------------
-
- method Resolve -unexport list {
- return $list
- }
-
- # ------------------------------------------------------------------
- #
- # Slot -set, -append, -clear, --default-operation --
- #
- # Standard public slot operations. If a slot can't figure out
- # what method to call directly, it uses --default-operation.
- #
- # ------------------------------------------------------------------
-
- method -set -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- tailcall my Set $args
- }
- method -append -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [list {*}$current {*}$args]
- }
- method -appendifnew -export args {
- set my [namespace which my]
- set current [uplevel 1 [list $my Get]]
- foreach a $args {
- set a [uplevel 1 [list $my Resolve $a]]
- if {$a ni $current} {
- lappend current $a
- }
- }
- tailcall my Set $current
- }
- method -clear -export {} {tailcall my Set {}}
- method -prepend -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [list {*}$args {*}$current]
- }
- method -remove -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [lmap val $current {
- if {$val in $args} continue else {set val}
- }]
- }
-
- # Default handling
- forward --default-operation my -append
- method unknown -unexport {args} {
- set def --default-operation
- if {[llength $args] == 0} {
- tailcall my $def
- } elseif {![string match -* [lindex $args 0]]} {
- tailcall my $def {*}$args
- }
- next {*}$args
- }
-
- # Hide destroy
- unexport destroy
- }
-
- # Set the default operation differently for these slots
- objdefine define::superclass forward --default-operation my -set
- objdefine define::mixin forward --default-operation my -set
- objdefine objdefine::mixin forward --default-operation my -set
-
- # ----------------------------------------------------------------------
- #
- # oo::object <cloned> --
- #
- # Handler for cloning objects that clones basic bits (only!) of the
- # object's namespace. Non-procedures, traces, sub-namespaces, etc. need
- # more complex (and class-specific) handling.
- #
- # ----------------------------------------------------------------------
-
- define object method <cloned> -unexport {originObject} {
- # Copy over the procedures from the original namespace
- foreach p [info procs [info object namespace $originObject]::*] {
- set args [info args $p]
- set idx -1
- foreach a $args {
- if {[info default $p $a d]} {
- lset args [incr idx] [list $a $d]
- } else {
- lset args [incr idx] [list $a]
- }
- }
- set b [info body $p]
- set p [namespace tail $p]
- proc $p $args $b
- }
- # Copy over the variables from the original namespace
- foreach v [info vars [info object namespace $originObject]::*] {
- upvar 0 $v vOrigin
- namespace upvar [namespace current] [namespace tail $v] vNew
- if {[info exists vOrigin]} {
- if {[array exists vOrigin]} {
- array set vNew [array get vOrigin]
- } else {
- set vNew $vOrigin
- }
- }
- }
- # General commands, sub-namespaces and advancd variable config (traces,
- # etc) are *not* copied over. Classes that want that should do it
- # themselves.
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::class <cloned> --
- #
- # Handler for cloning classes, which fixes up the delegates.
- #
- # ----------------------------------------------------------------------
-
- define class method <cloned> -unexport {originObject} {
- next $originObject
- # Rebuild the class inheritance delegation class
- ::oo::UpdateClassDelegatesAfterClone $originObject [self]
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::singleton --
- #
- # A metaclass that is used to make classes that only permit one instance
- # of them to exist. See singleton(n).
- #
- # ----------------------------------------------------------------------
-
- class create singleton {
- superclass class
- variable object
- unexport create createWithNamespace
- method new args {
- if {![info exists object] || ![info object isa object $object]} {
- set object [next {*}$args]
- ::oo::objdefine $object {
- method destroy {} {
- ::return -code error -errorcode {TCL OO SINGLETON} \
- "may not destroy a singleton object"
- }
- method <cloned> -unexport {originObject} {
- ::return -code error -errorcode {TCL OO SINGLETON} \
- "may not clone a singleton object"
- }
- }
- }
- return $object
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::abstract --
- #
- # A metaclass that is used to make classes that can't be directly
- # instantiated. See abstract(n).
- #
- # ----------------------------------------------------------------------
-
- class create abstract {
- superclass class
- unexport create createWithNamespace new
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::configuresupport --
- #
- # Namespace that holds all the implementation details of TIP #558.
- # Also includes the commands:
- #
- # * readableproperties
- # * writableproperties
- # * objreadableproperties
- # * objwritableproperties
- #
- # Those are all slot implementations that provide access to the C layer
- # of property support (i.e., very fast cached lookup of property names).
- #
- # ----------------------------------------------------------------------
-
- ::namespace eval configuresupport {
- namespace path ::tcl
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport --
- #
- # A metaclass that is used to make classes that can be configured.
- #
- # ------------------------------------------------------------------
-
- proc PropertyImpl {readslot writeslot args} {
- for {set i 0} {$i < [llength $args]} {incr i} {
- # Parse the property name
- set prop [lindex $args $i]
- if {[string match "-*" $prop]} {
- return -code error -level 2 \
- -errorcode {TCL OO PROPERTY_FORMAT} \
- "bad property name \"$prop\": must not begin with -"
- }
- if {$prop ne [list $prop]} {
- return -code error -level 2 \
- -errorcode {TCL OO PROPERTY_FORMAT} \
- "bad property name \"$prop\": must be a simple word"
- }
- if {[string first "::" $prop] != -1} {
- return -code error -level 2 \
- -errorcode {TCL OO PROPERTY_FORMAT} \
- "bad property name \"$prop\": must not contain namespace separators"
- }
- if {[string match {*[()]*} $prop]} {
- return -code error -level 2 \
- -errorcode {TCL OO PROPERTY_FORMAT} \
- "bad property name \"$prop\": must not contain parentheses"
- }
- set realprop [string cat "-" $prop]
- set getter [format {::set [my varname %s]} $prop]
- set setter [format {::set [my varname %s] $value} $prop]
- set kind readwrite
-
- # Parse the extra options
- while {[set next [lindex $args [expr {$i + 1}]]
- string match "-*" $next]} {
- set arg [lindex $args [incr i 2]]
- switch [prefix match -error [list -level 2 -errorcode \
- [list TCL LOOKUP INDEX option $next]] {-get -kind -set} $next] {
- -get {
- if {$i >= [llength $args]} {
- return -code error -level 2 \
- -errorcode {TCL WRONGARGS} \
- "missing body to go with -get option"
- }
- set getter $arg
- }
- -set {
- if {$i >= [llength $args]} {
- return -code error -level 2 \
- -errorcode {TCL WRONGARGS} \
- "missing body to go with -set option"
- }
- set setter $arg
- }
- -kind {
- if {$i >= [llength $args]} {
- return -code error -level 2\
- -errorcode {TCL WRONGARGS} \
- "missing kind value to go with -kind option"
- }
- set kind [prefix match -message "kind" -error [list \
- -level 2 \
- -errorcode [list TCL LOOKUP INDEX kind $arg]] {
- readable readwrite writable
- } $arg]
- }
- }
- }
-
- # Install the option
- set reader <ReadProp$realprop>
- set writer <WriteProp$realprop>
- switch $kind {
- readable {
- uplevel 2 [list $readslot -append $realprop]
- uplevel 2 [list $writeslot -remove $realprop]
- uplevel 2 [list method $reader -unexport {} $getter]
- }
- writable {
- uplevel 2 [list $readslot -remove $realprop]
- uplevel 2 [list $writeslot -append $realprop]
- uplevel 2 [list method $writer -unexport {value} $setter]
- }
- readwrite {
- uplevel 2 [list $readslot -append $realprop]
- uplevel 2 [list $writeslot -append $realprop]
- uplevel 2 [list method $reader -unexport {} $getter]
- uplevel 2 [list method $writer -unexport {value} $setter]
- }
- }
- }
- }
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport::configurableclass,
- # oo::configuresupport::configurableobject --
- #
- # Namespaces used as implementation vectors for oo::define and
- # oo::objdefine when the class/instance is configurable.
- #
- # ------------------------------------------------------------------
-
- namespace eval configurableclass {
- ::proc property args {
- ::oo::configuresupport::PropertyImpl \
- ::oo::configuresupport::readableproperties \
- ::oo::configuresupport::writableproperties {*}$args
- }
- # Plural alias just in case; deliberately NOT documented!
- ::proc properties args {::tailcall property {*}$args}
- ::namespace path ::oo::define
- ::namespace export property
- }
-
- namespace eval configurableobject {
- ::proc property args {
- ::oo::configuresupport::PropertyImpl \
- ::oo::configuresupport::objreadableproperties \
- ::oo::configuresupport::objwritableproperties {*}$args
- }
- # Plural alias just in case; deliberately NOT documented!
- ::proc properties args {::tailcall property {*}$args}
- ::namespace path ::oo::objdefine
- ::namespace export property
- }
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport::ReadAll --
- #
- # The implementation of [$o configure] with no extra arguments.
- #
- # ------------------------------------------------------------------
-
- proc ReadAll {object my} {
- set result {}
- foreach prop [info object properties $object -all -readable] {
- try {
- dict set result $prop [$my <ReadProp$prop>]
- } on error {msg opt} {
- dict set opt -level 2
- return -options $opt $msg
- } on return {msg opt} {
- dict incr opt -level 2
- return -options $opt $msg
- } on break {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property getter for $prop did a break"
- } on continue {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property getter for $prop did a continue"
- }
- }
- return $result
- }
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport::ReadOne --
- #
- # The implementation of [$o configure -prop] with that single
- # extra argument.
- #
- # ------------------------------------------------------------------
-
- proc ReadOne {object my propertyName} {
- set props [info object properties $object -all -readable]
- try {
- set prop [prefix match -message "property" $props $propertyName]
- } on error {msg} {
- catch {
- set wps [info object properties $object -all -writable]
- set wprop [prefix match $wps $propertyName]
- set msg "property \"$wprop\" is write only"
- }
- return -code error -level 2 -errorcode [list \
- TCL LOOKUP INDEX property $propertyName] $msg
- }
- try {
- set value [$my <ReadProp$prop>]
- } on error {msg opt} {
- dict set opt -level 2
- return -options $opt $msg
- } on return {msg opt} {
- dict incr opt -level 2
- return -options $opt $msg
- } on break {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property getter for $prop did a break"
- } on continue {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property getter for $prop did a continue"
- }
- return $value
- }
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport::WriteMany --
- #
- # The implementation of [$o configure -prop val ?-prop val...?].
- #
- # ------------------------------------------------------------------
-
- proc WriteMany {object my setterMap} {
- set props [info object properties $object -all -writable]
- foreach {prop value} $setterMap {
- try {
- set prop [prefix match -message "property" $props $prop]
- } on error {msg} {
- catch {
- set rps [info object properties $object -all -readable]
- set rprop [prefix match $rps $prop]
- set msg "property \"$rprop\" is read only"
- }
- return -code error -level 2 -errorcode [list \
- TCL LOOKUP INDEX property $prop] $msg
- }
- try {
- $my <WriteProp$prop> $value
- } on error {msg opt} {
- dict set opt -level 2
- return -options $opt $msg
- } on return {msg opt} {
- dict incr opt -level 2
- return -options $opt $msg
- } on break {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property setter for $prop did a break"
- } on continue {} {
- return -code error -level 2 -errorcode {TCL OO SHENANIGANS} \
- "property setter for $prop did a continue"
- }
- }
- return
- }
-
- # ------------------------------------------------------------------
- #
- # oo::configuresupport::configurable --
- #
- # The class that contains the implementation of the actual
- # 'configure' method (mixed into actually configurable classes).
- # Great care needs to be taken in these methods as they are
- # potentially used in classes where the current namespace is set
- # up very strangely.
- #
- # ------------------------------------------------------------------
-
- ::oo::class create configurable {
- private variable my
- #
- # configure --
- # Method for providing client access to the property mechanism.
- # Has a user-facing API similar to that of [chan configure].
- #
- method configure -export args {
- ::if {![::info exists my]} {
- ::set my [::namespace which my]
- }
- ::if {[::llength $args] == 0} {
- # Read all properties
- ::oo::configuresupport::ReadAll [self] $my
- } elseif {[::llength $args] == 1} {
- # Read a single property
- ::oo::configuresupport::ReadOne [self] $my \
- [::lindex $args 0]
- } elseif {[::llength $args] % 2 == 0} {
- # Set properties, one or several
- ::oo::configuresupport::WriteMany [self] $my $args
- } else {
- # Invalid call
- ::return -code error -errorcode {TCL WRONGARGS} \
- [::format {wrong # args: should be "%s"} \
- "[self] configure ?-option value ...?"]
- }
- }
-
- definitionnamespace -instance configurableobject
- definitionnamespace -class configurableclass
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::configurable --
- #
- # A metaclass that is used to make classes that can be configured in
- # their creation phase (and later too). All the metaclass itself does is
- # arrange for the class created to have a 'configure' method and for
- # oo::define and oo::objdefine (on the class and its instances) to have
- # a property definition for setting things up for 'configure'.
- #
- # ----------------------------------------------------------------------
-
- class create configurable {
- superclass class
-
- constructor {{definitionScript ""}} {
- next {mixin ::oo::configuresupport::configurable}
- next $definitionScript
- }
-
- definitionnamespace -class configuresupport::configurableclass
- }
-}
-
-# Local Variables:
-# mode: tcl
-# c-basic-offset: 4
-# fill-column: 78
-# End: