summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/oodialect/oodialect.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tcllib/modules/oodialect/oodialect.tcl')
-rw-r--r--tcllib/modules/oodialect/oodialect.tcl245
1 files changed, 245 insertions, 0 deletions
diff --git a/tcllib/modules/oodialect/oodialect.tcl b/tcllib/modules/oodialect/oodialect.tcl
new file mode 100644
index 0000000..ee5fc8a
--- /dev/null
+++ b/tcllib/modules/oodialect/oodialect.tcl
@@ -0,0 +1,245 @@
+###
+# oodialect.tcl
+#
+# Copyright (c) 2015 Sean Woods, Donald K Fellows
+#
+# BSD License
+###
+# @@ Meta Begin
+# Package oo::dialect 0.2
+# Meta platform tcl
+# Meta summary A utility for defining a domain specific language for TclOO systems
+# Meta description This package allows developers to generate
+# Meta description domain specific languages to describe TclOO
+# Meta description classes and objects.
+# Meta category TclOO
+# Meta subject oodialect
+# Meta require {Tcl 8.6}
+# Meta author Sean Woods
+# Meta author Donald K. Fellows
+# Meta license BSD
+# @@ Meta End
+
+namespace eval ::oo::dialect {
+ namespace export create
+}
+
+# A stack of class names
+proc ::oo::dialect::Push {class} {
+ ::variable class_stack
+ lappend class_stack $class
+}
+proc ::oo::dialect::Peek {} {
+ ::variable class_stack
+ return [lindex $class_stack end]
+}
+proc ::oo::dialect::Pop {} {
+ ::variable class_stack
+ set class_stack [lrange $class_stack 0 end-1]
+}
+
+###
+# This proc will generate a namespace, a "mother of all classes", and a
+# rudimentary set of policies for this dialect.
+###
+proc ::oo::dialect::create {name {parent ""}} {
+ set NSPACE [NSNormalize [uplevel 1 {namespace current}] $name]
+ ::namespace eval $NSPACE {::namespace eval define {}}
+ ###
+ # Build the "define" namespace
+ ###
+ if {$parent eq ""} {
+ ###
+ # With no "parent" language, begin with all of the keywords in
+ # oo::define
+ ###
+ foreach command [info commands ::oo::define::*] {
+ set procname [namespace tail $command]
+ interp alias {} ${NSPACE}::define::$procname {} \
+ ::oo::dialect::DefineThunk $procname
+ }
+ # Create an empty dynamic_methods proc
+ proc ${NSPACE}::dynamic_methods {class} {}
+ namespace eval $NSPACE {
+ ::namespace export dynamic_methods
+ ::namespace eval define {::namespace export *}
+ }
+ set ANCESTORS {}
+ } else {
+ ###
+ # If we have a parent language, that language already has the
+ # [oo::define] keywords as well as additional keywords and behaviors.
+ # We should begin with that
+ ###
+ set pnspace [NSNormalize [uplevel 1 {namespace current}] $parent]
+ apply [list parent {
+ ::namespace export dynamic_methods
+ ::namespace import -force ${parent}::dynamic_methods
+ } $NSPACE] $pnspace
+ apply [list parent {
+ ::namespace import -force ${parent}::define::*
+ ::namespace export *
+ } ${NSPACE}::define] $pnspace
+ set ANCESTORS [list ${pnspace}::object]
+ }
+ ###
+ # Build our dialect template functions
+ ###
+
+ proc ${NSPACE}::define {oclass args} [string map [list %NSPACE% $NSPACE] {
+ ###
+ # To facilitate library reloading, allow
+ # a dialect to create a class from DEFINE
+ ###
+ set class [::oo::dialect::NSNormalize [uplevel 1 {namespace current}] $oclass]
+ if {[info commands $class] eq {}} {
+ %NSPACE%::class create $class {*}${args}
+ } else {
+ ::oo::dialect::Define %NSPACE% $class {*}${args}
+ }
+ }]
+ interp alias {} ${NSPACE}::define::current_class {} \
+ ::oo::dialect::Peek
+ interp alias {} ${NSPACE}::define::aliases {} \
+ ::oo::dialect::Aliases $NSPACE
+ interp alias {} ${NSPACE}::define::superclass {} \
+ ::oo::dialect::SuperClass $NSPACE
+
+ if {[info command ${NSPACE}::class] ne {}} {
+ ::rename ${NSPACE}::class {}
+ }
+ ###
+ # Build the metaclass for our language
+ ###
+ ::oo::class create ${NSPACE}::class {
+ superclass ::oo::dialect::MotherOfAllMetaClasses
+ }
+ # Wire up the create method to add in the extra argument we need; the
+ # MotherOfAllMetaClasses will know what to do with it.
+ ::oo::objdefine ${NSPACE}::class \
+ method create {name {definitionScript ""}} \
+ "next \$name [list ${NSPACE}::define] \$definitionScript"
+
+ ###
+ # Build the mother of all classes. Note that $ANCESTORS is already
+ # guaranteed to be a list in canonical form.
+ ###
+ uplevel #0 [string map [list %NSPACE% [list $NSPACE] %name% [list $name] %ANCESTORS% $ANCESTORS] {
+ %NSPACE%::class create %NSPACE%::object {
+ superclass %ANCESTORS%
+ # Put MOACish stuff in here
+ }
+ }]
+}
+
+# Support commands; not intended to be called directly.
+proc ::oo::dialect::NSNormalize {namespace qualname} {
+ if {![string match ::* $qualname]} {
+ set qualname ${namespace}::$qualname
+ }
+ regsub -all {::+} $qualname "::"
+}
+
+proc ::oo::dialect::DefineThunk {target args} {
+ tailcall ::oo::define [Peek] $target {*}$args
+}
+
+proc ::oo::dialect::Canonical {namespace NSpace class} {
+ namespace upvar $namespace cname cname
+ if {[string match ::* $class]} {
+ return $class
+ }
+ if {[info exists cname($class)]} {
+ return $cname($class)
+ }
+ if {[info exists ::oo::dialect::cname($class)]} {
+ return $::oo::dialect::cname($class)
+ }
+ foreach item [list "${NSpace}::$class" "::$class"] {
+ if {[info command $item] ne {}} {
+ return $item
+ }
+ }
+ return ${NSpace}::$class
+}
+
+###
+# Implementation of the languages' define command
+###
+proc ::oo::dialect::Define {namespace class args} {
+ Push $class
+ try {
+ if {[llength $args]==1} {
+ namespace eval ${namespace}::define [lindex $args 0]
+ } else {
+ ${namespace}::define::[lindex $args 0] {*}[lrange $args 1 end]
+ }
+ ${namespace}::dynamic_methods $class
+ } finally {
+ Pop
+ }
+}
+
+###
+# Implementation of how we specify the other names that this class will answer
+# to
+###
+
+proc ::oo::dialect::Aliases {namespace args} {
+ set class [Peek]
+ namespace upvar $namespace cname cname
+ set NSpace [join [lrange [split $class ::] 1 end-2] ::]
+ set cname($class) $class
+ foreach name $args {
+ set alias $name
+ #set alias [NSNormalize $NSpace $name]
+ # Add a local metaclass reference
+ set cname($alias) $class
+ if {![info exists ::oo::dialect::cname($alias)]} {
+ ##
+ # Add a global reference, first come, first served
+ ##
+ set ::oo::dialect::cname($alias) $class
+ }
+ }
+}
+
+###
+# Implementation of a superclass keyword which will enforce the inheritance of
+# our language's mother of all classes
+###
+
+proc ::oo::dialect::SuperClass {namespace args} {
+ set class [Peek]
+ namespace upvar $namespace class_info class_info
+ dict set class_info($class) superclass 1
+ set ::oo::dialect::cname($class) $class
+ set NSpace [join [lrange [split $class ::] 1 end-2] ::]
+ set unique {}
+ foreach item $args {
+ set Item [Canonical $namespace $NSpace $item]
+ dict set unique $Item $item
+ }
+ set root ${namespace}::object
+ if {$class ne $root} {
+ dict set unique $root $root
+ }
+ tailcall ::oo::define $class superclass {*}[dict keys $unique]
+}
+
+###
+# Implementation of the common portions of the the metaclass for our
+# languages.
+###
+
+::oo::class create ::oo::dialect::MotherOfAllMetaClasses {
+ superclass ::oo::class
+ constructor {define definitionScript} {
+ $define [self] {
+ superclass
+ }
+ $define [self] $definitionScript
+ }
+}
+
+package provide oo::dialect 0.3 \ No newline at end of file