Index: TODO =================================================================== diff -u -rb368199d08236c6fefd2d48a3212c3014929a839 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- TODO (.../TODO) (revision b368199d08236c6fefd2d48a3212c3014929a839) +++ TODO (.../TODO) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1256,9 +1256,9 @@ - new parameter option "convert" to signal that an application specific parameter checker should convert the value (takes the result of the methods as conversion result) -- added parameters for slots "allowemtpy" and "convert" +- added parameters for slots "allowempty" and "convert" - extended regression test -- added handling of parameter options "allowemtpy" and "convert" +- added handling of parameter options "allowempty" and "convert" in createFromParameterSyntax - renamed slot attribute "noforwarder" to "nosetter" @@ -2992,7 +2992,7 @@ - nsf.c: * new file nsfPointer.c * generic new value checker ConvertToPointer to handle c-level - conversions (which can be registed from nsf extensions) + conversions (which can be registered from nsf extensions) * extern defined interface for the pointer converter: Nsf_PointerTypeLookup(), Nsf_PointerTypeRegister(), Nsf_PointerAdd(), Nsf_PointerDelete(), Index: doc/Object.3 =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/Object.3 (.../Object.3) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/Object.3 (.../Object.3) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -408,7 +408,7 @@ .TP \fBnx::Object\fR \fBnew\fR ?\fB-object-mixins\fR \fImixinSpec\fR? ?\fB-class\fR \fInewClassName\fR? ?\fB-object-filters\fR \fIfilterSpec\fR? ?\fIinitBlock\fR? To create a direct instance of \fBnx::Object\fR having an -automatically assigned, implict object name, use \fBnew\fR on \fBnx::Object\fR\&. Note +automatically assigned, implicit object name, use \fBnew\fR on \fBnx::Object\fR\&. Note that \fBnew\fR is defined by \fBnx::Class\fR and is available to \fBnx::Object\fR being an instance of \fBnx::Class\fR\&. Using \fBnew\fR allows for creating anonymous, inline objects, for example\&. Index: doc/Object.man =================================================================== diff -u -rbf42a9bc9fffff123de76674ddabb3125eb1faa0 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/Object.man (.../Object.man) (revision bf42a9bc9fffff123de76674ddabb3125eb1faa0) +++ doc/Object.man (.../Object.man) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -93,7 +93,7 @@ [call [cmd nx::Object] [method new] [opt "[option -object-mixins] [arg mixinSpec]"] [opt "[option -class] [arg newClassName]"] [opt "[option -object-filters] [arg filterSpec]"] [opt [arg initBlock]]] To create a direct instance of [cmd nx::Object] having an -automatically assigned, implict object name, use [method new] on [cmd nx::Object]. Note +automatically assigned, implicit object name, use [method new] on [cmd nx::Object]. Note that [method new] is defined by [cmd nx::Class] and is available to [cmd nx::Object] being an instance of [cmd nx::Class]. Using [method new] allows for creating anonymous, inline objects, for example. Index: doc/example-scripts/bagel.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/bagel.html (.../bagel.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/bagel.html (.../bagel.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,1117 +1,1117 @@ - - - - - -Listing of doc/example-scripts/bagel.tcl - - - - - -
-
-
-

This example is a straight translation of the OTcl Tutorial -http://www.isi.edu/nsnam/otcl/doc/tutorial.html to NX. It serves as -a very short intro to the basic elements of scripting with NX and -provides a comparison study to OTcl.

-
-
-
package req nx
-nx::test configure -count 1
-

Suppose we need to work with many bagels in our application. We -might start by creating a Bagel class.

-
-
-
% nx::Class create Bagel
-::Bagel
-

We can now create bagels and keep track of them using the info -method.

-
-
-
% Bagel create abagel
-::abagel
-
-% abagel info class
-::Bagel
-
-% Bagel info instances
-::abagel
-

Of course, bagels don’t do much yet. They should remember whether -they’ve been toasted. We can create and access an instance variable -by defining an property for the class. All instance variables are -per default public in the sense of C++.

-
-
-
% Bagel property {toasted 0}
-

Since abagel was created before the definition of the property we -have to set the default value for it using the setter method. Again, -the info method helps us keep track of things.

-
-
-
% abagel info vars
-
-% abagel configure -toasted 0
-
-% abagel info vars
-toasted
-
-% abagel cget -toasted
-0
-

But we really want them to begin in an untoasted state to start -with.

-
-
-
% Bagel create bagel2
-::bagel2
-
-% bagel2 info vars
-toasted
-
-% bagel2 cget -toasted
-0
-

Our bagels now remember whether they’ve been toasted. Let is -recreate the first one.

-
-
-
% lsort [Bagel info instances]
-::abagel ::bagel2
-
-% ::abagel destroy
-
-% Bagel info instances
-::bagel2
-
-% Bagel create abagel
-::abagel
-

Now we’re ready to add a method to bagels so that we can toast -them. Methods have an argument list and body like regular -Tcl procs. Here’s the toast method.

-
-
-
% Bagel public method toast {} {
-      if {[incr :toasted] > 1} then {
-        error "something's burning!"
-      }
-}
-::nsf::classes::Bagel::toast
-

The defined methods can be queried with info. We see as well the -setter method for the variable toasted.

-
-
-
% Bagel info methods
-toast
-

Aside from setting the toasted variable, the body of the toast -method demonstrates how to access instance variables by using a -leading colon in the name.

-

We invoke the toast method on bagels in the same way we use the -info and destroy methods that were provided by the system. That -is, there is no distinction between user and system methods.

-
-
-
% abagel toast
-% abagel toast
-something's burning!
-

Now we can add spreads to the bagels and start tasting them. If we -have bagels that aren’t topped, as well as bagels that are, we may -want to make toppable bagels a separate class. Let explore -inheritance with these two classes, starting by making a new class -SpreadableBagel that inherits from Bagel. A SpreadableBagel has an -property toppings which might have multiple values. Initially, -toppings are empty.

-
-
-
% nx::Class create SpreadableBagel -superclass Bagel {
-    :property -incremental {toppings:0..n ""}
-}
-::SpreadableBagel
-
-% SpreadableBagel cget -superclass
-::Bagel
-% SpreadableBagel info superclasses
-::Bagel
-
-% SpreadableBagel info heritage
-::Bagel ::nx::Object
-

Let’s add a taste method to bagels, splitting its functionality -between the two classes and combining it with next.

-
-
-
% Bagel public method taste {} {
-    if {${:toasted} == 0} then {
-    return raw!
-  } elseif {${:toasted} == 1} then {
-    return toasty
-  } else {
-    return burnt!
-  }
-}
-::nsf::classes::Bagel::taste
-
-% SpreadableBagel public method taste {} {
-    set t [next]
-    foreach i ${:toppings} {
-       lappend t $i
-    }
-    return $t
-}
-::nsf::classes::SpreadableBagel::taste
-
-% SpreadableBagel create abagel
-::abagel
-
-% abagel toast
-% abagel toppings add jam
-jam
-% abagel toppings add m&m
-m&m jam
-
-% abagel taste
-toasty m&m jam
-

Of course, along come sesame, onion, poppy, and a host of other -bagels, requiring us to expand our scheme. We could keep track of -flavor with an instance variable, but this may not be -appropriate. Flavor is an innate property of the bagels, and one -that can affect other behavior - you wouldn’t put jam on an onion -bagel, would you? Instead of making a class hierarchy, let’s use -multiple inheritance to make the flavor classes mixins that add a -their taste independent trait to bagels or whatever other food they -are mixed with.

-
-
-
% nx::Class create Sesame {
-    :public method taste {} {concat [next] "sesame"}
-}
-::Sesame
-
-% nx::Class create Onion {
-    :public method taste {} {concat [next] "onion"}
-}
-::Onion
-
-% nx::Class create Poppy {
-    :public method taste {} {concat [next] "poppy"}
-}
-::Poppy
-

Well, they don’t appear to do much, but the use of next allows them -to be freely mixed.

-
-
-
% nx::Class create SesameOnionBagel -superclass SpreadableBagel -mixin {Sesame Onion}
-::SesameOnionBagel
-
-% SesameOnionBagel create abagel -toppings butter
-::abagel
-
-% abagel taste
-raw! butter onion sesame
-

For multiple inheritance, the system determines a linear inheritance -ordering that respects all of the local superclass orderings. You -can examine this ordering with an info option. next follows this -ordering when it combines behavior.

-
-
-
% SesameOnionBagel info heritage
-::Sesame ::Onion ::SpreadableBagel ::Bagel ::nx::Object
-
-% abagel info precedence
-::Sesame ::Onion ::SesameOnionBagel ::SpreadableBagel ::Bagel ::nx::Object
-

We can also combine our mixins with other classes, classes that need -have nothing to do with bagels, leading to a family of chips.

-
-
-
% nx::Class create Chips {
-    :public method taste {} {return "crunchy"}
-}
-::Chips
-
-% nx::Class create OnionChips -superclass Chips -mixin Onion
-::OnionChips
-
-% OnionChips create abag
-::abag
-
-% abag taste
-crunchy onion
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/bagel.tcl + + + + + +
+
+
+

This example is a straight translation of the OTcl Tutorial +http://www.isi.edu/nsnam/otcl/doc/tutorial.html to NX. It serves as +a very short intro to the basic elements of scripting with NX and +provides a comparison study to OTcl.

+
+
+
package req nx
+nx::test configure -count 1
+

Suppose we need to work with many bagels in our application. We +might start by creating a Bagel class.

+
+
+
% nx::Class create Bagel
+::Bagel
+

We can now create bagels and keep track of them using the info +method.

+
+
+
% Bagel create abagel
+::abagel
+
+% abagel info class
+::Bagel
+
+% Bagel info instances
+::abagel
+

Of course, bagels don’t do much yet. They should remember whether +they’ve been toasted. We can create and access an instance variable +by defining an property for the class. All instance variables are +per default public in the sense of C++.

+
+
+
% Bagel property {toasted 0}
+

Since abagel was created before the definition of the property we +have to set the default value for it using the setter method. Again, +the info method helps us keep track of things.

+
+
+
% abagel info vars
+
+% abagel configure -toasted 0
+
+% abagel info vars
+toasted
+
+% abagel cget -toasted
+0
+

But we really want them to begin in an untoasted state to start +with.

+
+
+
% Bagel create bagel2
+::bagel2
+
+% bagel2 info vars
+toasted
+
+% bagel2 cget -toasted
+0
+

Our bagels now remember whether they’ve been toasted. Let is +recreate the first one.

+
+
+
% lsort [Bagel info instances]
+::abagel ::bagel2
+
+% ::abagel destroy
+
+% Bagel info instances
+::bagel2
+
+% Bagel create abagel
+::abagel
+

Now we’re ready to add a method to bagels so that we can toast +them. Methods have an argument list and body like regular +Tcl procs. Here’s the toast method.

+
+
+
% Bagel public method toast {} {
+      if {[incr :toasted] > 1} then {
+        error "something's burning!"
+      }
+}
+::nsf::classes::Bagel::toast
+

The defined methods can be queried with info. We see as well the +setter method for the variable toasted.

+
+
+
% Bagel info methods
+toast
+

Aside from setting the toasted variable, the body of the toast +method demonstrates how to access instance variables by using a +leading colon in the name.

+

We invoke the toast method on bagels in the same way we use the +info and destroy methods that were provided by the system. That +is, there is no distinction between user and system methods.

+
+
+
% abagel toast
+% abagel toast
+something's burning!
+

Now we can add spreads to the bagels and start tasting them. If we +have bagels that aren’t topped, as well as bagels that are, we may +want to make toppable bagels a separate class. Let explore +inheritance with these two classes, starting by making a new class +SpreadableBagel that inherits from Bagel. A SpreadableBagel has an +property toppings which might have multiple values. Initially, +toppings are empty.

+
+
+
% nx::Class create SpreadableBagel -superclass Bagel {
+    :property -incremental {toppings:0..n ""}
+}
+::SpreadableBagel
+
+% SpreadableBagel cget -superclass
+::Bagel
+% SpreadableBagel info superclasses
+::Bagel
+
+% SpreadableBagel info heritage
+::Bagel ::nx::Object
+

Let’s add a taste method to bagels, splitting its functionality +between the two classes and combining it with next.

+
+
+
% Bagel public method taste {} {
+    if {${:toasted} == 0} then {
+    return raw!
+  } elseif {${:toasted} == 1} then {
+    return toasty
+  } else {
+    return burnt!
+  }
+}
+::nsf::classes::Bagel::taste
+
+% SpreadableBagel public method taste {} {
+    set t [next]
+    foreach i ${:toppings} {
+       lappend t $i
+    }
+    return $t
+}
+::nsf::classes::SpreadableBagel::taste
+
+% SpreadableBagel create abagel
+::abagel
+
+% abagel toast
+% abagel toppings add jam
+jam
+% abagel toppings add m&m
+m&m jam
+
+% abagel taste
+toasty m&m jam
+

Of course, along come sesame, onion, poppy, and a host of other +bagels, requiring us to expand our scheme. We could keep track of +flavor with an instance variable, but this may not be +appropriate. Flavor is an innate property of the bagels, and one +that can affect other behavior - you wouldn’t put jam on an onion +bagel, would you? Instead of making a class hierarchy, let’s use +multiple inheritance to make the flavor classes mixins that add a +their taste independent trait to bagels or whatever other food they +are mixed with.

+
+
+
% nx::Class create Sesame {
+    :public method taste {} {concat [next] "sesame"}
+}
+::Sesame
+
+% nx::Class create Onion {
+    :public method taste {} {concat [next] "onion"}
+}
+::Onion
+
+% nx::Class create Poppy {
+    :public method taste {} {concat [next] "poppy"}
+}
+::Poppy
+

Well, they don’t appear to do much, but the use of next allows them +to be freely mixed.

+
+
+
% nx::Class create SesameOnionBagel -superclass SpreadableBagel -mixin {Sesame Onion}
+::SesameOnionBagel
+
+% SesameOnionBagel create abagel -toppings butter
+::abagel
+
+% abagel taste
+raw! butter onion sesame
+

For multiple inheritance, the system determines a linear inheritance +ordering that respects all of the local superclass orderings. You +can examine this ordering with an info option. next follows this +ordering when it combines behavior.

+
+
+
% SesameOnionBagel info heritage
+::Sesame ::Onion ::SpreadableBagel ::Bagel ::nx::Object
+
+% abagel info precedence
+::Sesame ::Onion ::SesameOnionBagel ::SpreadableBagel ::Bagel ::nx::Object
+

We can also combine our mixins with other classes, classes that need +have nothing to do with bagels, leading to a family of chips.

+
+
+
% nx::Class create Chips {
+    :public method taste {} {return "crunchy"}
+}
+::Chips
+
+% nx::Class create OnionChips -superclass Chips -mixin Onion
+::OnionChips
+
+% OnionChips create abag
+::abag
+
+% abag taste
+crunchy onion
+
+
+
+

+ + + Index: doc/example-scripts/container.html =================================================================== diff -u -r93bb0947d582f274afb1cdbc885909d55e100b36 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/container.html (.../container.html) (revision 93bb0947d582f274afb1cdbc885909d55e100b36) +++ doc/example-scripts/container.html (.../container.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,1150 +1,1150 @@ - - - - - -Listing of doc/example-scripts/container.tcl - - - - - -
-
-
-

This example is a small design study to implement container classes -with different features, namely a SimpleContainer, an -OrderedContainer and a SortedContainer. First of all, we require NX:

-
-
-
package req nx
-nx::test configure -count 1
-
-
-
-

Simple Container

-
-

The first container class presented here is called -SimpleContainer, which manages its contained items. As all -container classes presented here, the items are created as child -objects embedded in the container. If the container is deleted, all -items are deleted as well. The items, which will be put into the -container, should be instances of a certain class. We define here -for this purpose an arbitrary class C:

-
-
-
nx::Class create C
-

The class SimpleContainer keeps and manages items added to it. -Every instance of this class might have different item classes. We -might provide a prefix for naming the items, otherwise the default -is member.

-
-
-
nx::Class create SimpleContainer {
-  :property {memberClass ::MyItem}
-  :property {prefix member}
-
-  # Require the method "autoname" for generating nice names
-  :require method autoname
-
-  # The method new is responsible for creating a child of the current
-  # container.
-  :public method new {args} {
-    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
-    return $item
-  }
-}
-

Create and instance of the class SimpleContainer

-
-
-
% SimpleContainer create container1 -memberClass ::C
-::container1
-

and add a few items:

-
-
-
% container1 new
-::container1::member1
-
-% container1 new
-::container1::member2
-
-% container1 new
-::container1::member3
-

The elements of the container can be obtained via info children:

-
-
-
% container1 info children
-::container1::member1 ::container1::member2 ::container1::member3
-
-
-
-

Ordered Container

-
-

In the example with SimpleContainer, the order of the results of -info children just happens to be in the order of the added items, -but in general, this order is not guaranteed, but depends on the -population of the hash tables. In the next step, we extend the -example above by preserving the order of the elements.

-

The class OrderedContainer is similar to SimpleContainer, but -keeps a list of items that were added to the container. The item -list is managed in a property items which is defined as -incremental to make use of the add and delete methods provided -by the slots.

-
-
-
nx::Class create OrderedContainer -superclass SimpleContainer {
-  :property -incremental {items:0..n {}}
-
-  :public method new {args} {
-    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
-    :items add $item end
-    return $item
-  }
-
-  # Since we keep the list of items, we have to maintain it in case
-  # items are deleted.
-  :public method delete {item:object} {
-    :items delete $item
-    $item destroy
-  }
-
-}
-

Create an instance of OrderedContainer

-
-
-
% OrderedContainer create container2 -memberClass ::C
-::container2
-

and add a few items:

-
-
-
% container2 new
-::container2::member1
-
-% container2 new
-::container2::member2
-
-% container2 new
-::container2::member3
-

The elements of the container are obtained via the method items.

-
-
-
% container2 items
-::container2::member1 ::container2::member2 ::container2::member3
-

When we delete an item in the container …

-
-
-
% container2 delete ::container2::member2
-

the item is as well removed from the items list.

-
-
-
% container2 items
-::container2::member1 ::container2::member3
-
-
-
-

Sorted Container

-
-

In the next step, we define a SortedContainer, that keeps -additionally a sorted list for iterating through the items without -needing to sort the items when needed. The implementation maintains -an additional sorted list. The implementation of the SortedContainer -depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if -we have no Tcl 8.6, just return here.

-
-
-
if {[info command yield] eq ""} return
-

For sorting, we require the item class to have a key, that can be -freely specified. We use there the property name of Class D:

-
-
-
nx::Class create D {
-  :property name:required
-}
-
-nx::Class create SortedContainer -superclass OrderedContainer {
-
-  # In order to keep the index consisting of just the objects and to
-  # ease sorting, we maintain two list, one list of values and one
-  # list of objects. We assume for the time being, that the keys are
-  # not changing.
-
-  :variable values {}
-  :variable index {}
-  :property key
-
-  :public method index {} { return ${:index}}
-
-  :public method new {args} {
-    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
-    if {[info exists :key]} {
-      set value [$item cget -${:key}]
-      set pos [lsearch -bisect ${:values} $value]
-      set :values [linsert ${:values} [expr {$pos + 1}] $value]
-      set :index  [linsert ${:index}  [expr {$pos + 1}] $item]
-    }
-    lappend :items $item
-    return $item
-  }
-
-  # Since we keep the list of items, we have to maintain it in case
-  # items are deleted.
-  :public method delete {item:object} {
-    set pos [lsearch ${:index} $item]
-    if {$pos == -1} {error "item $item not found in container; items: ${:index}"}
-    set :values [lreplace ${:values} $pos $pos]
-    set :index  [lreplace ${:index}  $pos $pos]
-    next
-  }
-}
-

Create a container for class D with key name:

-
-
-
SortedContainer create container3 -memberClass ::D -key name
-

Add a few items

-
-
-
% container3 new -name victor
-::container3::member1
-
-% container3 new -name stefan
-::container3::member2
-
-% container3 new -name gustaf
-::container3::member3
-

The method items returns the items in the order of insertion (as before):

-
-
-
% container3 items
-::container3::member1 ::container3::member2 ::container3::member3
-

The method index returns the items in sorting order (sorted by the name member):

-
-
-
% container3 index
-::container3::member3 ::container3::member2 ::container3::member1
-

Now we delete an item:

-
-
-
% container3 delete ::container3::member2
-

The item is as well removed from the result lists

-
-
-
% container3 items
-::container3::member1 ::container3::member3
-
-% container3 index
-::container3::member3 ::container3::member1
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/container.tcl + + + + + +
+
+
+

This example is a small design study to implement container classes +with different features, namely a SimpleContainer, an +OrderedContainer and a SortedContainer. First of all, we require NX:

+
+
+
package req nx
+nx::test configure -count 1
+
+
+
+

Simple Container

+
+

The first container class presented here is called +SimpleContainer, which manages its contained items. As all +container classes presented here, the items are created as child +objects embedded in the container. If the container is deleted, all +items are deleted as well. The items, which will be put into the +container, should be instances of a certain class. We define here +for this purpose an arbitrary class C:

+
+
+
nx::Class create C
+

The class SimpleContainer keeps and manages items added to it. +Every instance of this class might have different item classes. We +might provide a prefix for naming the items, otherwise the default +is member.

+
+
+
nx::Class create SimpleContainer {
+  :property {memberClass ::MyItem}
+  :property {prefix member}
+
+  # Require the method "autoname" for generating nice names
+  :require method autoname
+
+  # The method new is responsible for creating a child of the current
+  # container.
+  :public method new {args} {
+    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
+    return $item
+  }
+}
+

Create and instance of the class SimpleContainer

+
+
+
% SimpleContainer create container1 -memberClass ::C
+::container1
+

and add a few items:

+
+
+
% container1 new
+::container1::member1
+
+% container1 new
+::container1::member2
+
+% container1 new
+::container1::member3
+

The elements of the container can be obtained via info children:

+
+
+
% container1 info children
+::container1::member1 ::container1::member2 ::container1::member3
+
+
+
+

Ordered Container

+
+

In the example with SimpleContainer, the order of the results of +info children just happens to be in the order of the added items, +but in general, this order is not guaranteed, but depends on the +population of the hash tables. In the next step, we extend the +example above by preserving the order of the elements.

+

The class OrderedContainer is similar to SimpleContainer, but +keeps a list of items that were added to the container. The item +list is managed in a property items which is defined as +incremental to make use of the add and delete methods provided +by the slots.

+
+
+
nx::Class create OrderedContainer -superclass SimpleContainer {
+  :property -incremental {items:0..n {}}
+
+  :public method new {args} {
+    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
+    :items add $item end
+    return $item
+  }
+
+  # Since we keep the list of items, we have to maintain it in case
+  # items are deleted.
+  :public method delete {item:object} {
+    :items delete $item
+    $item destroy
+  }
+
+}
+

Create an instance of OrderedContainer

+
+
+
% OrderedContainer create container2 -memberClass ::C
+::container2
+

and add a few items:

+
+
+
% container2 new
+::container2::member1
+
+% container2 new
+::container2::member2
+
+% container2 new
+::container2::member3
+

The elements of the container are obtained via the method items.

+
+
+
% container2 items
+::container2::member1 ::container2::member2 ::container2::member3
+

When we delete an item in the container …

+
+
+
% container2 delete ::container2::member2
+

the item is as well removed from the items list.

+
+
+
% container2 items
+::container2::member1 ::container2::member3
+
+
+
+

Sorted Container

+
+

In the next step, we define a SortedContainer, that keeps +additionally a sorted list for iterating through the items without +needing to sort the items when needed. The implementation maintains +an additional sorted list. The implementation of the SortedContainer +depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if +we have no Tcl 8.6, just return here.

+
+
+
if {[info command yield] eq ""} return
+

For sorting, we require the item class to have a key, that can be +freely specified. We use there the property name of Class D:

+
+
+
nx::Class create D {
+  :property name:required
+}
+
+nx::Class create SortedContainer -superclass OrderedContainer {
+
+  # In order to keep the index consisting of just the objects and to
+  # ease sorting, we maintain two list, one list of values and one
+  # list of objects. We assume for the time being, that the keys are
+  # not changing.
+
+  :variable values {}
+  :variable index {}
+  :property key
+
+  :public method index {} { return ${:index}}
+
+  :public method new {args} {
+    set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args]
+    if {[info exists :key]} {
+      set value [$item cget -${:key}]
+      set pos [lsearch -bisect ${:values} $value]
+      set :values [linsert ${:values} [expr {$pos + 1}] $value]
+      set :index  [linsert ${:index}  [expr {$pos + 1}] $item]
+    }
+    lappend :items $item
+    return $item
+  }
+
+  # Since we keep the list of items, we have to maintain it in case
+  # items are deleted.
+  :public method delete {item:object} {
+    set pos [lsearch ${:index} $item]
+    if {$pos == -1} {error "item $item not found in container; items: ${:index}"}
+    set :values [lreplace ${:values} $pos $pos]
+    set :index  [lreplace ${:index}  $pos $pos]
+    next
+  }
+}
+

Create a container for class D with key name:

+
+
+
SortedContainer create container3 -memberClass ::D -key name
+

Add a few items

+
+
+
% container3 new -name victor
+::container3::member1
+
+% container3 new -name stefan
+::container3::member2
+
+% container3 new -name gustaf
+::container3::member3
+

The method items returns the items in the order of insertion (as before):

+
+
+
% container3 items
+::container3::member1 ::container3::member2 ::container3::member3
+

The method index returns the items in sorting order (sorted by the name member):

+
+
+
% container3 index
+::container3::member3 ::container3::member2 ::container3::member1
+

Now we delete an item:

+
+
+
% container3 delete ::container3::member2
+

The item is as well removed from the result lists

+
+
+
% container3 items
+::container3::member1 ::container3::member3
+
+% container3 index
+::container3::member3 ::container3::member1
+
+
+
+

+ + + Index: doc/example-scripts/per-object-mixins.html =================================================================== diff -u -r23319e802e61ab370472347a8b8506bb650c8cc5 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/per-object-mixins.html (.../per-object-mixins.html) (revision 23319e802e61ab370472347a8b8506bb650c8cc5) +++ doc/example-scripts/per-object-mixins.html (.../per-object-mixins.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,966 +1,966 @@ - - - - - -Listing of doc/example-scripts/per-object-mixins.tcl - - - - - -
-
-
-

NX supports "open class definitions", object specific behavior -and mixin classes (among other things) to achieve dynamic behavior -extensions. The so-called per-object mixins are actually an -implementation of the decorator pattern.

-
-
-
package req nx
-

Here is the original example: a method from a derived class -extends the behavior of the baseclass; the primitive "next" -calls other same-named methods. In the example below, the -baseclass method "speak" is called at the beginning of the -derived-class method. The primitive "next" can be placed at -arbitrary places, or it can be omitted when the baseclass method -should not be called.

-
-
-
nx::Class create BaseClass {
-  :public method speak {} {
-    puts "Hello from BC."
-  }
-}
-
-nx::Class create DerivedClass -superclass BaseClass {
-  :public method speak {} {
-    next
-    puts "Hello from DC."
-  }
-}
-
-DerivedClass create o1
-o1 speak
-

The output is:

-
-
-
   Hello from BC.
-   Hello from DC.
-
-

There are many ways to extend the behavior NX classes at -runtime. The easiest thing is to add methods dynamically to -classes. E.g. we can extend the BaseClass with the method unknown, -which is called whenever an unknown method is called.

-
-
-
BaseClass method unknown {m args} {
-  puts "What? $m? I don't understand."
-}
-o1 sing
-

The output is:

-
-
-
    What? sing? I don't understand.
-
-

Often, you do not want to extend the class, but to -modify the behavior of a single object. In NX, an object -can have individual methods:

-
-
-
o1 public method sing {} {
-  puts "Ok, here it goes: Lala Lala!"
-}
-o1 sing
-

The output is:

-
-
-
    Ok, here it goes: Lala Lala!
-
-

In many situations, it is desired to add/remove a set -of methods dynamically to objects or classes. The mechanisms -above allow this, but they are rather cumbersome and do -support a systematic behavior engineering. One can add -so-called "mixin classes" to objects and/or classes. For -example, we can define a class M for a more verbose methods:

-
-
-
nx::Class create M {
-  :public method sing {} {
-    puts -nonewline "[self] sings: "
-    next
-  }
-  :method unknown args {
-    puts -nonewline "[self] is confused: "
-    next
-  }
-}
-

The behavior of M can be mixed into the behavior of o1 through -per object mixins …

-
-
-
o1 mixin M
-
    -
  1. -

    -and we call the methods again: -

    -
  2. -
-
-
-
o1 sing
-o1 read
-

The output is:

-
-
-
   ::o1 sings: Ok, here it goes: Lala Lala!
-   ::o1 is confused: What? read? I don't understand.
-
-

We can remove the new behavior easily by unregistering the -mixin class ….

-
-
-
o1 mixin ""
-
    -
  1. -

    -and we call the methods again: -

    -
  2. -
-
-
-
o1 sing
-o1 read
-

The output is:

-
-
-
   Ok, here it goes: Lala Lala!
-   What? read? I don't understand.
-
-

Mixin classes can be used to extend the behavior of classes -as well.

-
-
-
BaseClass mixin M
-
-o1 sing
-o1 read
-
-DerivedClass create o2
-o2 read
-

The output is:

-
-
-
   ::o1 sings: Ok, here it goes: Lala Lala!
-   ::o1 is confused: What? read? I don't understand.
-   ::o2 is confused: What? read? I don't understand.
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/per-object-mixins.tcl + + + + + +
+
+
+

NX supports "open class definitions", object specific behavior +and mixin classes (among other things) to achieve dynamic behavior +extensions. The so-called per-object mixins are actually an +implementation of the decorator pattern.

+
+
+
package req nx
+

Here is the original example: a method from a derived class +extends the behavior of the baseclass; the primitive "next" +calls other same-named methods. In the example below, the +baseclass method "speak" is called at the beginning of the +derived-class method. The primitive "next" can be placed at +arbitrary places, or it can be omitted when the baseclass method +should not be called.

+
+
+
nx::Class create BaseClass {
+  :public method speak {} {
+    puts "Hello from BC."
+  }
+}
+
+nx::Class create DerivedClass -superclass BaseClass {
+  :public method speak {} {
+    next
+    puts "Hello from DC."
+  }
+}
+
+DerivedClass create o1
+o1 speak
+

The output is:

+
+
+
   Hello from BC.
+   Hello from DC.
+
+

There are many ways to extend the behavior NX classes at +runtime. The easiest thing is to add methods dynamically to +classes. E.g. we can extend the BaseClass with the method unknown, +which is called whenever an unknown method is called.

+
+
+
BaseClass method unknown {m args} {
+  puts "What? $m? I don't understand."
+}
+o1 sing
+

The output is:

+
+
+
    What? sing? I don't understand.
+
+

Often, you do not want to extend the class, but to +modify the behavior of a single object. In NX, an object +can have individual methods:

+
+
+
o1 public method sing {} {
+  puts "Ok, here it goes: Lala Lala!"
+}
+o1 sing
+

The output is:

+
+
+
    Ok, here it goes: Lala Lala!
+
+

In many situations, it is desired to add/remove a set +of methods dynamically to objects or classes. The mechanisms +above allow this, but they are rather cumbersome and do +support a systematic behavior engineering. One can add +so-called "mixin classes" to objects and/or classes. For +example, we can define a class M for a more verbose methods:

+
+
+
nx::Class create M {
+  :public method sing {} {
+    puts -nonewline "[self] sings: "
+    next
+  }
+  :method unknown args {
+    puts -nonewline "[self] is confused: "
+    next
+  }
+}
+

The behavior of M can be mixed into the behavior of o1 through +per object mixins …

+
+
+
o1 mixin M
+
    +
  1. +

    +and we call the methods again: +

    +
  2. +
+
+
+
o1 sing
+o1 read
+

The output is:

+
+
+
   ::o1 sings: Ok, here it goes: Lala Lala!
+   ::o1 is confused: What? read? I don't understand.
+
+

We can remove the new behavior easily by unregistering the +mixin class ….

+
+
+
o1 mixin ""
+
    +
  1. +

    +and we call the methods again: +

    +
  2. +
+
+
+
o1 sing
+o1 read
+

The output is:

+
+
+
   Ok, here it goes: Lala Lala!
+   What? read? I don't understand.
+
+

Mixin classes can be used to extend the behavior of classes +as well.

+
+
+
BaseClass mixin M
+
+o1 sing
+o1 read
+
+DerivedClass create o2
+o2 read
+

The output is:

+
+
+
   ::o1 sings: Ok, here it goes: Lala Lala!
+   ::o1 is confused: What? read? I don't understand.
+   ::o2 is confused: What? read? I don't understand.
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-abstract-type.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-abstract-type.html (.../rosetta-abstract-type.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-abstract-type.html (.../rosetta-abstract-type.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,863 +1,863 @@ - - - - - -Listing of doc/example-scripts/rosetta-abstract-type.tcl - - - - - -
-
-

Rosetta Example: Abstract type

-
-

Define a class without instances and without implemented methods. -For detailed description of this example -see http://rosettacode.org/wiki/Abstract_type

-
-
-
package req nx
-

Define a class AbstractQueue

-
-
-
nx::Class create AbstractQueue {
-
-  :public method enqueue {item} {error "not implemented"}
-  :public method dequeue {} {error "not implemented"}
-
-  :public object method create {args} {
-    error "Cannot instantiate abstract class [self]"
-  }
-}
-

Define a concrete queue (named ListQueue) based -on the Abstract Queue

-
-
-
nx::Class create ListQueue -superclass AbstractQueue {
-
-  :variable list {}
-
-  :public method enqueue {item} {
-    lappend :list $item
-  }
-
-  :public method dequeue {} {
-    set item [lindex ${:list} 0]
-    set :list [lrange ${:list} 1 end]
-    return $item
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Trying to create an instance of the AbstraceQueue returns an error message:

-
-
-
% AbstractQueue new
-Cannot instantiate abstract class ::AbstractQueue
-

Create an instance of the concrete queue:

-
-
-
% set q [ListQueue new]
-

Enqueue and dequeue items

-
-
-
% $q enqueue 100
-100
-% $q enqueue 101
-100 101
-% $q dequeue
-100
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-abstract-type.tcl + + + + + +
+
+

Rosetta Example: Abstract type

+
+

Define a class without instances and without implemented methods. +For detailed description of this example +see http://rosettacode.org/wiki/Abstract_type

+
+
+
package req nx
+

Define a class AbstractQueue

+
+
+
nx::Class create AbstractQueue {
+
+  :public method enqueue {item} {error "not implemented"}
+  :public method dequeue {} {error "not implemented"}
+
+  :public object method create {args} {
+    error "Cannot instantiate abstract class [self]"
+  }
+}
+

Define a concrete queue (named ListQueue) based +on the Abstract Queue

+
+
+
nx::Class create ListQueue -superclass AbstractQueue {
+
+  :variable list {}
+
+  :public method enqueue {item} {
+    lappend :list $item
+  }
+
+  :public method dequeue {} {
+    set item [lindex ${:list} 0]
+    set :list [lrange ${:list} 1 end]
+    return $item
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Trying to create an instance of the AbstraceQueue returns an error message:

+
+
+
% AbstractQueue new
+Cannot instantiate abstract class ::AbstractQueue
+

Create an instance of the concrete queue:

+
+
+
% set q [ListQueue new]
+

Enqueue and dequeue items

+
+
+
% $q enqueue 100
+100
+% $q enqueue 101
+100 101
+% $q dequeue
+100
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-add-variable.html =================================================================== diff -u -red0f5cba982af963b62c36d8c8c75e89c81adf3e -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-add-variable.html (.../rosetta-add-variable.html) (revision ed0f5cba982af963b62c36d8c8c75e89c81adf3e) +++ doc/example-scripts/rosetta-add-variable.html (.../rosetta-add-variable.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,970 +1,970 @@ - - - - - -Listing of doc/example-scripts/rosetta-add-variable.tcl - - - - - -
-
-

Rosetta example: Add a variable to a class instance at runtime

-
-

Demonstrate how to dynamically add variables to an object (a class instance) at runtime.

- -
-
-
package req nx
-

The class Empty does not provide any structural or behaviora -features on behalf of future instances, they will remain empty.

-
-
-
nx::Class create Empty
-

Provide one instance of Empty to add an object variable to …

-
-
-
Empty create ::e
-

Is e truly empty?

-
-
-
% ::e info vars
-

NX offers different types of object-variable managers: properties, -variable slots, and plain Tcl per-object variables. Below, we -showcase variable slots (although the others can be added -dynamically alike).

-

a) Declare a variable slot foo; -accessor will provide -getter/setter methods for this one object e automatically.

-
-
-
::e object variable -accessor public foo
-

b) Define a value for the variable slot foo

-
-
-
% ::e foo set 1
-1
-

c) Is there a Tcl variable managed by the variable slot?

-
-
-
% ::e info vars
-foo
-

d) Retrieve foo's value

-
-
-
% ::e foo get
-1
-

A second instance of Empty has no such capability: foo

-
-
-
Empty create ::f
-

a) Is there any Tcl variable, one named foo? No …

-
-
-
% ::f info vars
-

b) Are there getter/setter methods for a foo? No …

-
-
-
% ::f foo set
-::f: unable to dispatch method 'foo'
-

c) Is there a variable slot foo? No …

-
-
-
% ::f info object variables foo
-

In NX, once dynamically added, a variable slot can also be dynamically removed again.

-
-
-
::e delete object variable foo
-

a) Is the variable slot foo gone? Yes …

-
-
-
% ::e info object variables foo
-

b) Is the Tcl variable gone? Yes …

-
-
-
% ::e info vars
-

c) Are the getter/setter methods gone? Yes …

-
-
-
% ::e foo get
-::e: unable to dispatch method 'foo'
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-add-variable.tcl + + + + + +
+
+

Rosetta example: Add a variable to a class instance at runtime

+
+

Demonstrate how to dynamically add variables to an object (a class instance) at runtime.

+ +
+
+
package req nx
+

The class Empty does not provide any structural or behaviora +features on behalf of future instances, they will remain empty.

+
+
+
nx::Class create Empty
+

Provide one instance of Empty to add an object variable to …

+
+
+
Empty create ::e
+

Is e truly empty?

+
+
+
% ::e info vars
+

NX offers different types of object-variable managers: properties, +variable slots, and plain Tcl per-object variables. Below, we +showcase variable slots (although the others can be added +dynamically alike).

+

a) Declare a variable slot foo; -accessor will provide +getter/setter methods for this one object e automatically.

+
+
+
::e object variable -accessor public foo
+

b) Define a value for the variable slot foo

+
+
+
% ::e foo set 1
+1
+

c) Is there a Tcl variable managed by the variable slot?

+
+
+
% ::e info vars
+foo
+

d) Retrieve foo's value

+
+
+
% ::e foo get
+1
+

A second instance of Empty has no such capability: foo

+
+
+
Empty create ::f
+

a) Is there any Tcl variable, one named foo? No …

+
+
+
% ::f info vars
+

b) Are there getter/setter methods for a foo? No …

+
+
+
% ::f foo set
+::f: unable to dispatch method 'foo'
+

c) Is there a variable slot foo? No …

+
+
+
% ::f info object variables foo
+

In NX, once dynamically added, a variable slot can also be dynamically removed again.

+
+
+
::e delete object variable foo
+

a) Is the variable slot foo gone? Yes …

+
+
+
% ::e info object variables foo
+

b) Is the Tcl variable gone? Yes …

+
+
+
% ::e info vars
+

c) Are the getter/setter methods gone? Yes …

+
+
+
% ::e foo get
+::e: unable to dispatch method 'foo'
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-classes.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-classes.html (.../rosetta-classes.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-classes.html (.../rosetta-classes.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,812 +1,812 @@ - - - - - -Listing of doc/example-scripts/rosetta-classes.tcl - - - - - -
-
-

Rosetta Example: Classes

-
- -
-
-
package req nx
-
-nx::Class create summation {
-  :method init {} {set :v 0}
-  :public method add {x} {incr :v $x}
-  :public method value {} {return ${:v}}
-  :public method destroy {} {puts "ended with value [:value]"; next}
-}
-
-

Demonstrating the behavior in a shell:

-
-
-
% set sum [summation new]
-% $sum value
-0
-% $sum add 1
-1
-% $sum add 2
-3
-% $sum add 3
-6
-% $sum add 4
-10
-% $sum value
-10
-

During the destroy of the object, ended with value 10 is printed

-
-
-
% $sum destroy
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-classes.tcl + + + + + +
+
+

Rosetta Example: Classes

+
+ +
+
+
package req nx
+
+nx::Class create summation {
+  :method init {} {set :v 0}
+  :public method add {x} {incr :v $x}
+  :public method value {} {return ${:v}}
+  :public method destroy {} {puts "ended with value [:value]"; next}
+}
+
+

Demonstrating the behavior in a shell:

+
+
+
% set sum [summation new]
+% $sum value
+0
+% $sum add 1
+1
+% $sum add 2
+3
+% $sum add 3
+6
+% $sum add 4
+10
+% $sum value
+10
+

During the destroy of the object, ended with value 10 is printed

+
+
+
% $sum destroy
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-clone.html =================================================================== diff -u -r922c0ab9c756265f0793bda2cc69d50742c5bf88 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-clone.html (.../rosetta-clone.html) (revision 922c0ab9c756265f0793bda2cc69d50742c5bf88) +++ doc/example-scripts/rosetta-clone.html (.../rosetta-clone.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,874 +1,874 @@ - - - - - -Listing of doc/example-scripts/rosetta-clone.tcl - - - - - -
-
-

Rosetta example: Polymorphic copy

-
-

Let a polymorphic object contain an instance of some specific type S -derived from a type T. The type T is known. The type S is possibly -unknown until run time. The objective is to create an exact copy of -such polymorphic object.

- -
-
-
package req nx
-

nx::Object provides a method copy which creates a deep copy of any -source object (hence, polymorphic in the sense of this task), i.e., -it contains all structural and behavioral features of the source and -preserves its signature type.

-
-
-
nx::Class create T {
-    :property -accessor public {label "T"}
-}
-nx::Class create S -superclasses T {
-    :property -accessor public {label "S"}
-}
-
-set client [nx::Object new {
-    :public object method duplicate {src} {
-        # this is the polymorphic call site
-        return [$src copy]
-    }
-}]
-
-set t [T new]
-% $t label get
-T
-set s [S new]
-% $s label get
-S
-

Provide two copies, using copy underneath

-
-
-
set t2 [$client duplicate $t]
-set s2 [$client duplicate $s]
-

Are the copies truly independent objects (identities)? Yes …

-
-
-
% expr {$t2 ne $t}
-1
-% expr {$s2 ne $s}
-1
-

Are the copies offsprings of the source types/classes? Yes …

-
-
-
% $t info class
-::T
-% $t2 info class
-::T
-
-% $s info class
-::S
-% $s2 info class
-::S
-

Do the copies operate exactly like their source objects? Yes …

-
-
-
% $t label get
-T
-% $t2 label get
-T
-
-% $s label get
-S
-% $s2 label get
-S
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-clone.tcl + + + + + +
+
+

Rosetta example: Polymorphic copy

+
+

Let a polymorphic object contain an instance of some specific type S +derived from a type T. The type T is known. The type S is possibly +unknown until run time. The objective is to create an exact copy of +such polymorphic object.

+ +
+
+
package req nx
+

nx::Object provides a method copy which creates a deep copy of any +source object (hence, polymorphic in the sense of this task), i.e., +it contains all structural and behavioral features of the source and +preserves its signature type.

+
+
+
nx::Class create T {
+    :property -accessor public {label "T"}
+}
+nx::Class create S -superclasses T {
+    :property -accessor public {label "S"}
+}
+
+set client [nx::Object new {
+    :public object method duplicate {src} {
+        # this is the polymorphic call site
+        return [$src copy]
+    }
+}]
+
+set t [T new]
+% $t label get
+T
+set s [S new]
+% $s label get
+S
+

Provide two copies, using copy underneath

+
+
+
set t2 [$client duplicate $t]
+set s2 [$client duplicate $s]
+

Are the copies truly independent objects (identities)? Yes …

+
+
+
% expr {$t2 ne $t}
+1
+% expr {$s2 ne $s}
+1
+

Are the copies offsprings of the source types/classes? Yes …

+
+
+
% $t info class
+::T
+% $t2 info class
+::T
+
+% $s info class
+::S
+% $s2 info class
+::S
+

Do the copies operate exactly like their source objects? Yes …

+
+
+
% $t label get
+T
+% $t2 label get
+T
+
+% $s label get
+S
+% $s2 label get
+S
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-constraint-genericity.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-constraint-genericity.html (.../rosetta-constraint-genericity.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-constraint-genericity.html (.../rosetta-constraint-genericity.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,855 +1,855 @@ - - - - - -Listing of doc/example-scripts/rosetta-constraint-genericity.tcl - - - - - -
-
-

Rosetta Example: Constrained genericity

-
- -
-
-
package req nx
-

Define the two classes Eatable and Fish. Eatable is a class -for all eatable things, a Fish is a subclass ant therefore -eatable.

-
-
-
nx::Class create Eatable
-nx::Class create Fish -superclass Eatable {
-  :property name
-}
-

A FoodBax may only contain eatable items. Therefore with we define -items as a property of type Eatable" which has a multiplicity of -+0..n (might contain 0 to n eatable items). Furthermore, we define -items as incremental, such we can add / remove items with item -add or item remove.

-
-
-
nx::Class create FoodBox {
-  :property -incremental item:object,type=::Eatable
-  :public method print {} {
-    set string "Foodbox contains:\n"
-    foreach i ${:item} {append string "   [$i cget -name]\n"}
-    return $string
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Create two fishes, Wanda and Nemo:

-
-
-
% set f1 [Fish new -name "Wanda"]
-% set f2 [Fish new -name "Nemo"]
-

Create a Foodbox and add the two fishes:

-
-
-
% set fb [FoodBox new]
-% $fb item add $f1
-% $fb item add $f2
-

Return the print string of the contents:

-
-
-
% $fb print
-Foodbox contains:
-   Nemo
-   Wanda
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-constraint-genericity.tcl + + + + + +
+
+

Rosetta Example: Constrained genericity

+
+ +
+
+
package req nx
+

Define the two classes Eatable and Fish. Eatable is a class +for all eatable things, a Fish is a subclass ant therefore +eatable.

+
+
+
nx::Class create Eatable
+nx::Class create Fish -superclass Eatable {
+  :property name
+}
+

A FoodBax may only contain eatable items. Therefore with we define +items as a property of type Eatable" which has a multiplicity of ++0..n (might contain 0 to n eatable items). Furthermore, we define +items as incremental, such we can add / remove items with item +add or item remove.

+
+
+
nx::Class create FoodBox {
+  :property -incremental item:object,type=::Eatable
+  :public method print {} {
+    set string "Foodbox contains:\n"
+    foreach i ${:item} {append string "   [$i cget -name]\n"}
+    return $string
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Create two fishes, Wanda and Nemo:

+
+
+
% set f1 [Fish new -name "Wanda"]
+% set f2 [Fish new -name "Nemo"]
+

Create a Foodbox and add the two fishes:

+
+
+
% set fb [FoodBox new]
+% $fb item add $f1
+% $fb item add $f2
+

Return the print string of the contents:

+
+
+
% $fb print
+Foodbox contains:
+   Nemo
+   Wanda
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-delegates.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-delegates.html (.../rosetta-delegates.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-delegates.html (.../rosetta-delegates.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,829 +1,829 @@ - - - - - -Listing of doc/example-scripts/rosetta-delegates.tcl - - - - - -
-
-

Rosetta Example: Delegates

-
- -
-
-
package req nx
-
-nx::Class create Delegator {
-
-  # The class Delegator has a property named "delegatee" which is an
-  # object:
-
-  :property delegatee:object
-
-  # The method "operation" decides, whether it deletates the action to
-  # another object, or it performs the action itself.
-
-  :public method operation {} {
-    if {[info exists :delegatee]} {
-      ${:delegatee} operation
-    } else {
-      return "default implementatiton"
-    }
-  }
-}
-
-nx::Class create Delegatee {
-
-  # The class "Delgatee" might receice invocations from the class
-  # "Delegator"
-
-  :public method operation {} {
-    return "delegatee implementatiton"
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Create a Delegator, which has no delegatee defined. Therefore -delegator performs the action by itself, the default implementation.

-
-
-
% set a [Delegator new]
-% $a operation
-default implementatiton
-

Now, we set the delegatee; therefore, the delegatee will perform -the action.

-
-
-
% $a configure -delegatee [Delegatee new]
-% $a operation
-delegatee implementatiton
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-delegates.tcl + + + + + +
+
+

Rosetta Example: Delegates

+
+ +
+
+
package req nx
+
+nx::Class create Delegator {
+
+  # The class Delegator has a property named "delegatee" which is an
+  # object:
+
+  :property delegatee:object
+
+  # The method "operation" decides, whether it deletates the action to
+  # another object, or it performs the action itself.
+
+  :public method operation {} {
+    if {[info exists :delegatee]} {
+      ${:delegatee} operation
+    } else {
+      return "default implementatiton"
+    }
+  }
+}
+
+nx::Class create Delegatee {
+
+  # The class "Delgatee" might receice invocations from the class
+  # "Delegator"
+
+  :public method operation {} {
+    return "delegatee implementatiton"
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Create a Delegator, which has no delegatee defined. Therefore +delegator performs the action by itself, the default implementation.

+
+
+
% set a [Delegator new]
+% $a operation
+default implementatiton
+

Now, we set the delegatee; therefore, the delegatee will perform +the action.

+
+
+
% $a configure -delegatee [Delegatee new]
+% $a operation
+delegatee implementatiton
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-multiple-distinct.html =================================================================== diff -u -r8648ec770a59ed911769dd51cf2658045110c748 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-multiple-distinct.html (.../rosetta-multiple-distinct.html) (revision 8648ec770a59ed911769dd51cf2658045110c748) +++ doc/example-scripts/rosetta-multiple-distinct.html (.../rosetta-multiple-distinct.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,830 +1,830 @@ - - - - - -Listing of doc/example-scripts/rosetta-multiple-distinct.tcl - - - - - -
-
-

Rosetta example: Multiple distinct objects

-
-

Create a sequence (array, list, whatever) consisting of n distinct, -initialized items of the same type. n should be determined at -runtime.

- -
-
-
package req nx
-

The class Klass defines and implements the item type. It can also -be used to query its population of instances.

-
-
-
nx::Class create Klass
-set n 100; # runtime parameter
-

Wrong: Only a single item (object) is created, its (command) name is replicated n times.

-
-
-
% llength [Klass info instances]
-0;
-
-set theList [lrepeat $n [Klass new]]
-
-% llength [Klass info instances]
-1;
-% llength [lsort -unique $theList]
-1;
-
-[lindex $theList 0] destroy
-

Correct: n items (objects) having distinct (command) names are -created and stored in the list.

-
-
-
% llength [Klass info instances]
-0;
-
-set theList {}
-
-for {set i 0} {$i<$n} {incr i} {
-    lappend theList [Klass new]
-}
-
-% llength [Klass info instances]
-100;
-% llength [lsort -unique $theList]
-100;
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-multiple-distinct.tcl + + + + + +
+
+

Rosetta example: Multiple distinct objects

+
+

Create a sequence (array, list, whatever) consisting of n distinct, +initialized items of the same type. n should be determined at +runtime.

+ +
+
+
package req nx
+

The class Klass defines and implements the item type. It can also +be used to query its population of instances.

+
+
+
nx::Class create Klass
+set n 100; # runtime parameter
+

Wrong: Only a single item (object) is created, its (command) name is replicated n times.

+
+
+
% llength [Klass info instances]
+0;
+
+set theList [lrepeat $n [Klass new]]
+
+% llength [Klass info instances]
+1;
+% llength [lsort -unique $theList]
+1;
+
+[lindex $theList 0] destroy
+

Correct: n items (objects) having distinct (command) names are +created and stored in the list.

+
+
+
% llength [Klass info instances]
+0;
+
+set theList {}
+
+for {set i 0} {$i<$n} {incr i} {
+    lappend theList [Klass new]
+}
+
+% llength [Klass info instances]
+100;
+% llength [lsort -unique $theList]
+100;
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-multiple-inheritance.html =================================================================== diff -u -r3617b69afeae834b81f88246de81db748f393420 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-multiple-inheritance.html (.../rosetta-multiple-inheritance.html) (revision 3617b69afeae834b81f88246de81db748f393420) +++ doc/example-scripts/rosetta-multiple-inheritance.html (.../rosetta-multiple-inheritance.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,830 +1,830 @@ - - - - - -Listing of doc/example-scripts/rosetta-multiple-inheritance.tcl - - - - - -
-
-

Rosetta example: Inheritance/Multiple

-
-

Write two classes (or interfaces) Camera and MobilePhone, then write -a class CameraPhone which is both a Camera and a MobilePhone.

- -
-
-
package req nx
-

NX offers class-based and mixin-based multiple inheritance. The -search order of features (methods, properties) along the class -hierarchy is computed using a scheme equivalent with C3 -linearization.

-

a) Class-based multiple inheritance

-
-
-
nx::Class create Camera
-nx::Class create MobilePhone
-
-nx::Class create CameraPhone -superclasses {Camera MobilePhone}
-

Show the resulting class search order:

-
-
-
% CameraPhone info superclasses -closure
-::Camera ::MobilePhone ::nx::Object
-% [CameraPhone new] info precedence
-::CameraPhone ::Camera ::MobilePhone ::nx::Object
-

b) Mixin-based multiple inheritance

-
-
-
nx::Class create CameraPhone -mixins {Camera MobilePhone}
-% CameraPhone info mixins
-::Camera ::MobilePhone
-

Show the resulting class search order:

-
-
-
% [CameraPhone new] info precedence
-::Camera ::MobilePhone ::CameraPhone ::nx::Object
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-multiple-inheritance.tcl + + + + + +
+
+

Rosetta example: Inheritance/Multiple

+
+

Write two classes (or interfaces) Camera and MobilePhone, then write +a class CameraPhone which is both a Camera and a MobilePhone.

+ +
+
+
package req nx
+

NX offers class-based and mixin-based multiple inheritance. The +search order of features (methods, properties) along the class +hierarchy is computed using a scheme equivalent with C3 +linearization.

+

a) Class-based multiple inheritance

+
+
+
nx::Class create Camera
+nx::Class create MobilePhone
+
+nx::Class create CameraPhone -superclasses {Camera MobilePhone}
+

Show the resulting class search order:

+
+
+
% CameraPhone info superclasses -closure
+::Camera ::MobilePhone ::nx::Object
+% [CameraPhone new] info precedence
+::CameraPhone ::Camera ::MobilePhone ::nx::Object
+

b) Mixin-based multiple inheritance

+
+
+
nx::Class create CameraPhone -mixins {Camera MobilePhone}
+% CameraPhone info mixins
+::Camera ::MobilePhone
+

Show the resulting class search order:

+
+
+
% [CameraPhone new] info precedence
+::Camera ::MobilePhone ::CameraPhone ::nx::Object
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-polymorphism.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-polymorphism.html (.../rosetta-polymorphism.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-polymorphism.html (.../rosetta-polymorphism.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,872 +1,872 @@ - - - - - -Listing of doc/example-scripts/rosetta-polymorphism.tcl - - - - - -
-
-

Rosetta Example: Polymorphism

-
- -
-
-
package req nx
-
-nx::Class create Point {
-
-  :property x:double
-  :property y:double
-
-  :public method print {} {
-    return "Point(${:x},${:y})"
-  }
-}
-
-nx::Class create Circle -superclass Point {
-
-  :property radius:double
-
-  :public method print {} {
-    return "Circle(${:x},${:y},${:radius})"
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Create a point and get the print string:

-
-
-
% set p [Point new -x 1.0 -y 2.0]
-% $p print
-Point(1.0,2.0)
-

Get the x coordinate of this point:

-
-
-
% $p cget -x
-1.0
-

Create a circle:

-
-
-
% set c [Circle new -x 3.0 -y 4.0 -radius 5.0]
-

Copy the circle

-
-
-
% set d [$c copy]
-

Change the radius of the copied circle:

-
-
-
% $d configure -radius 1.5
-

Print the two circles:

-
-
-
% $c print
-Circle(3.0,4.0,5.0)
-
-% $d print
-Circle(3.0,4.0,1.5)
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-polymorphism.tcl + + + + + +
+
+

Rosetta Example: Polymorphism

+
+ +
+
+
package req nx
+
+nx::Class create Point {
+
+  :property x:double
+  :property y:double
+
+  :public method print {} {
+    return "Point(${:x},${:y})"
+  }
+}
+
+nx::Class create Circle -superclass Point {
+
+  :property radius:double
+
+  :public method print {} {
+    return "Circle(${:x},${:y},${:radius})"
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Create a point and get the print string:

+
+
+
% set p [Point new -x 1.0 -y 2.0]
+% $p print
+Point(1.0,2.0)
+

Get the x coordinate of this point:

+
+
+
% $p cget -x
+1.0
+

Create a circle:

+
+
+
% set c [Circle new -x 3.0 -y 4.0 -radius 5.0]
+

Copy the circle

+
+
+
% set d [$c copy]
+

Change the radius of the copied circle:

+
+
+
% $d configure -radius 1.5
+

Print the two circles:

+
+
+
% $c print
+Circle(3.0,4.0,5.0)
+
+% $d print
+Circle(3.0,4.0,1.5)
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-serialization.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-serialization.html (.../rosetta-serialization.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-serialization.html (.../rosetta-serialization.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,882 +1,882 @@ - - - - - -Listing of doc/example-scripts/rosetta-serialization.tcl - - - - - -
-
-

Rosetta Example: Object serialization

-
- -
-
-
package req nx
-package req nx::serializer
-
-nx::Class create Being {
-  :property {alive:boolean true}
-}
-
-nx::Class create Animal -superclass Being {
-  :property name
-  :public method print {} {
-    puts "i am ${:name} alive ${:alive}"
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Create a few animals

-
-
-
% Animal new -name "Fido"
-% Animal new -name "Lupo"
-% Animal new -name "Kiki" -alive false
-

Print the created animals

-
-
-
% foreach i [Animal info instances] { $i print }
-

The loop prints:
- i am Kiki alive false
- i am Lupo alive true
- i am Fido alive true

-

Serialize the animals to a file

-
-
-
% set fpath [::nsf::tmpdir]/dump
-% set f [open $fpath w]
-% foreach i [Animal info instances] { puts $f [$i serialize] }
-% close $f
-

Destroy all animal instances:

-
-
-
% foreach i [Animal info instances] { $i destroy }
-% puts ===========
-

Print the existing animals (will print nothing)

-
-
-
% foreach i [Animal info instances] { $i print }
-% puts ===========
-

Load the animals again …

-
-
-
% source $fpath
-

and print it. The print output is the same as above

-
-
-
% foreach i [Animal info instances] { $i print }
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-serialization.tcl + + + + + +
+
+

Rosetta Example: Object serialization

+
+ +
+
+
package req nx
+package req nx::serializer
+
+nx::Class create Being {
+  :property {alive:boolean true}
+}
+
+nx::Class create Animal -superclass Being {
+  :property name
+  :public method print {} {
+    puts "i am ${:name} alive ${:alive}"
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Create a few animals

+
+
+
% Animal new -name "Fido"
+% Animal new -name "Lupo"
+% Animal new -name "Kiki" -alive false
+

Print the created animals

+
+
+
% foreach i [Animal info instances] { $i print }
+

The loop prints:
+ i am Kiki alive false
+ i am Lupo alive true
+ i am Fido alive true

+

Serialize the animals to a file

+
+
+
% set fpath [::nsf::tmpdir]/dump
+% set f [open $fpath w]
+% foreach i [Animal info instances] { puts $f [$i serialize] }
+% close $f
+

Destroy all animal instances:

+
+
+
% foreach i [Animal info instances] { $i destroy }
+% puts ===========
+

Print the existing animals (will print nothing)

+
+
+
% foreach i [Animal info instances] { $i print }
+% puts ===========
+

Load the animals again …

+
+
+
% source $fpath
+

and print it. The print output is the same as above

+
+
+
% foreach i [Animal info instances] { $i print }
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-single-inheritance.html =================================================================== diff -u -rc7738a386075dae91fb88e6a93232f37dfedc228 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-single-inheritance.html (.../rosetta-single-inheritance.html) (revision c7738a386075dae91fb88e6a93232f37dfedc228) +++ doc/example-scripts/rosetta-single-inheritance.html (.../rosetta-single-inheritance.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,788 +1,788 @@ - - - - - -Listing of doc/example-scripts/rosetta-single-inheritance.tcl - - - - - -
-
-

Rosetta example: Inheritance/Single

-
-

Show a tree of types which inherit from each other. The top of the -tree should be a class called Animal. The second level should have -Dog and Cat. Under Dog should be Lab and Collie.

- -
-
-
package req nx
-
-nx::Class create Animal
-nx::Class create Dog -superclasses Animal
-nx::Class create Cat -superclasses Animal
-nx::Class create Collie -superclasses Dog
-nx::Class create Lab -superclasses Dog
-

Show the resulting class search order:

-
-
-
% Lab info superclasses -closure
-::Dog ::Animal ::nx::Object
-% [Collie new] info precedence
-::Collie ::Dog ::Animal ::nx::Object
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-single-inheritance.tcl + + + + + +
+
+

Rosetta example: Inheritance/Single

+
+

Show a tree of types which inherit from each other. The top of the +tree should be a class called Animal. The second level should have +Dog and Cat. Under Dog should be Lab and Collie.

+ +
+
+
package req nx
+
+nx::Class create Animal
+nx::Class create Dog -superclasses Animal
+nx::Class create Cat -superclasses Animal
+nx::Class create Collie -superclasses Dog
+nx::Class create Lab -superclasses Dog
+

Show the resulting class search order:

+
+
+
% Lab info superclasses -closure
+::Dog ::Animal ::nx::Object
+% [Collie new] info precedence
+::Collie ::Dog ::Animal ::nx::Object
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-singleton.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-singleton.html (.../rosetta-singleton.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-singleton.html (.../rosetta-singleton.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,858 +1,858 @@ - - - - - -Listing of doc/example-scripts/rosetta-singleton.tcl - - - - - -
-
-

Rosetta Example: Singleton

-
- -
-

A Singleton Class

-
-
-
package req nx
-
-nx::Class create Singleton {
-  #
-  # We overload the system method "create". In the modified method we
-  # save the created instance in the instance variable named
-  # "instance"
-  #
-  :variable instance:object
-
-  :public object method create {args} {
-    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
-  }
-}
-
-
-

Demonstrating the behavior in a shell:

-

Calling Singleton new multiple times returns always the same object:

-
-
-
% expr {[Singleton new] eq [Singleton new]}
-1
-
-
-

A Singleton Meta-class

-

Alternatively, we can follow a more generic approach and define a -metaclass which allows to define several application classes as -singletons. The metaclass has the most general metaclass nx::Class -as superclass. In contrary to the example obove, the create method -is not defined as a class method, but it will be inherited to its -instances (to the application classes).

-
-
-
nx::Class create Singleton -superclass nx::Class {
-  #
-  # We overload the system method "create". In the modified method we
-  # save the created instance in the instance variable named
-  # "instance"
-  #
-  :variable instance:object
-
-  :public method create {args} {
-    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
-  }
-}
-

Create an application class named Counter as a singleton:

-
-
-
% Singleton create Counter
-::Counter
-

Calling Counter new multiple times returns always the same object:

-
-
-
% expr {[Counter new] eq [Counter new]}
-1
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-singleton.tcl + + + + + +
+
+

Rosetta Example: Singleton

+
+ +
+

A Singleton Class

+
+
+
package req nx
+
+nx::Class create Singleton {
+  #
+  # We overload the system method "create". In the modified method we
+  # save the created instance in the instance variable named
+  # "instance"
+  #
+  :variable instance:object
+
+  :public object method create {args} {
+    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
+  }
+}
+
+
+

Demonstrating the behavior in a shell:

+

Calling Singleton new multiple times returns always the same object:

+
+
+
% expr {[Singleton new] eq [Singleton new]}
+1
+
+
+

A Singleton Meta-class

+

Alternatively, we can follow a more generic approach and define a +metaclass which allows to define several application classes as +singletons. The metaclass has the most general metaclass nx::Class +as superclass. In contrary to the example obove, the create method +is not defined as a class method, but it will be inherited to its +instances (to the application classes).

+
+
+
nx::Class create Singleton -superclass nx::Class {
+  #
+  # We overload the system method "create". In the modified method we
+  # save the created instance in the instance variable named
+  # "instance"
+  #
+  :variable instance:object
+
+  :public method create {args} {
+    return [expr {[info exists :instance] ? ${:instance} : [set :instance [next]]}]
+  }
+}
+

Create an application class named Counter as a singleton:

+
+
+
% Singleton create Counter
+::Counter
+

Calling Counter new multiple times returns always the same object:

+
+
+
% expr {[Counter new] eq [Counter new]}
+1
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-sudoku.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-sudoku.html (.../rosetta-sudoku.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-sudoku.html (.../rosetta-sudoku.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,1101 +1,1101 @@ - - - - - -Listing of doc/example-scripts/rosetta-sudoku.tcl - - - - - -
-
-

Rosetta Example: Sudoku

-
-

Solve a partially filled-in 9x9 Sudoku grid and display the result -in a human-readable format. For detailed description of this -example, see http://rosettacode.org/wiki/Sudoku_Solver

-

This implementation is based on http://wiki.tcl.tk/19934

-
-
-
package require nx
-

The class Sudoku implements the basic interface to a sudoku 9x9 -board to load/dump data and to set/access cells, rows, columns and -regions.

-
-
-
nx::Class create Sudoku {
-
-    :variable board
-
-    # Setup an array from 0..9 to ease iterations over the cells of
-    # lines and columns.
-    for {set i 0} {$i < 9} {incr i} {lappend positions $i}
-    :variable positions $positions
-
-    :public method load {data} {
-        #
-        # Load a 9x9 partially solved sudoku. The unsolved cells are
-        # represented by a@ symbols.
-        #
-        set error "data must be a 9-element list, each element also being a\
-                list of 9 numbers from 1 to 9 or blank or an @ symbol."
-        if {[llength $data] != 9} {
-            error $error
-        }
-        foreach y ${:positions} {
-            set row [lindex $data $y]
-            if {[llength $row] != 9} {
-                error $error
-            }
-            foreach x ${:positions} {
-                set cell [lindex $row $x]
-                if {![regexp {^[@1-9]?$} $cell]} {
-                    error $cell-$error
-                }
-                if {$cell eq "@"} {set cell ""}
-                :set $x $y $cell
-            }
-        }
-    }
-
-    :public method dump {-pretty-print:switch} {
-        #
-        # Output the current state of the sudoku either as list or in
-        # a pretty-print style.
-        #
-        set rows [lmap y ${:positions} {:getRow 0 $y}]
-        if {${pretty-print}} {
-            set result +-----+-----+-----+\n
-            foreach line $rows postline {0 0 1 0 0 1 0 0 1} {
-                append result |[lrange $line 0 2]|[lrange $line 3 5]|[lrange $line 6 8]|\n
-                if {$postline} {
-                    append result +-----+-----+-----+\n
-                }
-            }
-            return $result
-        } else {
-            return $rows
-        }
-    }
-
-    :method log {msg} {
-        #puts "log: $msg"
-    }
-
-    :method set {x y value:integer,0..1} {
-        #
-        # Set cell at position x,y to the given value or empty.
-        #
-        if {$value<1 || $value>9} {
-            set :board($x,$y) {}
-        } else {
-            set :board($x,$y) $value
-        }
-    }
-    :method get {x y} {
-        #
-        # Get value of cell at position x, y.
-        #
-        return [set :board($x,$y)]
-    }
-
-    :method getRow {x y} {
-        #
-        # Return a row at constant position y.
-        #
-        return [lmap x ${:positions} {:get $x $y}]
-    }
-    :method getCol {x y} {
-        #
-        # Return a column at constant position x.
-        #
-        return [lmap y ${:positions} {:get $x $y}]
-    }
-
-    :method getRegion {x y} {
-        #
-        # Return a 3x3 region
-        #
-        set xR [expr {($x/3)*3}]
-        set yR [expr {($y/3)*3}]
-        set regn {}
-        for {set x $xR} {$x < $xR+3} {incr x} {
-            for {set y $yR} {$y < $yR+3} {incr y} {
-                lappend regn [:get $x $y]
-            }
-        }
-        return $regn
-    }
-}
-
-

The class SudokuSolver inherits from Sudoku, and adds the -ability to solve a given Sudoku game. The method solve applies all -rules for each unsolved cell until it finds a safe solution.

-
-
-
-nx::Class create SudokuSolver -superclass Sudoku {
-
-    :public method validchoices {x y} {
-        set v [:get $x $y]
-        if {$v ne {}} {
-            return $v
-        }
-
-        set row [:getRow $x $y]
-        set col [:getCol $x $y]
-        set regn [:getRegion $x $y]
-        set eliminate [list {*}$row {*}$col {*}$regn]
-        set eliminate [lsearch -all -inline -not $eliminate {}]
-        set eliminate [lsort -unique $eliminate]
-
-        set choices {}
-        for {set c 1} {$c < 10} {incr c} {
-            if {$c ni $eliminate} {
-                lappend choices $c
-            }
-        }
-        if {[llength $choices]==0} {
-            error "No choices left for square $x,$y"
-        }
-        return $choices
-    }
-
-    :method completion {} {
-        #
-        # Return the number of already solved items.
-        #
-        return [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}]
-    }
-
-    :public method solve {} {
-        #
-        # Try to solve the sudoku by applying the provided rules.
-        #
-        while {1} {
-            set begin [:completion]
-            foreach y ${:positions} {
-                foreach x ${:positions} {
-                    if {[:get $x $y] eq ""} {
-                        foreach rule [Rule info instances] {
-                            set c [$rule solve [self] $x $y]
-                            if {$c} {
-                                :set $x $y $c
-                                :log "[$rule info class] solved [self] at $x,$y for $c"
-                                break
-                            }
-                        }
-                    }
-                }
-            }
-            set end [:completion]
-            if {$end == 81} {
-                :log "Finished solving!"
-                break
-            } elseif {$begin == $end} {
-                :log "A round finished without solving any squares, giving up."
-                break
-            }
-        }
-    }
-}
-
-

The class rule provides "solve" as public interface for all rule -objects. The rule objects apply their logic to the values -passed in and return either 0 or a number to allocate to the -requested square.

-
-
-
nx::Class create Rule {
-
-    :public method solve {hSudoku:object,type=::SudokuSolver x y} {
-        :Solve $hSudoku $x $y [$hSudoku validchoices $x $y]
-    }
-
-    # Get all the allocated numbers for each square in the the row, column, and
-    # region containing $x,$y. If there is only one unallocated number among all
-    # three groups, it must be allocated at $x,$y
-    :create ruleOnlyChoice {
-        :object method Solve {hSudoku x y choices} {
-            if {[llength $choices] == 1} {
-                return $choices
-            } else {
-                return 0
-            }
-        }
-    }
-
-    # Test each column to determine if $choice is an invalid choice for all other
-    # columns in row $X. If it is, it must only go in square $x,$y.
-    :create RuleColumnChoice {
-        :object method Solve {hSudoku x y choices} {
-            foreach choice $choices {
-                set failed 0
-                for {set x2 0} {$x2 < 9} {incr x2} {
-                    if {$x2 != $x && $choice in [$hSudoku validchoices $x2 $y]} {
-                        set failed 1
-                        break
-                    }
-                }
-                if {!$failed} {return $choice}
-            }
-            return 0
-        }
-    }
-
-    # Test each row to determine if $choice is an invalid choice for all other
-    # rows in column $y. If it is, it must only go in square $x,$y.
-    :create RuleRowChoice {
-        :object method Solve {hSudoku x y choices} {
-            foreach choice $choices {
-                set failed 0
-                for {set y2 0} {$y2 < 9} {incr y2} {
-                    if {$y2 != $y && $choice in [$hSudoku validchoices $x $y2]} {
-                        set failed 1
-                        break
-                    }
-                }
-                if {!$failed} {return $choice}
-            }
-            return 0
-        }
-    }
-
-    # Test each square in the region occupied by $x,$y to determine if $choice is
-    # an invalid choice for all other squares in that region. If it is, it must
-    # only go in square $x,$y.
-    :create RuleRegionChoice {
-        :object method Solve {hSudoku x y choices} {
-            foreach choice $choices {
-                set failed 0
-                set regnX [expr {($x/3)*3}]
-                set regnY [expr {($y/3)*3}]
-                for {set y2 $regnY} {$y2 < $regnY+3} {incr y2} {
-                    for {set x2 $regnX} {$x2 < $regnX+3} {incr x2} {
-                        if {
-                            ($x2!=$x || $y2!=$y)
-                            && $choice in [$hSudoku validchoices $x2 $y2]
-                        } then {
-                            set failed 1
-                            break
-                        }
-                    }
-                }
-                if {!$failed} {return $choice}
-            }
-            return 0
-        }
-    }
-}
-
-SudokuSolver create sudoku {
-
-    :load {
-        {3 9 4    @ @ 2    6 7 @}
-        {@ @ @    3 @ @    4 @ @}
-        {5 @ @    6 9 @    @ 2 @}
-
-        {@ 4 5    @ @ @    9 @ @}
-        {6 @ @    @ @ @    @ @ 7}
-        {@ @ 7    @ @ @    5 8 @}
-
-        {@ 1 @    @ 6 7    @ @ 8}
-        {@ @ 9    @ @ 8    @ @ @}
-        {@ 2 6    4 @ @    7 3 5}
-    }
-    :solve
-
-    puts [:dump -pretty-print]
-}
-

The dump method outputs the solved Sudoku:

-
-
-
+-----+-----+-----+
-|3 9 4|8 5 2|6 7 1|
-|2 6 8|3 7 1|4 5 9|
-|5 7 1|6 9 4|8 2 3|
-+-----+-----+-----+
-|1 4 5|7 8 3|9 6 2|
-|6 8 2|9 4 5|3 1 7|
-|9 3 7|1 2 6|5 8 4|
-+-----+-----+-----+
-|4 1 3|5 6 7|2 9 8|
-|7 5 9|2 3 8|1 4 6|
-|8 2 6|4 1 9|7 3 5|
-+-----+-----+-----+
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-sudoku.tcl + + + + + +
+
+

Rosetta Example: Sudoku

+
+

Solve a partially filled-in 9x9 Sudoku grid and display the result +in a human-readable format. For detailed description of this +example, see http://rosettacode.org/wiki/Sudoku_Solver

+

This implementation is based on http://wiki.tcl.tk/19934

+
+
+
package require nx
+

The class Sudoku implements the basic interface to a sudoku 9x9 +board to load/dump data and to set/access cells, rows, columns and +regions.

+
+
+
nx::Class create Sudoku {
+
+    :variable board
+
+    # Setup an array from 0..9 to ease iterations over the cells of
+    # lines and columns.
+    for {set i 0} {$i < 9} {incr i} {lappend positions $i}
+    :variable positions $positions
+
+    :public method load {data} {
+        #
+        # Load a 9x9 partially solved sudoku. The unsolved cells are
+        # represented by a@ symbols.
+        #
+        set error "data must be a 9-element list, each element also being a\
+                list of 9 numbers from 1 to 9 or blank or an @ symbol."
+        if {[llength $data] != 9} {
+            error $error
+        }
+        foreach y ${:positions} {
+            set row [lindex $data $y]
+            if {[llength $row] != 9} {
+                error $error
+            }
+            foreach x ${:positions} {
+                set cell [lindex $row $x]
+                if {![regexp {^[@1-9]?$} $cell]} {
+                    error $cell-$error
+                }
+                if {$cell eq "@"} {set cell ""}
+                :set $x $y $cell
+            }
+        }
+    }
+
+    :public method dump {-pretty-print:switch} {
+        #
+        # Output the current state of the sudoku either as list or in
+        # a pretty-print style.
+        #
+        set rows [lmap y ${:positions} {:getRow 0 $y}]
+        if {${pretty-print}} {
+            set result +-----+-----+-----+\n
+            foreach line $rows postline {0 0 1 0 0 1 0 0 1} {
+                append result |[lrange $line 0 2]|[lrange $line 3 5]|[lrange $line 6 8]|\n
+                if {$postline} {
+                    append result +-----+-----+-----+\n
+                }
+            }
+            return $result
+        } else {
+            return $rows
+        }
+    }
+
+    :method log {msg} {
+        #puts "log: $msg"
+    }
+
+    :method set {x y value:integer,0..1} {
+        #
+        # Set cell at position x,y to the given value or empty.
+        #
+        if {$value<1 || $value>9} {
+            set :board($x,$y) {}
+        } else {
+            set :board($x,$y) $value
+        }
+    }
+    :method get {x y} {
+        #
+        # Get value of cell at position x, y.
+        #
+        return [set :board($x,$y)]
+    }
+
+    :method getRow {x y} {
+        #
+        # Return a row at constant position y.
+        #
+        return [lmap x ${:positions} {:get $x $y}]
+    }
+    :method getCol {x y} {
+        #
+        # Return a column at constant position x.
+        #
+        return [lmap y ${:positions} {:get $x $y}]
+    }
+
+    :method getRegion {x y} {
+        #
+        # Return a 3x3 region
+        #
+        set xR [expr {($x/3)*3}]
+        set yR [expr {($y/3)*3}]
+        set regn {}
+        for {set x $xR} {$x < $xR+3} {incr x} {
+            for {set y $yR} {$y < $yR+3} {incr y} {
+                lappend regn [:get $x $y]
+            }
+        }
+        return $regn
+    }
+}
+
+

The class SudokuSolver inherits from Sudoku, and adds the +ability to solve a given Sudoku game. The method solve applies all +rules for each unsolved cell until it finds a safe solution.

+
+
+
+nx::Class create SudokuSolver -superclass Sudoku {
+
+    :public method validchoices {x y} {
+        set v [:get $x $y]
+        if {$v ne {}} {
+            return $v
+        }
+
+        set row [:getRow $x $y]
+        set col [:getCol $x $y]
+        set regn [:getRegion $x $y]
+        set eliminate [list {*}$row {*}$col {*}$regn]
+        set eliminate [lsearch -all -inline -not $eliminate {}]
+        set eliminate [lsort -unique $eliminate]
+
+        set choices {}
+        for {set c 1} {$c < 10} {incr c} {
+            if {$c ni $eliminate} {
+                lappend choices $c
+            }
+        }
+        if {[llength $choices]==0} {
+            error "No choices left for square $x,$y"
+        }
+        return $choices
+    }
+
+    :method completion {} {
+        #
+        # Return the number of already solved items.
+        #
+        return [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}]
+    }
+
+    :public method solve {} {
+        #
+        # Try to solve the sudoku by applying the provided rules.
+        #
+        while {1} {
+            set begin [:completion]
+            foreach y ${:positions} {
+                foreach x ${:positions} {
+                    if {[:get $x $y] eq ""} {
+                        foreach rule [Rule info instances] {
+                            set c [$rule solve [self] $x $y]
+                            if {$c} {
+                                :set $x $y $c
+                                :log "[$rule info class] solved [self] at $x,$y for $c"
+                                break
+                            }
+                        }
+                    }
+                }
+            }
+            set end [:completion]
+            if {$end == 81} {
+                :log "Finished solving!"
+                break
+            } elseif {$begin == $end} {
+                :log "A round finished without solving any squares, giving up."
+                break
+            }
+        }
+    }
+}
+
+

The class rule provides "solve" as public interface for all rule +objects. The rule objects apply their logic to the values +passed in and return either 0 or a number to allocate to the +requested square.

+
+
+
nx::Class create Rule {
+
+    :public method solve {hSudoku:object,type=::SudokuSolver x y} {
+        :Solve $hSudoku $x $y [$hSudoku validchoices $x $y]
+    }
+
+    # Get all the allocated numbers for each square in the the row, column, and
+    # region containing $x,$y. If there is only one unallocated number among all
+    # three groups, it must be allocated at $x,$y
+    :create ruleOnlyChoice {
+        :object method Solve {hSudoku x y choices} {
+            if {[llength $choices] == 1} {
+                return $choices
+            } else {
+                return 0
+            }
+        }
+    }
+
+    # Test each column to determine if $choice is an invalid choice for all other
+    # columns in row $X. If it is, it must only go in square $x,$y.
+    :create RuleColumnChoice {
+        :object method Solve {hSudoku x y choices} {
+            foreach choice $choices {
+                set failed 0
+                for {set x2 0} {$x2 < 9} {incr x2} {
+                    if {$x2 != $x && $choice in [$hSudoku validchoices $x2 $y]} {
+                        set failed 1
+                        break
+                    }
+                }
+                if {!$failed} {return $choice}
+            }
+            return 0
+        }
+    }
+
+    # Test each row to determine if $choice is an invalid choice for all other
+    # rows in column $y. If it is, it must only go in square $x,$y.
+    :create RuleRowChoice {
+        :object method Solve {hSudoku x y choices} {
+            foreach choice $choices {
+                set failed 0
+                for {set y2 0} {$y2 < 9} {incr y2} {
+                    if {$y2 != $y && $choice in [$hSudoku validchoices $x $y2]} {
+                        set failed 1
+                        break
+                    }
+                }
+                if {!$failed} {return $choice}
+            }
+            return 0
+        }
+    }
+
+    # Test each square in the region occupied by $x,$y to determine if $choice is
+    # an invalid choice for all other squares in that region. If it is, it must
+    # only go in square $x,$y.
+    :create RuleRegionChoice {
+        :object method Solve {hSudoku x y choices} {
+            foreach choice $choices {
+                set failed 0
+                set regnX [expr {($x/3)*3}]
+                set regnY [expr {($y/3)*3}]
+                for {set y2 $regnY} {$y2 < $regnY+3} {incr y2} {
+                    for {set x2 $regnX} {$x2 < $regnX+3} {incr x2} {
+                        if {
+                            ($x2!=$x || $y2!=$y)
+                            && $choice in [$hSudoku validchoices $x2 $y2]
+                        } then {
+                            set failed 1
+                            break
+                        }
+                    }
+                }
+                if {!$failed} {return $choice}
+            }
+            return 0
+        }
+    }
+}
+
+SudokuSolver create sudoku {
+
+    :load {
+        {3 9 4    @ @ 2    6 7 @}
+        {@ @ @    3 @ @    4 @ @}
+        {5 @ @    6 9 @    @ 2 @}
+
+        {@ 4 5    @ @ @    9 @ @}
+        {6 @ @    @ @ @    @ @ 7}
+        {@ @ 7    @ @ @    5 8 @}
+
+        {@ 1 @    @ 6 7    @ @ 8}
+        {@ @ 9    @ @ 8    @ @ @}
+        {@ 2 6    4 @ @    7 3 5}
+    }
+    :solve
+
+    puts [:dump -pretty-print]
+}
+

The dump method outputs the solved Sudoku:

+
+
+
+-----+-----+-----+
+|3 9 4|8 5 2|6 7 1|
+|2 6 8|3 7 1|4 5 9|
+|5 7 1|6 9 4|8 2 3|
++-----+-----+-----+
+|1 4 5|7 8 3|9 6 2|
+|6 8 2|9 4 5|3 1 7|
+|9 3 7|1 2 6|5 8 4|
++-----+-----+-----+
+|4 1 3|5 6 7|2 9 8|
+|7 5 9|2 3 8|1 4 6|
+|8 2 6|4 1 9|7 3 5|
++-----+-----+-----+
+
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-tokenizer.html =================================================================== diff -u -rb689afd2df2077ab7d033a0a411808fef36149b1 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-tokenizer.html (.../rosetta-tokenizer.html) (revision b689afd2df2077ab7d033a0a411808fef36149b1) +++ doc/example-scripts/rosetta-tokenizer.html (.../rosetta-tokenizer.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,850 +1,850 @@ - - - - - -Listing of doc/example-scripts/rosetta-tokenizer.tcl - - - - - -
-
-
-

Assumes Tcl 8.6 (couroutine support)

-
-
-
if {[catch {package req Tcl 8.6}]} return
-
-
-
-

Rosetta example: Tokenize a string with escaping

-
-

Write a class which allows for splitting a string at each non-escaped -occurrence of a separator character.

- -
-
-
package req nx
-
-nx::Class create Tokenizer {
-    :property s:required
-    :method init {} {
-        :require namespace
-        set coro [coroutine [current]::nextCoro [current] iter ${:s}]
-        :public object forward next $coro
-    }
-    :public method iter {s} {
-        yield [info coroutine]
-        for {set i 0} {$i < [string length $s]} {incr i} {
-            yield [string index $s $i]
-        }
-        return -code break
-    }
-    :public object method tokenize {{-sep |} {-escape ^} s} {
-        set t [[current] new -s $s]
-        set part ""
-        set parts [list]
-        while {1} {
-            set c [$t next]
-            if {$c eq $escape} {
-                append part [$t next]
-            } elseif {$c eq $sep} {
-                lappend parts $part
-                set part ""
-            } else {
-                append part $c
-            }
-        }
-        lappend parts $part
-        return $parts
-    }
-}
-

Run some tests incl. the escape character:

-
-
-
% Tokenizer tokenize -sep | -escape ^ ^|
-|
-% Tokenizer tokenize -sep | -escape ^ ^|^|
-||
-% Tokenizer tokenize -sep | -escape ^ ^^^|
-^|
-% Tokenizer tokenize -sep | -escape ^ |
-{} {}
-

Test for the output required by the Rosetta example:

-
-
-
% Tokenizer tokenize -sep | -escape ^ one^|uno||three^^^^|four^^^|^cuatro|
-one|uno {} three^^ four^|cuatro {}
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-tokenizer.tcl + + + + + +
+
+
+

Assumes Tcl 8.6 (couroutine support)

+
+
+
if {[catch {package req Tcl 8.6}]} return
+
+
+
+

Rosetta example: Tokenize a string with escaping

+
+

Write a class which allows for splitting a string at each non-escaped +occurrence of a separator character.

+ +
+
+
package req nx
+
+nx::Class create Tokenizer {
+    :property s:required
+    :method init {} {
+        :require namespace
+        set coro [coroutine [current]::nextCoro [current] iter ${:s}]
+        :public object forward next $coro
+    }
+    :public method iter {s} {
+        yield [info coroutine]
+        for {set i 0} {$i < [string length $s]} {incr i} {
+            yield [string index $s $i]
+        }
+        return -code break
+    }
+    :public object method tokenize {{-sep |} {-escape ^} s} {
+        set t [[current] new -s $s]
+        set part ""
+        set parts [list]
+        while {1} {
+            set c [$t next]
+            if {$c eq $escape} {
+                append part [$t next]
+            } elseif {$c eq $sep} {
+                lappend parts $part
+                set part ""
+            } else {
+                append part $c
+            }
+        }
+        lappend parts $part
+        return $parts
+    }
+}
+

Run some tests incl. the escape character:

+
+
+
% Tokenizer tokenize -sep | -escape ^ ^|
+|
+% Tokenizer tokenize -sep | -escape ^ ^|^|
+||
+% Tokenizer tokenize -sep | -escape ^ ^^^|
+^|
+% Tokenizer tokenize -sep | -escape ^ |
+{} {}
+

Test for the output required by the Rosetta example:

+
+
+
% Tokenizer tokenize -sep | -escape ^ one^|uno||three^^^^|four^^^|^cuatro|
+one|uno {} three^^ four^|cuatro {}
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-tree.html =================================================================== diff -u -rf769aa3bf33311a58e91f5041130d34daf840e7c -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-tree.html (.../rosetta-tree.html) (revision f769aa3bf33311a58e91f5041130d34daf840e7c) +++ doc/example-scripts/rosetta-tree.html (.../rosetta-tree.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,872 +1,872 @@ - - - - - -Listing of doc/example-scripts/rosetta-tree.tcl - - - - - -
-
-

Rosetta example:https://rosettacode.org/wiki/Tree_traversal

-
-

Implement a binary tree structure, with each node carrying an -integer as a node label, and four traversal strategies: pre-order, -in-order, postorder, and levelorder traversals.

- -
-
-
package req nx
-

The class Tree implements the basic binary composite structure (left, right).

-
-
-
nx::Class create Tree {
-    :property -accessor public value:required
-    :property -accessor public left:object,type=[current]
-    :property -accessor public right:object,type=[current]
-
-    :public method traverse {order} {
-        set list {}
-        :$order v {
-            lappend list $v
-        }
-        return $list
-    }
-
-    # Traversal methods
-    :public method preOrder {varName script {level 0}} {
-        upvar [incr level] $varName var
-        set var ${:value}
-        uplevel $level $script
-        if {[info exists :left]} {${:left} preOrder $varName $script $level}
-        if {[info exists :right]} {${:right} preOrder $varName $script $level}
-    }
-
-    :public method inOrder {varName script {level 0}} {
-        upvar [incr level] $varName var
-        if {[info exists :left]} {${:left} inOrder $varName $script $level}
-        set var ${:value}
-        uplevel $level $script
-        if {[info exists :right]} {${:right} inOrder $varName $script $level}
-    }
-    :public method postOrder {varName script {level 0}} {
-        upvar [incr level] $varName var
-        if {[info exists :left]} {${:left} postOrder $varName $script $level}
-        if {[info exists :right]} {${:right} postOrder $varName $script $level}
-        set var ${:value}
-        uplevel $level $script
-    }
-    :public method levelOrder {varName script} {
-        upvar 1 $varName var
-        set nodes [list [current]]
-        while {[llength $nodes] > 0} {
-            set nodes [lassign $nodes n]
-            set var [$n value get]
-            uplevel 1 $script
-            if {[$n eval {info exists :left}]} {lappend nodes [$n left get]}
-            if {[$n eval {info exists :right}]} {lappend nodes [$n right get]}
-        }
-    }
-}
-

This is a factory method to build up the object tree recursively -from a nested Tcl list. Note that we create left and right childs by -nesting them in their parent, this provides for a cascading cleanup -of an entire tree (there is no need for an explicit cascading of -destroy methods down the composite).

-
-
-
Tree public object method newFromList {-parent l} {
-    lassign $l value left right
-    set n [:new {*}[expr {[info exists parent]?[list -childof $parent]:""}] -value $value]
-    set props [list]
-    if {$left ne ""} {lappend props -left [:newFromList -parent $n $left]}
-    if {$right ne ""} {lappend props -right [:newFromList -parent $n $right]}
-    $n configure {*}$props
-    return $n
-}
-

Run the required tests:

-
-
-
set t [Tree newFromList {1 {2 {4 7} 5} {3 {6 8 9}}}]
-% $t traverse preOrder
-1 2 4 7 5 3 6 8 9
-% $t traverse inOrder
-7 4 2 5 1 8 6 9 3
-% $t traverse postOrder
-7 4 5 2 8 9 6 3 1
-% $t traverse levelOrder
-1 2 3 4 5 6 7 8 9
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-tree.tcl + + + + + +
+
+

Rosetta example:https://rosettacode.org/wiki/Tree_traversal

+
+

Implement a binary tree structure, with each node carrying an +integer as a node label, and four traversal strategies: pre-order, +in-order, postorder, and levelorder traversals.

+ +
+
+
package req nx
+

The class Tree implements the basic binary composite structure (left, right).

+
+
+
nx::Class create Tree {
+    :property -accessor public value:required
+    :property -accessor public left:object,type=[current]
+    :property -accessor public right:object,type=[current]
+
+    :public method traverse {order} {
+        set list {}
+        :$order v {
+            lappend list $v
+        }
+        return $list
+    }
+
+    # Traversal methods
+    :public method preOrder {varName script {level 0}} {
+        upvar [incr level] $varName var
+        set var ${:value}
+        uplevel $level $script
+        if {[info exists :left]} {${:left} preOrder $varName $script $level}
+        if {[info exists :right]} {${:right} preOrder $varName $script $level}
+    }
+
+    :public method inOrder {varName script {level 0}} {
+        upvar [incr level] $varName var
+        if {[info exists :left]} {${:left} inOrder $varName $script $level}
+        set var ${:value}
+        uplevel $level $script
+        if {[info exists :right]} {${:right} inOrder $varName $script $level}
+    }
+    :public method postOrder {varName script {level 0}} {
+        upvar [incr level] $varName var
+        if {[info exists :left]} {${:left} postOrder $varName $script $level}
+        if {[info exists :right]} {${:right} postOrder $varName $script $level}
+        set var ${:value}
+        uplevel $level $script
+    }
+    :public method levelOrder {varName script} {
+        upvar 1 $varName var
+        set nodes [list [current]]
+        while {[llength $nodes] > 0} {
+            set nodes [lassign $nodes n]
+            set var [$n value get]
+            uplevel 1 $script
+            if {[$n eval {info exists :left}]} {lappend nodes [$n left get]}
+            if {[$n eval {info exists :right}]} {lappend nodes [$n right get]}
+        }
+    }
+}
+

This is a factory method to build up the object tree recursively +from a nested Tcl list. Note that we create left and right childs by +nesting them in their parent, this provides for a cascading cleanup +of an entire tree (there is no need for an explicit cascading of +destroy methods down the composite).

+
+
+
Tree public object method newFromList {-parent l} {
+    lassign $l value left right
+    set n [:new {*}[expr {[info exists parent]?[list -childof $parent]:""}] -value $value]
+    set props [list]
+    if {$left ne ""} {lappend props -left [:newFromList -parent $n $left]}
+    if {$right ne ""} {lappend props -right [:newFromList -parent $n $right]}
+    $n configure {*}$props
+    return $n
+}
+

Run the required tests:

+
+
+
set t [Tree newFromList {1 {2 {4 7} 5} {3 {6 8 9}}}]
+% $t traverse preOrder
+1 2 4 7 5 3 6 8 9
+% $t traverse inOrder
+7 4 2 5 1 8 6 9 3
+% $t traverse postOrder
+7 4 5 2 8 9 6 3 1
+% $t traverse levelOrder
+1 2 3 4 5 6 7 8 9
+
+
+
+

+ + + Index: doc/example-scripts/rosetta-unknown-method.html =================================================================== diff -u -r24cb8f4bffd49c9375c1c64aa0610933b62511bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/rosetta-unknown-method.html (.../rosetta-unknown-method.html) (revision 24cb8f4bffd49c9375c1c64aa0610933b62511bb) +++ doc/example-scripts/rosetta-unknown-method.html (.../rosetta-unknown-method.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,819 +1,819 @@ - - - - - -Listing of doc/example-scripts/rosetta-unknown-method.tcl - - - - - -
-
-

Rosetta Example: Respond to an unknown method call

-
- -
-
-
package req nx
-

Define a class Example modelled after the -Python version of Rosetta:

-
-
-
nx::Class create Example {
-
-  :public method foo {} {return "This is foo."}
-  :public method bar {} {return "This is bar."}
-
-  :method unknown {method args} {
-    set result "Tried to handle unknown method '$method'."
-    if {[llength $args] > 0} {
-      append result " It had arguments '$args'."
-    }
-    return $result
-  }
-}
-
-

Demonstrating the behavior in a shell:

-

Create an instance of the class Example:

-
-
-
% set e [Example new]
-
-% $e foo
-This is foo.
-
-% $e bar
-This is bar.
-
-% $e grill
-Tried to handle unknown method 'grill'.
-
-% $e ding dong
-Tried to handle unknown method 'ding'. It had arguments 'dong'.
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/rosetta-unknown-method.tcl + + + + + +
+
+

Rosetta Example: Respond to an unknown method call

+
+ +
+
+
package req nx
+

Define a class Example modelled after the +Python version of Rosetta:

+
+
+
nx::Class create Example {
+
+  :public method foo {} {return "This is foo."}
+  :public method bar {} {return "This is bar."}
+
+  :method unknown {method args} {
+    set result "Tried to handle unknown method '$method'."
+    if {[llength $args] > 0} {
+      append result " It had arguments '$args'."
+    }
+    return $result
+  }
+}
+
+

Demonstrating the behavior in a shell:

+

Create an instance of the class Example:

+
+
+
% set e [Example new]
+
+% $e foo
+This is foo.
+
+% $e bar
+This is bar.
+
+% $e grill
+Tried to handle unknown method 'grill'.
+
+% $e ding dong
+Tried to handle unknown method 'ding'. It had arguments 'dong'.
+
+
+
+
+

+ + + Index: doc/example-scripts/ruby-mixins.html =================================================================== diff -u -r93bb0947d582f274afb1cdbc885909d55e100b36 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/ruby-mixins.html (.../ruby-mixins.html) (revision 93bb0947d582f274afb1cdbc885909d55e100b36) +++ doc/example-scripts/ruby-mixins.html (.../ruby-mixins.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,1136 +1,1136 @@ - - - - - -Listing of doc/example-scripts/ruby-mixins.tcl - - - - - -
-
-

Design study to show the differences between decorator mixin classes and Ruby’s mixin modules

-
-

This example shows that the dynamic class structure of NX (and -XOTcl) is able to support Ruby style mixins (called modules) and -decorator style mixins (named after the design pattern Decorator) -in the same script.

-
-
-
nx::test configure -count 1
-

One important difference between mixin classes in NX and Ruby’s -mixins is the precedence order. While in NX, mixins are decorators -(the mixins have higher precedence than the intrinsic classes, -therefore a mixin class can overload the methods of the current -class and its subclasses), the mixins of Ruby have a lower -precedence (they extend the base behavior; although Ruby’s modules -are not full classes, they are folded into the intrinsic class -hierarchy). Therefore, a Ruby style mixin can be refined by the -class, into which it is mixed in (or by a subclass). Decorator style -mixins modify the behavior of a full intrinsic class tree, while -Ruby style mixins are compositional units for a single class.

-

To show the differences, we define the method module, which -behaves somewhat similar to Ruby’s module command. This method -adds the provided class to the precedence order after the current -class. The easiest way to achieve this is via multiple inheritance -(i.e. via the superclass relationship).

-
-
-
package req nx
-
-nx::Class eval {
-  :protected method module {name:class} {
-    nsf::relation [self] superclass [concat $name [:info superclass]]
-  }
-}
-

For illustration of the behavior of module we define a class -Enumerable somewhat inspired by the Ruby module with the same -name. We define here just the methods map, each, count, and -count_if.

-
-
-
nx::Class create Enumerable {
-  :property members:0..n
-
-  # The method 'each' applies the provided block on every element of
-  # 'members'
-  :public method each {var block} {
-    foreach member ${:members} {
-      uplevel [list set $var $member]
-      uplevel $block
-    }
-  }
-
-  # The method 'map' applies the provided block on every element of
-  # 'members' and returns a list, where every element is the mapped
-  # result of the source.
-  :public method map {var block} {
-    set result [list]
-    :each $var {
-      uplevel [list set $var [set $var]]
-      lappend result [uplevel $block]
-    }
-    return $result
-  }
-
-  # The method 'count' returns the number of elements.
-  :public method count {} {
-    return [llength ${:members}]
-  }
-
-  # The method 'count_if' returns the number of elements for which
-  # the provided expression is true.
-  :public method count_if {var expr} {
-    set result 0
-    :each $var {
-      incr result [expr $expr]
-    }
-    return $result
-  }
-}
-

After having defined the class Enumerable, we define a class -Group using Enumerable as a Ruby style mixin. This makes -essentially Group a subclass of Enumerable, but with the only -difference that Group might have other superclasses as well.

-
-
-
nx::Class create Group {
-  #
-  # Include the "module" Enumerable
-  #
-  :module Enumerable
-}
-

Define now a group g1 with the three provided members.

-
-
-
% Group create g1 -members {mini trix trax}
-::g1
-

Since the definition of Group includes the module Enumerable, -this class is listed in the precedence order after the class Group:

-
-
-
% g1 info precedence
-::Group ::Enumerable ::nx::Object
-

Certainly, we can call the methods of Enumerable as usual:

-
-
-
% g1 count
-3
-
-% g1 map x {list pre-$x-post}
-pre-mini-post pre-trix-post pre-trax-post
-
-% g1 count_if x {[string match tr*x $x] > 0}
-2
-

To show the difference between a module and a decorator mixin we -define a class named Mix with the single method count, which -wraps the result of the underlaying count method between the -alpha and omega.

-
-
-
nx::Class create Mix {
-  :public method count {} {
-    return [list alpha [next] omega]
-  }
-}
-

When the mixin class is added to g1, it is added to the front of -the precedence list. A decorator is able to modify the behavior of -all of the methods of the class, where it is mixed into.

-
-
-
% g1 object mixin Mix
-::Mix
-
-% g1 info precedence
-::Mix ::Group ::Enumerable ::nx::Object
-
-% g1 count
-alpha 3 omega
-

For the time being, remove the mixin class again.

-
-
-
% g1 object mixin ""
-% g1 info precedence
-::Group ::Enumerable ::nx::Object
-

An important difference between NX/XOTcl style mixins (decorators) -and Ruby style modules is that the decorator will have always a -higher precedence than the intrinsic classes, while the module is -folded into the precedence path.

-

Define a class ATeam that uses Enumerable in the style of a Ruby -module. The class might refine some of the provided methods. We -refined the method each, which is used as well by the other -methods. In general, by defining each one can define very -different kind of enumerators (for lists, databases, etc.).

-

Since Enumerable is a module, the definition of each in the -class ATeam has a higher precedence than the definition in the -class Enumerable. If Enumerable would be a decorator style mixin -class, it would not e possible to refine the definition in the class -ATeam, but maybe via another mixin class.

-
-
-
nx::Class create ATeam {
-  #
-  # Include the "module" Enumerable
-  #
-  :module Enumerable
-
-  #
-  # Overload "each"
-  #
-  :public method each {var block} {
-    foreach member ${:members} {
-      uplevel [list set $var $member-[string length $member]]
-      uplevel $block
-    }
-  }
-
-  #
-  # Use "map", which uses the "each" method defined in this class.
-  #
-  :public method foo {} {
-    return [:map x {string totitle $x}]
-  }
-}
-

Define now a team t1 with the three provided members.

-
-
-
% ATeam create t1 -members {arthur bill chuck}
-::t1
-

As above, the precedence of ATeam is higher than the precedence of -Enumerable. Therefore, the object t1 uses the method each specialized in -class ATeam:

-
-
-
% t1 info precedence
-::ATeam ::Enumerable ::nx::Object
-
-% t1 foo
-Arthur-6 Bill-4 Chuck-5
-

The class ATeam can be specialized further by a class SpecialForce:

-
-
-
nx::Class create SpecialForce -superclass ATeam {
-  # ...
-}
-

Define a special force s1 with the four provided members.

-
-
-
% SpecialForce create s1 -members {Donald Micky Daniel Gustav}
-::s1
-

As above, the precedence of Enumerable is lower then the -precedence of ATeam and Enumerable. Therefore ATeam can refine -the behavior of Enumerable, the class SpecialForce can refine -the behavior of ATeam.

-
-
-
% s1 info precedence
-::SpecialForce ::ATeam ::Enumerable ::nx::Object
-
-% s1 foo
-Donald-6 Micky-5 Daniel-6 Gustav-6
-

Let us look again on decorator style mixin classes. If we add a -per-class mixin to ATeam, the mixin class has highest precedence, -and decorates the instances of ATeam as well the instances of its -specializations (like e.g. SpecialForce).

-
-
-
% ATeam mixin Mix
-::Mix
-
-% s1 info precedence
-::Mix ::SpecialForce ::ATeam ::Enumerable ::nx::Object
-
-% s1 count
-alpha 4 omega
-

This example showed that NX/XOTcl dynamic class structure is able to -support Ruby-style mixins, and decorator style mixins in the same script.

-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/ruby-mixins.tcl + + + + + +
+
+

Design study to show the differences between decorator mixin classes and Ruby’s mixin modules

+
+

This example shows that the dynamic class structure of NX (and +XOTcl) is able to support Ruby style mixins (called modules) and +decorator style mixins (named after the design pattern Decorator) +in the same script.

+
+
+
nx::test configure -count 1
+

One important difference between mixin classes in NX and Ruby’s +mixins is the precedence order. While in NX, mixins are decorators +(the mixins have higher precedence than the intrinsic classes, +therefore a mixin class can overload the methods of the current +class and its subclasses), the mixins of Ruby have a lower +precedence (they extend the base behavior; although Ruby’s modules +are not full classes, they are folded into the intrinsic class +hierarchy). Therefore, a Ruby style mixin can be refined by the +class, into which it is mixed in (or by a subclass). Decorator style +mixins modify the behavior of a full intrinsic class tree, while +Ruby style mixins are compositional units for a single class.

+

To show the differences, we define the method module, which +behaves somewhat similar to Ruby’s module command. This method +adds the provided class to the precedence order after the current +class. The easiest way to achieve this is via multiple inheritance +(i.e. via the superclass relationship).

+
+
+
package req nx
+
+nx::Class eval {
+  :protected method module {name:class} {
+    nsf::relation [self] superclass [concat $name [:info superclass]]
+  }
+}
+

For illustration of the behavior of module we define a class +Enumerable somewhat inspired by the Ruby module with the same +name. We define here just the methods map, each, count, and +count_if.

+
+
+
nx::Class create Enumerable {
+  :property members:0..n
+
+  # The method 'each' applies the provided block on every element of
+  # 'members'
+  :public method each {var block} {
+    foreach member ${:members} {
+      uplevel [list set $var $member]
+      uplevel $block
+    }
+  }
+
+  # The method 'map' applies the provided block on every element of
+  # 'members' and returns a list, where every element is the mapped
+  # result of the source.
+  :public method map {var block} {
+    set result [list]
+    :each $var {
+      uplevel [list set $var [set $var]]
+      lappend result [uplevel $block]
+    }
+    return $result
+  }
+
+  # The method 'count' returns the number of elements.
+  :public method count {} {
+    return [llength ${:members}]
+  }
+
+  # The method 'count_if' returns the number of elements for which
+  # the provided expression is true.
+  :public method count_if {var expr} {
+    set result 0
+    :each $var {
+      incr result [expr $expr]
+    }
+    return $result
+  }
+}
+

After having defined the class Enumerable, we define a class +Group using Enumerable as a Ruby style mixin. This makes +essentially Group a subclass of Enumerable, but with the only +difference that Group might have other superclasses as well.

+
+
+
nx::Class create Group {
+  #
+  # Include the "module" Enumerable
+  #
+  :module Enumerable
+}
+

Define now a group g1 with the three provided members.

+
+
+
% Group create g1 -members {mini trix trax}
+::g1
+

Since the definition of Group includes the module Enumerable, +this class is listed in the precedence order after the class Group:

+
+
+
% g1 info precedence
+::Group ::Enumerable ::nx::Object
+

Certainly, we can call the methods of Enumerable as usual:

+
+
+
% g1 count
+3
+
+% g1 map x {list pre-$x-post}
+pre-mini-post pre-trix-post pre-trax-post
+
+% g1 count_if x {[string match tr*x $x] > 0}
+2
+

To show the difference between a module and a decorator mixin we +define a class named Mix with the single method count, which +wraps the result of the underlaying count method between the +alpha and omega.

+
+
+
nx::Class create Mix {
+  :public method count {} {
+    return [list alpha [next] omega]
+  }
+}
+

When the mixin class is added to g1, it is added to the front of +the precedence list. A decorator is able to modify the behavior of +all of the methods of the class, where it is mixed into.

+
+
+
% g1 object mixin Mix
+::Mix
+
+% g1 info precedence
+::Mix ::Group ::Enumerable ::nx::Object
+
+% g1 count
+alpha 3 omega
+

For the time being, remove the mixin class again.

+
+
+
% g1 object mixin ""
+% g1 info precedence
+::Group ::Enumerable ::nx::Object
+

An important difference between NX/XOTcl style mixins (decorators) +and Ruby style modules is that the decorator will have always a +higher precedence than the intrinsic classes, while the module is +folded into the precedence path.

+

Define a class ATeam that uses Enumerable in the style of a Ruby +module. The class might refine some of the provided methods. We +refined the method each, which is used as well by the other +methods. In general, by defining each one can define very +different kind of enumerators (for lists, databases, etc.).

+

Since Enumerable is a module, the definition of each in the +class ATeam has a higher precedence than the definition in the +class Enumerable. If Enumerable would be a decorator style mixin +class, it would not e possible to refine the definition in the class +ATeam, but maybe via another mixin class.

+
+
+
nx::Class create ATeam {
+  #
+  # Include the "module" Enumerable
+  #
+  :module Enumerable
+
+  #
+  # Overload "each"
+  #
+  :public method each {var block} {
+    foreach member ${:members} {
+      uplevel [list set $var $member-[string length $member]]
+      uplevel $block
+    }
+  }
+
+  #
+  # Use "map", which uses the "each" method defined in this class.
+  #
+  :public method foo {} {
+    return [:map x {string totitle $x}]
+  }
+}
+

Define now a team t1 with the three provided members.

+
+
+
% ATeam create t1 -members {arthur bill chuck}
+::t1
+

As above, the precedence of ATeam is higher than the precedence of +Enumerable. Therefore, the object t1 uses the method each specialized in +class ATeam:

+
+
+
% t1 info precedence
+::ATeam ::Enumerable ::nx::Object
+
+% t1 foo
+Arthur-6 Bill-4 Chuck-5
+

The class ATeam can be specialized further by a class SpecialForce:

+
+
+
nx::Class create SpecialForce -superclass ATeam {
+  # ...
+}
+

Define a special force s1 with the four provided members.

+
+
+
% SpecialForce create s1 -members {Donald Micky Daniel Gustav}
+::s1
+

As above, the precedence of Enumerable is lower then the +precedence of ATeam and Enumerable. Therefore ATeam can refine +the behavior of Enumerable, the class SpecialForce can refine +the behavior of ATeam.

+
+
+
% s1 info precedence
+::SpecialForce ::ATeam ::Enumerable ::nx::Object
+
+% s1 foo
+Donald-6 Micky-5 Daniel-6 Gustav-6
+

Let us look again on decorator style mixin classes. If we add a +per-class mixin to ATeam, the mixin class has highest precedence, +and decorates the instances of ATeam as well the instances of its +specializations (like e.g. SpecialForce).

+
+
+
% ATeam mixin Mix
+::Mix
+
+% s1 info precedence
+::Mix ::SpecialForce ::ATeam ::Enumerable ::nx::Object
+
+% s1 count
+alpha 4 omega
+

This example showed that NX/XOTcl dynamic class structure is able to +support Ruby-style mixins, and decorator style mixins in the same script.

+
+
+
+

+ + + Index: doc/example-scripts/tk-geo.html =================================================================== diff -u -rbcd5af5620d7d282eb203c315ccb8372332eff11 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-geo.html (.../tk-geo.html) (revision bcd5af5620d7d282eb203c315ccb8372332eff11) +++ doc/example-scripts/tk-geo.html (.../tk-geo.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,959 +1,959 @@ - - - - - -Listing of doc/example-scripts/tk-geo.tcl - - - - - -
-
-
-

Drawing geometric figures - the result of airplane travel.

-

The example script shows the use of canvas and geometric figues -(regular, convex polygons) with different number of edges based on -trigonometric functions.

-

-gustaf neumann (Aug 2, 2013)

-
-
-tk-geo1.png -
-
-
-
-tk-geo2.png -
-
-
-
-
package require Tk
-package require nx
-

Class Canvas is a simple convenience wrapper for the tk canvas, -which packs itself.

-
-
-
nx::Class create Canvas {
-  :property {canvas .canvas}
-  :property {bg beige}
-  :property {height 500}
-  :property {width 500}
-
-  :method init {} {
-    canvas ${:canvas} -bg ${:bg} -height ${:height} -width ${:width}
-    pack ${:canvas}
-  }
-}
-

Class Area provides a center point (x, y) and a radius

-
-
-
nx::Class create Area {
-  :property {canvas .canvas}
-  :property {x 250}
-  :property {y 250}
-  :property {radius 200}
-
-  :variable pi [expr {acos(-1)}]
-
-  :method degree {d} {
-    #
-    # return a coordinate pair on a circle around the center point with
-    # :radius at the provided degrees (0..360)
-    #
-    set x  [expr {$d*${:pi}/180.0 - ${:pi}/2.0}]
-    set x0 [expr {cos($x)*${:radius}+${:x}}]
-    set y0 [expr {sin($x)*${:radius}+${:y}}]
-    list $x0 $y0
-  }
-
-  :method n-tangle {n} {
-    #
-    # Draw a regular n-tangle (e.g. when n==3, a triangle) inscribed to
-    # a circle with radius :radius
-    #
-    for {set i 0} {$i < $n} {incr i} {
-      set p($i) [:degree [expr {$i*360/$n}]]
-    }
-    lassign $p(0) x0 y0
-    for {set i 1} {$i < $n} {incr i} {
-      lassign $p($i) x1 y1
-      ${:canvas} create line $x0 $y0 $x1 $y1
-      lassign $p($i) x0 y0
-    }
-    lassign $p(0) x1 y1
-    ${:canvas} create line $x0 $y0 $x1 $y1
-  }
-}
-

Class Inscribe draws multiple n-tangles with the came center point.

-
-
-
nx::Class create Inscribe -superclass Area {
-  :property {count 4}
-  :property {edges 3}
-  :method init {} {
-    for {set i 0} {$i < ${:count}} {incr i} {
-      ${:canvas} create oval \
-          [expr {${:x}-${:radius}}] [expr {${:y}-${:radius}}] \
-          [expr {${:x}+${:radius}}] [expr {${:y}+${:radius}}]
-      :n-tangle ${:edges}
-      set :radius [expr {${:radius}/2.0}]
-    }
-  }
-}
-

Class Hull creates an n-tangle with :density hull lines between -neighboring edges

-
-
-
nx::Class create Hull -superclass Area {
-  :property {edges 3}
-  :property {density 10}
-
-  :method n-tangle {n} {
-    for {set i 0} {$i < $n} {incr i} {
-      set p($i) [:degree [expr {$i*360/$n}]]
-    }
-    lassign $p(0) x0 y0
-    for {set i 1} {$i < $n} {incr i} {
-      lassign $p($i) x1 y1
-      set line($i) [list $x0 $y0 $x1 $y1]
-      ${:canvas} create line $x0 $y0 $x1 $y1
-      lassign $p($i) x0 y0
-    }
-    lassign $p(0) x1 y1
-    ${:canvas} create line $x0 $y0 $x1 $y1
-    set line(0) [list $x0 $y0 $x1 $y1]
-    set line($n) [list $x0 $y0 $x1 $y1]
-
-    for {set i 0} {$i < $n} {incr i} {
-      lassign $line($i) x0 y0 x1 y1
-      lassign $line([expr {$i+1}]) x2 y2 x3 y3
-      set dx1 [expr {($x0 - $x1)*1.0/${:density}}]
-      set dy1 [expr {($y0 - $y1)*1.0/${:density}}]
-      set dx2 [expr {($x2 - $x3)*1.0/${:density}}]
-      set dy2 [expr {($y2 - $y3)*1.0/${:density}}]
-      for {set j 1} {$j < ${:density}} {incr j} {
-        ${:canvas} create line [expr {$x0-$dx1*$j}] [expr {$y0-$dy1*$j}] \
-            [expr {$x2-$dx2*$j}] [expr {$y2-$dy2*$j}]
-      }
-    }
-  }
-
-  :method init {} {
-    :n-tangle ${:edges}
-  }
-}
-

Draw either one larger figure with inner figures -or a series of smaller figures next to each other.

-
-
-
set multiple 0
-
-if {$multiple} {
-  # Draw a series of figures next to each other
-  set c [::Canvas new -width 650 -height 750 -bg white]
-  ::Inscribe new -canvas [$c cget -canvas] -x 100 -y 100 -radius 80 -count 7
-  ::Inscribe new -canvas [$c cget -canvas] -x 300 -y 100 -radius 80 -count 7 -edges 4
-  ::Inscribe new -canvas [$c cget -canvas] -x 500 -y 100 -radius 80 -count 7 -edges 5
-  ::Hull new -canvas [$c cget -canvas] -x 100 -y 300 -radius 80 -edges 3 -density 10
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 300 -radius 80 -edges 4 -density 10
-  ::Hull new -canvas [$c cget -canvas] -x 500 -y 300 -radius 80 -edges 5 -density 10
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 600 -radius 200 -edges 3 -density 40
-} else {
-  # Draw a several series of figures with the same center
-  set c [::Canvas new -width 650 -height 650 -bg white]
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 300 -edges 5 -density 40
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 150 -edges 4 -density 20
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 75 -edges 3 -density 10
-  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 30 -edges 5 -density 5
-}
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-geo.tcl + + + + + +
+
+
+

Drawing geometric figures - the result of airplane travel.

+

The example script shows the use of canvas and geometric figues +(regular, convex polygons) with different number of edges based on +trigonometric functions.

+

-gustaf neumann (Aug 2, 2013)

+
+
+tk-geo1.png +
+
+
+
+tk-geo2.png +
+
+
+
+
package require Tk
+package require nx
+

Class Canvas is a simple convenience wrapper for the tk canvas, +which packs itself.

+
+
+
nx::Class create Canvas {
+  :property {canvas .canvas}
+  :property {bg beige}
+  :property {height 500}
+  :property {width 500}
+
+  :method init {} {
+    canvas ${:canvas} -bg ${:bg} -height ${:height} -width ${:width}
+    pack ${:canvas}
+  }
+}
+

Class Area provides a center point (x, y) and a radius

+
+
+
nx::Class create Area {
+  :property {canvas .canvas}
+  :property {x 250}
+  :property {y 250}
+  :property {radius 200}
+
+  :variable pi [expr {acos(-1)}]
+
+  :method degree {d} {
+    #
+    # return a coordinate pair on a circle around the center point with
+    # :radius at the provided degrees (0..360)
+    #
+    set x  [expr {$d*${:pi}/180.0 - ${:pi}/2.0}]
+    set x0 [expr {cos($x)*${:radius}+${:x}}]
+    set y0 [expr {sin($x)*${:radius}+${:y}}]
+    list $x0 $y0
+  }
+
+  :method n-tangle {n} {
+    #
+    # Draw a regular n-tangle (e.g. when n==3, a triangle) inscribed to
+    # a circle with radius :radius
+    #
+    for {set i 0} {$i < $n} {incr i} {
+      set p($i) [:degree [expr {$i*360/$n}]]
+    }
+    lassign $p(0) x0 y0
+    for {set i 1} {$i < $n} {incr i} {
+      lassign $p($i) x1 y1
+      ${:canvas} create line $x0 $y0 $x1 $y1
+      lassign $p($i) x0 y0
+    }
+    lassign $p(0) x1 y1
+    ${:canvas} create line $x0 $y0 $x1 $y1
+  }
+}
+

Class Inscribe draws multiple n-tangles with the came center point.

+
+
+
nx::Class create Inscribe -superclass Area {
+  :property {count 4}
+  :property {edges 3}
+  :method init {} {
+    for {set i 0} {$i < ${:count}} {incr i} {
+      ${:canvas} create oval \
+          [expr {${:x}-${:radius}}] [expr {${:y}-${:radius}}] \
+          [expr {${:x}+${:radius}}] [expr {${:y}+${:radius}}]
+      :n-tangle ${:edges}
+      set :radius [expr {${:radius}/2.0}]
+    }
+  }
+}
+

Class Hull creates an n-tangle with :density hull lines between +neighboring edges

+
+
+
nx::Class create Hull -superclass Area {
+  :property {edges 3}
+  :property {density 10}
+
+  :method n-tangle {n} {
+    for {set i 0} {$i < $n} {incr i} {
+      set p($i) [:degree [expr {$i*360/$n}]]
+    }
+    lassign $p(0) x0 y0
+    for {set i 1} {$i < $n} {incr i} {
+      lassign $p($i) x1 y1
+      set line($i) [list $x0 $y0 $x1 $y1]
+      ${:canvas} create line $x0 $y0 $x1 $y1
+      lassign $p($i) x0 y0
+    }
+    lassign $p(0) x1 y1
+    ${:canvas} create line $x0 $y0 $x1 $y1
+    set line(0) [list $x0 $y0 $x1 $y1]
+    set line($n) [list $x0 $y0 $x1 $y1]
+
+    for {set i 0} {$i < $n} {incr i} {
+      lassign $line($i) x0 y0 x1 y1
+      lassign $line([expr {$i+1}]) x2 y2 x3 y3
+      set dx1 [expr {($x0 - $x1)*1.0/${:density}}]
+      set dy1 [expr {($y0 - $y1)*1.0/${:density}}]
+      set dx2 [expr {($x2 - $x3)*1.0/${:density}}]
+      set dy2 [expr {($y2 - $y3)*1.0/${:density}}]
+      for {set j 1} {$j < ${:density}} {incr j} {
+        ${:canvas} create line [expr {$x0-$dx1*$j}] [expr {$y0-$dy1*$j}] \
+            [expr {$x2-$dx2*$j}] [expr {$y2-$dy2*$j}]
+      }
+    }
+  }
+
+  :method init {} {
+    :n-tangle ${:edges}
+  }
+}
+

Draw either one larger figure with inner figures +or a series of smaller figures next to each other.

+
+
+
set multiple 0
+
+if {$multiple} {
+  # Draw a series of figures next to each other
+  set c [::Canvas new -width 650 -height 750 -bg white]
+  ::Inscribe new -canvas [$c cget -canvas] -x 100 -y 100 -radius 80 -count 7
+  ::Inscribe new -canvas [$c cget -canvas] -x 300 -y 100 -radius 80 -count 7 -edges 4
+  ::Inscribe new -canvas [$c cget -canvas] -x 500 -y 100 -radius 80 -count 7 -edges 5
+  ::Hull new -canvas [$c cget -canvas] -x 100 -y 300 -radius 80 -edges 3 -density 10
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 300 -radius 80 -edges 4 -density 10
+  ::Hull new -canvas [$c cget -canvas] -x 500 -y 300 -radius 80 -edges 5 -density 10
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 600 -radius 200 -edges 3 -density 40
+} else {
+  # Draw a several series of figures with the same center
+  set c [::Canvas new -width 650 -height 650 -bg white]
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 300 -edges 5 -density 40
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 150 -edges 4 -density 20
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 75 -edges 3 -density 10
+  ::Hull new -canvas [$c cget -canvas] -x 300 -y 320 -radius 30 -edges 5 -density 5
+}
+
+
+
+

+ + + Index: doc/example-scripts/tk-horse-race.html =================================================================== diff -u -r71e18053ef7dd9a53f11d14f3a91b8a1091e90bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-horse-race.html (.../tk-horse-race.html) (revision 71e18053ef7dd9a53f11d14f3a91b8a1091e90bb) +++ doc/example-scripts/tk-horse-race.html (.../tk-horse-race.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,917 +1,917 @@ - - - - - -Listing of doc/example-scripts/tk-horse-race.tcl - - - - - -
-
-
-

A small Horse Race game, originally developed by Richard Suchenwirth -in plain Tcl (see http://wiki.tcl.tk/3467). The game was rewritten -as a design study in NX by Gustaf Neumann in May 2011.

-
-
-tk-horse-race.png -
-
-
-
-
package require Tk
-package require nx::trait
-
- ##############################################################################
- # Trait ListUtils
- #
- # Some list utilities, not part of a package we can require here.
- ##############################################################################
-
- nx::Trait create ::nx::trait::listUtils {
-
-   :protected method lpick {list} {
-     # return a random entry from a given list
-     lindex $list [expr {int(rand()*[llength $list])}]
-   }
-   :protected method lremove {listName what} {
-     # remove a list element referenced by the elements value
-     :upvar $listName list
-     set pos  [lsearch $list $what]
-     set list [lreplace $list $pos $pos]
-   }
- }
-
- ##############################################################################
- # Class Horse
- #
- # This class defines the logic, how and where a single horse and
- # jockey are drawn. The painting of the horse happens just at startup
- # time, later the horses are moved via their tags.
- ##############################################################################
-
- nx::Class create Horse {
-   :property name:required      ;# name is the external name of the horse
-   :property tag:required       ;# tag is an internal id
-   :property canvas:required    ;# the canvas, on which the horse is drawn
-   :property n:integer,required ;# the position on the canvas
-
-   :require trait nx::trait::callback
-   :require trait nx::trait::listUtils
-
-   :method draw {x y} {
-     set hide [:lpick {black brown white gray brown3 brown4}]
-     set c1 [:lpick {red yellow blue purple pink green}]
-     set c2 [:lpick {red yellow blue purple pink green}]
-     ${:canvas} create oval 0 -1 18 4 -fill $hide -outline $hide -tag ${:tag}
-     ${:canvas} create line 1 12 3 0 5 12 -fill $hide -tag ${:tag} -width 2
-     ${:canvas} create line 15 12 17 0 19 12 -fill $hide -tag ${:tag} -width 2
-     ${:canvas} create line 16 0 20 -7 24 -5 -fill $hide -tag ${:tag} -width 3
-     # Jockey:
-     ${:canvas} create line 9 4 11 1 7 -1 -fill $c1 -width 2 -tag ${:tag}
-     ${:canvas} create line 7 -2 10 -6 15 -3 -fill $c2 -width 2 -tag ${:tag}
-     ${:canvas} create oval 9 -7 12 -10 -fill orange -outline orange -tag ${:tag}
-     ${:canvas} move ${:tag} $x $y
-   }
-
-   :method init {} {
-     set w [entry ${:canvas}.e${:n} -textvar [:bindvar name] -width 7 -bg green3]
-     ${:canvas} create window 5 [expr {${:n}*30+5}] -window $w -anchor nw
-     :draw 70 [expr {${:n}*30+14}]
-   }
- }
-
- ##############################################################################
- # Class HorseGame
- #
- # Defines the main canvas of the Game and contains the logic of
- # starting, resetting etc.
- ##############################################################################
-
- nx::Class create HorseGame {
-   :property {bg1 green4}   ;# background color of the canvas
-   :property {bg2 green3}   ;# background color of the result label
-   :property {width 750}    ;# width of the canvas
-   :property {height 330}   ;# height of the canvas
-   :property {horses}       ;# a list of horse names participating in the game
-
-   :require trait nx::trait::callback
-   :require trait nx::trait::listUtils
-
-   :method init {} {
-     #
-     # create the canvas
-     #
-     set :canvas [canvas .c -bg ${:bg1} -width ${:width} -height ${:height}]
-     pack ${:canvas}
-     #
-     # create the Horses
-     #
-     set n 0
-     foreach name ${:horses} {
-       set h [::Horse create horse$n -name $name -canvas ${:canvas} -n $n -tag horse$n]
-       lappend :tags horse$n
-       incr n
-     }
-
-     # finish line
-     set w [expr {${:width} - 20}]
-     ${:canvas} create line $w 0 $w ${:height} -fill white -tag finish
-
-     # start button
-     button ${:canvas}.button -text Start -pady 0 -width 0 \
-         -command [:callback start ${:tags}]
-     ${:canvas} create window 5 [expr {$n*30}] -window ${:canvas}.button -anchor nw
-
-     # label for the results
-     label ${:canvas}.winners -textvar [:bindvar winners] -bg ${:bg2} -width 80
-     ${:canvas} create window 70 [expr {$n*30}] -window ${:canvas}.winners -anchor nw
-   }
-
-   :public method start {running} {
-     #
-     # When the "Start" button is pressed, we turn this button into a
-     # "Reset" button and the horse race starts. We stop, when more
-     # than two horses pass the finish line.
-     #
-     ${:canvas}.button config -text Reset -command [:callback reset]
-     set :winners {}
-     set finish [expr {[lindex [${:canvas} bbox finish] 2]+10}]
-     while {[llength ${:winners}]<3} {
-       set this [:lpick $running]
-       ${:canvas} move $this [:lpick {0 1 2 3}] 0
-       update
-       if {[lindex [${:canvas} bbox $this] 2] > $finish} {
-         lappend :winners [expr {[llength ${:winners}]+1}]:[$this cget -name]
-         :lremove running $this
-       }
-     }
-   }
-
-   :public method reset {} {
-     #
-     # When the "Reset" button is pressed, we switch back to the start
-     # configuration, the horses come back to the start.
-     #
-     ${:canvas}.button config -text Start -command [:callback start ${:tags}]
-     foreach tag ${:tags} {
-       set x [lindex [${:canvas} bbox $tag] 0]
-       ${:canvas} move $tag [expr {70-$x}] 0
-     }
-   }
- }
-
- #
- # everything is defined, create the game
- #
- bind . <space> {exec wish $argv0 &; exit}
- HorseGame new -horses {Blaise NX Animal Ada Alan XOTcl Grace itcl John Linus}
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-horse-race.tcl + + + + + +
+
+
+

A small Horse Race game, originally developed by Richard Suchenwirth +in plain Tcl (see http://wiki.tcl.tk/3467). The game was rewritten +as a design study in NX by Gustaf Neumann in May 2011.

+
+
+tk-horse-race.png +
+
+
+
+
package require Tk
+package require nx::trait
+
+ ##############################################################################
+ # Trait ListUtils
+ #
+ # Some list utilities, not part of a package we can require here.
+ ##############################################################################
+
+ nx::Trait create ::nx::trait::listUtils {
+
+   :protected method lpick {list} {
+     # return a random entry from a given list
+     lindex $list [expr {int(rand()*[llength $list])}]
+   }
+   :protected method lremove {listName what} {
+     # remove a list element referenced by the elements value
+     :upvar $listName list
+     set pos  [lsearch $list $what]
+     set list [lreplace $list $pos $pos]
+   }
+ }
+
+ ##############################################################################
+ # Class Horse
+ #
+ # This class defines the logic, how and where a single horse and
+ # jockey are drawn. The painting of the horse happens just at startup
+ # time, later the horses are moved via their tags.
+ ##############################################################################
+
+ nx::Class create Horse {
+   :property name:required      ;# name is the external name of the horse
+   :property tag:required       ;# tag is an internal id
+   :property canvas:required    ;# the canvas, on which the horse is drawn
+   :property n:integer,required ;# the position on the canvas
+
+   :require trait nx::trait::callback
+   :require trait nx::trait::listUtils
+
+   :method draw {x y} {
+     set hide [:lpick {black brown white gray brown3 brown4}]
+     set c1 [:lpick {red yellow blue purple pink green}]
+     set c2 [:lpick {red yellow blue purple pink green}]
+     ${:canvas} create oval 0 -1 18 4 -fill $hide -outline $hide -tag ${:tag}
+     ${:canvas} create line 1 12 3 0 5 12 -fill $hide -tag ${:tag} -width 2
+     ${:canvas} create line 15 12 17 0 19 12 -fill $hide -tag ${:tag} -width 2
+     ${:canvas} create line 16 0 20 -7 24 -5 -fill $hide -tag ${:tag} -width 3
+     # Jockey:
+     ${:canvas} create line 9 4 11 1 7 -1 -fill $c1 -width 2 -tag ${:tag}
+     ${:canvas} create line 7 -2 10 -6 15 -3 -fill $c2 -width 2 -tag ${:tag}
+     ${:canvas} create oval 9 -7 12 -10 -fill orange -outline orange -tag ${:tag}
+     ${:canvas} move ${:tag} $x $y
+   }
+
+   :method init {} {
+     set w [entry ${:canvas}.e${:n} -textvar [:bindvar name] -width 7 -bg green3]
+     ${:canvas} create window 5 [expr {${:n}*30+5}] -window $w -anchor nw
+     :draw 70 [expr {${:n}*30+14}]
+   }
+ }
+
+ ##############################################################################
+ # Class HorseGame
+ #
+ # Defines the main canvas of the Game and contains the logic of
+ # starting, resetting etc.
+ ##############################################################################
+
+ nx::Class create HorseGame {
+   :property {bg1 green4}   ;# background color of the canvas
+   :property {bg2 green3}   ;# background color of the result label
+   :property {width 750}    ;# width of the canvas
+   :property {height 330}   ;# height of the canvas
+   :property {horses}       ;# a list of horse names participating in the game
+
+   :require trait nx::trait::callback
+   :require trait nx::trait::listUtils
+
+   :method init {} {
+     #
+     # create the canvas
+     #
+     set :canvas [canvas .c -bg ${:bg1} -width ${:width} -height ${:height}]
+     pack ${:canvas}
+     #
+     # create the Horses
+     #
+     set n 0
+     foreach name ${:horses} {
+       set h [::Horse create horse$n -name $name -canvas ${:canvas} -n $n -tag horse$n]
+       lappend :tags horse$n
+       incr n
+     }
+
+     # finish line
+     set w [expr {${:width} - 20}]
+     ${:canvas} create line $w 0 $w ${:height} -fill white -tag finish
+
+     # start button
+     button ${:canvas}.button -text Start -pady 0 -width 0 \
+         -command [:callback start ${:tags}]
+     ${:canvas} create window 5 [expr {$n*30}] -window ${:canvas}.button -anchor nw
+
+     # label for the results
+     label ${:canvas}.winners -textvar [:bindvar winners] -bg ${:bg2} -width 80
+     ${:canvas} create window 70 [expr {$n*30}] -window ${:canvas}.winners -anchor nw
+   }
+
+   :public method start {running} {
+     #
+     # When the "Start" button is pressed, we turn this button into a
+     # "Reset" button and the horse race starts. We stop, when more
+     # than two horses pass the finish line.
+     #
+     ${:canvas}.button config -text Reset -command [:callback reset]
+     set :winners {}
+     set finish [expr {[lindex [${:canvas} bbox finish] 2]+10}]
+     while {[llength ${:winners}]<3} {
+       set this [:lpick $running]
+       ${:canvas} move $this [:lpick {0 1 2 3}] 0
+       update
+       if {[lindex [${:canvas} bbox $this] 2] > $finish} {
+         lappend :winners [expr {[llength ${:winners}]+1}]:[$this cget -name]
+         :lremove running $this
+       }
+     }
+   }
+
+   :public method reset {} {
+     #
+     # When the "Reset" button is pressed, we switch back to the start
+     # configuration, the horses come back to the start.
+     #
+     ${:canvas}.button config -text Start -command [:callback start ${:tags}]
+     foreach tag ${:tags} {
+       set x [lindex [${:canvas} bbox $tag] 0]
+       ${:canvas} move $tag [expr {70-$x}] 0
+     }
+   }
+ }
+
+ #
+ # everything is defined, create the game
+ #
+ bind . <space> {exec wish $argv0 &; exit}
+ HorseGame new -horses {Blaise NX Animal Ada Alan XOTcl Grace itcl John Linus}
+
+
+
+

+ + + Index: doc/example-scripts/tk-locomotive.html =================================================================== diff -u -r71e18053ef7dd9a53f11d14f3a91b8a1091e90bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-locomotive.html (.../tk-locomotive.html) (revision 71e18053ef7dd9a53f11d14f3a91b8a1091e90bb) +++ doc/example-scripts/tk-locomotive.html (.../tk-locomotive.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,970 +1,970 @@ - - - - - -Listing of doc/example-scripts/tk-locomotive.tcl - - - - - -
-
-
-

Example by <Richard Suchenwirth> -http://wiki.tcl.tk/1329

-
    -
  • -

    -translated from Tcl to XOTcl by gustaf neumann in 2001 -

    -
  • -
  • -

    -translated from XOTcl to NX by gustaf neumann in 2010 -

    -
  • -
-
-
-tk-locomotive.png -
-
-

Left mousebutton starts, middle slows down, right stops

-
-
-
package require Tk
-package require nx
-package require nx::trait
-
-nx::Class create Wheel {
-  :property x
-  :property y
-  :property r
-  :property {spokes 24}
-  :property {pivot 0}
-  :property {color red}
-  :property {tag ""}
-
-  :public method drawSpokes {} {
-    ::nx::var import [:info parent] c alpha
-    set delta [expr {360.0 / ${:spokes}}]
-    set deg2arc [expr {atan(1.0)*8/360.}]
-    for {set i 0} {$i < ${:spokes}} {incr i} {
-      set x1 [expr {${:x} + cos($deg2arc*$alpha) * ${:r}}]
-      set y1 [expr {${:y} + sin($deg2arc*$alpha) * ${:r}}]
-      $c create line ${:x} ${:y} $x1 $y1 -fill ${:color} -tag spoke
-      set alpha [expr {$alpha + $delta}]
-    }
-    if {[info exists :act_pivot]} {
-      lassign [set :act_pivot] item perc
-      set rp [expr {${:r} * $perc}]
-      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
-      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
-      $c coords $item $xp $yp [expr {$xp + 1}] [expr {$yp + 1}]
-    }
-  }
-
-  :method init {} {
-    ::nx::var import [:info parent] c alpha
-    set alpha 0.
-
-    set :y [expr {${:y} - ${:r}}]
-    $c create oval \
-        [expr {${:x} - ${:r}}] [expr {${:y} - ${:r}}] \
-        [expr {${:x} + ${:r}}] [expr {${:y} + ${:r}}] \
-        -outline white
-    set r1 [expr {${:r}-2}]
-    set W [$c create oval \
-               [expr {${:x} - $r1}] [expr {${:y} - $r1}] \
-               [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
-               -outline ${:color} -width 2]
-    :drawSpokes
-
-    if {${:pivot}} {
-      set deg2arc [expr {atan(1.0) * 8 / 360.0}]
-      set rp [expr {$r1*${:pivot}}]
-      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
-      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
-      set new_pivot [$c create rect $xp $yp [expr {$xp + 1}] [expr {$yp + 1}] \
-                         -fill ${:color} -tag [list ${:tag} pivot]]
-      set :act_pivot [list $new_pivot ${:pivot}]
-
-      $c create arc [expr {${:x} - $r1}] [expr {${:y} - $r1}]\
-          [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
-          -style chord -fill ${:color} -start 310 \
-          -extent 80 -tag counterweight
-      set :pivot $new_pivot
-    }
-    set rh [expr {${:r} / 12.0}]
-    $c create oval \
-        [expr {${:x} - $rh}] [expr {${:y} - $rh}] \
-        [expr {${:x} + $rh}] [expr {${:y} + $rh}] \
-        -fill white -tag hub
-    set :r $r1
-  }
-}
-
-
-nx::Class create Locomotive {
-  :property {speed 4}
-
-  :require trait nx::trait::callback
-
-  :method turn {} {
-    set :alpha [expr {round(${:alpha} + 360 - ${:speed}) % 360}]
-    foreach i [${:c} find withtag counterweight] {
-      ${:c} itemconfig $i -start [expr {310 - ${:alpha}}]
-    }
-    ${:c} delete spoke
-    foreach wheel [:info children] { $wheel drawSpokes }
-    ${:c} raise hub
-    set xp0 [expr {105 + 15 * sin((${:alpha} - 90) * atan(1.0) * 8 / 360)}]
-    ${:c} delete piston
-    ${:c} coords p0 $xp0 120 [expr {$xp0+2}] 122 ;#CW
-    ${:c} create line 90 121 $xp0 121 -width 2 -fill white -tag piston ;#CW
-    :drawRod p0 p1 p2 p3
-    ${:c} raise p0
-    foreach i [${:c} find withtag smoke] {
-      if {[lindex [${:c} bbox $i] 3]<0} {
-        ${:c} delete $i
-      } else {
-        ${:c} move $i [expr {rand() * ${:speed} / 3.0}] [expr {rand() * 2 - 2}]
-      }
-    }
-    set t [${:c} create oval [${:c} bbox chimney] -fill white -outline white -tag smoke]
-    ${:c} move $t 0 -10
-    ${:c} lower smoke
-  }
-
-  :method drawRod {p0 p1 p2 p3} {
-    ${:c} delete rod
-    ${:c} create rect [${:c} bbox $p1 $p3] -fill white -tag rod
-    ${:c} create line {*}[lrange [${:c} bbox $p0] 0 1] \
-        {*}[lrange [${:c} bbox $p2] 0 1] -width 3 -fill white -tag rod
-    ${:c} raise rod
-    ${:c} raise pivot
-  }
-
-  :public method tick {} {
-    :turn
-    foreach i [after info] {after cancel $i}
-    after 10 [self] tick
-  }
-
-  :public method throttle {} {
-    incr :speed 2
-    :tick
-  }
-
-  :public method break {} {
-    incr :speed -2
-    if {${:speed}<0} {set :speed 0}
-    :tick
-  }
-
-  :public method emergencyBreak {} {
-    set :speed 0
-    :tick
-  }
-
-  :method init {} {
-    set :c [canvas .c -width 600 -height 160 -background lightblue]
-    pack ${:c}
-
-    bind ${:c} <1> [:callback throttle]
-    bind ${:c} <2> [:callback break]
-    bind ${:c} <3> [:callback emergencyBreak]
-
-    ${:c} delete all
-    ${:c} create rect 32 115 360 125 -fill black ;# frame
-    ${:c} create rect 22 118 32 122 -fill grey30 ;# buffer
-    ${:c} create line 22 115 22 125
-    ${:c} create poly 60 95 40 115 50 115 70 95 -fill black
-    ${:c} create rect 60 45 310 95 -fill grey25 ;# boiler
-    ${:c} create oval 55 50 65 90 -fill black ;# smokebox
-    ${:c} create rect 70 32 85 50 -fill black -tag chimney
-    ${:c} create rect 40 52 90 75 -fill black ;# wind diverter
-    ${:c} create oval 130 36 150 52 -fill black ;# dome
-    ${:c} create rect 195 35 215 50 -fill black ;# sandbox
-    ${:c} create oval 260 36 280 52 -fill black ;# dome
-    ${:c} create rect 65 100 90 135 -fill black ;# cylinder
-    ${:c} create rect 90 120 92 122 -fill red -tag p0 ;# crossbar
-    ${:c} create rect 72 87 82 100 -fill black ;# steam tube
-    ${:c} create rect 310 40 370 115 -fill black ;# cab
-    ${:c} create rect 310 32 390 42 -fill grey30 ;# cab roof
-    ${:c} create text 338 82 -text "01 234" -fill gold -font {Times 7}
-    ${:c} create rect 318 48 333 66 -fill white ;# cab window #1
-    ${:c} create rect 338 48 355 66 -fill white ;# cab window #2
-    Wheel new -childof [self] -x 50 -y 150 -r 13 -spokes 12
-    Wheel new -childof [self] -x 105 -y 150 -r 13 -spokes 12
-    Wheel new -childof [self] -x 150 -y 150 -r 30 -pivot 0.5 -tag p1
-    Wheel new -childof [self] -x 215 -y 150 -r 30 -pivot 0.5 -tag p2
-    Wheel new -childof [self] -x 280 -y 150 -r 30 -pivot 0.5 -tag p3
-    :drawRod p0 p1 p2 p3
-    Wheel new -childof [self] -x 340 -y 150 -r 16 -spokes 12
-    ${:c} create rect 360 110 380 118 -fill black
-    ${:c} create rect 380 65 560 125 -fill black -tag tender
-    ${:c} create rect 560 118 570 122 -fill grey30 ;# buffer
-    ${:c} create line 571 116 571 125
-    ${:c} create rect 390 45 525 65 -fill black -tag tender
-    Wheel new -childof [self] -x 395 -y 150 -r 13 -spokes 12
-    Wheel new -childof [self] -x 440 -y 150 -r 13 -spokes 12
-    ${:c} create rect 380 132 456 142 -fill red
-    Wheel new -childof [self] -x 495 -y 150 -r 13 -spokes 12
-    Wheel new -childof [self] -x 540 -y 150 -r 13 -spokes 12
-    ${:c} create rect 480 132 556 142 -fill red -outline red
-    ${:c} create rect 0 150 600 160 -fill brown ;# earth
-    ${:c} create line 0 150 600 150 -fill grey -width 2 ;# rail
-    :tick
-  }
-}
-
-Locomotive new
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-locomotive.tcl + + + + + +
+
+
+

Example by <Richard Suchenwirth> +http://wiki.tcl.tk/1329

+
    +
  • +

    +translated from Tcl to XOTcl by gustaf neumann in 2001 +

    +
  • +
  • +

    +translated from XOTcl to NX by gustaf neumann in 2010 +

    +
  • +
+
+
+tk-locomotive.png +
+
+

Left mousebutton starts, middle slows down, right stops

+
+
+
package require Tk
+package require nx
+package require nx::trait
+
+nx::Class create Wheel {
+  :property x
+  :property y
+  :property r
+  :property {spokes 24}
+  :property {pivot 0}
+  :property {color red}
+  :property {tag ""}
+
+  :public method drawSpokes {} {
+    ::nx::var import [:info parent] c alpha
+    set delta [expr {360.0 / ${:spokes}}]
+    set deg2arc [expr {atan(1.0)*8/360.}]
+    for {set i 0} {$i < ${:spokes}} {incr i} {
+      set x1 [expr {${:x} + cos($deg2arc*$alpha) * ${:r}}]
+      set y1 [expr {${:y} + sin($deg2arc*$alpha) * ${:r}}]
+      $c create line ${:x} ${:y} $x1 $y1 -fill ${:color} -tag spoke
+      set alpha [expr {$alpha + $delta}]
+    }
+    if {[info exists :act_pivot]} {
+      lassign [set :act_pivot] item perc
+      set rp [expr {${:r} * $perc}]
+      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
+      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
+      $c coords $item $xp $yp [expr {$xp + 1}] [expr {$yp + 1}]
+    }
+  }
+
+  :method init {} {
+    ::nx::var import [:info parent] c alpha
+    set alpha 0.
+
+    set :y [expr {${:y} - ${:r}}]
+    $c create oval \
+        [expr {${:x} - ${:r}}] [expr {${:y} - ${:r}}] \
+        [expr {${:x} + ${:r}}] [expr {${:y} + ${:r}}] \
+        -outline white
+    set r1 [expr {${:r}-2}]
+    set W [$c create oval \
+               [expr {${:x} - $r1}] [expr {${:y} - $r1}] \
+               [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
+               -outline ${:color} -width 2]
+    :drawSpokes
+
+    if {${:pivot}} {
+      set deg2arc [expr {atan(1.0) * 8 / 360.0}]
+      set rp [expr {$r1*${:pivot}}]
+      set xp [expr {${:x} - $rp * cos($deg2arc * $alpha)}]
+      set yp [expr {${:y} - $rp * sin($deg2arc * $alpha)}]
+      set new_pivot [$c create rect $xp $yp [expr {$xp + 1}] [expr {$yp + 1}] \
+                         -fill ${:color} -tag [list ${:tag} pivot]]
+      set :act_pivot [list $new_pivot ${:pivot}]
+
+      $c create arc [expr {${:x} - $r1}] [expr {${:y} - $r1}]\
+          [expr {${:x} + $r1}] [expr {${:y} + $r1}] \
+          -style chord -fill ${:color} -start 310 \
+          -extent 80 -tag counterweight
+      set :pivot $new_pivot
+    }
+    set rh [expr {${:r} / 12.0}]
+    $c create oval \
+        [expr {${:x} - $rh}] [expr {${:y} - $rh}] \
+        [expr {${:x} + $rh}] [expr {${:y} + $rh}] \
+        -fill white -tag hub
+    set :r $r1
+  }
+}
+
+
+nx::Class create Locomotive {
+  :property {speed 4}
+
+  :require trait nx::trait::callback
+
+  :method turn {} {
+    set :alpha [expr {round(${:alpha} + 360 - ${:speed}) % 360}]
+    foreach i [${:c} find withtag counterweight] {
+      ${:c} itemconfig $i -start [expr {310 - ${:alpha}}]
+    }
+    ${:c} delete spoke
+    foreach wheel [:info children] { $wheel drawSpokes }
+    ${:c} raise hub
+    set xp0 [expr {105 + 15 * sin((${:alpha} - 90) * atan(1.0) * 8 / 360)}]
+    ${:c} delete piston
+    ${:c} coords p0 $xp0 120 [expr {$xp0+2}] 122 ;#CW
+    ${:c} create line 90 121 $xp0 121 -width 2 -fill white -tag piston ;#CW
+    :drawRod p0 p1 p2 p3
+    ${:c} raise p0
+    foreach i [${:c} find withtag smoke] {
+      if {[lindex [${:c} bbox $i] 3]<0} {
+        ${:c} delete $i
+      } else {
+        ${:c} move $i [expr {rand() * ${:speed} / 3.0}] [expr {rand() * 2 - 2}]
+      }
+    }
+    set t [${:c} create oval [${:c} bbox chimney] -fill white -outline white -tag smoke]
+    ${:c} move $t 0 -10
+    ${:c} lower smoke
+  }
+
+  :method drawRod {p0 p1 p2 p3} {
+    ${:c} delete rod
+    ${:c} create rect [${:c} bbox $p1 $p3] -fill white -tag rod
+    ${:c} create line {*}[lrange [${:c} bbox $p0] 0 1] \
+        {*}[lrange [${:c} bbox $p2] 0 1] -width 3 -fill white -tag rod
+    ${:c} raise rod
+    ${:c} raise pivot
+  }
+
+  :public method tick {} {
+    :turn
+    foreach i [after info] {after cancel $i}
+    after 10 [self] tick
+  }
+
+  :public method throttle {} {
+    incr :speed 2
+    :tick
+  }
+
+  :public method break {} {
+    incr :speed -2
+    if {${:speed}<0} {set :speed 0}
+    :tick
+  }
+
+  :public method emergencyBreak {} {
+    set :speed 0
+    :tick
+  }
+
+  :method init {} {
+    set :c [canvas .c -width 600 -height 160 -background lightblue]
+    pack ${:c}
+
+    bind ${:c} <1> [:callback throttle]
+    bind ${:c} <2> [:callback break]
+    bind ${:c} <3> [:callback emergencyBreak]
+
+    ${:c} delete all
+    ${:c} create rect 32 115 360 125 -fill black ;# frame
+    ${:c} create rect 22 118 32 122 -fill grey30 ;# buffer
+    ${:c} create line 22 115 22 125
+    ${:c} create poly 60 95 40 115 50 115 70 95 -fill black
+    ${:c} create rect 60 45 310 95 -fill grey25 ;# boiler
+    ${:c} create oval 55 50 65 90 -fill black ;# smokebox
+    ${:c} create rect 70 32 85 50 -fill black -tag chimney
+    ${:c} create rect 40 52 90 75 -fill black ;# wind diverter
+    ${:c} create oval 130 36 150 52 -fill black ;# dome
+    ${:c} create rect 195 35 215 50 -fill black ;# sandbox
+    ${:c} create oval 260 36 280 52 -fill black ;# dome
+    ${:c} create rect 65 100 90 135 -fill black ;# cylinder
+    ${:c} create rect 90 120 92 122 -fill red -tag p0 ;# crossbar
+    ${:c} create rect 72 87 82 100 -fill black ;# steam tube
+    ${:c} create rect 310 40 370 115 -fill black ;# cab
+    ${:c} create rect 310 32 390 42 -fill grey30 ;# cab roof
+    ${:c} create text 338 82 -text "01 234" -fill gold -font {Times 7}
+    ${:c} create rect 318 48 333 66 -fill white ;# cab window #1
+    ${:c} create rect 338 48 355 66 -fill white ;# cab window #2
+    Wheel new -childof [self] -x 50 -y 150 -r 13 -spokes 12
+    Wheel new -childof [self] -x 105 -y 150 -r 13 -spokes 12
+    Wheel new -childof [self] -x 150 -y 150 -r 30 -pivot 0.5 -tag p1
+    Wheel new -childof [self] -x 215 -y 150 -r 30 -pivot 0.5 -tag p2
+    Wheel new -childof [self] -x 280 -y 150 -r 30 -pivot 0.5 -tag p3
+    :drawRod p0 p1 p2 p3
+    Wheel new -childof [self] -x 340 -y 150 -r 16 -spokes 12
+    ${:c} create rect 360 110 380 118 -fill black
+    ${:c} create rect 380 65 560 125 -fill black -tag tender
+    ${:c} create rect 560 118 570 122 -fill grey30 ;# buffer
+    ${:c} create line 571 116 571 125
+    ${:c} create rect 390 45 525 65 -fill black -tag tender
+    Wheel new -childof [self] -x 395 -y 150 -r 13 -spokes 12
+    Wheel new -childof [self] -x 440 -y 150 -r 13 -spokes 12
+    ${:c} create rect 380 132 456 142 -fill red
+    Wheel new -childof [self] -x 495 -y 150 -r 13 -spokes 12
+    Wheel new -childof [self] -x 540 -y 150 -r 13 -spokes 12
+    ${:c} create rect 480 132 556 142 -fill red -outline red
+    ${:c} create rect 0 150 600 160 -fill brown ;# earth
+    ${:c} create line 0 150 600 150 -fill grey -width 2 ;# rail
+    :tick
+  }
+}
+
+Locomotive new
+
+
+
+

+ + + Index: doc/example-scripts/tk-ludo.html =================================================================== diff -u -r71e18053ef7dd9a53f11d14f3a91b8a1091e90bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-ludo.html (.../tk-ludo.html) (revision 71e18053ef7dd9a53f11d14f3a91b8a1091e90bb) +++ doc/example-scripts/tk-ludo.html (.../tk-ludo.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,1391 +1,1391 @@ - - - - - -Listing of doc/example-scripts/tk-ludo.tcl - - - - - -
-
-
-

A small Ludo/Mensch ärgere Dich nicht/Pachisie game, originally -developed by Richard Suchenwirth in plain Tcl (see -http://wiki.tcl.tk/956). The game was rewritten as a design study in -NX by Gustaf Neumann in July 2013.

-

Major changes:

-
    -
  • -

    -object-oriented design (no global variables) -

    -
  • -
  • -

    -knowledge about the paths of the figures -

    -
  • -
  • -

    -animated moves -

    -
  • -
  • -

    -knowledge about the basic rules (e.g. need 6 to move out of the - nest, have to move figures from starting position) -

    -
  • -
  • -

    -throw opponents out -

    -
  • -
  • -

    -sanity checks -

    -
  • -
  • -

    -user feedback -

    -
  • -
-
-
-tk-ludo.png -
-
-

Short Instructions

-
    -
  • -

    -The active player (marked with the button) has to dice (click on - the die, or press somewhere on the board "d"). -

    -
  • -
  • -

    -If all figures are in the nest (start position), the player needs - to dice a 6. The player is allowed to try three times, then the - player is done (press "done" button, or type "n") and the turn - moves to the next player. -

    -
  • -
  • -

    -When a player got 6 eyes, he can move out of the nest. This is - done by clicking on the figure the player wants to move. -

    -
  • -
  • -

    -After dicing 6, the player can dice again and move the player on - the field (always by clicking on the figure). -

    -
  • -
-
-
-
-

Implementation

-
-
-
-
package require Tk
-package require nx::trait
-

Define an application specific converter "expr" that passes the -scalar result of the expression. Since the converter is defined on -nx::Slot, it is applicable to all method and configure arguments.

-
-
-
::nx::Slot method type=expr {name value} {return [expr $value]}
-

Class Figure

-
-
-
nx::Class create Figure {
-    :property canvas:required
-    :property x:double
-    :property y:double
-    :property size:double
-    :property position:integer
-    :property color
-    :property no:integer
-    :property board:object,required
-    :variable tag ""
-
-    :require trait nx::trait::callback
-
-    :method init {} {
-        #
-        # Draw figure and define interactions
-        #
-        set d [expr {${:size}/6.}]
-        set s [expr {${:size}/1.5}]
-        set y [expr {${:y}-$d*2.5}]
-        set :tag ${:color}${:no}
-        set id [${:canvas} create arc [expr {${:x}-$s}] [expr {${:y}-$s}] \
-                    [expr {${:x}+$s}] [expr {${:y}+$s}] -outline grey \
-                    -start 250 -extent 40 -fill ${:color} \
-                    -tags [list mv ${:tag}]]
-        ${:canvas} create oval \
-            [expr {${:x}-$d}] [expr {${:y}-$d}] \
-            [expr {${:x}+$d}] [expr {${:y}+$d}] \
-            -fill ${:color} -outline grey -tags [list mv ${:tag}]
-        #${:board} figure set $id [self]
-        ${:canvas} bind ${:tag} <B1-ButtonRelease> [:callback go]
-    }
-
-    :public method go {} {
-        #
-        # Start moving the figure if the draw is permitted.
-        # The board knows the die and the rules.
-        #
-        if {![${:board} moveFigure [self]]} {
-            # stay at old position
-            :gotoNr ${:position}
-        }
-    }
-
-    :public method gotoNr {nr {-path ""} {-afterCmd ""}} {
-        #
-        # Move figure to the numbered position. If a path is given it
-        # moves stepwise from position to position.
-        #
-        set oldPos ${:position}
-        set :position $nr
-        if {$path eq ""} {set path $nr}
-        return [:move {*}[${:board} getPointCenter $oldPos] $path \
-                    -afterCmd $afterCmd]
-    }
-
-    :protected method move {x0 y0 path:integer,1..n {-afterCmd ""}} {
-        #
-        # Move figure from old position (x0 y0) stepwise along the
-        # path using animation. At the end of the move, 'afterCmd' is
-        # issued.
-        #
-        set t 0
-        foreach pos $path {
-            lassign [${:board} getPointCenter $pos] x y
-            set stepx [expr {($x-$x0)/50.0}]
-            set stepy [expr {($y-$y0)/50.0}]
-            for {set i 0} {$i < 50} {incr i} {
-                after [incr t 8] ${:canvas} move ${:tag} $stepx $stepy
-            }
-            lassign [list $x $y] x0 y0
-            incr t 100
-        }
-        after $t ${:canvas} raise ${:tag}
-        after $t $afterCmd
-        set :x $x; set :y $y
-    }
-
-    :public object method lookup {position} {
-        #
-        # Return the figure at the provided position.  This function
-        # could be made faster, but is efficient enough as it is.
-        #
-        foreach f [Figure info instances] {
-            if {[$f cget -position] == $position} {
-                return $f
-            }
-        }
-        return ""
-    }
-}
-

Helper functions for the die

-
-
-
proc random:select L {lindex $L [expr int(rand()*[llength $L].)]}
-proc lexpr {term L} {
-    # map an expr term to each element \$i of a list
-    set res [list]
-    foreach i $L {lappend res [eval expr $term]}
-    set res
-}
-

Class Die

-
-
-
nx::Class create Die {
-    :property canvas:required
-    :property x:double
-    :property y:double
-    :property {size:double 25}
-    :property {fg gold}
-    :property {bg red}
-    :property {eyes 0}
-
-    :require trait nx::trait::callback
-
-    :method set {n} {
-        #
-        # Set the eyes of the die.
-        #
-        ${:canvas} itemconfig ${:grouptag} -fill ${:bg} -outline ${:bg}
-        foreach i [lindex [list \
-               {} {d5} [random:select {{d3 d7} {d1 d9}}] \
-               [random:select {{d1 d5 d9} {d3 d5 d7}}] \
-               {d1 d3 d7 d9} {d1 d3 d5 d7 d9} \
-               [random:select {{d1 d3 d4 d6 d7 d9} {d1 d2 d3 d7 d8 d9}}] \
-              ] $n] {
-            ${:canvas} itemconfig ${:id}$i -fill ${:fg} -outline ${:fg}
-        }
-        set :eyes $n
-    }
-
-    :public method invalidate {} {
-        #
-        # Invalidate the eyes to avoid double uses of the eyes.
-        #
-        set :eyes 0
-    }
-
-    :public method roll {} {
-        #
-        # Roll the dice and animate rolling
-        #
-        # wiggle: amount, pick one of eight wiggle directions
-        set dwig [expr {${:size}/5}]
-        for {set i 10} {$i<100} {incr i 10} {
-            :set [expr {int(rand() * 6) + 1}]
-            set wig [random:select {0,1 0,-1 1,0 -1,0 1,1 -1,1 1,-1 -1,-1}]
-            set wig [lexpr \$i*$dwig [split $wig ,]]
-            ${:canvas} move group${:id} {*}$wig
-            update
-            set wig [lexpr \$i*-1 $wig] ;# wiggle back
-            ${:canvas} move group${:id} {*}$wig
-            after $i
-        }
-    }
-
-    :method init {} {
-        #
-        # initialize the widgets with tags interactions
-        #
-        set x [expr {${:x} - ${:size}/2.0}]
-        set y [expr {${:y} - ${:size}/2.0}]
-        set :id [${:canvas} create rect $x $y \
-                     [expr {$x+${:size}}] [expr {$y+${:size}}] \
-                     -fill ${:bg} -tags mvg]
-        set :grouptag group${:id}
-        ${:canvas} addtag ${:grouptag} withtag ${:id}
-        set ex [expr {$x+${:size}/10.}]
-        set ey [expr {$y+${:size}/10.}]
-        set d  [expr {${:size}/5.}];# dot diameter
-        set dotno 1 ;# dot counter
-        foreach y [list $ey [expr {$ey+$d*1.5}] [expr {$ey+$d*3}]] {
-            foreach x [list $ex [expr {$ex+$d*1.5}] [expr {$ex+$d*3}]] {
-                ${:canvas} create oval $x $y [expr {$x+$d}] [expr {$y+$d}] \
-                    -fill ${:bg} -outline ${:bg} \
-                    -tags [list mvg ${:grouptag} ${:id}d$dotno]
-                incr dotno
-            }
-        }
-        :set [expr {int(rand()*6)+1}]
-        :invalidate
-        #
-        # To dice, let people click on the die, or press <d> on the
-        # board
-        #
-        ${:canvas} bind mvg <1> [:callback roll]
-        bind . <d> [:callback roll]
-    }
-}
-

Class Board

-
-
-
nx::Class create Board {
-    :property canvas:required
-    :property {size:integer 25}
-    :property {bg LightBlue1}
-    :property {fg white}
-    :property {colors:1..n {red green yellow blue}}
-
-    :require trait nx::trait::callback
-
-    :method lookup {var idx} {
-        #
-        # Convenience lookup function for arbitrary instance
-        # variables.
-        #
-        set key "${var}($idx)"
-        if {[info exists $key]} {return [set $key]}
-        return ""
-    }
-
-    :public method getPointCenter {nr} {:lookup :pointCenter $nr}
-    :public method getPointId {nr}     {:lookup :pointId $nr}
-
-    :method line {
-        x0:expr,convert y0:expr,convert x1:expr,convert y1:expr,convert
-        {-width 1} {-arrow none}
-    } {
-        #
-        # Convenience function for line drawing, evaluates passed
-        # expressions.
-        #
-        ${:canvas} create line $x0 $y0 $x1 $y1 -width $width -arrow $arrow
-    }
-
-    :method point {x:expr,convert y:expr,convert d {-number:switch false} -fill} {
-        #
-        # Draw a point (a position on the game board) and keep its
-        # basic data in instance variables. We could as well turn the
-        # positions into objects.
-        #
-        if {![info exists fill]} {set fill ${:fg}}
-        incr :pointCounter
-        set id [${:canvas} create oval \
-                    [expr {$x-$d/2.}] [expr {$y-$d/2.}] \
-                    [expr {$x+$d/2.}] [expr {$y+$d/2.}] \
-                    -fill $fill -tags [list point] -outline brown -width 2]
-        #${:canvas} create text $x $y -text ${:pointCounter} -fill grey
-        set :pointNr($id) ${:pointCounter}
-        set :pointCenter(${:pointCounter}) [list $x $y]
-        set :pointId(${:pointCounter}) $id
-        return ${:pointCounter}
-    }
-
-    :method fpoint {x:expr,convert y:expr,convert psize fsize color no} {
-        #
-        # Draw a point with a figure, note the position in the board
-        # in the figure
-        #
-        set nr [:point $x $y $psize -fill $color]
-        Figure new -board [self] -canvas ${:canvas} \
-            -x $x -y [expr {$y-$fsize/2.0}] \
-            -size $fsize -color $color -no $no -position $nr
-        return $nr
-    }
-
-    :method pnest {x:expr,convert y:expr,convert d colorNr xf yf} {
-        #
-        # Draw the nest with the figures in it
-        #
-        set fsize [expr {$d/0.75}]
-        set color [lindex ${:colors} $colorNr]
-        lappend :nest($colorNr) [:fpoint $x-$d $y-$d $d $fsize $color 0]
-        lappend :nest($colorNr) [:fpoint $x-$d $y+$d $d $fsize $color 1]
-        lappend :nest($colorNr) [:fpoint $x+$d $y-$d $d $fsize $color 2]
-        lappend :nest($colorNr) [:fpoint $x+$d $y+$d $d $fsize $color 3]
-        set :buttonPos($colorNr) [list [expr $x+($xf*$d)] [expr $y+($yf*$d)]]
-    }
-
-    :method pline {
-        x0:expr,convert y0:expr,convert
-        x1:expr,convert y1:expr,convert d {-width 1} {-arrow none}
-    } {
-        #
-        # Draw a path of the play-field with points (potential player
-        # positions) on it.
-        #
-        set id [${:canvas} create line $x0 $y0 $x1 $y1 \
-                    -width $width -arrow $arrow -fill brown]
-        if {$x0 eq $x1} {
-            # vertical
-            set f [expr {$y1<$y0 ? -1.25 : 1.25}]
-            for {set i 0} {$i < int(abs($y1-$y0)/($d*1.25))} {incr i} {
-                :point $x0 $y0+$i*$d*$f $d
-            }
-        } else {
-            # horizontal
-            set f [expr {$x1<$x0 ? -1.25 : 1.25}]
-            for {set i 0} {$i < int(abs($x1-$x0)/($d*1.25))} {incr i} {
-                :point $x0+$i*$d*$f $y0 $d -number
-            }
-        }
-        ${:canvas} lower $id
-    }
-
-    :method draw {m} {
-        #
-        # Draw board and create figures
-        #
-        set d ${:size}
-        set u [expr {$d * 1.25}]
-        #
-        # Major positions: p0 .. p1 ..m.. p2 .. p3
-        #
-        set p0 [expr {$u-$d/2.0}]
-        set p1 [expr {$m-$u}]
-        set p2 [expr {$m+$u}]
-        set p3 [expr {2*$m-$u+$d/2}]
-
-        :pline $p0 $p1 $p1 $p1 $d -width 4
-        :pline $p1 $p1 $p1 $p0 $d -width 4
-        :pline $p1 $p0 $p2 $p0 $d -width 4 ;# horizonal short line
-        :pline $p2 $p0 $p2 $p1 $d -width 4
-        :pline $p2 $p1 $p3 $p1 $d -width 4
-        :pline $p3 $p1 $p3 $p2 $d -width 4 ;# vertical short line
-        :pline $p3 $p2 $p2 $p2 $d -width 4
-        :pline $p2 $p2 $p2 $p3 $d -width 4
-        :pline $p2 $p3 $p1 $p3 $d -width 4 ;# horizonal short line
-        :pline $p1 $p3 $p1 $p2 $d -width 4
-        :pline $p1 $p2 $p0 $p2 $d -width 4
-        :pline $p0 $p2 $p0 $p1 $d -width 4 ;# vertical short line
-        :line $m+5*$d  $m+2*$d  $m+6*$d  $m+2*$d -arrow first
-        :line $m-2*$d  $m+5*$d  $m-2*$d  $m+6*$d -arrow first
-        :line $m-5*$d  $m-2*$d  $m-6*$d  $m-2*$d -arrow first
-        :line $m+2*$d  $m-5*$d  $m+2*$d  $m-6*$d -arrow first
-
-        set d2 [expr {$d*0.75}]
-        set d15 $d2*2
-        set o [expr {$u*5}]
-        :pnest $m+$o-$d $m-$o+$d $d2 0 -1  3
-        :pnest $m+$o-$d $m+$o-$d $d2 1 -1 -2.5
-        :pnest $d15     $m+$o-$d $d2 2  1 -2.5
-        :pnest $d15     $m-$o+$d $d2 3  1  3
-        for {set i 0; set y [expr $d*2]} {$i<4} {incr i;set y [expr {$y+$d}]} {
-            lappend p(0) [:point $m      $y      $d2 -fill [lindex ${:colors} 0]]
-            lappend p(1) [:point $m*2-$y $m      $d2 -fill [lindex ${:colors} 1]]
-            lappend p(2) [:point $m      $m*2-$y $d2 -fill [lindex ${:colors} 2]]
-            lappend p(3) [:point $y      $m      $d2 -fill [lindex ${:colors} 3]]
-        }
-        #
-        # Setup the path per player and color the starting points
-        #
-        for {set i 1} {$i < 41} {incr i} {lappend path $i}
-        foreach c {0 1 2 3} pos {11 21 31 1} o {11 21 31 1} {
-            ${:canvas} itemconfig [:getPointId $pos] -fill [lindex ${:colors} $c]
-            set :path($c) [concat [lrange $path $o-1 end] [lrange $path 0 $o-2] $p($c)]
-        }
-    }
-
-    :public method msg {text} {
-        #
-        # Report a message to the user.
-        #
-        ${:canvas} itemconfig ${:msgId} -text $text
-        return 0
-    }
-
-    :public method wannaGo {obj pos {-path ""}} {
-        #
-        # We know that we can move the figure in principle.  We have
-        # to check, whether the target position is free. If the target
-        # is occupied by our own player, we give up, otherwise we
-        # through the opponent out.
-        #
-        if {$pos eq ""} {return [:msg "beyond path"]}
-        set other [Figure lookup $pos]
-        set afterCmd ""
-        if {$other ne ""} {
-            if {[$obj cget -color] eq [$other cget -color]} {
-                # On player can't have two figure at the same place.
-                return [:msg "My player is already at pos $pos"]
-            } else {
-                # Opponent is at the target position. Find a free
-                # position in the opponents nest and though her out.
-                set opponent [$other cget -color]
-                foreach p [set :nest([lsearch ${:colors} $opponent])] {
-                    if {[Figure lookup $p] eq ""} {
-                        set afterCmd [list $other gotoNr $p]
-                        break
-                    }
-                }
-            }
-        }
-        :msg "[$obj cget -color]-[$obj cget -no] went to $pos"
-        $obj gotoNr $pos -path $path -afterCmd $afterCmd
-        ${:die} invalidate
-    }
-
-    :public method moveFigure {obj} {
-        #
-        # Move the provided figure by the diced eyes according to the
-        # rules. First we check, if we are allowed to move this
-        # figure, which might be in the nest or on the run.
-        #
-        set currentColor [lindex ${:colors} ${:player}]
-        if {[$obj cget -color] ne $currentColor} {
-            return [:msg "figure is not from the current player"]
-        }
-        set eyes [${:die} cget -eyes]
-        if {$eyes == 0} {
-            return [:msg "Must dice first"]
-        }
-        set position [$obj cget -position]
-        if {$position in [set :nest(${:player})]} {
-            # Figure is in the nest, just accept eyes == 6
-            if {$eyes == 6} {
-                :wannaGo $obj [lindex [set :path(${:player})] 0]
-            } else {
-                return [:msg "Need 6 to move this figure"]
-            }
-        } else {
-            #
-            # Check, if we have still figures in the nest
-            #
-            set inNest ""
-            foreach p [set :nest(${:player})] {
-                set inNest [Figure lookup $p]
-                if {$inNest ne ""} break
-            }
-            #
-            # Check, if the actual figure is at the start position.
-            #
-            set startPos [lindex [set :path(${:player})] 0]
-            set atStart [Figure lookup $startPos]
-            if {$eyes == 6} {
-                if {$inNest ne ""} {
-                    # Move a figure out from the nest, if we can
-                    if {$atStart ne ""} {
-                        if {[$atStart cget -color] eq $currentColor} {
-                            set path [set :path(${:player})]
-                            set current [lsearch $path $position]
-                            set targetPos [expr {$current + [${:die} cget -eyes]}]
-                            :wannaGo $obj [lindex $path $targetPos] \
-                                -path [lrange $path $current+1 $targetPos]
-                            return 1
-                        }
-                    }
-                    return [:msg "You have to move the figures from your nest first"]
-                }
-            }
-            if {$atStart ne "" && $inNest ne "" && $obj ne $atStart} {
-                return [:msg "You have to move the figures from the start first"]
-            }
-            set path [set :path(${:player})]
-            set current [lsearch $path $position]
-            set targetPos [expr {$current + [${:die} cget -eyes]}]
-            :wannaGo $obj [lindex $path $targetPos] \
-                -path [lrange $path $current+1 $targetPos]
-        }
-        return 1
-    }
-
-    :public method nextPlayer {} {
-        #
-        # Switch to the next player.
-        #
-        set :player [expr {(${:player}+1) % 4}]
-        ${:canvas} coords ${:buttonWindow} {*}[set :buttonPos(${:player})]
-    }
-
-    :method init {} {
-        set hw [expr {14 * ${:size}}]
-        set center [expr {$hw / 2}]
-        canvas ${:canvas} -bg ${:bg} -height $hw -width $hw
-        :draw $center
-        set :die [Die new -canvas ${:canvas} -x $center -y $center -size ${:size}]
-        set :msgId [${:canvas} create text [expr {${:size}*4}] 10 -text ""]
-        #
-        # Player management (signal which player is next, etc.)
-        #
-        set :player 2
-        button .b1 -text "Done" -command [:callback nextPlayer]
-        set :buttonWindow [.p create window 22 14 -window .b1]
-        :nextPlayer
-        bind . <n> [:callback nextPlayer]
-    }
-}
-

Finally, create the board and pack it

-
-
-
Board new -canvas .p -bg beige -size 40
-pack .p
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-ludo.tcl + + + + + +
+
+
+

A small Ludo/Mensch ärgere Dich nicht/Pachisie game, originally +developed by Richard Suchenwirth in plain Tcl (see +http://wiki.tcl.tk/956). The game was rewritten as a design study in +NX by Gustaf Neumann in July 2013.

+

Major changes:

+
    +
  • +

    +object-oriented design (no global variables) +

    +
  • +
  • +

    +knowledge about the paths of the figures +

    +
  • +
  • +

    +animated moves +

    +
  • +
  • +

    +knowledge about the basic rules (e.g. need 6 to move out of the + nest, have to move figures from starting position) +

    +
  • +
  • +

    +throw opponents out +

    +
  • +
  • +

    +sanity checks +

    +
  • +
  • +

    +user feedback +

    +
  • +
+
+
+tk-ludo.png +
+
+

Short Instructions

+
    +
  • +

    +The active player (marked with the button) has to dice (click on + the die, or press somewhere on the board "d"). +

    +
  • +
  • +

    +If all figures are in the nest (start position), the player needs + to dice a 6. The player is allowed to try three times, then the + player is done (press "done" button, or type "n") and the turn + moves to the next player. +

    +
  • +
  • +

    +When a player got 6 eyes, he can move out of the nest. This is + done by clicking on the figure the player wants to move. +

    +
  • +
  • +

    +After dicing 6, the player can dice again and move the player on + the field (always by clicking on the figure). +

    +
  • +
+
+
+
+

Implementation

+
+
+
+
package require Tk
+package require nx::trait
+

Define an application specific converter "expr" that passes the +scalar result of the expression. Since the converter is defined on +nx::Slot, it is applicable to all method and configure arguments.

+
+
+
::nx::Slot method type=expr {name value} {return [expr $value]}
+

Class Figure

+
+
+
nx::Class create Figure {
+    :property canvas:required
+    :property x:double
+    :property y:double
+    :property size:double
+    :property position:integer
+    :property color
+    :property no:integer
+    :property board:object,required
+    :variable tag ""
+
+    :require trait nx::trait::callback
+
+    :method init {} {
+        #
+        # Draw figure and define interactions
+        #
+        set d [expr {${:size}/6.}]
+        set s [expr {${:size}/1.5}]
+        set y [expr {${:y}-$d*2.5}]
+        set :tag ${:color}${:no}
+        set id [${:canvas} create arc [expr {${:x}-$s}] [expr {${:y}-$s}] \
+                    [expr {${:x}+$s}] [expr {${:y}+$s}] -outline grey \
+                    -start 250 -extent 40 -fill ${:color} \
+                    -tags [list mv ${:tag}]]
+        ${:canvas} create oval \
+            [expr {${:x}-$d}] [expr {${:y}-$d}] \
+            [expr {${:x}+$d}] [expr {${:y}+$d}] \
+            -fill ${:color} -outline grey -tags [list mv ${:tag}]
+        #${:board} figure set $id [self]
+        ${:canvas} bind ${:tag} <B1-ButtonRelease> [:callback go]
+    }
+
+    :public method go {} {
+        #
+        # Start moving the figure if the draw is permitted.
+        # The board knows the die and the rules.
+        #
+        if {![${:board} moveFigure [self]]} {
+            # stay at old position
+            :gotoNr ${:position}
+        }
+    }
+
+    :public method gotoNr {nr {-path ""} {-afterCmd ""}} {
+        #
+        # Move figure to the numbered position. If a path is given it
+        # moves stepwise from position to position.
+        #
+        set oldPos ${:position}
+        set :position $nr
+        if {$path eq ""} {set path $nr}
+        return [:move {*}[${:board} getPointCenter $oldPos] $path \
+                    -afterCmd $afterCmd]
+    }
+
+    :protected method move {x0 y0 path:integer,1..n {-afterCmd ""}} {
+        #
+        # Move figure from old position (x0 y0) stepwise along the
+        # path using animation. At the end of the move, 'afterCmd' is
+        # issued.
+        #
+        set t 0
+        foreach pos $path {
+            lassign [${:board} getPointCenter $pos] x y
+            set stepx [expr {($x-$x0)/50.0}]
+            set stepy [expr {($y-$y0)/50.0}]
+            for {set i 0} {$i < 50} {incr i} {
+                after [incr t 8] ${:canvas} move ${:tag} $stepx $stepy
+            }
+            lassign [list $x $y] x0 y0
+            incr t 100
+        }
+        after $t ${:canvas} raise ${:tag}
+        after $t $afterCmd
+        set :x $x; set :y $y
+    }
+
+    :public object method lookup {position} {
+        #
+        # Return the figure at the provided position.  This function
+        # could be made faster, but is efficient enough as it is.
+        #
+        foreach f [Figure info instances] {
+            if {[$f cget -position] == $position} {
+                return $f
+            }
+        }
+        return ""
+    }
+}
+

Helper functions for the die

+
+
+
proc random:select L {lindex $L [expr int(rand()*[llength $L].)]}
+proc lexpr {term L} {
+    # map an expr term to each element \$i of a list
+    set res [list]
+    foreach i $L {lappend res [eval expr $term]}
+    set res
+}
+

Class Die

+
+
+
nx::Class create Die {
+    :property canvas:required
+    :property x:double
+    :property y:double
+    :property {size:double 25}
+    :property {fg gold}
+    :property {bg red}
+    :property {eyes 0}
+
+    :require trait nx::trait::callback
+
+    :method set {n} {
+        #
+        # Set the eyes of the die.
+        #
+        ${:canvas} itemconfig ${:grouptag} -fill ${:bg} -outline ${:bg}
+        foreach i [lindex [list \
+               {} {d5} [random:select {{d3 d7} {d1 d9}}] \
+               [random:select {{d1 d5 d9} {d3 d5 d7}}] \
+               {d1 d3 d7 d9} {d1 d3 d5 d7 d9} \
+               [random:select {{d1 d3 d4 d6 d7 d9} {d1 d2 d3 d7 d8 d9}}] \
+              ] $n] {
+            ${:canvas} itemconfig ${:id}$i -fill ${:fg} -outline ${:fg}
+        }
+        set :eyes $n
+    }
+
+    :public method invalidate {} {
+        #
+        # Invalidate the eyes to avoid double uses of the eyes.
+        #
+        set :eyes 0
+    }
+
+    :public method roll {} {
+        #
+        # Roll the dice and animate rolling
+        #
+        # wiggle: amount, pick one of eight wiggle directions
+        set dwig [expr {${:size}/5}]
+        for {set i 10} {$i<100} {incr i 10} {
+            :set [expr {int(rand() * 6) + 1}]
+            set wig [random:select {0,1 0,-1 1,0 -1,0 1,1 -1,1 1,-1 -1,-1}]
+            set wig [lexpr \$i*$dwig [split $wig ,]]
+            ${:canvas} move group${:id} {*}$wig
+            update
+            set wig [lexpr \$i*-1 $wig] ;# wiggle back
+            ${:canvas} move group${:id} {*}$wig
+            after $i
+        }
+    }
+
+    :method init {} {
+        #
+        # initialize the widgets with tags interactions
+        #
+        set x [expr {${:x} - ${:size}/2.0}]
+        set y [expr {${:y} - ${:size}/2.0}]
+        set :id [${:canvas} create rect $x $y \
+                     [expr {$x+${:size}}] [expr {$y+${:size}}] \
+                     -fill ${:bg} -tags mvg]
+        set :grouptag group${:id}
+        ${:canvas} addtag ${:grouptag} withtag ${:id}
+        set ex [expr {$x+${:size}/10.}]
+        set ey [expr {$y+${:size}/10.}]
+        set d  [expr {${:size}/5.}];# dot diameter
+        set dotno 1 ;# dot counter
+        foreach y [list $ey [expr {$ey+$d*1.5}] [expr {$ey+$d*3}]] {
+            foreach x [list $ex [expr {$ex+$d*1.5}] [expr {$ex+$d*3}]] {
+                ${:canvas} create oval $x $y [expr {$x+$d}] [expr {$y+$d}] \
+                    -fill ${:bg} -outline ${:bg} \
+                    -tags [list mvg ${:grouptag} ${:id}d$dotno]
+                incr dotno
+            }
+        }
+        :set [expr {int(rand()*6)+1}]
+        :invalidate
+        #
+        # To dice, let people click on the die, or press <d> on the
+        # board
+        #
+        ${:canvas} bind mvg <1> [:callback roll]
+        bind . <d> [:callback roll]
+    }
+}
+

Class Board

+
+
+
nx::Class create Board {
+    :property canvas:required
+    :property {size:integer 25}
+    :property {bg LightBlue1}
+    :property {fg white}
+    :property {colors:1..n {red green yellow blue}}
+
+    :require trait nx::trait::callback
+
+    :method lookup {var idx} {
+        #
+        # Convenience lookup function for arbitrary instance
+        # variables.
+        #
+        set key "${var}($idx)"
+        if {[info exists $key]} {return [set $key]}
+        return ""
+    }
+
+    :public method getPointCenter {nr} {:lookup :pointCenter $nr}
+    :public method getPointId {nr}     {:lookup :pointId $nr}
+
+    :method line {
+        x0:expr,convert y0:expr,convert x1:expr,convert y1:expr,convert
+        {-width 1} {-arrow none}
+    } {
+        #
+        # Convenience function for line drawing, evaluates passed
+        # expressions.
+        #
+        ${:canvas} create line $x0 $y0 $x1 $y1 -width $width -arrow $arrow
+    }
+
+    :method point {x:expr,convert y:expr,convert d {-number:switch false} -fill} {
+        #
+        # Draw a point (a position on the game board) and keep its
+        # basic data in instance variables. We could as well turn the
+        # positions into objects.
+        #
+        if {![info exists fill]} {set fill ${:fg}}
+        incr :pointCounter
+        set id [${:canvas} create oval \
+                    [expr {$x-$d/2.}] [expr {$y-$d/2.}] \
+                    [expr {$x+$d/2.}] [expr {$y+$d/2.}] \
+                    -fill $fill -tags [list point] -outline brown -width 2]
+        #${:canvas} create text $x $y -text ${:pointCounter} -fill grey
+        set :pointNr($id) ${:pointCounter}
+        set :pointCenter(${:pointCounter}) [list $x $y]
+        set :pointId(${:pointCounter}) $id
+        return ${:pointCounter}
+    }
+
+    :method fpoint {x:expr,convert y:expr,convert psize fsize color no} {
+        #
+        # Draw a point with a figure, note the position in the board
+        # in the figure
+        #
+        set nr [:point $x $y $psize -fill $color]
+        Figure new -board [self] -canvas ${:canvas} \
+            -x $x -y [expr {$y-$fsize/2.0}] \
+            -size $fsize -color $color -no $no -position $nr
+        return $nr
+    }
+
+    :method pnest {x:expr,convert y:expr,convert d colorNr xf yf} {
+        #
+        # Draw the nest with the figures in it
+        #
+        set fsize [expr {$d/0.75}]
+        set color [lindex ${:colors} $colorNr]
+        lappend :nest($colorNr) [:fpoint $x-$d $y-$d $d $fsize $color 0]
+        lappend :nest($colorNr) [:fpoint $x-$d $y+$d $d $fsize $color 1]
+        lappend :nest($colorNr) [:fpoint $x+$d $y-$d $d $fsize $color 2]
+        lappend :nest($colorNr) [:fpoint $x+$d $y+$d $d $fsize $color 3]
+        set :buttonPos($colorNr) [list [expr $x+($xf*$d)] [expr $y+($yf*$d)]]
+    }
+
+    :method pline {
+        x0:expr,convert y0:expr,convert
+        x1:expr,convert y1:expr,convert d {-width 1} {-arrow none}
+    } {
+        #
+        # Draw a path of the play-field with points (potential player
+        # positions) on it.
+        #
+        set id [${:canvas} create line $x0 $y0 $x1 $y1 \
+                    -width $width -arrow $arrow -fill brown]
+        if {$x0 eq $x1} {
+            # vertical
+            set f [expr {$y1<$y0 ? -1.25 : 1.25}]
+            for {set i 0} {$i < int(abs($y1-$y0)/($d*1.25))} {incr i} {
+                :point $x0 $y0+$i*$d*$f $d
+            }
+        } else {
+            # horizontal
+            set f [expr {$x1<$x0 ? -1.25 : 1.25}]
+            for {set i 0} {$i < int(abs($x1-$x0)/($d*1.25))} {incr i} {
+                :point $x0+$i*$d*$f $y0 $d -number
+            }
+        }
+        ${:canvas} lower $id
+    }
+
+    :method draw {m} {
+        #
+        # Draw board and create figures
+        #
+        set d ${:size}
+        set u [expr {$d * 1.25}]
+        #
+        # Major positions: p0 .. p1 ..m.. p2 .. p3
+        #
+        set p0 [expr {$u-$d/2.0}]
+        set p1 [expr {$m-$u}]
+        set p2 [expr {$m+$u}]
+        set p3 [expr {2*$m-$u+$d/2}]
+
+        :pline $p0 $p1 $p1 $p1 $d -width 4
+        :pline $p1 $p1 $p1 $p0 $d -width 4
+        :pline $p1 $p0 $p2 $p0 $d -width 4 ;# horizonal short line
+        :pline $p2 $p0 $p2 $p1 $d -width 4
+        :pline $p2 $p1 $p3 $p1 $d -width 4
+        :pline $p3 $p1 $p3 $p2 $d -width 4 ;# vertical short line
+        :pline $p3 $p2 $p2 $p2 $d -width 4
+        :pline $p2 $p2 $p2 $p3 $d -width 4
+        :pline $p2 $p3 $p1 $p3 $d -width 4 ;# horizonal short line
+        :pline $p1 $p3 $p1 $p2 $d -width 4
+        :pline $p1 $p2 $p0 $p2 $d -width 4
+        :pline $p0 $p2 $p0 $p1 $d -width 4 ;# vertical short line
+        :line $m+5*$d  $m+2*$d  $m+6*$d  $m+2*$d -arrow first
+        :line $m-2*$d  $m+5*$d  $m-2*$d  $m+6*$d -arrow first
+        :line $m-5*$d  $m-2*$d  $m-6*$d  $m-2*$d -arrow first
+        :line $m+2*$d  $m-5*$d  $m+2*$d  $m-6*$d -arrow first
+
+        set d2 [expr {$d*0.75}]
+        set d15 $d2*2
+        set o [expr {$u*5}]
+        :pnest $m+$o-$d $m-$o+$d $d2 0 -1  3
+        :pnest $m+$o-$d $m+$o-$d $d2 1 -1 -2.5
+        :pnest $d15     $m+$o-$d $d2 2  1 -2.5
+        :pnest $d15     $m-$o+$d $d2 3  1  3
+        for {set i 0; set y [expr $d*2]} {$i<4} {incr i;set y [expr {$y+$d}]} {
+            lappend p(0) [:point $m      $y      $d2 -fill [lindex ${:colors} 0]]
+            lappend p(1) [:point $m*2-$y $m      $d2 -fill [lindex ${:colors} 1]]
+            lappend p(2) [:point $m      $m*2-$y $d2 -fill [lindex ${:colors} 2]]
+            lappend p(3) [:point $y      $m      $d2 -fill [lindex ${:colors} 3]]
+        }
+        #
+        # Setup the path per player and color the starting points
+        #
+        for {set i 1} {$i < 41} {incr i} {lappend path $i}
+        foreach c {0 1 2 3} pos {11 21 31 1} o {11 21 31 1} {
+            ${:canvas} itemconfig [:getPointId $pos] -fill [lindex ${:colors} $c]
+            set :path($c) [concat [lrange $path $o-1 end] [lrange $path 0 $o-2] $p($c)]
+        }
+    }
+
+    :public method msg {text} {
+        #
+        # Report a message to the user.
+        #
+        ${:canvas} itemconfig ${:msgId} -text $text
+        return 0
+    }
+
+    :public method wannaGo {obj pos {-path ""}} {
+        #
+        # We know that we can move the figure in principle.  We have
+        # to check, whether the target position is free. If the target
+        # is occupied by our own player, we give up, otherwise we
+        # through the opponent out.
+        #
+        if {$pos eq ""} {return [:msg "beyond path"]}
+        set other [Figure lookup $pos]
+        set afterCmd ""
+        if {$other ne ""} {
+            if {[$obj cget -color] eq [$other cget -color]} {
+                # On player can't have two figure at the same place.
+                return [:msg "My player is already at pos $pos"]
+            } else {
+                # Opponent is at the target position. Find a free
+                # position in the opponents nest and though her out.
+                set opponent [$other cget -color]
+                foreach p [set :nest([lsearch ${:colors} $opponent])] {
+                    if {[Figure lookup $p] eq ""} {
+                        set afterCmd [list $other gotoNr $p]
+                        break
+                    }
+                }
+            }
+        }
+        :msg "[$obj cget -color]-[$obj cget -no] went to $pos"
+        $obj gotoNr $pos -path $path -afterCmd $afterCmd
+        ${:die} invalidate
+    }
+
+    :public method moveFigure {obj} {
+        #
+        # Move the provided figure by the diced eyes according to the
+        # rules. First we check, if we are allowed to move this
+        # figure, which might be in the nest or on the run.
+        #
+        set currentColor [lindex ${:colors} ${:player}]
+        if {[$obj cget -color] ne $currentColor} {
+            return [:msg "figure is not from the current player"]
+        }
+        set eyes [${:die} cget -eyes]
+        if {$eyes == 0} {
+            return [:msg "Must dice first"]
+        }
+        set position [$obj cget -position]
+        if {$position in [set :nest(${:player})]} {
+            # Figure is in the nest, just accept eyes == 6
+            if {$eyes == 6} {
+                :wannaGo $obj [lindex [set :path(${:player})] 0]
+            } else {
+                return [:msg "Need 6 to move this figure"]
+            }
+        } else {
+            #
+            # Check, if we have still figures in the nest
+            #
+            set inNest ""
+            foreach p [set :nest(${:player})] {
+                set inNest [Figure lookup $p]
+                if {$inNest ne ""} break
+            }
+            #
+            # Check, if the actual figure is at the start position.
+            #
+            set startPos [lindex [set :path(${:player})] 0]
+            set atStart [Figure lookup $startPos]
+            if {$eyes == 6} {
+                if {$inNest ne ""} {
+                    # Move a figure out from the nest, if we can
+                    if {$atStart ne ""} {
+                        if {[$atStart cget -color] eq $currentColor} {
+                            set path [set :path(${:player})]
+                            set current [lsearch $path $position]
+                            set targetPos [expr {$current + [${:die} cget -eyes]}]
+                            :wannaGo $obj [lindex $path $targetPos] \
+                                -path [lrange $path $current+1 $targetPos]
+                            return 1
+                        }
+                    }
+                    return [:msg "You have to move the figures from your nest first"]
+                }
+            }
+            if {$atStart ne "" && $inNest ne "" && $obj ne $atStart} {
+                return [:msg "You have to move the figures from the start first"]
+            }
+            set path [set :path(${:player})]
+            set current [lsearch $path $position]
+            set targetPos [expr {$current + [${:die} cget -eyes]}]
+            :wannaGo $obj [lindex $path $targetPos] \
+                -path [lrange $path $current+1 $targetPos]
+        }
+        return 1
+    }
+
+    :public method nextPlayer {} {
+        #
+        # Switch to the next player.
+        #
+        set :player [expr {(${:player}+1) % 4}]
+        ${:canvas} coords ${:buttonWindow} {*}[set :buttonPos(${:player})]
+    }
+
+    :method init {} {
+        set hw [expr {14 * ${:size}}]
+        set center [expr {$hw / 2}]
+        canvas ${:canvas} -bg ${:bg} -height $hw -width $hw
+        :draw $center
+        set :die [Die new -canvas ${:canvas} -x $center -y $center -size ${:size}]
+        set :msgId [${:canvas} create text [expr {${:size}*4}] 10 -text ""]
+        #
+        # Player management (signal which player is next, etc.)
+        #
+        set :player 2
+        button .b1 -text "Done" -command [:callback nextPlayer]
+        set :buttonWindow [.p create window 22 14 -window .b1]
+        :nextPlayer
+        bind . <n> [:callback nextPlayer]
+    }
+}
+

Finally, create the board and pack it

+
+
+
Board new -canvas .p -bg beige -size 40
+pack .p
+
+
+
+

+ + + Index: doc/example-scripts/tk-mini.html =================================================================== diff -u -r71e18053ef7dd9a53f11d14f3a91b8a1091e90bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-mini.html (.../tk-mini.html) (revision 71e18053ef7dd9a53f11d14f3a91b8a1091e90bb) +++ doc/example-scripts/tk-mini.html (.../tk-mini.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,799 +1,799 @@ - - - - - -Listing of doc/example-scripts/tk-mini.tcl - - - - - -
-
-
-

Tiny Tk example scriped based on NX.

-
-
-tk-mini.png -
-
-
-
-
package require Tk
-package require nx::trait
-
-nx::Class create MyClass {
-  #
-  # A sample application class that creates a text entry field bound
-  # to an instance variable. When the provided button is pressed, the
-  # content of the variable is placed into an additional output label.
-
-  #
-  # The callback trait imports methods "callback" and "bindvar":
-  #
-  :require trait nx::trait::callback
-
-  :public method button-pressed {} {
-    # When this method is invoked, the content of the ".label" widget
-    # is updated with the content of the instance variable "myvar".
-    .label configure -text ${:myvar}
-  }
-
-  :method init {} {
-    wm geometry . -500+500
-    pack [label .title -text "Type something and press the start button ..."]
-    pack [entry .text -textvariable [:bindvar myvar]]
-    pack [label .label]
-    pack [button .button -text start -command [:callback button-pressed]]
-  }
-}
-
-MyClass new
-
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-mini.tcl + + + + + +
+
+
+

Tiny Tk example scriped based on NX.

+
+
+tk-mini.png +
+
+
+
+
package require Tk
+package require nx::trait
+
+nx::Class create MyClass {
+  #
+  # A sample application class that creates a text entry field bound
+  # to an instance variable. When the provided button is pressed, the
+  # content of the variable is placed into an additional output label.
+
+  #
+  # The callback trait imports methods "callback" and "bindvar":
+  #
+  :require trait nx::trait::callback
+
+  :public method button-pressed {} {
+    # When this method is invoked, the content of the ".label" widget
+    # is updated with the content of the instance variable "myvar".
+    .label configure -text ${:myvar}
+  }
+
+  :method init {} {
+    wm geometry . -500+500
+    pack [label .title -text "Type something and press the start button ..."]
+    pack [entry .text -textvariable [:bindvar myvar]]
+    pack [label .label]
+    pack [button .button -text start -command [:callback button-pressed]]
+  }
+}
+
+MyClass new
+
+
+
+
+

+ + + Index: doc/example-scripts/tk-spread.html =================================================================== diff -u -r71e18053ef7dd9a53f11d14f3a91b8a1091e90bb -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/tk-spread.html (.../tk-spread.html) (revision 71e18053ef7dd9a53f11d14f3a91b8a1091e90bb) +++ doc/example-scripts/tk-spread.html (.../tk-spread.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,908 +1,908 @@ - - - - - -Listing of doc/example-scripts/tk-spread.tcl - - - - - -
-
-
-

A small Spreadsheet implementation, originally developed by Richard -Suchenwirth in plain Tcl (see http://wiki.tcl.tk/1287). The -spreadsheet was rewritten in an object oriented manner as a design -study in NX by Gustaf Neumann in May 2011.

-
-
-tk-spread.png -
-
-
-
-
package require Tk
-package require nx::trait
-
- ##############################################################################
- # Class SpreadSheet
- #
- # The SpreadSheet computes simply totals for rows and columns.
- ##############################################################################
- nx::Class create SpreadSheet {
-   #
-   # The following attributes can be used for configuring the
-   # spreadsheet.
-   #
-   :property {rows:integer 3}
-   :property {cols:integer 2}
-   :property {width:integer 8}
-
-   #
-   # If no widget is provided, use the name of the object as widget
-   # name.
-   #
-   :property {widget ".[namespace tail [self]]"}
-
-   #
-   # Use the nx callback trait
-   #
-   :require trait nx::trait::callback
-
-   #
-   # The method "cell" hides the internal respresentation and sets a
-   # cell to a value.
-   #
-   :method cell {pair value} {
-     set :data($pair) $value
-   }
-
-   #
-   # The constructor builds the SpreadSheet matrix via multiple text
-   # entry fields.
-   #
-   :method init {} {
-     set :last ${:rows},${:cols}  ;# keep grand total field
-     trace var [:bindvar data] w [:callback redo]
-     frame ${:widget}
-     for {set y 0} {$y <= ${:rows}} {incr y} {
-       set row [list]
-       for {set x 0} {$x <= ${:cols}} {incr x} {
-         set e [entry ${:widget}.$y,$x -width ${:width} \
-                    -textvar [:bindvar data($y,$x)] -just right]
-         if {$x==${:cols} || $y==${:rows}} {
-           $e config -state disabled -background grey -relief flat
-         }
-         lappend row $e
-       }
-       grid {*}$row -sticky news
-     }
-     $e config -relief solid
-   }
-
-   #
-   # The method "redo" is triggered via the updates in the cells
-   #
-   :public method redo {varname el op} {
-     if {$el ne ${:last}} {
-       lassign [split $el ,] y x
-       if {$x ne ""} {
-         :sum $y,* $y,${:cols}
-         :sum *,$x ${:rows},$x
-       } ;# otherwise 'el' was not a cell index
-     }   ;# prevent endless recalculation of grand total
-   }
-
-   #
-   # The method "sum" adds the values matched by pattern (typically a
-   # row or column) and sets finally the target column with the total
-   #
-   :method sum {pat target} {
-     set sum 0
-     set total "" ;# default if no addition succeeds
-     foreach {i value} [array get :data $pat] {
-       if {$i != $target} {
-         if {[string is double -strict $value]} {
-           set total [set sum [expr {$sum + $value}]]
-         }
-       }
-     }
-     :cell $target $total
-   }
- }
-

Build spreadsheet "x"

-
-
-
SpreadSheet create x {
-   # populate with some values
-   :cell 0,0 Spread1
-   :cell 1,0 47
-   :cell 2,1 11
- }
-

Build spreadsheet "y"

-
-
-
SpreadSheet create y -rows 4 -cols 4 {
-   :cell 0,0 Spread2
-   :cell 1,0 12
-   :cell 2,2 22
- }
-

Pack the spreadsheets into one pane

-
-
-
pack [x cget -widget] [y cget -widget] -fill both
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/tk-spread.tcl + + + + + +
+
+
+

A small Spreadsheet implementation, originally developed by Richard +Suchenwirth in plain Tcl (see http://wiki.tcl.tk/1287). The +spreadsheet was rewritten in an object oriented manner as a design +study in NX by Gustaf Neumann in May 2011.

+
+
+tk-spread.png +
+
+
+
+
package require Tk
+package require nx::trait
+
+ ##############################################################################
+ # Class SpreadSheet
+ #
+ # The SpreadSheet computes simply totals for rows and columns.
+ ##############################################################################
+ nx::Class create SpreadSheet {
+   #
+   # The following attributes can be used for configuring the
+   # spreadsheet.
+   #
+   :property {rows:integer 3}
+   :property {cols:integer 2}
+   :property {width:integer 8}
+
+   #
+   # If no widget is provided, use the name of the object as widget
+   # name.
+   #
+   :property {widget ".[namespace tail [self]]"}
+
+   #
+   # Use the nx callback trait
+   #
+   :require trait nx::trait::callback
+
+   #
+   # The method "cell" hides the internal respresentation and sets a
+   # cell to a value.
+   #
+   :method cell {pair value} {
+     set :data($pair) $value
+   }
+
+   #
+   # The constructor builds the SpreadSheet matrix via multiple text
+   # entry fields.
+   #
+   :method init {} {
+     set :last ${:rows},${:cols}  ;# keep grand total field
+     trace var [:bindvar data] w [:callback redo]
+     frame ${:widget}
+     for {set y 0} {$y <= ${:rows}} {incr y} {
+       set row [list]
+       for {set x 0} {$x <= ${:cols}} {incr x} {
+         set e [entry ${:widget}.$y,$x -width ${:width} \
+                    -textvar [:bindvar data($y,$x)] -just right]
+         if {$x==${:cols} || $y==${:rows}} {
+           $e config -state disabled -background grey -relief flat
+         }
+         lappend row $e
+       }
+       grid {*}$row -sticky news
+     }
+     $e config -relief solid
+   }
+
+   #
+   # The method "redo" is triggered via the updates in the cells
+   #
+   :public method redo {varname el op} {
+     if {$el ne ${:last}} {
+       lassign [split $el ,] y x
+       if {$x ne ""} {
+         :sum $y,* $y,${:cols}
+         :sum *,$x ${:rows},$x
+       } ;# otherwise 'el' was not a cell index
+     }   ;# prevent endless recalculation of grand total
+   }
+
+   #
+   # The method "sum" adds the values matched by pattern (typically a
+   # row or column) and sets finally the target column with the total
+   #
+   :method sum {pat target} {
+     set sum 0
+     set total "" ;# default if no addition succeeds
+     foreach {i value} [array get :data $pat] {
+       if {$i != $target} {
+         if {[string is double -strict $value]} {
+           set total [set sum [expr {$sum + $value}]]
+         }
+       }
+     }
+     :cell $target $total
+   }
+ }
+

Build spreadsheet "x"

+
+
+
SpreadSheet create x {
+   # populate with some values
+   :cell 0,0 Spread1
+   :cell 1,0 47
+   :cell 2,1 11
+ }
+

Build spreadsheet "y"

+
+
+
SpreadSheet create y -rows 4 -cols 4 {
+   :cell 0,0 Spread2
+   :cell 1,0 12
+   :cell 2,2 22
+ }
+

Pack the spreadsheets into one pane

+
+
+
pack [x cget -widget] [y cget -widget] -fill both
+
+
+
+

+ + + Index: doc/example-scripts/traits-composite.html =================================================================== diff -u -r93bb0947d582f274afb1cdbc885909d55e100b36 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/traits-composite.html (.../traits-composite.html) (revision 93bb0947d582f274afb1cdbc885909d55e100b36) +++ doc/example-scripts/traits-composite.html (.../traits-composite.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,918 +1,918 @@ - - - - - -Listing of doc/example-scripts/traits-composite.tcl - - - - - -
-
-
-

Implementation study based on

-
    -
  1. -

    -Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: -Traits: A Mechanism for Fine-grained Reuse, -ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 -

    -
  2. -
-

Fig 13: tReadStream and tWriteStream as composite traits

-

In this example, traits are used to extend classes and other traits -(called then composite traits).

-
-
-
package req nx::trait
-

Create a Trait called tPositionableStream

-
-
-
nx::Trait create tPositionableStream {
-  #
-  # Define the methods provided by this trait:
-  #
-  :public method atStart {} {expr {[:position] == [:minPosition]}}
-  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
-  :public method setToStart {} {set :position [:minPosition]}
-  :public method setToEnd {} {set :position [:maxPosition]}
-  :public method maxPosition {} {llength ${:collection}}
-  :public method minPosition {} {return 0}
-  :public method nextPosition {} {incr :position 1}
-
-  # The trait requires a method "position" and a variable "collection"
-  # from the base class or other traits. The definition is incomplete
-  # in these regards
-
-  :requiredMethods position
-  :requiredVariables collection
-}
-

Create a composite trait called tReadStream based on the trait -tPositionableStream:

-
-
-
nx::Trait create tReadStream {
-  #
-  # Methods provided by this trait:
-  #
-  :public method on {collection} {set :collection $collection; :setToStart}
-  :public method next {} {
-    if {[:atEnd]} {return ""} else {
-      set r [lindex ${:collection} ${:position}]
-      :nextPosition
-      return $r
-    }
-  }
-
-  # This trait requires these methods:
-  :requiredMethods {setToStart atEnd nextPosition}
-
-  # Require the trait "tPositionableStream"
-  :require trait tPositionableStream
-}
-

Create a composite trait called tWriteStream based on the trait -tPositionableStream:

-
-
-
nx::Trait create tWriteStream {
-  #
-  # Methods provided by this trait:
-  #
-  :public method on {collection} {set :collection $collection; :setToEnd}
-  :public method nextPut {element} {
-    lappend :collection $element
-    :nextPosition
-    return ""
-  }
-
-  # This trait requires these methods:
-  :requiredMethods {setToEnd nextPosition}
-
-  # Require the trait "tPositionableStream"
-  :require trait tPositionableStream
-}
-

Define a class ReadStream with properties position and -collection that uses the composite trait tReadStream:

-
-
-
nx::Class create ReadStream {
-  :property {collection ""}
-  :property -accessor public {position 0}
-  :require trait tReadStream
-}
-

Create an instance of ReadStream:

-
-
-
ReadStream create r1 -collection {a b c d e}
-

Test the behavior of the composed class:

-
-
-
% r1 atStart
-1
-% r1 atEnd
-0
-% r1 next
-a
-% r1 next
-b
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/traits-composite.tcl + + + + + +
+
+
+

Implementation study based on

+
    +
  1. +

    +Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: +Traits: A Mechanism for Fine-grained Reuse, +ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +

    +
  2. +
+

Fig 13: tReadStream and tWriteStream as composite traits

+

In this example, traits are used to extend classes and other traits +(called then composite traits).

+
+
+
package req nx::trait
+

Create a Trait called tPositionableStream

+
+
+
nx::Trait create tPositionableStream {
+  #
+  # Define the methods provided by this trait:
+  #
+  :public method atStart {} {expr {[:position] == [:minPosition]}}
+  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
+  :public method setToStart {} {set :position [:minPosition]}
+  :public method setToEnd {} {set :position [:maxPosition]}
+  :public method maxPosition {} {llength ${:collection}}
+  :public method minPosition {} {return 0}
+  :public method nextPosition {} {incr :position 1}
+
+  # The trait requires a method "position" and a variable "collection"
+  # from the base class or other traits. The definition is incomplete
+  # in these regards
+
+  :requiredMethods position
+  :requiredVariables collection
+}
+

Create a composite trait called tReadStream based on the trait +tPositionableStream:

+
+
+
nx::Trait create tReadStream {
+  #
+  # Methods provided by this trait:
+  #
+  :public method on {collection} {set :collection $collection; :setToStart}
+  :public method next {} {
+    if {[:atEnd]} {return ""} else {
+      set r [lindex ${:collection} ${:position}]
+      :nextPosition
+      return $r
+    }
+  }
+
+  # This trait requires these methods:
+  :requiredMethods {setToStart atEnd nextPosition}
+
+  # Require the trait "tPositionableStream"
+  :require trait tPositionableStream
+}
+

Create a composite trait called tWriteStream based on the trait +tPositionableStream:

+
+
+
nx::Trait create tWriteStream {
+  #
+  # Methods provided by this trait:
+  #
+  :public method on {collection} {set :collection $collection; :setToEnd}
+  :public method nextPut {element} {
+    lappend :collection $element
+    :nextPosition
+    return ""
+  }
+
+  # This trait requires these methods:
+  :requiredMethods {setToEnd nextPosition}
+
+  # Require the trait "tPositionableStream"
+  :require trait tPositionableStream
+}
+

Define a class ReadStream with properties position and +collection that uses the composite trait tReadStream:

+
+
+
nx::Class create ReadStream {
+  :property {collection ""}
+  :property -accessor public {position 0}
+  :require trait tReadStream
+}
+

Create an instance of ReadStream:

+
+
+
ReadStream create r1 -collection {a b c d e}
+

Test the behavior of the composed class:

+
+
+
% r1 atStart
+1
+% r1 atEnd
+0
+% r1 next
+a
+% r1 next
+b
+
+
+
+

+ + + Index: doc/example-scripts/traits-simple.html =================================================================== diff -u -r93bb0947d582f274afb1cdbc885909d55e100b36 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/example-scripts/traits-simple.html (.../traits-simple.html) (revision 93bb0947d582f274afb1cdbc885909d55e100b36) +++ doc/example-scripts/traits-simple.html (.../traits-simple.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,866 +1,866 @@ - - - - - -Listing of doc/example-scripts/traits-simple.tcl - - - - - -
-
-
-

Implementation study based on

-
    -
  1. -

    -Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: - Traits: A Mechanism for Fine-grained Reuse, - ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 -

    -
  2. -
-

Example in Fig 12: ReadStream and Trait tReadStream

-

In this example, traits are used to extend classes and other traits.

-
-
-
package require nx::trait
-

Create a simple trait called tReadStream which provides the -interface to a stream. In contrary to a composite trait, a simple -trait does not inherit from another trait.

-
-
-
nx::Trait create tReadStream {
-  #
-  # Define the methods provided by this trait:
-  #
-  :public method atStart {} {expr {[:position] == [:minPosition]}}
-  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
-  :public method setToStart {} {set :position [:minPosition]}
-  :public method setToEnd {} {set :position [:maxPosition]}
-  :public method maxPosition {} {llength ${:collection}}
-  :public method on {collection} {set :collection $collection; :setToStart}
-  :public method next {} {
-    if {[:atEnd]} {return ""} else {
-      set r [lindex ${:collection} ${:position}]
-      :nextPosition
-      return $r
-    }
-  }
-  :public method minPosition {} {return 0}
-  :public method nextPosition {} {incr :position 1}
-
-  # This trait requires a method "position" and a variable
-  # "collection" from the base class. The definition is incomplete in
-  # these regards.
-
-  :requiredMethods position
-  :requiredVariables collection
-}
-

Define the class ReadStream with properties position and -collection that uses the trait. The method require trait checks the -requirements of the trait and imports the methods into ReadStream.

-
-
-
nx::Class create ReadStream {
-  :property {collection ""}
-  :property -accessor public {position 0}
-  :require trait tReadStream
-}
-

Create an instance of the class ReadStream:

-
-
-
ReadStream create r1 -collection {a b c d e}
-

Test the behavior of the composed class:

-
-
-
% r1 atStart
-1
-% r1 atEnd
-0
-% r1 next
-a
-% r1 next
-b
-
-
-
-

- - - + + + + + +Listing of doc/example-scripts/traits-simple.tcl + + + + + +
+
+
+

Implementation study based on

+
    +
  1. +

    +Ducasse, O. Nierstrasz, N. Schärli, R. Wuyts, A. Black: + Traits: A Mechanism for Fine-grained Reuse, + ACM transactions on Programming Language Systems, Vol 28, No 2, March 2006 +

    +
  2. +
+

Example in Fig 12: ReadStream and Trait tReadStream

+

In this example, traits are used to extend classes and other traits.

+
+
+
package require nx::trait
+

Create a simple trait called tReadStream which provides the +interface to a stream. In contrary to a composite trait, a simple +trait does not inherit from another trait.

+
+
+
nx::Trait create tReadStream {
+  #
+  # Define the methods provided by this trait:
+  #
+  :public method atStart {} {expr {[:position] == [:minPosition]}}
+  :public method atEnd {} {expr {[:position] == [:maxPosition]}}
+  :public method setToStart {} {set :position [:minPosition]}
+  :public method setToEnd {} {set :position [:maxPosition]}
+  :public method maxPosition {} {llength ${:collection}}
+  :public method on {collection} {set :collection $collection; :setToStart}
+  :public method next {} {
+    if {[:atEnd]} {return ""} else {
+      set r [lindex ${:collection} ${:position}]
+      :nextPosition
+      return $r
+    }
+  }
+  :public method minPosition {} {return 0}
+  :public method nextPosition {} {incr :position 1}
+
+  # This trait requires a method "position" and a variable
+  # "collection" from the base class. The definition is incomplete in
+  # these regards.
+
+  :requiredMethods position
+  :requiredVariables collection
+}
+

Define the class ReadStream with properties position and +collection that uses the trait. The method require trait checks the +requirements of the trait and imports the methods into ReadStream.

+
+
+
nx::Class create ReadStream {
+  :property {collection ""}
+  :property -accessor public {position 0}
+  :require trait tReadStream
+}
+

Create an instance of the class ReadStream:

+
+
+
ReadStream create r1 -collection {a b c d e}
+

Test the behavior of the composed class:

+
+
+
% r1 atStart
+1
+% r1 atEnd
+0
+% r1 next
+a
+% r1 next
+b
+
+
+
+

+ + + Index: doc/next-migration.html =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/next-migration.html (.../next-migration.html) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/next-migration.html (.../next-migration.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,6473 +1,6473 @@ - - - - - -Migration Guide for the Next Scripting Language - - - - - + +
+
+
+
+
+
Abstract
+

This document describes the differences between the Next Scripting +Language Framework and XOTcl 1. In particular, it presents a +migration guide from XOTcl 1 to NX, and presents potential +incompatibilities between XOTcl 1 and XOTcl 2.

+
+

The Next Scripting Language (NX) is a successor of XOTcl 1 and is +based on 10 years of experience with XOTcl in projects containing +several hundert thousand lines of code. While XOTcl was the first +language designed to provide language support for design patterns, the +focus of the Next Scripting Framework and NX are on combining this +with Language Oriented Programming. In many respects, NX was designed +to ease the learning of the language by novices (by using a more +mainstream terminology, higher orthogonality of the methods, less +predefined methods), to improve maintainability (remove sources of +common errors) and to encourage developer to write better structured +programs (to provide interfaces) especially for large projects, where +many developers are involved.

+

The Next Scripting Language is based on the Next Scripting Framework +which was developed based on the notion of language oriented +programming. The Next Scripting Frameworks provides C-level support +for defining and hosting multiple object systems in a single Tcl +interpreter. The whole definition of NX is fully scripted +(e.g. defined in nx.tcl). The Next Scripting Framework is shipped +with three language definitions, containing NX and XOTcl 2. Most of +the existing XOTcl 1 programs can be used without modification in the +Next Scripting Framework by using XOTcl 2. The Next Scripting +Framework requires Tcl 8.5 or newer.

+

Although NX is fully scripted (as well as XOTcl 2), our benchmarks +show that scripts based on NX are often 2 or 4 times faster than the +counterparts in XOTcl 1. But speed was not the primary focus on the +Next Scripting Environment: The goal was primarily to find ways to +repackage the power of XOTcl in an easy to learn environment, highly +orthogonal environment, which is better suited for large projects, +trying to reduce maintenance costs.

+

We expect that many user will find it attractive to upgrade +from XOTcl 1 to XOTcl 2, and some other users will upgrade to NX. +This document focuses mainly on the differences between XOTcl 1 and +NX, but addresses as well potential incompatibilities between XOTcl 1 +and XOTcl 2. For an introduction to NX, please consult the NX tutorial.

+
+
+
+

1. Differences Between XOTcl and NX

+
+

The Next Scripting Framework supports Language Oriented Programming +by providing means to define potentially multiple object systems with +different naming and functionality in a single interpreter. This +makes the Next Scripting Framework a powerful instrument for defining +multiple languages such as e.g. domain specific languages. This focus +differs from XOTcl 1.

+

Technically, the language framework approach means that the languages +implemented by the Next Scripting Framework (most prominently XOTcl 2 +and NX) are typically fully scripted and can be loaded via the usual +Tcl package require mechanism.

+

Some of the new features below are provided by the Next Scripting +Framework, some are implemented via the script files for XOTcl 2 and +NX.

+
+

1.1. Features of NX

+

In general, the Next Scripting Language (NX) differs from XOTcl +in the following respects:

+
    +
  1. +

    +Stronger Encapsulation: The Next Scripting Language favors + a stronger form of encapsulation than XOTcl. Calling the own + methods or accessing the own instance variables is typographically + easier and computationally faster than these operations on other + objects. This behavior is achieved via resolvers, which make some + methods necessary in XOTcl 1 obsolete in NX (especially for importing + instance variables). The encapsulation of NX is stronger than in + XOTcl but still weak compared to languages like C++; a developer can + still access other objects' variables via some idioms, but NX makes + accesses to other objects' variables explicit. The requiredness to + make these accesses explicit should encourage developer to implement + well defined interfaces to provide access to instance variables. +

    +
  2. +
  3. +

    +Additional Forms of Method Definition and Reuse: + The Next Scripting Language + provides much more orthogonal means to define, reuse and + introspect scripted and C-implemented methods. +

    +
      +
    1. +

      +It is possible to use NX alias to register methods + under arbitrary names for arbitrary objects or classes. +

      +
    2. +
    3. +

      +NX provides means for method protection (method modifiers + public, protected, and private). Therefore developers have + to define explicitly public interfaces in order to use methods + from other objects. +

      +
    4. +
    5. +

      +One can invoke in NX fully qualified methods to invoke + methods outside the precedence path. +

      +
    6. +
    7. +

      +One can define in NX hierarchical method names (similar to + commands and subcommands, called method ensembles) in a + convenient way to provide extensible, hierarchical naming of + methods. +

      +
    8. +
    9. +

      +One can use in NX the same interface to query (introspect) + C-implemented and scripted methods/commands. +

      +
    10. +
    +
  4. +
  5. +

    +Orthogonal Parameterization: + The Next Scripting Language provides an orthogonal framework for + parametrization of methods and objects. +

    +
      +
    1. +

      +In NX, the same argument parser is used for +

      +
        +
      • +

        +Scripted Methods +

        +
      • +
      • +

        +C-implemented methods and Tcl commands +

        +
      • +
      • +

        +Object Parametrization +

        +
      • +
      +
    2. +
    3. +

      +While XOTcl 1 provided only value-checkers for non-positional + arguments for methods, the Next Scripting Framework provides + the same value checkers for positional and non-positional + arguments of methods, as well as for positional and + non-positional configure parameters (-parameter in + XOTcl 1). +

      +
    4. +
    5. +

      +While XOTcl 1 supported only non-positional arguments at the + begin of the argument list, these can be used now at arbitrary + positions. +

      +
    6. +
    +
  6. +
  7. +

    +Value Checking: +

    +
      +
    1. +

      +The Next Scripting Language supports checking of the input + parmeters and the return values of scripted and C-implemented + methods and commands. +

      +
    2. +
    3. +

      +NX provides a set of predefined checkers (like e.g. integer, + boolean, object, …) which can be extended by the + applications. +

      +
    4. +
    5. +

      +Value Checking can be used for single and multi-valued + parameters. One can e.g. define a list of integers + with at least one entry by the parameter specification + integer,1..n. +

      +
    6. +
    7. +

      +Value Checking can be turned on/off globally or on the + method/command level. +

      +
    8. +
    +
  8. +
  9. +

    +Scripted Init Blocks: The Next Scripting Language provides + scripted init blocks for objects and classes (replacement for the + dangerous dash "-" mechanism in XOTcl that allows to set variables + and invoke methods upon object creation). +

    +
  10. +
  11. +

    +More Conventional Naming for Predefined Methods: The naming of + the methods in the Next Scripting Language is much more in line with + the mainstream naming conventions in OO languages. While for example + XOTcl uses proc and instproc for object specific and inheritable + methods, NX uses simply method. +

    +
  12. +
  13. +

    +Profiling Support: The Next Scripting Language provides now two + forms of profiling +

    +
      +
    • +

      +Profiling via a DTrace provider (examples are e.g. in the dtrace + subdirectory of the source tree) +

      +
    • +
    • +

      +Significantly improved built-in profiling (results can be + processed in Tcl). +

      +
    • +
    +
  14. +
  15. +

    +Significantly Improved Test Suite: The regression test suite of + Next Scripting Scripting framework contain now more than + 5.000 tests, and order of magnitude more than in XOTcl 1.6 +

    +
  16. +
  17. +

    +Much Smaller Interface: The Next Scripting Language has a much + smaller interface (i.e. provides less predefined methods) than + XOTcl (see Table 1), although the expressiveness was increased in + NX. +

    +
  18. +
+
+ + ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Comparison of the Number of Predefined Methods in NX and XOTcl
NXXOTcl

Total

45

124

Methods for Objects

14

51

Methods for Classes

9

24

Info-methods for Objects

11

25

Info-methods for Classes

11

24

+
+

This comparison list compares mostly XOTcl 1 with NX, some features +are also available in XOTcl 2 (2a, 2c 2d, 3, 4).

+
+
+

1.2. NX and XOTcl Scripts

+

Below is a small, introductory example showing an implementation of a +class Stack in NX and XOTcl. The purpose of this first example is +just a quick overview. We will go into much more detailed comparison +in the next sections.

+

NX supports a block syntax, where the methods are defined during the +creation of the class. The XOTcl syntax is slightly more redundant, +since every definition of a method is a single toplevel command +starting with the class name (also NX supports the style used in +XOTcl). In NX, all methods are per default protected (XOTcl does not +support protection). In NX methods are defined in the definition of +the class via :method or :public method. In XOTcl methods are +defined via the instproc method.

+

Another difference is the notation to refer to instance variables. In +NX, instance variable are named with a single colon in the front. In +XOTcl, instance variables are imported using instvar.

+
+ +++ + + + + + + + + + + + +
Stack example in NX Stack example in XOTcl
+
+
Class create Stack {
+
+   #
+   # Stack of Things
+   #
+
+   :variable things ""
+
+   :public method push {thing} {
+      set :things [linsert ${:things} 0 $thing]
+      return $thing
+   }
+
+   :public method pop {} {
+      set top [lindex ${:things} 0]
+      set :things [lrange ${:things} 1 end]
+      return $top
+   }
+}
+
+
#
+# Stack of Things
+#
+
+Class Stack
+
+Stack instproc init {} {
+   my instvar things
+   set things ""
+}
+
+Stack instproc push {thing} {
+   my instvar things
+   set things [linsert $things 0 $thing]
+   return $thing
+}
+
+Stack instproc pop {} {
+   my instvar things
+   set top [lindex $things 0]
+   set things [lrange $things 1 end]
+}
+
+
+
+

1.3. Using XOTcl 2.0 and the Next Scripting Language in a Single Interpreter

+

In general, the Next Scripting Framework supports multiple object +systems concurrently. Effectively, every object system has different +base classes for creating objects and classes. Therefore, these object +systems can have different interfaces and names of built-in +methods. Currently, the Next Scripting Framework is packaged with +three object systems:

+
    +
  • +

    +NX +

    +
  • +
  • +

    +XOTcl 2.0 +

    +
  • +
  • +

    +TclCool +

    +
  • +
+

XOTcl 2 is highly compatible with XOTcl 1, the language NX is +described below in more details, the language TclCool was introduced +in Tip#279 and serves primarily an example of a small OO language.

+

A single Tcl interpreter can host multiple Next Scripting Object +Systems at the same time. This fact makes migration from XOTcl to NX +easier. The following example script shows to use XOTcl and NX in a +single script:

+
+
Using Multiple Object Systems in a single Script
+
+
namespace eval mypackage {
+
+  package require XOTcl 2.0
+
+  # Define a class with a public method foo using XOTcl
+  xotcl::Class C1
+  C1 instproc foo {} {puts "hello world"}
+
+  package require nx
+
+  # Define a class with a public method foo using NX
+  nx::Class create C2 {
+    :public method foo {} {puts "hello world"}
+  }
+}
+

One could certainly create object or classes from the different object +systems via fully qualified names (e.g. using e.g. ::xotcl::Class or +::nx::Class), but for migration for systems without explicit +namespaces switching between the object systems eases migration. +"Switching" between XOTcl and NX effectively means the load some +packages (if needed) and to import either the base classes (Object and +Class) of XOTcl or NX into the current namespace.

+
+
+
+
+

2. XOTcl Idioms in the Next Scripting Language

+
+

The following sections are intended for reader familiar with XOTcl and +show, how certain language Idioms of XOTcl can be expressed in NX. In +some cases, multiple possible realizations are listed

+
+

2.1. Defining Objects and Classes

+

When creating objects or classes, one should use the method create +explicitly. In XOTcl, a default unknown method handler was provided for +classes, which create for every unknown method invocation an +object/class with the name of the invoked method. This technique was +convenient, but as well dangerous, since typos in method names lead +easily to unexpected behavior. This default unknown method handler is not +provided in NX (but can certainly be provided as a one-liner in NX by +the application).

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
Class ClassName
+
+
Class create ClassName
+
+
Object ObjectName
+
+
Object create ObjectName
+
+
+
+

2.2. Defining Methods

+

In general, both XOTcl and NX support methods on the object level +(per-object methods, i.e. methods only applicable to a single object) +and on the class level (methods inherited to instances of the +classes). While the naming in XOTcl tried to follow closely the Tcl +tradition (using the term proc for functions/methods), NX uses the +term method for defining scripted methods.

+

XOTcl uses the prefix inst to denote that methods are provided for +instances, calling therefore scripted methods for instances +instproc. This is certainly an unusual term. The approach with the +name prefix has the disadvantage, that for every different kind of +method, two names have to be provided (eg. proc and instproc, +forward and instforward).

+

NX on the contrary uses the same term for defining instance method or +object-specific methods. When the term (e.g. method) is used on a +class, the method will be an instance method (i.e. applicable to the +instances of the class). When the term is used on an object with the +modifier object, an object-specific method is defined. This way one +can define the same way object specific methods on an object as well +as on a class.

+

Furthermore, both XOTcl and NX distinguish between scripted methods +(section 3.2.1) and C-defined methods (section 3.2.2). Section 3.2.3 +introduces method protection, which is only supported by NX.

+
+

2.2.1. Scripted Methods Defined in the Init-block of a Class/Object or with Separate Calls

+

The following examples show the definition of a class and its methods +in the init-block of a class (NX only), and the definition of methods +via separate top level calls (XOTcl and NX).

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Define instance method 'foo' and object
+# method 'bar' for a Class 'C' with separate
+# toplevel commands
+
+Class C
+C instproc foo args {...}
+C proc bar args {...}
+
+
# Define instance method and object method
+# in the init-block of a class
+
+Class create C {
+  :method foo args {...}
+  :object method bar args {...}
+}
+
+
+
# Define instance method and object method
+# with separate commands
+
+Class create C
+C method foo args {...}
+C object method bar args {...}
+
+
# Define object-specific method foo
+# for an object 'o' with separate commands
+
+Object o
+o set x 1
+o proc foo args {...}
+
+
# Define object method and set
+# instance variable in the init-block of
+# an object
+
+Object create o {
+  set :x 1
+  :object method foo args {...}
+}
+
+
+
# Define object method and set
+# instance variable with separate
+# commands
+
+Object create o
+o eval {set :x 1}
+o object method foo args {...}
+
+
+
+

2.2.2. Different Kinds of Methods

+

This section describes various kinds of methods. The different kinds +of methods are defined via different method-defining methods, which +are summarized in the following table for XOTcl and NX.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Methods for defining methods:
+#
+#     proc
+#     instproc
+#     forward
+#     instforward
+#     parametercmd
+#     instparametercmd
+#
+# All these methods return empty.
+
+
# Methods for defining methods:
+#
+#     alias
+#     forward
+#     method
+#
+# All these methods return method-handles.
+
+

In addition to scripted methods (previous section) XOTcl supports +forwarder (called forward and instforward) and accessor functions +to variables (called parametercmd and instparametercmd). The +accessor functions are used normally internally when object-specific +parameters are defined (see Section 3.4).

+

In NX forwarders are called forward. NX does not provide an public +available method to define variable accessors like parametercmd in +XOTcl, but use internally the Next Scripting Framework primitive +nsf::method::setter when appropriate.

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
Class C
+C instforward f1 ...
+C forward f2 ...
+
+Object o
+o forward f3 ...
+
+
# Define forwarder
+
+Class create C {
+  :forward f1 ...
+  :object forward f2 ...
+}
+
+Object create o {
+  :object forward f3 ...
+}
+
+
# Define setter and getter methods in XOTcl.
+#
+# XOTcl provides methods for these.
+
+Class C
+C instparametercmd p1
+C parametercmd p2
+
+Object o
+o parametercmd p3
+
+
# Define setter and getter methods in NX.
+#
+# NX does not provide own methods, but uses
+# the low level framework commands, since
+# application developer will only seldom
+# need it.
+
+Class create C
+::nsf::method::setter C p1
+::nsf::method::setter C -per-object p2
+
+Object create o
+::nsf::method::setter o p3
+
+

NX supports in contrary to XOTcl the method alias which can be used +to register arbitrary Tcl commands or methods for an object or class +under a provided method name. Aliases can be used to reuse a certain implementation in +e.g. different object systems under potentially different names. In +some respects aliases are similar to forwarders, but they do not +involve forwarding overhead.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Method "alias" not available
+
+
# Define method aliases
+# (to scripted or non-scripted methods)
+
+Class create C {
+  :alias a1 ...
+  :object alias a2 ...
+}
+
+Object create o {
+  :object alias a3 ...
+}
+
+
+
+

2.2.3. Method Modifiers and Method Protection

+

NX supports four method modifiers object, public, protected and +private. All method modifiers can be written in front of every +method defining command. The method modifier object is used to denote +object-specific methods (see above). The concept of method protection +is new in NX.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Method modifiers
+#
+#   "object",
+#   "public",
+#   "protected", and
+#   "private"
+#
+# are not available
+
+
# Method modifiers
+#
+#   "object",
+#   "public",
+#   "protected"
+#
+# are applicable for all kinds of
+# method defining methods:
+#
+#    method, forward, alias
+#
+# The modifier "private" is available for
+#
+#    method, forward, alias
+#
+Class create C {
+  :/method-definition-method/ ...
+  :public /method-definition-method/ ...
+  :protected /method-definition-method/ ...
+  :private /method-definition-method/ ...
+  :object /method-definition-method/ ...
+  :public object /method-definition-method/ ...
+  :protected object /method-definition-method/ ...
+  :private object /method-definition-method/ ...
+}
+
+

XOTcl does not provide method protection. In NX, all methods are +defined per default as protected. This default can be changed by the +application developer in various ways. The command ::nx::configure +defaultMethodCallProtection true|false can be used to set the default +call protection for scripted methods, forwarder and aliases. +The defaults can be overwritten also on a class level.

+

NX provides means for method hiding via the method modifier +private. Hidden methods can be invoked only via the -local flag, +which means: "call the specified method defined in the same +class/object as the currently executing method".

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# XOTcl provides no means for
+# method hiding
+
+
# Hiding of methods via "private"
+#
+nx::Class create Base {
+  :private method baz {a b} {expr {$a + $b}}
+  :public method foo {a b} {: -local baz $a $b}
+}
+
+nx::Class create Sub -superclass Base {
+  :public method bar {a b} {: -local baz $a $b}
+  :private method baz {a b} {expr {$a * $b}}
+
+  :create s1
+}
+
+s1 foo 3 4  ;# returns 7
+s1 bar 3 4  ;# returns 12
+s1 baz 3 4  ;# unable to dispatch method 'baz'
+
+
+
+

2.2.4. Method Deletion

+

NX provides an explicit delete method for the deletion of methods.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# XOTcl provides only method deletion with
+# the equivalent of Tcl's "proc foo {} {}"
+/cls/ instproc foo {} {}
+/obj/ proc foo {} {}
+
+
# Deletion of Methods
+#
+/cls/ delete method /name/
+/obj/ delete object method /name/
+
+
+
+
+

2.3. Resolvers

+

The Next Scripting Framework defines Tcl resolvers for method and +variable names to implement object specific behavior. Within the +bodies of scripted methods these resolver treat variable and function +names starting with a colon : specially. In short, a colon-prefixed +variable name refers to an instance variable, and a colon-prefixed +function name refers to a method. The sub-sections below provide +detailed examples.

+

Note that the resolvers of the Next Scripting Framework can be used in +the XOTcl 2.* environment as well.

+
+

2.3.1. Invoking Methods

+

In XOTcl, a method of the same object can be invoked via my, or in +general via using the name of the object in front of the method name.

+

In NX, the own methods are called via the method name prefixed with a +single colon. The invocation of the methods of other objects is the +same in NX and XOTcl.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
Class C
+C instproc foo args {...}
+C instproc bar args {
+  my foo 1 2 3 ;# invoke own method
+  o baz        ;# invoke other object's method
+}
+Object o
+o proc baz {} {...}
+
+
Class create C {
+  :method foo args {...}
+  :method bar args {
+     :foo 1 2 3 ;# invoke own method
+     o baz      ;# invoke other object's method
+  }
+}
+Object create o {
+  :public object method baz {} {...}
+}
+
+
+
+

2.3.2. Accessing Own Instance Variables from Method Bodies

+

In general, the Next Scripting Language favors the access to an +objects’s own instance variables over variable accesses of other +objects. This means that in NX it is syntactically easier to access +the own instance variables. On the contrary, in XOTcl, the variable +access to own and other variables are fully symmetric.

+

In XOTcl, the following approaches are used to access instance +variables:

+
    +
  • +

    +Import instance variables via instvar and access variables via $varName +

    +
  • +
  • +

    +Set or get instance variables via my set varName ?value? or other + variable accessing methods registered on xotcl::Object such as + append, lappend, incr, etc. +

    +
  • +
  • +

    +Register same-named accessor functions and set/get values + of instance variables via my varName ?value? +

    +
  • +
+

In NX, the favored approach to access instance variables is to use +the name resolvers, although it is as well possible to import +variables via nx::var import or to check for the existence of +instance variables via nx::var exists.

+

The following examples summary the use cases for accessing the own and +other instance variables.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
Class C
+C instproc foo args {
+  # Method scoped variable a
+  set a 1
+  # Instance variable b
+  my instvar b
+  set b 2
+  # Global variable/namespaced variable c
+  set ::c 3
+}
+
+
Class create C {
+  :method foo args {...}
+    # Method scoped variable a
+    set a 1
+    # Instance variable b
+    set :b 2
+    # Global variable/namespaced variable c
+    set ::c 3
+  }
+}
+
+
... instproc ... {
+   my set /varName/ ?value?
+}
+
+
# Set own instance variable to a value via
+# resolver (preferred and fastest way)
+
+... method ... {
+   set :/newVar/ ?value?
+}
+
+
... instproc ... {
+   my instvar /varName/
+   set /varName/ ?value?
+}
+
+
# Set own instance variable via
+# variable import
+
+... method ... {
+   ::nx::var import [self] /varName/
+   set /varName/ ?value?
+}
+
+
... instproc ... {
+   set /varName/ [my set /otherVar/]
+}
+
+
# Read own instance variable
+
+... method ... {
+   set /varName/ [set :/otherVar/]
+}
+
+
+
... method ... {
+   set /newVar/ ${:/otherVar/}
+}
+
+
... instproc ... {
+   my exists /varName/
+}
+
+
# Test existence of own instance variable
+
+... method ... {
+   info :/varName/
+}
+
+
+
 ... method ... {
+   ::nx::var exists [self] /varName/
+}
+
+
+
+

2.3.3. Accessing Instance Variables of other Objects

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ set /varName/ ?value?
+
+
# Set instance variable of object obj to a
+# value via resolver
+# (preferred way: define property on obj)
+
+/obj/ eval [list set :/varName/ ?value?]
+
+
set /varName/ [/obj/ set /otherVar/]
+
+
# Read instance variable of object obj
+# via resolver
+
+set /varName/ [/obj/ eval {set :/otherVar/}]
+
+
... instproc ... {
+   /obj/ instvar /varName/
+   set /varName/ ?value?
+}
+
+
# Read instance variable of object /obj/
+# via import
+
+... method ... {
+   ::nx::var import /obj/ /varName/
+   set /varName/ ?value?
+}
+
+
/obj/ exists varName
+
+
# Test existence of instance variable of
+# object obj
+
+/obj/ eval {info exists :/varName/}
+
+
+
::nx::var exists /obj/ /varName/
+
+
+
+
+

2.4. Parameters

+

While XOTcl 1 had very limited forms of parameters, XOTcl 2 and NX +provide a generalized and highly orthogonal parameter machinery +handling various kinds of value constraints (also called value +checkers). Parameters are used to specify,

+
    +
  • +

    +how objects and classes are initialized (we call these parameter types + Configure Parameters), and +

    +
  • +
  • +

    +what values can be passed to methods (we call these Method + Parameters). +

    +
  • +
+

Furthermore, parameters might be positional or non-positional, they +might be optional or required, they might have a defined multiplicity, +and value-types, they might be introspected, etc. The Next Scripting +Framework provide a unified, C-implemented infrastructure to handle +both, object and method parameters in the same way with a high degree +of orthogonality.

+

Configuration parameters were specified in XOTcl 1 primarily via the +method parameter in a rather limited way, XOTcl 1 only supported +non-positional parameters in front of positional ones, supported no +value constraints for positional parameters, provided no distinction +between optional and required, and did not support multiplicity.

+

Furthermore, the Next Scripting Framework provides optionally Return +Value Checking based on the same mechanism to check whether some +methods return always the values as specified.

+
+

2.4.1. Parameters for Configuring Objects: Variables and Properties

+

Configure parameters are used for specifying values for configuring +objects when they are created (i.e. how instance variables are +initialized, what parameters can be passed in for initialization, what +default values are used, etc.). Such configuration parameters are +supported in XOTcl primarily via the method parameter, which is used +in XOTcl to define multiple parameters via a list of parameter +specifications.

+

Since the term "parameter" is underspecified, NX uses a more +differentiated terminology. NX distinguishes between configurable +instance variables (also called properties) and non configurable +instance variables (called variables), which might have as well +e.g. default values. The values of configurable properties can be +queried at runtime via cget, and their values can be altered via +configure. When the value of a configure parameter is provided or +changed, the value checkers from the variable definition are used to +ensure, the value is permissible (i.e. it is for example an integer +value). The sum of all configurable object parameters are called +configure parameters. To define a define a configurable variable, NX +uses the method property, for non-configurable variables, the method +variable is used.

+

Optionally, one can define in NX, that a property or a +variable should have a public, protected or private accessor. Such +an accessor is a method with the same name as the variable. In XOTcl, +every parameter defined as well automatically a same-named accessor +method, leading to potential name conflicts with other method names.

+

In the examples below we show the definition of configurable an +non-configurable instance variables using variable and property +respectively.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Define class "Foo" with instance
+# variables "x" and "y" initialized
+# on instance creation. The initialization
+# has to be performed in the constructor.
+
+Class Foo
+Foo instproc init args {
+   instvar x y
+   set x 1
+   set y 2
+}
+
+# Create instance of the class Foo
+Foo f1
+
+# Object f1 has instance variables
+# x == 1 and y == 2
+
+
# Define class "Foo" with instance variables
+# "x" and "y" initialized on instance creation.
+# The method "variable" is similar in syntax
+# to Tcl's "variable" command. During
+# instance creation, the variable
+# definitions are used for the
+# initialization of the variables of the object.
+
+Class create Foo {
+  :variable x 1
+  :variable y 2
+}
+
+# Create instance of the class Foo
+Foo create f1
+
+# Object f1 has instance variables
+# x == 1 and y == 2
+
+

While XOTcl follows a procedural way to initialize variables via the +constructor init, NX follows a more declarative approach. Often, +classes have superclasses, which often want to provide their own +instance variables and default values. The declarative approach from +NX solves this via inheritance, while an procedural approach via +assign statements in the constructor requires explicit constructor +calls, which are often error-prone. Certainly, when a user prefers to +assign initial values to instance variables via explicit assign +operations in constructors, this is as well possible in NX.

+

NX uses the same mechanism to define class variables or object +variables.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# No syntactic support for creating
+# class variables
+
+
+# Define a object variable "V" with value 100 and
+# an instance variable "x". "V" is defined for the
+# class object Foo, "x" is defined in the
+# instances of the class. "object variable" works
+# similar to "object method".
+
+Class create Foo {
+  :object variable V 100
+  :variable x 1
+}
+
+

In the next step, we define configurable instance variables which we +call properties in NX.

+

XOTcl uses the method parameter is a shortcut for creating multiple +configurable variables with automatically created accessors (methods for +reading and writing of the variables). In NX, the preferred way to +create configurable variables is to use the method property. The +method property in NX is similar to variable, but makes the +variables configurable, which means that

+
    +
  1. +

    +one can specify the property as a non-positional parameter upon + creation of the object, +

    +
  2. +
  3. +

    +one can query the value via the method cget, and +

    +
  4. +
  5. +

    +one can modify the value of the underlying variable via the method + configure. +

    +
  6. +
+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Parameters specified as a list
+# (short form); parameter
+# "a" has no default, "b" has default "1"
+
+Class Foo -parameter {a {b 1}}
+
+# Create instance of the class Foo
+Foo f1 -a 0
+
+# Object f1 has instance variables
+# a == 0 and b == 1
+
+# XOTcl registers automatically accessors
+# for the parameters. Use the accessor
+# "b" to output the value of variable "b"
+puts [f1 b]
+
+# Use the setter to alter value of
+# instance variable "b"
+f1 b 100
+
+
# Define property "a" and "b". The
+# property "a" has no default, "b" has
+# default value "1"
+
+Class create Foo {
+  :property a
+  :property {b 1}
+}
+
+# Create instance of the class Foo
+Foo create f1 -a 0
+
+# Object f1 has instance variables
+# a == 0 and b == 1
+
+# Use the method "cget" to query the value
+# of a configuration parameter
+puts [f1 cget -b]
+
+# Use the method "configure" to alter the
+# value of instance variable "b"
+f1 configure -b 100
+
+

In general, NX allows to create variables and properties with and +without accessor methods. The created accessor methods might be +public, protected or public. When the value none is provided +to -accessor, no accessor will be created. This is actually the +default in NX. In order to change the default behavior in NX, one can use +::nx::configure defaultAccessor none|public|protected|private.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# "parameter" creates always accessor
+# methods, accessor methods are
+# always public, no "cget" is available.
+
+Class create Foo -parameter {a {b1}}
+
+# Use the accessor method to query
+# the value of a configuration parameter
+puts [f1 b]
+
+# Use the accessor method to set the
+# value of instance variable "a"
+f1 a 100
+
+# Use the accessor method to unset the
+# value of instance variable "a" n.a. via
+# accessor
+
+
# Define property "a" and "b". The
+# property "a" has no default, "b" has
+# default value "1"
+
+Class create Foo {
+  :variable -accessor public a
+  :property -accessor public {b 1}
+}
+
+# Use the accessor method to query
+# the value of a configuration parameter
+puts [f1 b get]
+
+# Use the accessor method to set the
+# value of instance variable "a"
+f1 a set 100
+
+# Use the accessor method to unset the
+# value of instance variable "a"
+f1 a unset
+
+

Similar to variable, properties can be defined in NX on the class +and on the object level.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# XOTcl provides no means to define
+# configurable variables at the object
+# level
+
+
# Define class with a property for the class object
+# named "cp". This is similar to "static variables"
+# in some other object-oriented programming
+# languages.
+
+Class create Foo {
+  ...
+  :object property cp 101
+}
+
+# Define object property "op"
+
+Object create o {
+  :object property op 102
+}
+
+

NX supports value constraints (value-checkers) for object and method +parameters in an orthogonal manner. NX provides a predefined set of +value checkers, which can be extended by the application developer. +In NX, the value checking is optional. This means that it is possible to +develop e.g. which a large amount of value-checking and deploy the +script with value checking turned off, if the script is highly +performance sensitive.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# No value constraints for
+# parameter available
+
+
# Predefined value constraints:
+#    object, class, alnum, alpha, ascii, boolean,
+#    control, digit, double, false, graph, integer,
+#    lower, parameter, print, punct, space, true,
+#    upper, wordchar, xdigit
+#
+# User defined value constraints are possible.
+# All parameter value checkers can be turned on
+# and off at runtime.
+#
+# Define a required boolean property "a"
+# and an integer property "b" with a default.
+# The first definition uses "properties",
+# the second definition uses multiple
+# "property" statements.
+
+Class create Foo -properties {
+   a:boolean
+   {b:integer 1}
+}
+
+
+
Class create Foo {
+   :property a:boolean
+   :property {b:integer 1}
+}
+
+

In XOTcl all configure parameters were optional. Required parameters have +to be passed to the constructor of the object.

+

NX allows to define optional and required configure parameters (as +well as method parameters). Therefore, configure parameters can be used +as the single mechanism to parametrize objects. It is in NX not +necessary (and per default not possible) to pass arguments to the +constructor.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Required parameter not available
+
+
# Required parameter:
+# Define a required property "a" and a
+# required boolean property "b"
+
+Class create Foo -properties {
+   a:required
+   b:boolean,required
+}
+
+
+
+Class create Foo {
+   :property a:required
+   :property b:boolean,required
+}
+
+

NX supports in contrary to XOTcl to define the multiplicity of values +per parameter. In NX, one can specify that a parameter can accept the +value "" (empty) in addition to e.g. an integer, or one can specify that the +value is an empty or non-empty ist of values via the multiplicity. For +every specified value, the value checkers are applied.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Multiplicity for parameter
+# not available
+
+
# Parameter with multiplicity
+#   ints is a list of integers, with default
+#   objs is a non-empty list of objects
+#   obj is a single object, maybe empty
+
+Class create Foo -properties {
+  {ints:integer,0..n ""}
+   objs:object,1..n
+   obj:object,0..1
+}
+
+
+
Class create Foo {
+  :property {ints:integer,0..n ""}
+  :property objs:object,1..n
+  :property obj:object,0..1
+}
+
+

For the implementation of variables and properties, NX uses slot +objects, which are an extension to the -slots already available in +XOTcl. While very for every property in NX, a slot object is created, +for performance reasons, not every variable has a slot associated.

+

When an property is created, NX does actually three things:

+
    +
  1. +

    +Create a slot object, which can be specified in more detail + using the init-block of the slot object +

    +
  2. +
  3. +

    +Create a parameter definition for the initialization of the + object (usable via a non-positional parameter during object + creation), and +

    +
  4. +
  5. +

    +register optionally an accessor function (setter), for which the usual + protection levels (public, protected or private) can be used. +

    +
  6. +
+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Define parameters via slots
+
+Class Foo -slots {
+   Attribute a
+   Attribute b -default 1
+}
+
+# Create instance of the class Foo
+# and provide a value for instance
+# variable "a"
+Foo f1 -a 0
+
+# Object f1 has a == 0 and b == 1
+
+
# Configurable parameters specified via the
+# method "property" (supports method
+# modifiers and scripted configuration;
+# see below)
+
+Class create Foo {
+   :property a
+   :property {b 1}
+}
+
+# Create instance of the class Foo and
+# provide a value for instance variable "a"
+Foo create f1 -a 0
+
+# Object f1 has a == 0 and b == 1
+
+

Since the slots are objects, the slot objects can be configured and +parametrized like every other object in NX. Slot objects can be +provided with a scripted initialization as well. We show first the +definition of properties similar to the functionality provided as well +by XOTcl and show afterwards how to use value constraints, optional +parameters, etc. in NX.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Define parameter with an an
+# attribute-specific type checker
+
+Class Person -slots {
+  Attribute create sex -type "sex" {
+    my proc type=sex {name value} {
+      switch -glob $value {
+        m* {return m}
+        f* {return f}
+        default {
+          error "expected sex but got $value"
+        }
+      }
+    }
+  }
+}
+
+
# Configure parameter with scripted
+# definition (init-block), defining a
+# property specific type checker
+
+Class create Person {
+    :property -accessor public sex:sex,convert {
+
+      # define a converter to standardize representation
+      :object method type=sex {name value} {
+        switch -glob $value {
+          m* {return m}
+          f* {return f}
+          default {error "expected sex but got $value"}
+        }
+      }
+
+    }
+}
+
+

The parameters provided by a class for the initialization of +instances can be introspected via querying the parameters +of the method create: /cls/ info lookup parameters create +(see [info_configure_parameter]).

+
+
+

2.4.2. Delete Variable Handlers

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# No syntactic support for deleting
+# variable handlers
+
+
# Like deletion of Methods:
+# Delete on the object, where the
+# variable handler is defined.
+
+/cls/ delete property /name/
+/obj/ delete object property /name/
+
+/cls/ delete variable /name/
+/obj/ delete object variable /name/
+
+
+
+

2.4.3. Method Parameters

+

Method parameters are used to specify the interface of a single method +(what kind of values may be passed to a method, what default values +are provided etc.). The method parameters specifications in XOTcl 1 +were limited and allowed only value constraints for non positional +arguments.

+

NX and XOTcl 2 provide value constraints for all kind of method parameters. +While XOTcl 1 required non-positional arguments to be listed in front of +positional arguments, this limitation is lifted in XOTcl 2.

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Define method foo with non-positional
+# parameters (x, y and y) and positional
+# parameter (a and b)
+
+Class C
+C instproc foo {
+   -x:integer
+   -y:required
+   -z
+   a
+   b
+} {
+   # ...
+}
+C create c1
+
+# invoke method foo
+c1 foo -x 1 -y a 2 3
+
+
# Define method foo with
+# non-positional parameters
+# (x, y and y) and positional
+# parameter (a and b)
+
+Class create C {
+   :public method foo {
+      -x:integer
+      -y:required
+      -z
+      a
+      b
+   } {
+      # ...
+   }
+   :create c1
+}
+# invoke method foo
+c1 foo -x 1 -y a 2 3
+
+
# Only leading non-positional
+# parameters are available; no
+# optional positional parameters,
+# no value constraints on
+# positional parameters,
+# no multiplicity, ...
+
+
# Define various forms of parameters
+# not available in XOTcl 1
+
+Class create C {
+  # trailing (or interleaved) non-positional
+  # parameters
+  :public method m1 {a b -x:integer -y} {
+    # ...
+  }
+
+  # positional parameters with value constraints
+  :public method m2 {a:integer b:boolean} {
+    #...
+  }
+
+  # optional positional parameter (trailing)
+  :public method set {varName value:optional} {
+    # ....
+  }
+
+  # parameter with multiplicity
+  :public method m3 {-objs:object,1..n c:class,0..1} {
+    # ...
+  }
+
+  # In general, the same list of value
+  # constraints as for configure parameter is
+  # available (see above).
+  #
+  # User defined value constraints are
+  # possible. All parameter value checkers
+  # can be turned on and off.
+}
+
+
+
+

2.4.4. Return Value Checking

+

Return value checking is a functionality available in the Next +Scripting Framework, that was not yet available in XOTcl 1. A return +value checker assures that a method returns always a value satisfying +some value constraints. Return value checkers can be defined on all +forms of methods (scripted or C-implemented). Like for other value +checkers, return value checkers can be turned on and off.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# No return value checking
+# available
+
+
# Define method foo with non-positional
+# parameters (x, y and y) and positional
+# parameter (a and b)
+
+Class create C {
+
+  # Define method foo which returns an
+  # integer value
+  :method foo -returns integer {-x:integer} {
+    # ...
+   }
+
+  # Define an alias for the Tcl command ::incr
+  # and assure, it always returns an integer
+  # value
+  :alias incr -returns integer ::incr
+
+  # Define a forwarder that has to return an
+  # integer value
+  :forward ++ -returns integer ::expr 1 +
+
+ # Define a method that has to return a
+ # non-empty list of objects
+ :public object method instances {} \
+    -returns object,1..n {
+   return [:info instances]
+  }
+}
+
+
+
+
+

2.5. Interceptors

+

XOTcl and NX allow the definition of the same set of interceptors, +namely class- and object-level mixins and class- and object-level +filters. The primary difference in NX is the naming, since NX abandons +the prefix "inst" from the names of instance specific method, but uses +the modifier object" for object specific methods.

+

Therefore, in NX, if a mixin is registered on a class-level, it is +applicable for the instances (a per-class mixin), and if and object +mixin is registered, it is a per-object mixin. In both cases, the +term mixin is used, in the second case with the modifier +object. As in all other cases, one can register the same way a +per-object mixin on a plain object or on a class object.

+
+

2.5.1. Register Mixin Classes and Mixin Guards

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/cls/ instmixin ...
+/cls/ instmixinguard /mixin/ ?condition?
+
+# Query per-class mixin
+/cls/ instmixin
+
+
# Register/clear per-class mixin and guard for
+# a class
+
+/cls/ mixins add|set|clear ...
+/cls/ mixins guard /mixin/ ?condition?
+/cls/ configure -mixin ...
+
+# Query per-class mixins
+/cls/ mixins get
+/cls/ cget -mixins
+
+# Query per-class mixins (without guards)
+/cls/ mixins classes
+
+
/obj/ mixin ...
+/obj/ mixinguard /mixin/ ?condition?
+
+# Query per-object mixins
+/obj/ mixin
+
+
# Register/clear per-object mixin and guard for
+# an object
+
+/obj/ object mixins add|set|clear ...
+/obj/ object mixins guard /mixin/ ?condition?
+/obj/ configure -object-mixins ...
+
+# Query per-object mixin
+/obj/ object mixins get
+/obj/ cget -object-mixin
+
+# Query per-object mixins (without guards)
+/cls/ mixins classes
+
+
+
+

2.5.2. Register Filters and Filter Guards

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# Register per-class filter and guard for
+# a class
+/cls/ instfilter ...
+/cls/ instfilterguard /filter/ ?condition?
+
+# Query per-class filter
+/cls/ instfilter
+
+
# Register/clear per-class filter and guard for
+# a class
+
+/cls/ filters add|set|clear ...
+/cls/ filters guard /filter/ ?condition?
+/cls/ configure -filters ...
+
+# Query per-class filters
+/cls/ filters get
+/cls/ cget -filters
+
+# Query per-class filters (without guards)
+/cls/ filters methods
+
+
/obj/ filter ...
+/obj/ filterguard /filter/ ?condition?
+
+
# Register(clear per-object filter and guard for
+# an object
+
+/obj/ object filters add|set|clear ...
+/obj/ object filters guard /filter/ ?condition?
+/obj/ configure -object-filters ...
+
+# Query per-object filters
+/cls/ object filters get
+/obj/ cget -object-filters
+
+# Query per-object filters (without guards)
+/cls/ object filters methods
+
+
+
+
+

2.6. Introspection

+

In general, introspection in NX became more orthogonal and less +dependent on the type of the method. In XOTcl it was e.g. necessary +that a developer had to know, whether a method is e.g. scripted or not +and has to use accordingly different sub-methods of info.

+

In NX, one can use e.g. always info method with a subcommand and the +framework tries to hide the differences as far as possible. So, one +can for example obtain with info method parameter the parameters of +scripted and C-implemented methods the same way, one can get the +definition of all methods via info method definition and one can get +an manual-like interface description via info method +syntax. In addition, NX provides means to query the type of +a method, and NX allows to filter by the type of the method.

+
+

2.6.1. List sub- and superclass relations

+

While XOTcl used singular words for introspecting sub- and superclass +relations, NX uses plural word to indicate that potentially a list of +values is returned.

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/cls/ info superclass ?pattern?
+
+
/cls/ info superclasses ?pattern?
+
+
/cls/ info subclass ?pattern?
+
+
/cls/ info subclasses -type setter ?pattern?
+
+
+
+

2.6.2. List methods defined by classes

+

While XOTcl uses different names for obtaining different kinds of +methods defined by a class, NX uses info methods in an orthogonal +manner. NX allows as well to use the call protection to filter the +returned methods.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/cls/ info instcommands ?pattern?
+
+
/cls/ info methods ?pattern?
+
+
/cls/ info instparametercmd ?pattern?
+
+
/cls/ info methods -type setter ?pattern?
+
+
/cls/ info instprocs ?pattern?
+
+
/cls/ info methods -type scripted ?pattern?
+
+
# n.a.
+
+
/cls/ info methods -type alias ?pattern?
+/cls/ info methods -type forwarder ?pattern?
+/cls/ info methods -type object ?pattern?
+/cls/ info methods -callprotection public|protected ...
+
+
+
+

2.6.3. List methods defined by objects

+

While XOTcl uses different names for obtaining different kinds of +methods defined by an object, NX uses info methods in an orthogonal +manner. NX allows as well to use the call protection to filter the +returned methods.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ info commands ?pattern?
+
+
/obj/ info object methods ?pattern?
+
+
/obj/ info parametercmd ?pattern?
+
+
/obj/ info object methods -type setter ?pattern?
+
+
/obj/ info procs ?pattern?
+
+
/obj/ info object methods -type scripted ?pattern?
+
+
# n.a.
+
+
/obj/ info object methods -type alias ?pattern?
+/obj/ info object methods -type forwarder ?pattern?
+/obj/ info object methods -type object ?pattern?
+/obj/ info object methods -callprotection public|protected ...
+
+
+
+

2.6.4. Check existence of a method

+

NX provides multiple ways of checking, whether a method exists; one +can use info method exists to check, if a given method exists +(return boolean), or one can use info methods ?pattern?, where +pattern might be a single method name without wild-card +characters. The method info methods ?pattern? returns a list of +matching names, which might be empty. These different methods appear +appropriate depending on the context.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj|cls/ info \
+   [inst](commands|procs|parametercmd) \
+   ?pattern?
+
+
/cls/ info method exists /methodName/
+/cls/ info methods /methodName/
+/obj/ info object method exists /methodName/
+/obj/ info object methods /methodName/
+
+
+
+

2.6.5. List callable methods

+

In order to obtain for an object the set of artefacts defined in the + class hierarchy, NX uses info lookup. One can either lookup methods + (via info lookup methods) or slots (via info lookup slots). The + plural term refers to a potential set of return values.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ info methods ?pattern?
+
+
/obj/ info lookup methods ... ?pattern?
+# Returns list of method names
+
+
# n.a.
+
+
# List only application specific methods
+/obj/ info lookup methods -source application ... ?pattern?
+# Returns list of method names
+
+
# Options for 'info methods'
+#
+# -incontext
+# -nomixins
+
+
# Options for 'info lookup methods'
+#
+# -source ...
+# -callprotection ...
+# -incontext
+# -type ...
+# -nomixins
+
+
# n.a.
+
+
# List slot objects defined for obj
+# -source might be all|application|baseclasses
+# -type is the class of the slot object
+
+/obj/ info lookup slots ?-type ...? ?-source ...? ?pattern?
+
+# Returns list of slot objects
+
+
# List registered filters
+/obj/ info filters -order ?-guards? ?pattern?
+
+# List registered mixins
+/obj/ info mixins -heritage ?-guards? ?pattern?
+
+
# List registered filters
+/obj/ info lookup filters ?-guards? ?pattern?
+
+# List registered mixins
+/obj/ info lookup mixins ?-guards? ?pattern?
+
+
+
+

2.6.6. List object/class where a specified method is defined

+

info lookup can be used as well to determine, where exactly an + artefact is located. One can obtain this way a method handle, where +a method or filter is defined.

+

The concept of a method-handle is new in NX. The method-handle +can be used to obtain more information about the method, such as +e.g. the definition of the method.

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ procsearch /methodName/
+
+
/obj/ info lookup method /methodName/
+# Returns method-handle
+
+
/obj/ filtersearch /methodName/
+
+
/obj/ info lookup filter /methodName/
+# Returns method-handle
+
+
+
+

2.6.7. List definition of scripted methods

+

XOTcl contains a long list of info subcommands for different kinds of +methods and for obtaining more detailed information about these +methods.

+

In NX, this list of info subcommands is much shorter and more +orthogonal. For example info method definition can be used to obtain +with a single command the full definition of a scripted method, and +furthermore, it works as well the same way to obtain e.g. the +definition of a forwarder or an alias.

+

While XOTcl uses different names for info options for objects and +classes (using the prefix "inst" for instance specific method), NX +uses for object specific method the modifier object. For definition +of class object specific methods, use the modifier object as usual.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
/cls/ info method definition /methodName/
+/obj/ info object method definition /methodName/
+
+
/cls/ info instbody /methodName/
+/obj/ info body /methodName/
+
+
/cls/ info method body /methodName/
+/obj/ info object method body /methodName/
+
+
/cls/ info instargs /methodName/
+/obj/ info args /methodName/
+
+
/cls/ info method args /methodName/
+/obj/ info object method args /methodName/
+
+
/cls/ info instnonposargs /methodName/
+/obj/ info object method args /methodName/
+
+
/cls/ info method parameter /methodName/
+/obj/ info object method parameter /methodName/
+
+
/cls/ info instdefault /methodName/
+/obj/ info default /methodName/
+
+
# not needed, part of
+# "info ?object? method parameter"
+
+
/cls/ info instpre /methodName/
+/obj/ info pre /methodName/
+
+
/cls/ info method precondition /methodName/
+/obj/ info object method precondition /methodName/
+
+
/cls/ info instpost /methodName/
+/obj/ info post /methodName/
+
+
/cls/ info method postcondition /methodName/
+/obj/ info object method postcondition /methodName/
+
+

Another powerful introspection option in NX is info ?object? method +syntax which obtains a representation of the parameters of a +method in the style of Tcl man pages (regardless of the kind of +method).

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
/cls/ info method syntax /methodName/
+/obj/ info object method syntax /methodName/
+
+
+
+

2.6.8. List Configure Parameters

+

The way, how newly created objects can be configured is determined in NX +via properties. The configuration happens during creation via the +methods create or new or during runtime via configure. These +methods have therefore virtual argument lists, depending on the object +or class on which they are applied.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
# Return the parameters applicable to
+# the create method of a certain class.
+# class can be configured. A pattern can
+# be used to filter the results.
+
+/cls/ info lookup parameters create ?/pattern/?
+
+# Return in the result in documentation syntax
+
+/cls/ info lookup syntax create ?/pattern/?
+
+# "info lookup parameters configure" returns
+# parameters available for configuring the
+# current object  (might contain object
+# specific information)
+
+/obj/ info lookup parameters configure ?pattern?
+
+# "info lookup configure syntax" returns syntax of
+# a call to configure in the Tcl parameter syntax
+
+/obj/ info lookup syntax configure
+
+# Obtain information from a parameter
+# (as e.g. returned from "info lookup
+# parameters configure").
+
+nsf::parameter::info name /parameter/
+nsf::parameter::info syntax /parameter/
+nsf::parameter::info type /parameter/
+
+
+
+

2.6.9. List Variable Declarations (property and variable)

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# obtain parameter definitions defined
+# for a class
+/cls/ info parameter
+
+
# "info variables" returns handles of
+# properties and variables defined by this
+# class or object
+
+/cls/ info variables ?pattern?
+/obj/ info object variables ?pattern?
+
+# "info lookup variables" returns handles
+# of variables and properties applicable
+# for the current object (might contain
+# object specific information)
+
+/obj/ info lookup variables /pattern/
+
+# "info variable" lists details about a
+# single property or variable.
+
+/obj/ info variable definition /handle/
+/obj/ info variable name /handle/
+/obj/ info variable parameter /handle/
+
+
+
+

2.6.10. List Slots

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
# Return list of slots objects defined on the
+# object or class
+#
+# -source might be all|application|baseclasses
+# -type is the class of the slot object
+# -closure includes slots of superclasses
+
+/cls/ info slots \
+   ?-type value? ?-closure? ?-source value? ?pattern?
+/obj/ info object slots ?-type ...? ?pattern?
+
+# List reachable slot objects defined for obj
+# -source might be all|application|baseclasses
+# -type is the class of the slot object
+# Returns list of slot objects.
+
+/obj/ info lookup slots \
+   ?-type ...? ?-source ... ?pattern?
+
+# Obtain definition, name or parameter from
+# slot object
+
+/slotobj/ definition
+/slotobj/ name
+/slotobj/ parameter
+
+
+
+

2.6.11. List Filter or Mixins

+

In NX all introspection options for filters are provided via +info filters and all introspection options for mixins are +provided via info mixins.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ info filter ?-guards? ?-order? ?pattern?
+/obj/ info filterguard /name/
+
+
/obj/ info object filters \
+   ?-guards? ?pattern?
+
+
/cls/ info instfilter \
+   ?-guards? ?-order? ?pattern?
+/cls/ info instfilterguard /name/
+
+
/cls/ info filters \
+   ?-guards? ?pattern?
+
+
/obj/ info mixin ?-guards? ?-order ?pattern?
+/obj/ info mixinguard /name/
+
+
/obj/ info object mixins \
+   ?-guards? ?pattern?
+
+
/cls/ info instmixin \
+   ?-guards? ?-order? ?pattern?
+/cls/ info instmixinguard /name/
+
+
/cls/ info mixins \
+   ?-closure? ?-guards? ?-heritage? ?pattern?
+
+
+
+

2.6.12. List definition of methods defined by aliases, setters or forwarders

+

As mentioned earlier, info method definition can be used on every +kind of method. The same call can be used to obtain the definition of +a scripted method, a method-alias, a forwarder or a setter method.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
/cls/ info method definition /methodName/
+/obj/ info object method definition /methodName/
+
+
+
+

2.6.13. List Method-Handles

+

NX supports method-handles to provide means to obtain further +information about a method or to change maybe some properties of a +method. When a method is created, the method creating method returns +the method handle to the created method.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
#
+# List the method handle of the specified method,
+# can be used e.g. for aliases. "handle" is the short
+# form of "definitionhandle".
+#
+/cls/ info method handle /methodName/
+/obj/ info object method handle /methodName/
+#
+# For ensemble methods (method name contains
+# spaces) one can query as well the registration
+# handle, which is the handle to the root of the
+# ensemble; the definition handle points to the
+# leaf of the ensemble.
+#
+/cls/ info method registrationhandle /methodName/
+/obj/ info object method registrationhandle /methodName/
+#
+# For aliases, one can query the original
+# definition via "info method origin"
+#
+/cls/ info method origin /methodName/
+/obj/ info object method origin /methodName/
+
+
+
+

2.6.14. List type of a method

+

The method info ?object? method type is new in NX to obtain the type of the +specified method.

+
+ +++ + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
# n.a.
+
+
/cls/ info method type /methodName/
+/obj/ info object method type /methodName/
+
+
+
+

2.6.15. List the scope of mixin classes

+

NX provides a richer set of introspection options to obtain +information, where mixins classes are mixed into.

+
+ +++ + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/cls/ info mixinof ?-closure? ?pattern?
+
+
# List objects, where /cls/ is a
+# per-object mixin
+
+/cls/ info mixinof -scope object ?-closure? \
+   ?pattern?
+
+
/cls/ info instmixinof ?-closure? ?pattern?
+
+
# List classes, where /cls/ is a per-class mixin
+
+/cls/ info mixinof -scope class ?-closure? \
+   ?pattern?
+
+
# n.a.
+
+
# List objects and classes, where /cls/ is
+# either a per-object or a per-class mixin
+
+/cls/ info mixinof -scope all ?-closure? \
+   ?pattern?
+
+
+
/cls/ info mixinof ?-closure? ?pattern?
+
+
+
+

2.6.16. Check properties of object and classes

+

Similar as noted before, NX uses rather a hierarchical approach of +naming using multiple layers of subcommands).

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ istype /sometype/
+
+
# Check if object is a subtype of some class
+/obj/ info has type /sometype/
+
+
/obj/ ismixin /cls/
+
+
# Check if object has the specified mixin registered
+/obj/ info has mixin /cls/
+
+
/obj/ isclass ?/cls/?
+
+
# Check if object is an NX class
+/obj/ has type ::nx::Class
+
+# Check if object is a class in one of the
+# NSF object systems
+::nsf::is class /obj/
+
+
/obj/ ismetaclass /cls/
+
+
# Check if class is an NX metaclass
+expr {[/cls/ info heritage ::nx::Class] ne ""}
+
+# Check if object is a metaclass in one of the
+# NSF object systems
+::nsf::is metaclass /obj/
+
+
# n.a.
+
+
# Check if object is a baseclass of an object system
+::nsf::is baseclass /obj/
+
+
# n.a.
+
+
# Return name of object (without namespace prefix)
+/obj/ info name
+
+
/obj/ object::exists /obj/
+
+
# Check for existence of object (nsf primitive)
+::nsf::object::exists /obj/
+
+
+
+

2.6.17. Call-stack Introspection

+

Call-stack introspection is very similar in NX and XOTcl. NX uses for +subcommand the term current instead of self, since self has a +strong connotation to the current object. The term proc is renamed +by method.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
self
+
+
self
+
+
+
current object
+
+
self class
+
+
current class
+
+
self args
+
+
current args
+
+
self proc
+
+
current method
+
+
self callingclass
+
+
current calledclass
+
+
self callingobject
+
+
current callingobject
+
+
self callingproc
+
+
current callingmethod
+
+
self calledclass
+
+
current calledclass
+
+
self calledproc
+
+
current calledmethod
+
+
self isnextcall
+
+
current isnextcall
+
+
self next
+
+
# Returns method-handle of the
+# method to be called via "next"
+current next
+
+
self filterreg
+
+
# Returns method-handle of the
+# filter method
+current filterreg
+
+
self callinglevel
+
+
current callinglevel
+
+
self activelevel
+
+
current activelevel
+
+
+
+
+

2.7. Other Predefined Methods

+
+ +++ + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ requireNamespace
+
+
/obj/ require namespace
+
+
# n.a.
+
+
/obj/ require method
+
+
+
+

2.8. Dispatch, Aliases, etc.

+

todo: to be done or omitted

+
+
+

2.9. Assertions

+

In contrary to XOTcl, NX provides no pre-registered methods for +assertion handling. All assertion handling can e performed via the +Next Scripting primitive nsf::method::assertion.

+
+ +++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
XOTcl Next Scripting Language
+
+
/obj/ check /checkoptions/
+
+
::nsf::method::assertion /obj/ check /checkoptions/
+
+
/obj/ info check
+
+
::nsf::method::assertion /obj/ check
+
+
/obj/ invar /conditions/
+
+
::nsf::method::assertion /obj/ object-invar /conditions/
+
+
/obj/ info invar
+
+
::nsf::method::assertion /obj/ object-invar
+
+
/cls/ instinvar /conditions/
+
+
::nsf::method::assertion /cls/ class-invar /conditions/
+
+
/cls/ info instinvar
+
+
::nsf::method::assertion /cls/ class-invar
+
+
/cls/ invar /conditions/
+
+
::nsf::method::assertion /cls/ object-invar /conditions/
+
+
/cls/ info invar
+
+
::nsf::method::assertion /cls/ object-invar
+
+
+
+

2.10. Method Protection

+

As described above, NX supports method +protection via the method modifiers protected and public. A +protected method can be only called from an object of that class, +while public methods can be called from every object. The method +protection can be used to every kind of method, such as e.g. scripted +methods, aliases, forwarders, or accessors. For invocations, +the most specific definition (might be a mixin) is used for +determining the protection.

+
+
+
+
+

3. Incompatibilities between XOTcl 1 and XOTcl 2

+
+
+

3.1. Resolvers

+

The resolvers (variable resolvers, function resolvers) of the Next +Scripting Framework are used as well within XOTcl 2. When variable +names or method names starting with a single colon are used in XOTcl 1 +scripts, conflicts will arise with the resolver. These names must be +replaced.

+
+
+

3.2. Parameters

+

The following changes for parameters could be regarded as bug-fixes.

+
+

3.2.1. Parameter usage without a value

+

In XOTcl 1, it was possible to call a parameter method during object +creation via the dash-interface without a value (in the example below -x).

+
+
+
# XOTcl example
+
+Class Foo -parameter {x y}
+Foo f1 -x -y 1
+

Such cases are most likely mistakes. All parameter configurations in XOTcl 2 require an argument.

+
+
+

3.2.2. Ignored Parameter definitions

+

In XOTcl 1, a more specific parameter definition without a default was ignored +when a more general parameter definition with a default was +present. In the example below, the object b1 contained in XOTcl 1 +incorrectly the parameter x (set via default from Foo), while in +XOTcl 2, the variable won’t be set.

+
+
+
# XOTcl example
+
+Class Foo -parameter {{x 1}}
+Class Bar -superclass Foo -parameter x
+Bar b1
+
+
+

3.2.3. Changing classes and superclasses

+

NX does not define the methods class and superclass (like XOTcl), but allows to +alter all object/class relations (including +class/superclass/object-mixin/…) +nsf::relation::set. The class and superclass can be certainly queried +in all variants with info class or info superclasses.

+
+
+
# NX example
+
+nx::Class create Foo
+Foo create f1
+
+# now alter the class of object f1
+nsf::relation::set f1 class ::nx::Object
+
+
+

3.2.4. Overwriting procs/methods with objects and vice versa

+

NSF is now more conservative on object/method creation. In contrary to +XOTcl 1 NSF does not allow to redefined a pre-existing command +(e.g. "set") with an object and vice versa. Like in XOTcl 1, +preexisting objects and classes con be redefined (necessary for +reloading objects/classes in an running interpreter).

+
+
+

3.2.5. Info heritage

+

info heritage returns in XOTcl 1 the transitive superclass +hierarchy, which is equivalent with info superclasses -closure and +therefore not necessary. In XOTcl 2 (and NX), info heritage includes +as well the transitive per-class mixins.

+
+
+
+

3.3. Slots

+

All slot objects (also XOTcl slot objects) are now next-scripting +objects of baseclass ::nx::Slot. The name of the experimental +default-setter initcmd was changed to defaultcmd. Code directly +working on the slots objects has to be adapted.

+
+
+

3.4. Obsolete Commands

+

Parameter-classes were rarely used and have been replaced by the more +general object parametrization. Therefore, cl info parameterclass has +been removed.

+
+
+

3.5. Stronger Checking

+

The Next Scripting Framework performs stronger checking than XOTcl 1 +For example, the requiredness of slots in XOTcl 1 was just a +comment, while XOTcl 2 enforces it.

+
+
+

3.6. Exit Handlers

+

The exit hander interface changed from a method of ::xotcl::Object +into the Tcl command ::nsf::exithandler:

+
+
+
# NX example
+::nsf::exithandler set|get|unset ?arg?
+
+
+
+
+

+ + + Index: doc/next-migration.txt =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/next-migration.txt (.../next-migration.txt) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/next-migration.txt (.../next-migration.txt) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1465,7 +1465,7 @@ Since the slots are objects, the slot objects can be configured and parametrized like every other object in NX. Slot objects can be provided with a scripted initialization as well. We show first the -definition of properties simliar to the functionality provided as well +definition of properties similar to the functionality provided as well by XOTcl and show afterwards how to use value constraints, optional parameters, etc. in NX. Index: doc/next-tutorial/next-tutorial.html =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/next-tutorial/next-tutorial.html (.../next-tutorial.html) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/next-tutorial/next-tutorial.html (.../next-tutorial.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -1,3398 +1,3398 @@ - - - - - -Tutorial for the Next Scripting Language - - - - - + +
+
+
+
+
+
Abstract
+

This document provides a tutorial for the Next Scripting +Language NX.

+
+

The Next Scripting Language (NX) is a highly flexible object oriented +scripting language based on Tcl [Ousterhout 1990]. NX is a successor +of XOTcl 1 [Neumann and Zdun 2000a] and was developed based on 10 +years of experience with XOTcl in projects containing several hundred +thousand lines of code. While XOTcl was the first language designed to +provide language support for design patterns, the focus of the Next +Scripting Framework and NX is on combining this with Language +Oriented Programming. In many respects, NX was designed to ease the +learning of the language for novices (by using a more mainstream +terminology, higher orthogonality of the methods, less predefined +methods), to improve maintainability (remove sources of common errors) +and to encourage developers to write better structured programs (to +provide interfaces) especially for large projects, where many +developers are involved.

+

The Next Scripting Language is based on the Next Scripting Framework +(NSF) which was developed based on the notion of language oriented +programming. The Next Scripting Frameworks provides C-level support +for defining and hosting multiple object systems in a single Tcl +interpreter. The name of the Next Scripting Framework is derived from +the universal method combinator "next", which was introduced in XOTcl. +The combinator "next" serves as a single instrument for method +combination with filters, per-object and transitive per-class mixin +classes, object methods and multiple inheritance.

+

The definition of NX is fully scripted (e.g. defined in +nx.tcl). The Next Scripting Framework is shipped with three language +definitions, containing NX and XOTcl 2. Most of the existing XOTcl 1 +programs can be used without modification in the Next Scripting +Framework by using XOTcl 2. The Next Scripting Framework requires Tcl +8.5 or newer.

+
+
+
+

1. NX and its Roots

+
+

Object oriented extensions of Tcl have quite a +long history. Two of the most prominent early Tcl based OO languages +were incr Tcl (abbreviated as itcl) and Object Tcl (OTcl +[Wetherall and Lindblad 1995]). While itcl provides a traditional +C++/Java-like object system, OTcl was following the CLOS approach and +supports a dynamic object system, allowing incremental class and +object extensions and re-classing of objects.

+

Extended Object Tcl (abbreviated as XOTcl [Neumann and Zdun 2000a]) +is a successor of OTcl and was the first language providing language +support for design patterns. XOTcl extends OTcl by providing namespace +support, adding assertions, dynamic object aggregations, slots and by +introducing per-object and per-class filters and per-object and +per-class mixins.

+

XOTcl was so far released in more than 30 versions. It is described in +its detail in more than 20 papers and serves as a basis for other +object systems like TclOO [Donal ???]. The scripting language NX and +the Next Scripting Framework [Neumann and Sobernig 2009] extend +the basic ideas of XOTcl by providing support for language-oriented +programming. The the Next Scripting Framework supports multiple +object systems concurrently. Effectively, every object system has +different base classes for creating objects and classes. Therefore, +these object systems can have different interfaces and can +follow different naming conventions for built-in methods. Currently, +the Next Scripting Framework is packaged with three object systems: +NX, XOTcl 2.0, and TclCool (the language introduced by TIP#279).

+
+
+Languages +
+
Figure 1. Language History of the Next Scripting Language
+
+

+

The primary purpose of this document is to introduce NX to beginners. +We expect some prior knowledge of programming languages, and some +knowledge about Tcl. In the following sections we introduce NX by +examples. In later sections we introduce the more advanced concepts of +the language. Conceptually, most of the addressed concepts are very +similar to XOTcl. Concerning the differences between NX and XOTcl, +please refer to the Migration Guide for the Next Scripting Language.

+
+
+
+

2. Introductory Overview Example: Stack

+
+

A classical programming example is the implementation of a stack, which +is most likely familiar to many readers from many introductory +programming courses. A stack is a last-in first-out data structure +which is manipulated via operations like push (add something to the +stack) and pop remove an entry from the stack. These operations are +called methods in the context of object oriented programming +systems. Primary goals of object orientation are encapsulation and +abstraction. Therefore, we define a common unit (a class) that defines +and encapsulates the behavior of a stack and provides methods to a user +of the data structure that abstract from the actual implementation.

+
+

2.1. Define a Class "Stack"

+

In our first example, we define a class named Stack with the methods +push and pop. When an instance of the stack is created (e.g. a +concrete stack s1) the stack will contain an instance variable named +things, initialized with the an empty list.

+
Listing 2: Class Stack

+
+
+
nx::Class create Stack {
+
+   #
+   # Stack of Things
+   #
+
+   :variable things {}
+
+   :public method push {thing} {
+      set :things [linsert ${:things} 0 $thing]
+      return $thing
+   }
+
+   :public method pop {} {
+      set top [lindex ${:things} 0]
+      set :things [lrange ${:things} 1 end]
+      return $top
+   }
+}
+

Typically, classes are defined in NX via nx::Class create, followed +by the name of the new class (here: Stack). The definition of the +stack placed between curly braces and contains here just the method +definitions. Methods of the class are defined via :method followed +by the name of the method, an argument list and the body of the +method, consisting of Tcl and NX statements.

+

When an instance of Stack is created, it will contain an instance +variable named things. If several Stack instances are created, +each of the instances will have their own (same-named but different) +instance variable. The instance variable things is used in our +example as a list for the internal representation of the stack. We +define in a next step the methods to access and modify this list +structure. A user of the stack using the provided methods does not +have to have any knowledge about the name or the structure of the +internal representation (the instance variable things).

+

The method push receives an argument thing which should be placed +on the stack. Note that we do not have to specify the type of the +element on the stack, so we can push strings as well as numbers or +other kind of things. When an element is pushed, we add this element +as the first element to the list things. We insert the element using +the Tcl command linsert which receives the list as first element, +the position where the element should be added as second and the new +element as third argument. To access the value of the instance +variable we use Tcl’s dollar operator followed by the name. The +names of instance variables are preceded with a colon :. Since the +name contains a non-plain character, Tcl requires us to put braces +around the name. The command linsert and its arguments are placed +between square brackets. This means that the function linsert is called and +a new list is returned, where the new element is inserted at the first +position (index 0) in the list things. The result of the linsert +function is assigned again to the instance variable things, which is +updated this way. Finally the method push returns the pushed thing +using the return statement.

+

The method pop returns the most recently stacked element and removes +it from the stack. Therefore, it takes the first element from the list +(using the Tcl command lindex), assigns it to the method-scoped +variable top, removes the element from the instance variable +things (by using the Tcl command lrange) and returns the value +popped element top.

+

This finishes our first implementation of the stack, more enhanced +versions will follow. Note that the methods push and pop are +defined as public; this means that these methods can be +used from all other objects in the system. Therefore, these methods +provide an interface to the stack implementation.

+
Listing 3: Using the Stack

+
+
+
#!/usr/bin/env tclsh
+package require nx
+
+nx::Class create Stack {
+
+   #
+   # Stack of Things
+   #
+   ....
+}
+
+Stack create s1
+s1 push a
+s1 push b
+s1 push c
+puts [s1 pop]
+puts [s1 pop]
+s1 destroy
+

Now we want to use the stack. The code snippet in Listing 3 shows how to use the class Stack in a script. +Since NX is based on Tcl, the script will be called with the Tcl shell +tclsh. In the Tcl shell we have to require package nx to use the +Next Scripting Framework and NX. The next lines contain the definition +of the stack as presented before. Of course, it is as well possible to +make the definition of the stack an own package, such we could simple +say package require stack, or to save the definition of a stack +simply in a file and load it via source.

+

In line 12 we create an instance of the stack, namely the stack object +s1. The object s1 is an instance of Stack and has therefore +access to its methods. The methods like push or pop can be invoked +via a command starting with the object name followed by the +method name. In lines 13-15 we push on the stack the values a, then +b, and c. In line 16 we output the result of the pop method +using the Tcl command puts. We will see on standard output the +value+c+ (the last stacked item). The output of the line 17 is the +value b (the previously stacked item). Finally, in line 18 we +destroy the object. This is not necessary here, but shows the life +cycle of an object. In some respects, destroy is the counterpart of +create from line 12.

+
+
+object-class-appclass.png +
+
Figure 4. Class and Object Diagram
+
+

+

Figure 4 shows the actual class and +object structure of the first Stack example. Note that the common +root class is nx::Object that contains methods for all objects. +Since classes are as well objects in NX, nx::Class is a +specialization of nx::Object. nx::Class provides methods for +creating objects, such as the method create which is used to create +objects (and classes as well).

+
+
+

2.2. Define an Object Named "stack"

+

The definition of the stack in Listing 2 +follows the traditional object oriented approach, found in +practically every object oriented programming language: Define a class +with some methods, create instances from this class, and use the +methods defined in the class in the instances of the class.

+

In our next example, we introduce generic objects and object +specific methods. With NX, we can define generic objects, which are +instances of the most generic class nx::Object (sometimes called +common root class). nx::Object is predefined and contains a +minimal set of methods applicable to all NX objects. In this example, +we define a generic object named stack and provide methods for this +object. The methods defined above were methods provided by a class for +objects. Now we define object specific methods, which are methods +applicable only to the object for which they are defined.

+
Listing 5: Object stack

+
+
+
nx::Object create stack {
+
+   :object variable things {}
+
+   :public object method push {thing} {
+      set :things [linsert ${:things} 0 $thing]
+      return $thing
+   }
+
+   :public object method pop {} {
+      set top [lindex ${:things} 0]
+      set :things [lrange ${:things} 1 end]
+      return $top
+   }
+}
+

The example in Listing 5 defines the +object stack in a very similar way as the class Stack. But the +following points are different.

+
    +
  • +

    +First, we use nx::Object instead of nx::Class to denote + that we want to create a generic object, not a class. +

    +
  • +
  • +

    +We use :object variable to define the variable things just for + this single instance (the object stack). +

    +
  • +
  • +

    +The definition for the methods push and pop are the same as + before, but here we defined these with object method. Therefore, + these two methods push and pop are object-specific. +

    +
  • +
+

In order to use +the stack, we can use directly the object stack in the same way as +we have used the object s1 in Listing 3 +the class diagram for this the object stack.

+
+
+object-stack.png +
+
Figure 6. Object stack
+
+

+

A reader might wonder when to use a class Stack or rather an object +stack. A big difference is certainly that one can define easily +multiple instances of a class, while the object is actually a +single, tailored entity. The concept of the object stack is similar to a module, +providing a certain functionality via a common interface, without +providing the functionality to create multiple instances. The reuse of +methods provided by the class to objects is as well a difference. If +the methods of the class are updated, all instances of the class will +immediately get the modified behavior. However, this does not mean that +the reuse for the methods of stack is not possible. NX allows for +example to copy objects (similar to prototype based languages) or to +reuse methods via e.g. aliases (more about this later).

+

Note that we use capitalized names for classes and lowercase names for +instances. This is not required and a pure convention making it easier +to understand scripts without much analysis.

+
+
+

2.3. Implementing Features using Mixin Classes

+

So far, the definition of the stack methods was pretty minimal. +Suppose, we want to define "safe stacks" that protect e.g. against +stack under-runs (a stack under-run happens, when more pop than +push operations are issued on a stack). Safety checking can be +implemented mostly independent from the implementation details of the +stack (usage of internal data structures). There are as well different +ways of checking the safety. Therefore we say that safety checking is +orthogonal to the stack core implementation.

+

With NX we can define stack-safety as a separate class using methods +with the same names as the implementations before, and "mix" this +behavior into classes or objects. The implementation of Safety in +stack under-runs and to issue error messages, when this happens.

+
Listing 7: Class Safety

+
+
+
nx::Class create Safety {
+
+  #
+  # Implement stack safety by defining an additional
+  # instance variable named "count" that keeps track of
+  # the number of stacked elements. The methods of
+  # this class have the same names and argument lists
+  # as the methods of Stack; these methods "shadow"
+  # the methods of class Stack.
+  #
+
+  :variable count 0
+
+  :public method push {thing} {
+    incr :count
+    next
+  }
+
+  :public method pop {} {
+    if {${:count} == 0} then { error "Stack empty!" }
+    incr :count -1
+    next
+  }
+}
+

Note that all the methods of the class Safety end with next. +This command is a primitive command of NX, which calls the +same-named method with the same argument list as the current +invocation.

+

Assume we save the definition of the class Stack in a file named +Stack.tcl and the definition of the class Safety in a file named +Safety.tcl in the current directory. When we load the classes +Stack and Safety into the same script (see the terminal dialog in +e.g. a certain stack s2 as a safe stack, while all other stacks +(such as s1) might be still "unsafe". This can be achieved via the +option -mixin at the object creation time (see line 9 in +option -mixin mixes the class Safety into the new instance s2.

+
Listing 8: Using the Class Safety

+
+
+
% package require nx
+2.0
+% source Stack.tcl
+::Stack
+% source Safety.tcl
+::Safety
+% Stack create s1
+::s1
+% Stack create s2 -object-mixin Safety
+::s2
+% s2 push a
+a
+% s2 pop
+a
+% s2 pop
+Stack empty!
+
+% s1 info precedence
+::Stack ::nx::Object
+
+% s2 info precedence
+::Safety ::Stack ::nx::Object
+

When the method push of s2 is called, first the method of the +mixin class Safety will be invoked that increments the counter and +continues with next to call the shadowed method, here the method +push of the Stack implementation that actually pushes the item. +The same happens, when s2 pop is invoked, first the method of +Safety is called, then the method of the Stack. When the stack is +empty (the value of count reaches 0), and pop is invoked, the +mixin class Safety generates an error message (raises an exception), +and does not invoke the method of the Stack.

+

The last two commands in +Listing 8 +use introspection to query for the objects +s1 and s2 in which order the involved classes are processed. This +order is called the precedence order and is obtained via info +precedence. We see that the mixin class Safety is only in use for +s2, and takes there precedence over Stack. The common root class +nx::Object is for both s1 and s2 the base class.

+
+
+per-object-mixin.png +
+
Figure 9. Per-object Mixin
+
+

+

Note that in Listing 8, +the class Safety is only mixed into a single object (here +s2), therefore we refer to this case as a per-object mixin. +Figure 9 shows the class +diagram, where the class Safety is used as a per-object mixin for +s2.

+

The mixin class Safety can be used as well in other ways, such as e.g. for +defining classes of safe stacks:

+
Listing 10: Class SafeStack

+
+
+
#
+# Create a safe stack class by using Stack and mixin
+# Safety
+#
+nx::Class create SafeStack -superclass Stack -mixin Safety
+
+SafeStack create s3
+

The difference of a per-class mixin and an per-object mixin is that +the per-class mixin is applicable to all instances of the +class. Therefore, we call these mixins also sometimes instance mixins. +In our example in Listing 10, +Safety is mixed into the definition of +SafeStack. Therefore, all instances of the class SafeStack (here +the instance s3) will be using the safety definitions.

+
+
+per-class-mixin.png +
+
Figure 11. Per-class Mixin
+
+

+

Figure 11 shows the class diagram +for this definition. +Note that we could use Safety as well as a per-class mixin on +Stack. In this case, all stacks would be safe stacks and we could +not provide a selective feature selection (which might be perfectly +fine).

+
+
+

2.4. Define Different Kinds of Stacks

+

The definition of Stack is generic and allows all kind of elements +to be stacked. Suppose, we want to use the generic stack definition, +but a certain stack (say, stack s4) should be a stack for integers +only. This behavior can be achieved by the same means as introduced +already in Listing 5, namely +object-specific methods.

+
Listing 12: Object Integer Stack

+
+
+
Stack create s4 {
+
+  #
+  # Create a stack with a object-specific method
+  # to check the type of entries
+  #
+
+  :public object method push {thing:integer} {
+    next
+  }
+}
+

The program snippet in Listing 12 defines an instance s4 of the class +Stack and provides an object specific method for push to implement +an integer stack. The method pull is the same for the integer stack +as for all other stacks, so it will be reused as usual from the class +Stack. The object-specific method push of s4 has a value +constraint in its argument list (thing:integer) that makes sure, +that only integers can be stacked. In case the argument is not an +integer, an exception will be raised. Of course, one could perform the +value constraint checking as well in the body of the method proc by +accepting an generic argument and by performing the test for the value +in the body of the method. In the case, the passed value is an +integer, the push method of Listing 12 calls next, and therefore calls the +shadowed generic definition of push as provided by Stack.

+
Listing 13: Class IntegerStack

+
+
+
nx::Class create IntegerStack -superclass Stack {
+
+  #
+  # Create a Stack accepting only integers
+  #
+
+  :public method push {thing:integer} {
+    next
+  }
+}
+

An alternative approach is shown in +Listing 13, where the class +IntegerStack is defined, using the same method definition +as s4, this time on the class level.

+
+
+

2.5. Define Object Specific Methods on Classes

+

In our previous examples we defined methods provided by classes +(applicable for their instances) and object-specific methods (methods +defined on objects, which are only applicable for these objects). In +this section, we introduce methods that are defined on the class +objects. Such methods are sometimes called class methods or +static methods.

+

In NX classes are objects, they are specialized objects with +additional methods. Methods for classes are often used for managing +the life-cycles of the instances of the classes (we will come to this +point in later sections in more detail). Since classes are objects, we +can use exactly the same notation as above to define class methods by +using object method. The methods defined on the class object are +in all respects identical with object specific methods shown in the +examples above.

+
Listing 14: Class Stack2

+
+
+
nx::Class create Stack2 {
+
+   :public object method available_stacks {} {
+      return [llength [:info instances]]
+   }
+
+   :variable things {}
+
+   :public method push {thing} {
+      set :things [linsert ${:things} 0 $thing]
+      return $thing
+   }
+
+   :public method pop {} {
+      set top [lindex ${:things} 0]
+      set :things [lrange ${:things} 1 end]
+      return $top
+   }
+}
+
+Stack2 create s1
+Stack2 create s2
+
+puts [Stack2 available_stacks]
+

The class Stack2 in Listing 14 consists of the +earlier definition of the class Stack and is extended by the +class-specific method available_stacks, which returns the +current number of instances of the stack. The final command puts +(line 26) prints 2 to the console.

+
+
+stack2.png +
+
Figure 15. Stack2
+
+

+

The class diagram in Figure 15 shows the +diagrammatic representation of the class object-specific method +available_stacks. Since every class is a specialization of the +common root class nx::Object, the common root class is often omitted +from the class diagrams, so it was omitted here as well in the diagram.

+
+
+
+
+

3. Basic Language Features of NX

+
+
+

3.1. Variables and Properties

+

In general, NX does not need variable declarations. It allows to +create or modify variables on the fly by using for example the Tcl +commands set and unset. Depending on the variable name (or more +precisely, depending on the variable name’s prefix consisting of +colons ":") a variable is either local to a method, or it is an +instance variable, or a global variable. The rules are:

+
    +
  • +

    +A variable without any colon prefix refers typically to a method + scoped variable. Such a variable is created during the invocation + of the method, and it is deleted, when the method ends. In the + example below, the variable a is method scoped. +

    +
  • +
  • +

    +A variable with a single colon prefix refers to an instance + variable. An instance variable is part of the object; when the + object is destroyed, its instance variables are deleted as well. In the + example below, the variable b is an instance variable. +

    +
  • +
  • +

    +A variable with two leading colons refers to a global variable. The + lifespan of a globale variable ends when the variable is explicitly + unset or the script terminates. Variables, which are placed in Tcl + namespaces, are also global variables. In the example below, the + variable c is a global variable. +

    +
  • +
+
Listing 16: Scopes of Variables

+
+
+
nx::Class create Foo {
+
+  :method foo args {...}
+    # "a" is a method scoped variable
+    set a 1
+    # "b" is an Instance variable
+    set :b 2
+    # "c" is a global variable/namespaced variable
+    set ::c 3
+  }
+}
+

Listing 16 shows a method foo +of some class Foo referring to differently scoped variables.

+
+

3.1.1. Properties: Configurable Instance Variables

+

As described above, there is no need to declare instance variables in +NX. In many cases, a developer might want to define some value +constraints for variables, or to provide defaults, or to make +variables configurable upon object creation. Often, variables are +"inherited", meaning that the variables declared in a general class +are also available in a more specialized class. For these purposes NX +provides variable handlers responsible for the management of +instance variables. We distinguish in NX between configurable +variables (called property) and variables that are not configurable +(called variable).

+
+
+

A property is a definition of a configurable instance variable.

+
+

The term configurable means that (a) one can provide at creation time of +an instance a value for this variable, and (b), one can query the +value via the accessor function cget and (c), one can change the +value of the variable via configure at runtime. Since the general +accessor function cget and configure are available, an application +developer does not have to program own accessor methods. When value +checkers are provided, each time, the value of the variable is to be +changed, the constrained are checked as well.

+
+
+person-student.png +
+
Figure 17. Classes Person and Student
+
+

+

The class diagram above defines the classes Person and +Student. For both classes, configurable instance variable are +specified by defining these as properties. The listing below shows +an implementation of this conceptual model in NX.

+
Listing 18: Properties

+
+
+
#
+# Define a class Person with properties "name"
+# and "birthday"
+#
+nx::Class create Person {
+  :property name:required
+  :property birthday
+}
+
+#
+# Define a class Student as specialization of Person
+# with additional properties
+#
+nx::Class create Student -superclass Person {
+  :property matnr:required
+  :property {oncampus:boolean true}
+}
+
+#
+# Create instances using configure parameters
+# for the initialization
+#
+Person create p1 -name Bob
+Student create s1 -name Susan -matnr 4711
+
+# Access property value via accessor method
+puts "The name of s1 is [s1 cget -name]"
+

By defining name and birthday as properties of Person, NX makes +these configurable. When we create an instance of Person named +p1, we can provide a value for e.g. the name by specifying -name +during creation. The properties result in non-positional configure parameters +which can be provided in any order. In our listing, we create an instance of +Person using the configure parameter name and provide the value of +Bob to the instance variable name.

+

The class Student is defined as a specialization of Person with +two additional properties: matnr and oncampus. The property +matnr is required (it has to be provided, when an instance of this +class is created), and the property oncampus is boolean, and is per +default set to true. Note that the class Student inherits the +properties of Person. So, Student has four properties in total.

+

The property definitions provide the configure parameters for +instance creation. Many other languages require such parameters to be +passed via arguments of a constructor, which is often error prone, +when values are to be passed to superclasses. Also in dynamic +languages, the relationships between classes can be easily changed, +and different superclasses might have different requirements in their +constructors. The declarative approach in NX reduces the need for +tailored constructor methods significantly.

+

Note, that the property matnr of class Student is required. This +means, that if we try to create an instance of Student, a runtime +exception will be triggered. The property oncamups is boolean and +contains a default value. Providing a default value means that +whenever we create an instance of this class the object will contain +such an instance variable, even when we provide no value via the +configure parameters.

+

In our listing, we create an instance of Student using the two +configure parameters name and matnr. Finally, we use method cget +to obtain the value of the instance variable name of object s1.

+
+
+

3.1.2. Non-configurable Instance Variables

+

In practice, not all instance variables should be configurable. But +still, we want to be able to provide defaults similar to +properties. To define non-configurable instance variables the +predefined method variable can be used. Such instance variables are +often used for e.g. keeping the internal state of an object. The +usage of variable is in many respects similar to property. One +difference is, that property uses the same syntax as for method +parameters, whereas variable receives the default value as a +separate argument (similar to the variable command in plain +Tcl). The introductory Stack example in Listing 2 uses already the method variable.

+
Listing 19: Declaring Variables

+
+
+
nx::Class create Base {
+  :variable x 1
+  # ...
+}
+
+nx::Class create Derived -superclass Base {
+  :variable y 2
+  # ...
+}
+
+# Create instance of the class Derived
+Derived create d1
+
+# Object d1 has instance variables
+# x == 1 and y == 2
+

Note that the variable definitions are inherited in the same way as +properties. The example in Listing 19 shows a +class Derived that inherits from Base. When an instance d1 is +created, it will contain the two instance variables x and y. +Note that the variable declarations from property and variable are +used to initialize (and to configure) the instances variables of an object.

+
Listing 20: Setting Variables in the Constructor

+
+
+
nx::Class create Base2 {
+ # ...
+ :method init {} {
+   set :x 1
+   # ....
+ }
+}
+
+nx::Class create Derived2 -superclass Base2 {
+ # ...
+ :method init {} {
+   set :y 2
+   next
+   # ....
+ }
+}
+
+# Create instance of the class Derived2
+Derived2 create d2
+

In many other object oriented languages, the instance variables are +initialized solely by the constructor (similar to class Derived2 in +Listing 20). This approach is certainly +also possible in NX. Note that the approach using constructors +requires an explicit method chaining between the constructors and is +less declarative than the approach in NX using property and variable.

+

Both, property and variable provide much more functionalities. One +can for example declare public, protected or private accessor +methods, or one can define variables to be incremental (for +e.g. adding values to a list of values), or one can define variables +specific behavior.

+
+
+
+

3.2. Method Definitions

+

The basic building blocks of an object oriented program are object and +classes, which contain named pieces of code, the methods.

+
+
+

Methods are subroutines (pieces of code) associated with objects +and/or classes. A method has a name, receives optionally arguments +during invocation and returns a value.

+
+

Plain Tcl provides subroutines, which are not associated with objects +or classes. Tcl distinguishes between +proc+s (scripted subroutines) +and commands (system-languages implemented subroutines).

+

Methods might have different scopes, defining, on which kind of +objects these methods are applicable to. These are described in more +detail later on. For the time being, we deal here with methods defined +on classes, which are applicable for the instance of these classes.

+
+

3.2.1. Scripted Methods

+

Since NX is a scripting language, most methods are most likely +scripted methods, in which the method body contains Tcl code.

+
Listing 21: Scripted method

+
+
+
# Define a class
+nx::Class create Dog {
+
+  # Define a scripted method for the class
+  :public method bark {} {
+    puts "[self] Bark, bark, bark."
+  }
+}
+
+# Create an instance of the class
+Dog create fido
+
+# The following line prints "::fido Bark, bark, bark."
+fido bark
+

In the example above we create a class Dog with a scripted method +named bark. The method body defines the code, which is executed when +the method is invoked. In this example, the method bar prints out a +line on the terminal starting with the object name (this is determined +by the built in command self) followed by "Bark, bark, bark.". This +method is defined on a class and applicable to instances of the class +(here the instance fido).

+
+
+

3.2.2. C-implemented Methods

+

Not all of the methods usable in NX are scripted methods; many +predefined methods are defined in the underlying system language, +which is typically C. For example, in Listing 21 we +used the method create to create the class Dog and to create the +dog instance fido. These methods are implemented in C in the next +scripting framework.

+

C-implemented methods are not only provided by the underlying +framework but might be as well defined by application developers. This +is an advanced topic, not covered here. However, application developer +might reuse some generic C code to define their own C-implemented +methods. Such methods are for example accessors, forwarders and +aliases.

+
+
+

An accessor method is a method that accesses instance +variables of an object. A call to an accessor +without arguments uses the accessor as a getter, obtaining the actual +value of the associated variable. A call to an accessor with an +argument uses it as a setter, setting the value of the associated +variable.

+
+

NX provides support for C-implemented accessor methods. Accessors have +already been mentioned in the section about properties. When +the option -accessor public|protected|private is provided to a +variable or property definition, NX creates automatically a +same-named accessors method.

+
Listing 22: Accessor Methods

+
+
+
nx::Class create Dog {
+ :public method bark {} { puts "[self] Bark, bark, bark." }
+ :method init {} { Tail create [self]::tail}
+}
+
+nx::Class create Tail {
+  :property -accessor public {length:double 5}
+  :public method wag {} {return Joy}
+}
+
+# Create an instance of the class
+Dog create fido
+
+# Use the accessor "length" as a getter, to obtain the value
+# of a property. The following call returns the length of the
+# tail of fido
+fido::tail length get
+
+# Use the accessor "length" as a setter, to alter the value
+# of a property. The following call changes the length of
+# the tail of fido
+fido::tail length set 10
+
+# Proving an invalid values will raise an error
+fido::tail length set "Hello"
+

Listing 22 shows an extended example, where every dog +has a tail. The object tail is created as a subobject of the dog in +the constructor init. The subobject can be accessed by providing the +full name of the subobject fido::tail. The method length is an +C-implemented accessor, that enforces the value constraint (here a +floating point number, since length uses the value constraint +double). Line 25 will therefore raise an exception, since the +provided values cannot be converted to a double number.

+
Listing 23: Forwarder Methods

+
+
+
nx::Class create Dog {
+  :public method bark {} { puts "[self] Bark, bark, bark." }
+  :method init {} {
+    Tail create [self]::tail
+    :public object forward wag [self]::tail wag
+  }
+}
+
+nx::Class create Tail {
+  :property {length 5}
+  :public method wag {} {return Joy}
+}
+
+# Create an instance of the class
+Dog create fido
+
+# The invocation of "fido wag" is delegated to "fido::tail wag".
+# Therefore, the following method returns "Joy".
+fido wag
+

Listing 23 again extends the example by adding a +forwarder named wag to the object (e.g. fido). The forwarder +redirects all calls of the form fido wag with arbitrary arguments to +the subobject fido::tail.

+
+
+

A forwarder method is a +C-implemented method that redirects an invocation for a certain method +to either a method of another object or to some other method of the +same object. Forwarding an invocation of a method to some other +object is a means of delegation.

+
+

The functionality of the forwarder can just as well be implemented as +a scripted method, but for the most common cases, the forward +implementation is more efficient, and the forward method expresses +the intention of the developer.

+

The method forwarder has several options to change e.g. the order of +the arguments, or to substitute certain patterns in the argument list +etc. This will be described in later sections.

+
+
+

3.2.3. Method-Aliases

+
+
+

An alias method is a means to register either an existing method, +or a Tcl proc, or a Tcl command as a method with the provided +name on a class or object.

+
+

In some way, the method alias is a restricted form of a forwarder, +though it does not support delegation to different objects or argument +reordering. The advantage of the method alias compared to a forwarder +is that it has close to zero overhead, especially for aliasing +c-implemented methods.

+
Listing 24: Method-Alias

+
+
+
nx::Class create Dog {
+  :public method bark {} { puts "[self] Bark, bark, bark." }
+
+  # Define a public alias for the method "bark"
+  :public alias warn [:info method handle bark]
+  # ...
+}
+
+# Create an instance of the class
+Dog create fido
+
+# The following line prints "::fido Bark, bark, bark."
+fido warn
+

Listing 24 extends the last example by defining an +alias for the method bark. The example only shows the bare +mechanism. In general, method aliases are very powerful means for +reusing pre-existing functionality. The full object system of NX and +XOTcl2 is built from aliases, reusing functionality provided by the +next scripting framework under different names. Method aliases +are as well a means for implementing traits in NX.

+
+
+
+

3.3. Method Protection

+

All kinds of methods might have different kind of protections in NX. +The call-protection defines from which calling context methods might +be called. The Next Scripting Framework supports as well redefinition +protection for methods.

+

NX distinguishes between public, protected and private methods, +where the default call-protection is protected.

+
+
+

A public method can be called from every context. A protected +method can only be invoked from the same object. A private method +can only be invoked from methods defined on the same entity +(defined on the same class or on the same object) via the invocation +with the local flag (i.e. ": -local foo").

+
+

All kind of method protections are applicable for all kind of methods, +either scripted or C-implemented.

+

The distinction between public and protected leads to interfaces for +classes and objects. Public methods are intended for consumers of +these entities. Public methods define the intended ways of providing +methods for external usages (usages, from other objects or +classes). Protected methods are intended for the implementor of the +class or subclasses and not for public usage. The distinction between +protected and public reduces the coupling between consumers and the +implementation, and offers more flexibility to the developer.

+
Listing 25: Protected Methods

+
+
+
nx::Class create Foo {
+
+  # Define a public method
+  :public method foo {} {
+    # ....
+    return [:helper]
+  }
+
+  # Define a protected method
+  :method helper {} {
+     return 1
+  }
+}
+
+# Create an instance of the class:
+Foo create f1
+
+# The invocation of the public method "foo" returns 1
+f1 foo
+
+# The invocation of the protected method "helper" raises an error:
+f1 helper
+

The example above uses :protected method helper …. We could have +used here as well :method helper …, since the default method +call-protection is already protected.

+

The method call-protection of private goes one step further and +helps to hide implementation details also for implementors of +subclasses. Private methods are a means for avoiding unanticipated name +clashes. Consider the following example:

+
Listing 26: Private Methods

+
+
+
nx::Class create Base {
+  :private method helper {a b} {expr {$a + $b}}
+  :public method foo     {a b} {: -local helper $a $b}
+}
+
+nx::Class create Sub -superclass Base {
+  :public method bar     {a b} {: -local helper $a $b}
+  :private method helper {a b} {expr {$a * $b}}
+  :create s1
+}
+
+s1 foo 3 4     ;# returns 7
+s1 bar 3 4     ;# returns 12
+s1 helper 3 4  ;# raises error: unable to dispatch method helper
+

The base class implements a public method foo using the helper +method named helper. The derived class implements a as well a public +method bar, which is also using a helper method named helper. When +an instance s1 is created from the derived class, the method foo +is invoked which uses in turn the private method of the base +class. Therefore, the invocation s1 foo 3 4 returns its sum. If +the local flag had not beed used in helper, s1 would +have tried to call the helper of Sub, which would be incorrect. For +all other purposes, the private methods are "invisible" in all +situations, e.g., when mixins are used, or within the next-path, etc.

+

By using the -local flag at the call site it is possible to invoke +only the local definition of the method. If we would call the method +without this flag, the resolution order would be the standard +resolution order, starting with filters, mixins, object methods +and the full intrinsic class hierarchy.

+

NX supports the modifier private for methods and properties. In all +cases private is an instrument to avoid unanticipated interactions +and means actually "accessible for methods defined on the same entity +(object or class)". The main usage for private is to improve +locality of the code e.g. for compositional operations.

+

In order to improve locality for properties, a private property +defines therefore internally a variable with a different name to avoid +unintended interactions. The variable should be accessed via the +private accessor, which can be invoked with the -local flag. In the +following example class D introduces a private property with the +same name as a property in the superclass.

+
Listing 27: Private Properties

+
+
+
#
+# Define a class C with a property "x" and a public accessor
+#
+nx::Class create C {
+  :property -accessor public {x c}
+}
+
+#
+# Define a subclass D with a private property "x"
+# and a method bar, which is capable of accessing
+# the private property.
+#
+nx::Class create D -superclass C {
+  :property -accessor private {x d}
+  :public method bar {p} {return [: -local $p get]}
+}
+
+#
+# The private and public (or protected) properties
+# define internally separate variable that do not
+# conflict.
+#
+D create d1
+puts [d1 x get]   ;# prints "c"
+puts [d1 bar x]   ;# prints "d"
+

Without the private definition of the property, the definition of +property x in class D would shadow the +definition of the property in the superclass C for its instances +(d1 x or set :x would return d instead of c).

+
+
+

3.4. Applicability of Methods

+

As defined above, a method is a subroutine defined on an object or +class. This object (or class) contains the method. If the object (or +class) is deleted, the contained methods will be deleted as well.

+
+

3.4.1. Instance Methods

+
+
+

Typically, methods are defined on a class, and the methods defined on the +class are applicable to the instances (direct or indirect) of this +class. These methods are called instance methods.

+
+

In the following example method, foo is an instance method defined +on class C.

+
Listing 28: Methods applicable for instances

+
+
+
nx::Class create C {
+  :public method foo {} {return 1}
+  :create c1
+}
+
+# Method "foo" is defined on class "C"
+# and applicable to the instances of "C"
+c1 foo
+

There are many programming languages that only allow these types of methods. +However, NX also allows methods to be defined on objects.

+
+
+

3.4.2. Object Methods

+
+
+

Methods defined on objects are object methods. Object +methods are only applicable on the object, on which they are defined. +Object methods cannot be inherited from other objects.

+
+

The following example defines an object method bar on the +instance c1 of class C, and as well as the object specific method +baz defined on the object o1. An object method is defined +via object method.

+

Note that we can define a object method that shadows (redefines) +for this object methods provided from classes.

+
Listing 29: Object Method

+
+
+
nx::Class create C {
+  :public method foo {} {return 1}
+  :create c1 {
+     :public object method foo {} {return 2}
+     :public object method bar {} {return 3}
+  }
+}
+
+# Method "bar" is an object specific method of "c1"
+c1 bar
+
+# object-specific method "foo" returns 2
+c1 foo
+
+# Method "baz" is an object specific method of "o1"
+nx::Object create o1 {
+  :public object method baz {} {return 4}
+}
+o1 baz
+
+
+

3.4.3. Class Methods

+
+
+

A class method is a method defined on a class, which is only +applicable to the class object itself. The class method is actually +an object method of the class object.

+
+

In NX, all classes are objects. Classes are in NX special kind of +objects that have e.g. the ability to create instances and to provide +methods for the instances. Classes manage their instances. The general +method set for classes is defined on the meta-classes (more about +this later).

+

The following example defines a public class method bar on class +C. The class method is specified by using the modifier object in +front of method in the method definition command.

+
Listing 30: Class Methods

+
+
+
nx::Class create C {
+  #
+  # Define a class method "bar" and an instance
+  # method "foo"
+  #
+  :public object method bar {} {return 2}
+  :public method foo {} {return 1}
+
+  #
+  # Create an instance of the current class
+  #
+  :create c1
+}
+
+# Method "bar" is a class method of class "C"
+# therefore applicable on the class object "C"
+C bar
+
+# Method "foo" is an instance method of "C"
+# therefore applicable on instance "c1"
+c1 foo
+
+# When trying to invoke the class method on the
+# instance, an error will be raised.
+c1 bar
+

In some other object oriented programming languages, class methods +are called "static methods".

+
+
+
+

3.5. Ensemble Methods

+

NX provides ensemble methods as a means to structure the method name +space and to group related methods. Ensemble methods are similar in +concept to Tcl’s ensemble commands.

+
+
+

An ensemble method is a form of a hierarchical method consisting of +a container method and sub-methods. The first argument of the +container method is interpreted as a selector (the sub-method). Every +sub-method can be an container method as well.

+
+

Ensemble methods provide a means to group related commands together, +and they are extensible in various ways. It is possible to add +sub-methods at any time to existing ensembles. Furthermore, it is +possible to extend ensemble methods via mixin classes.

+

The following example defines an ensemble method for string. An +ensemble method is defined when the provide method name contains a +space.

+
Listing 31: Ensemble Method

+
+
+
nx::Class create C {
+
+    # Define an ensemble method "string" with sub-methods
+    # "length", "tolower" and "info"
+
+    :public method "string length"  {x} {....}
+    :public method "string tolower" {x} {...}
+    :public method "string info" {x} {...}
+    #...
+    :create c1
+}
+
+# Invoke the ensemble method
+c1 string length "hello world"
+
+
+

3.6. Method Resolution

+

When a method is invoked, the applicable method is searched in the +following order:

+Per-object Mixins -> Per-class Mixins -> Object -> Intrinsic Class Hierarchy +

In the case, no mixins are involved, first the object is searched for +an object method with the given name, and then the class hierarchy +of the object. The method can be defined multiple times on the search +path, so some of these method definitions might be shadowed by the +more specific definitions.

+
Listing 32: Method Resolution with Intrinsic Classes

+
+
+
nx::Class create C {
+  :public method foo {} {
+    return "C foo: [next]"
+  }
+}
+
+nx::Class create D -superclass C {
+
+  :public method foo {} {
+    return "D foo: [next]"
+  }
+
+   :create d1 {
+     :public object method foo {} {
+       return "d1 foo: [next]"
+     }
+   }
+}
+
+# Invoke the method foo
+d1 foo
+# result: "d1 foo: D foo: C foo: "
+
+# Query the precedence order from NX via introspection
+d1 info precedence
+# result: "::D ::C ::nx::Object"
+

Consider the example in +Listing 32. When the method +foo is invoked on object d1, the object method has the highest +precedence and is therefore invoked. The object methods shadows +the same-named methods in the class hierarchy, namely the method foo +of class D and the method foo of class C. The shadowed methods +can be still invoked, either via the primitive next or via method +handles (we used already method handles in the section about method +aliases). In the example above, next calls the shadowed method and +add their results to the results of every method. So, the final result +contains parts from d1, D and C. Note, that the topmost next +in method foo of class C shadows no method foo and simply +returns empty (and not an error message).

+

The introspection method info precedence provides information about +the order, in which classes processed during method resolution.

+
Listing 33: Method Resolution with Mixin Classes

+
+
+
nx::Class create M1 {
+  :public method foo {} { return "M1 foo: [next]"}
+}
+nx::Class create M2 {
+  :public method foo {} { return "M2 foo: [next]"}
+}
+
+#
+# "d1" is created based on the definitions of the last example
+#
+# Add the methods from "M1" as per-object mixin to "d1"
+d1 object mixins add M1
+
+#
+# Add the methods from "M2" as per-class mixin to class "C"
+C mixins add M2
+
+# Invoke the method foo
+d1 foo
+# result: "M1 foo: M2 foo: d1 foo: D foo: C foo: "
+
+# Query the precedence order from NX via introspection
+d1 info precedence
+# result: "::M1 ::M2 ::D ::C ::nx::Object"
+

The example in Listing 33 is +an extension of the previous example. We define here two additional +classes M1 and M2 which are used as per-object and per-class +mixins. Both classes define the method foo, these methods shadow +the definitions of the intrinsic class hierarchy. Therefore an +invocation of foo on object d1 causes first an invocation of +method in the per-object mixin.

+
Listing 34: Method Invocation Flags

+
+
+
#
+# "d1" is created based on the definitions of the last two examples,
+# the mixins "M1" and "M2" are registered.
+#
+# Define a public object method "bar", which calls the method
+# "foo" which various invocation options:
+#
+d1 public object method bar {} {
+   puts [:foo]
+   puts [: -local foo]
+   puts [: -intrinsic foo]
+   puts [: -system foo]
+}
+
+# Invoke the method "bar"
+d1 bar
+

In the first line of the body of method bar, the method foo is +called as usual with an implicit receiver, which defaults to the +current object (therefore, the call is equivalent to d1 foo). The +next three calls show how to provide flags that influence the method +resolution. The flags can be provided between the colon and the method +name. These flags are used rather seldom but can be helpful in some +situations.

+

The invocation flag -local means that the method has to be resolved +from the same place, where the current method is defined. Since the +current method is defined as a object method, foo is resolved as +a object method. The effect is that the mixin definitions are +ignored. The invocation flag -local was already introduced int the +section about method protection, where it was used to call private +methods.

+

The invocation flag -intrinsic means that the method has to be resolved +from the intrinsic definitions, meaning simply without mixins. The +effect is here the same as with the invocation flag -local.

+

The invocation flag -system means that the method has to be resolved +from basic - typically predefined - classes of the object system. This +can be useful, when script overloads system methods, but still want to +call the shadowed methods from the base classes. In our case, we have +no definitions of foo on the base clases, therefore an error message +is returned.

+

The output of Listing 34 is:

+
+
+
   M1 foo: M2 foo: d1 foo: D foo: C foo:
+   d1 foo: D foo: C foo:
+   d1 foo: D foo: C foo:
+   ::d1: unable to dispatch method 'foo'
+
+
+
+

3.7. Parameters

+

NX provides a generalized mechanism for passing values to either +methods (we refer to these as method parameters) or to objects +(these are called configure parameters). Both kind of parameters +might have different features, such as:

+
    +
  • +

    +Positional and non-positional parameters +

    +
  • +
  • +

    +Required and non-required parameters +

    +
  • +
  • +

    +Default values for parameters +

    +
  • +
  • +

    +Value-checking for parameters +

    +
  • +
  • +

    +Multiplicity of parameters +

    +
  • +
+

TODO: complete list above and provide a short summary of the section

+

Before we discuss method and configure parameters in more detail, we +describe the parameter features in the subsequent sections based on +method parameters.

+
+

3.7.1. Positional and Non-Positional Parameters

+

If the position of a parameter in the list of formal arguments +(e.g. passed to a function) is significant for its meaning, this is a +positional parameter. If the meaning of the parameter is independent +of its position, this is a non-positional parameter. When we call a +method with positional parameters, the meaning of the parameters (the +association with the argument in the argument list of the method) is +determined by its position. When we call a method with non-positional +parameters, their meaning is determined via a name passed with the +argument during invocation.

+
Listing 35: Positional and Non-Positional Method Parameters

+
+
+
nx::Object create o1 {
+
+  #
+  # Method foo has positional parameters:
+  #
+  :public object method foo {x y} {
+    puts "x=$x y=$y"
+  }
+
+  #
+  # Method bar has non-positional parameters:
+  #
+  :public object method bar {-x -y} {
+    puts "x=$x y=$y"
+  }
+
+  #
+  # Method baz has non-positional and
+  # positional parameters:
+  #
+  :public object method baz {-x -y a} {
+    puts "x? [info exists x] y? [info exists y] a=$a"
+  }
+}
+
+# invoke foo (positional parameters)
+o1 foo 1 2
+
+# invoke bar (non-positional parameters)
+o1 bar -y 3 -x 1
+o1 bar -x 1 -y 3
+
+# invoke baz (positional and non-positional parameters)
+o1 baz -x 1 100
+o1 baz 200
+o1 baz -- -y
+

Consider the example in Listing 35. The method +foo has the argument list x y. This means that the first argument +is passed in an invocation like o1 foo 1 2 to x (here, the value +1), and the second argument is passed to y (here the value 2). +Method bar has in contrary just with non-positional arguments. Here +we pass the names of the parameter together with the values. In the +invocation o1 bar -y 3 -x 1 the names of the parameters are prefixed +with a dash ("-"). No matter whether in which order we write the +non-positional parameters in the invocation (see line 30 and 31 in +Listing 35) in both cases the variables x +and y in the body of the method bar get the same values assigned +(x becomes 1, y becomes 3).

+

It is certainly possible to combine positional and non-positional +arguments. Method baz provides two non-positional parameter (-y +and -y) and one positional parameter (namely a). The invocation in +line 34 passes the value of 1 to x and the value of 100 to a. +There is no value passed to y, therefore value of y will be +undefined in the body of baz, info exists y checks for the +existence of the variable y and returns 0.

+

The invocation in line 35 passes only a value to the positional +parameter. A more tricky case is in line 36, where we want to pass +-y as a value to the positional parameter a. The case is more +tricky since syntactically the argument parser might consider -y as +the name of one of the non-positional parameter. Therefore we use -- +(double dash) to indicate the end of the block of the non-positional +parameters and therefore the value of -y is passed to a.

+
+
+

3.7.2. Optional and Required Parameters

+

Per default positional parameters are required, and non-positional +parameters are optional (they can be left out). By using parameter +options, we can as well define positional parameters, which are +optional, and non-positional parameters, which are required.

+
Listing 36: Optional and Required Method Parameters

+
+
+
nx::Object create o2 {
+
+  #
+  # Method foo has one required and one optional
+  # positional parameter:
+  #
+  :public object method foo {x:required y:optional} {
+    puts "x=$x y? [info exists y]"
+  }
+
+  #
+  # Method bar has one required and one optional
+  # non-positional parameter:
+  #
+  :public object method bar {-x:required -y:optional} {
+    puts "x=$x y? [info exists y]"
+  }
+}
+
+# invoke foo (one optional positional parameter is missing)
+o2 foo 1
+

The example in Listing 36 defined method foo +with one required and one optional positional parameter. For this +purpose we use the parameter options required and optional. The +parameter options are separated from the parameter name by a colon. If +there are multiple parameter options, these are separated by commas +(we show this in later examples).

+

The parameter definition x:required for method foo is equivalent +to x without any parameter options (see e.g. previous example), +since positional parameters are per default required. The invocation +in line 21 of Listing 36 will lead to an +undefined variable y in method foo, because no value us passed to +the optional parameter. Note that only trailing positional parameters might be +optional. If we would call method foo of Listing 35 with only one argument, the system would raise an +exception.

+

Similarly, we define method bar in Listing 36 with one required and one optional non-positional +parameter. The parameter definition -y:optional is equivalent to +-y, since non-positional parameter are per default optional. +However, the non-positional parameter -x:required is required. If we +invoke bar without it, the system will raise an exception.

+
+
+

3.7.3. Default Values for Parameters

+

Optional parameters might have a default value, which will be used, +when not value is provided for this parameter. Default values can be +specified for positional and non-positional parameters.

+
Listing 37: Method Parameters with Default Values

+
+
+
nx::Object create o3 {
+
+  #
+  # Positional parameter with default value:
+  #
+  :public object method foo {{x 1} {y 2}} {
+    puts "x=$x y=$y"
+  }
+
+  #
+  # Non-positional parameter with default value:
+  #
+  :public object method bar {{-x 10} {-y 20}} {
+    puts "x=$x y=$y"
+  }
+}
+
+# use default values
+o3 foo
+o3 bar
+

In order to define a default value for a parameter, the parameter +specification must be of the form of a 2 element list, where the +second argument is the default value. See for an example in +Listing 37.

+
+
+

3.7.4. Value Constraints

+

NX provides value constraints for all kind of parameters. By +specifying value constraints a developer can restrict the permissible +values for a parameter and document the expected values in the source +code. Value checking in NX is conditional, it can be turned on or off +in general or on a per-usage level (more about this later). The same +mechanisms can be used not only for input value checking, but as well +for return value checking (we will address this point as well later).

+
+
Built-in Value Constraints
+

NX comes with a set of built-in value constraints, which can be +extended on the scripting level. The built-in checkers are either the +native checkers provided directly by the Next Scripting Framework (the +most efficient checkers) or the value checkers provided by Tcl through +string is …. The built-in checkers have as well the advantage that +they can be used also at any time during bootstrap of an object +system, at a time, when e.g. no objects or methods are defined. The +same checkers are used as well for all C-implemented primitives of NX +and the Next Scripting Framework.

+
+
+value-checkers.png +
+
Figure 38. General Applicable Value Checkers in NX
+
+

+

Figure 38 shows the built-in +general applicable value checkers available in NX, which can be used +for all method and configure parameters. In the next step, we show how to +use these value-checkers for checking permissible values for method +parameters. Then we will show, how to provide more detailed value +constraints.

+
Listing 39: Method Parameters with Value Constraints

+
+
+
nx::Object create o4 {
+
+  #
+  # Positional parameter with value constraints:
+  #
+  :public object method foo {x:integer o:object,optional} {
+    puts "x=$x o? [info exists o]"
+  }
+
+  #
+  # Non-positional parameter with value constraints:
+  #
+  :public object method bar {{-x:integer 10} {-verbose:boolean false}} {
+    puts "x=$x verbose=$verbose"
+  }
+}
+
+# The following invocation raises an exception, since the
+# value "a" for parameter "x" is not an integer
+o4 foo a
+

Value contraints are specified as parameter options in the parameter +specifications. The parameter specification x:integer defines x as +a required positional parmeter which value is constraint to an +integer. The parameter specification o:object,optional shows how to +combine multiple parameter options. The parameter o is an optional +positional parameter, its value must be an object (see +Listing 39). Value constraints are +specified exactly the same way for non-positional parameters (see +method bar in Listing 39).

+
Listing 40: Parameterized Value Constraints

+
+
+
#
+# Create classes for Person and Project
+#
+nx::Class create Person
+nx::Class create Project
+
+nx::Object create o5 {
+  #
+  # Parameterized value constraints
+  #
+  :public object method work {
+     -person:object,type=Person
+     -project:object,type=Project
+   } {
+    # ...
+  }
+}
+
+#
+# Create a Person and a Project instance
+#
+Person create gustaf
+Project create nx
+
+#
+# Use method with value constraints
+#
+o5 work -person gustaf -project nx
+

The native checkers object, class, metaclass and baseclass can +be further specialized with the parameter option type to restrict +the permissible values to instances of certain classes. We can use for +example the native value constraint object either for testing +whether an argument is some object (without further constraints, as in +Listing 37, method foo), or we can +constrain the value further to some type (direct or indirect instance +of a class). This is shown by method work in +Listing 40 which requires +the parameter -person to be an instance of class Person and the +parameter -project to be an instance of class Project.

+
+
+
Scripted Value Constraints
+

The set of predefined value checkers can be extended by application +programs via defining methods following certain conventions. The user +defined value checkers are defined as methods of the class nx::Slot +or of one of its subclasses or instances. We will address such cases +in the next sections. In the following example we define two new +value checkers on class nx::Slot. The first value checker is called +groupsize, the second one is called choice.

+
Listing 41: Scripted Value Checker for Method Parameters

+
+
+
#
+# Value checker named "groupsize"
+#
+::nx::Slot method type=groupsize {name value} {
+  if {$value < 1 || $value > 6} {
+    error "Value '$value' of parameter $name is not between 1 and 6"
+  }
+}
+
+#
+# Value checker named "choice" with extra argument
+#
+::nx::Slot method type=choice {name value arg} {
+  if {$value ni [split $arg |]} {
+    error "Value '$value' of parameter $name not in permissible values $arg"
+  }
+}
+
+#
+# Create an application class D
+# using the new value checkers
+#
+nx::Class create D {
+  :public method foo {a:groupsize} {
+    # ...
+  }
+  :public method bar {a:choice,arg=red|yellow|green b:choice,arg=good|bad} {
+    # ...
+  }
+}
+
+D create d1
+
+# testing "groupsize";
+# the second call (with value 10) will raise an exception:
+d1 foo 2
+d1 foo 10
+
+# testing "choice"
+# the second call (with value pink for parameter a)
+# will raise an exception:
+d1 bar green good
+d1 bar pink bad
+

In order to define a checker groupsize a method of the name +type=groupsize is defined. This method receives two arguments, +name and value. The first argument is the name of the parameter +(mostly used for the error message) and the second parameter is +provided value. The value checker simply tests whether the provided +value is between 1 and 3 and raises an exception if this is not the +case (invocation in line 36 in Listing 41).

+

The checker groupsize has the permissible values defined in its +method’s body. It is as well possible to define more generic checkers +that can be parameterized. For this parameterization, one can pass an +argument to the checker method (last argument). The checker choice +can be used for restricting the values to a set of predefined +constants. This set is defined in the parameter specification. The +parameter a of method bar in Listing 41 +is restricted to the values red, yellow or green, and the +parameter b is restricted to good or bad. Note that the syntax +of the permissible values is solely defined by the definition of the +value checker in lines 13 to 17. The invocation in line 39 will be ok, +the invocation in line 40 will raise an exception, since pink is not +allowed.

+

If the same checks are used in many places in the program, +defining names for the value checker will be the better choice since +it improves maintainability. For seldom used kind of checks, the +parameterized value checkers might be more convenient.

+
+
+
+

3.7.5. Multiplicity

+
+
+

Multiplicity is used to define whether a parameter should receive +single or multiple values.

+
+

A multiplicity specification has a lower and an upper bound. A lower +bound of 0 means that the value might be empty. A lower bound of 1 +means that the parameter needs at least one value. The upper bound +might be 1 or n (or synonymously *). While the upper bound of +1 states that at most one value has to be passed, the upper bound of +n says that multiple values are permitted. Other kinds of +multiplicity are currently not allowed.

+

The multiplicity is written as parameter option in the parameter +specification in the form lower-bound..upper-bound. If no +multiplicity is defined the default multiplicity is 1..1, which +means: provide exactly one (atomic) value (this was the case in the +previous examples).

+
Listing 42: Method Parameters with Explicit Multiplicity

+
+
+
nx::Object create o6 {
+
+  #
+  # Positional parameter with an possibly empty
+  # single value
+  #
+  :public object method foo {x:integer,0..1} {
+    puts "x=$x"
+  }
+
+  #
+  # Positional parameter with an possibly empty
+  # list of values value
+  #
+  :public object method bar {x:integer,0..n} {
+    puts "x=$x"
+  }
+
+  #
+  # Positional parameter with a non-empty
+  # list of values
+  #
+  :public object method baz {x:integer,1..n} {
+    puts "x=$x"
+  }
+}
+

Listing 42 contains three examples for +positional parameters with different multiplicities. Multiplicity is +often combined with value constraints. A parameter specification of +the form x:integer,0..n means that the parameter x receives a list +of integers, which might be empty. Note that the value constraints are +applied to every single element of the list.

+

The parameter specification x:integer,0..1 means that x might be +an integer or it might be empty. This is one style of specifying that +no explicit value is passed for a certain parameter. Another style is +to use required or optional parameters. NX does not enforce any +particular style for handling unspecified values.

+

All the examples in Listing 42 are for +single positional parameters. Certainly, multiplicity is fully +orthogonal with the other parameter features and can be used as well +for multiple parameters, non-positional parameter, default values, +etc.

+
+
+
+
+
+

4. Advanced Language Features

+
+

+
+

4.1. Objects, Classes and Meta-Classes

+

+
+
+

4.2. Resolution Order and Next-Path

+

+
+
+

4.3. Details on Method and Configure Parameters

+

The parameter specifications are used in NX for the following +purposes. They are used for

+
    +
  • +

    +the specification of input arguments of methods and commands, for +

    +
  • +
  • +

    +the specification of return values of methods and commands, and for +

    +
  • +
  • +

    +the specification for the initialization of objects. +

    +
  • +
+

We refer to the first two as method parameters and the last one as +configure parameters. The examples in the previous sections all parameter +specification were specifications of method parameters.

+
+
+

Method parameters specify properties about permissible values passed +to methods.

+
+

The method parameter specify how methods are invoked, how the +actual arguments are passed to local variables of the invoked method +and what kind of checks should be performed on these.

+
+
+

Configure parameters are parameters that specify, how objects +can be parameterized upon creation.

+
+

Syntactically, configure parameters and method parameters are the same, +although there are certain differences (e.g. some parameter options +are only applicable for objects parameters, the list of object +parameters is computed dynamically from the class structures, object +parameters are often used in combination with special setter methods, +etc.). Consider the following example, where we define the two +application classes Person and Student with a few properties.

+
Listing 43: Configure Parameters

+
+
+
#
+# Define a class Person with properties "name"
+# and "birthday"
+#
+nx::Class create Person {
+  :property name:required
+  :property birthday
+}
+
+#
+# Define a class Student as specialization of Person
+# with and additional property
+#
+nx::Class create Student -superclass Person {
+  :property matnr:required
+  :property {oncampus:boolean true}
+}
+
+#
+# Create instances using configure parameters
+# for the initialization
+#
+Person create p1 -name Bob
+Student create s1 -name Susan -matnr 4711
+
+# Access property value via "cget" method
+puts "The name of s1 is [s1 cget -name]"
+

The class Person has two properties name and birthday, where the +property name is required, the property birthday is not. The +class Student is a subclass of Person with the additional required +property matnr and an optional property oncampus with the +default value true (see Listing 43). The class diagram below visualizes these +definitions.

+
+
+configure-parameter.png +
+
Figure 44. System and Application Classes
+
+

+

In NX, these definitions imply that instances of the class of Person +have the properties name and birthday as non-positional object +parameters. Furthermore it implies that instances of Student will +have the configure parameters of Person augmented with the object +parameters from Student (namely matnr and oncampus). Based on +these configure parameters, we can create a Person named Bob and a +Student named Susan with the matriculation number 4711 (see line +23 and 24 in <<xmp-object-parameters, +instance variables name, matnr and oncampus (the latter is +initialized with the default value).

+
+

4.3.1. Configure Parameters available for all NX Objects

+

The configure parameters are not limited to the application defined +properties, also NX provides some predefined definitions. Since +Person is a subclass of nx::Object also the configure parameters of +nx::Object are inherited. In the introductory stack example, we used +-mixins applied to an object to denote per-object mixins (see +Listing 8). Since mixins +is defined as a parameter on nx::Object it can be used as an object +parameter -mixins for all objects in NX. To put it in other words, +every object can be configured to have per-object mixins. If we would +remove this definition, this feature would be removed as well.

+

As shown in the introductory examples, every object can be configured +via a scripted initialization block (the optional scripted block +specified at object creation as last argument; see +Listing 5 or +Listing 12). The +scripted block and its meaning are as well defined by the means of +configure parameters. However, this configure parameter is positional (last +argument) and optional (it can be omitted). The following listing shows +the configure parameters of Person p1 and Student s1.

+
Listing 45: Computed Actual Configure Parameter

+
+
+
Configure parameters for Person p1:
+   Command:
+      p1 info lookup syntax configure
+   Result:
+      -name /value/ ?-birthday /value/? ?-object-mixins /mixinreg .../?
+      ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/?
+
+Configure parameter for Student s1:
+   Command:
+      s1 info lookup syntax configure
+   Result:
+      ?-oncampus /boolean/? -matnr /value/ -name /value/
+      ?-birthday /value/? ?-object-mixins /mixinreg .../? ?-class /class/?
+      ?-object-filters /filterreg .../? ?/__initblock/?
+

The given parameter show, how (a) objects can be configured +at runtime or (b) how new instances can be configured +at creation time via the new or create methods. +Introspection can be used to obtain the configuration +parameters from an object via +p1 info lookup parameters configure +(returning the configure parameters currently applicable for +configure or cget) or from a class +Person info lookup parameters create on a class +(returning the configure parameters applicable when an object +of this class is created)

+

The listed configure parameter types mixinreg and +filterreg are for converting definitions of filters and mixins. The +last value __initblock says that the content of this variable +will be executed in the context of the object being created (before +the constructor init is called). More about the configure parameter +types later.

+
+
+

4.3.2. Configure Parameters available for all NX Classes

+

Since classes are certain kind of objects, classes are parameterized +in the same way as objects. A typical parameter for a class definition +is the relation of the class to its superclass.In our example, we have +specified, that Student has Person as superclass via the +non-positional configure parameter -superclass. If no superclass is +specified for a class, the default superclass is +nx::Object. Therefore nx::Object is the default value for the +parameter superclass.

+

Another frequently used parameter for classes is -mixins to denote +per-class mixins (see e.g. the introductory Stack example in +Listing 10), which is defined in +the same way.

+

Since Student is an instance of the meta-class nx::Class it +inherits the configure parameters from nx::Class (see class diagram +Figure 44). +Therefore, one can use e.g. -superclass in the definition of classes.

+

Since nx::Class is a subclass of nx::Object, the meta-class +nx::Class inherits the parameter definitions from the most general +class nx::Object. Therefore, every class might as well be configured +with a scripted initialization block the same way as objects can be +configured. We used actually this scripted initialization block in +most examples for defining the methods of the class. The following +listing shows (simplified) the parameters applicable for Class +Student.

+
Listing 46: Parameters for Classes

+
+
+
Configure parameter for class nx::Class
+   Command:
+      nx::Class info lookup syntax configure
+   Result:
+      ?-superclass /class .../? ?-mixins /mixinreg .../?
+      ?-filters /filterreg .../? ?-object-mixins /mixinreg .../?
+      ?-class /class/? ?-object-filters /filterreg .../? ?/__initblock/?
+
+
+

4.3.3. User defined Parameter Types

+

More detailed definition of the configure parameter types comes here.

+
+
+

4.3.4. Slot Classes and Slot Objects

+

In one of the previous sections, we defined scripted (application +defined) checker methods on a class named nx::Slot. In general NX +offers the possibility to define value checkers not only for all +usages of parameters but as well differently for method parameters or +configure parameters

+
+
+slots.png +
+
Figure 47. Slot Classes and Objects
+
+

+
+
+

4.3.5. Attribute Slots

+

Still Missing

+
    +
  • +

    +return value checking +

    +
  • +
  • +

    +switch +

    +
  • +
  • +

    +initcmd … +

    +
  • +
  • +

    +subst rules +

    +
  • +
  • +

    +converter +

    +
  • +
  • +

    +incremental slots +

    +
  • +
+
+
+
+
+
+

5. Miscellaneous

+
+

+
+

5.1. Profiling

+

+
+
+

5.2. Unknown Handlers

+

NX provides two kinds of unknown handlers:

+
    +
  • +

    +Unknown handlers for methods +

    +
  • +
  • +

    +Unknown handlers for objects and classes +

    +
  • +
+
+

5.2.1. Unknown Handlers for Methods

+

Object and classes might be equipped +with a method unknown which is called in cases, where an unknown +method is called. The method unknown receives as first argument the +called method followed by the provided arguments

+
Listing 48: Unknown Method Handler

+
+
+
::nx::Object create o {
+  :object method unknown {called_method args} {
+    puts "Unknown method '$called_method' called"
+  }
+}
+
+# Invoke an unknown method for object o:
+o foo 1 2 3
+
+# Output will be: "Unknown method 'foo' called"
+

Without any provision of an unknown method handler, an error will be +raised, when an unknown method is called.

+
+
+

5.2.2. Unknown Handlers for Objects and Classes

+

The next scripting framework provides in addition to unknown method +handlers also a means to dynamically create objects and classes, when +these are referenced. This happens e.g. when superclasses, mixins, or +parent objects are referenced. This mechanism can be used to implement +e.g. lazy loading of these classes. Nsf allows to register multiple +unknown handlers, each identified by a key (a unique name, different +from the keys of other unknown handlers).

+
Listing 49: Unknown Class Handler

+
+
+
::nx::Class public object method __unknown {name} {
+  # A very simple unknown handler, showing just how
+  # the mechanism works.
+  puts "***** __unknown called with <$name>"
+  ::nx::Class create $name
+}
+
+# Register an unknown handler as a method of ::nx::Class
+::nsf::object::unknown::add nx {::nx::Class __unknown}
+
+::nx::Object create o {
+  # The class M is unknown at this point
+
+  :object mixins add M
+  # The line above has triggered the unknown class handler,
+  # class M is now defined
+
+  puts [:info object mixins]
+  # The output will be:
+  #     ***** __unknown called with <::M>
+  #     ::M
+}
+

The Next Scripting Framework allows to add, query, delete and list unknown handlers.

+
Listing 50: Unknown Handler registration

+
+
+
# Interface for unknown handlers:
+# nsf::object::unknown::add /key/ /handler/
+# nsf::object::unknown::get /key/
+# nsf::object::unknown::delete /key/
+# nsf::object::unknown::keys
+
References
    +
  • +

    + U. Zdun, M. Strembeck, G. Neumann: + Object-Based and Class-Based Composition of Transitive Mixins, + Information and Software Technology, 49(8) 2007 . +

    +
  • +
  • +

    + G. Neumann and U. Zdun: Filters as a + language support for design patterns in object-oriented scripting + languages. In Proceedings of COOTS’99, 5th Conference on + Object-Oriented Technologies and Systems, San Diego, May 1999. +

    +
  • +
  • +

    + G. Neumann and U. Zdun: Implementing + object-specific design patterns using per-object mixins. In Proc. of + NOSA`99, Second Nordic Workshop on Software Architecture, Ronneby, + Sweden, August 1999. +

    +
  • +
  • +

    + G. Neumann and U. Zdun: Enhancing + object-based system composition through per-object mixins. In + Proceedings of Asia-Pacific Software Engineering Conference (APSEC), + Takamatsu, Japan, December 1999. +

    +
  • +
  • +

    + G. Neumann and U. Zdun: XOTCL, an + object-oriented scripting language. In Proceedings of Tcl2k: The + 7th USENIX Tcl/Tk Conference, Austin, Texas, February 2000. +

    +
  • +
  • +

    + G. Neumann and U. Zdun: Towards the Usage + of Dynamic Object Aggregations as a Form of Composition In: + Proceedings of Symposium of Applied Computing (SAC’00), Como, + Italy, Mar 19-21, 2000. +

    +
  • +
  • +

    + G. Neumann, S. Sobernig: XOTcl 2.0 - A + Ten-Year Retrospective and Outlook, in: Proceedings of the Sixteenth + Annual Tcl/Tk Conference, Portland, Oregon, October, 2009. +

    +
  • +
  • +

    + J. K. Ousterhout: Tcl: An embeddable command + language. In Proc. of the 1990 Winter USENIX Conference, January 1990. +

    +
  • +
  • +

    + J. K. Ousterhout: Scripting: Higher Level + Programming for the 21st Century, IEEE Computer 31(3), March 1998. +

    +
  • +
  • +

    + D. Wetherall and C. J. Lindblad: Extending Tcl for + Dynamic Object-Oriented Programming. Proc. of the Tcl/Tk Workshop '95, + July 1995. +

    +
  • +
+
+
+
+
+
+

+ + + Index: doc/next-tutorial/next-tutorial.txt =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/next-tutorial/next-tutorial.txt (.../next-tutorial.txt) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/next-tutorial/next-tutorial.txt (.../next-tutorial.txt) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -2189,7 +2189,7 @@ ?-object-filters /filterreg .../? ?/__initblock/? -------------------------------------------------- -The given paramter show, how (a) objects can be configured +The given parameter show, how (a) objects can be configured at runtime or (b) how new instances can be configured at creation time via the +new+ or +create+ methods. Introspection can be used to obtain the configuration Index: doc/tutorial2.html =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- doc/tutorial2.html (.../tutorial2.html) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ doc/tutorial2.html (.../tutorial2.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -4228,12 +4228,12 @@ the specified command (callee) is take at invocation time (should only be done for (builtin) commands inplemented in C), and -default provides a means for providing default methods when -none are specifed.

+none are specified.

Each of the arguments after the method name (including callee) can be be substituted an invocation time, or they are taken literally. The arguments to be substituted are starting always -with a percent sign. These arguemnts can be %self, +with a percent sign. These arguments can be %self, %proc, %1, %argclindex, or % followed by a Tcl command, and it can be prefixed with a positional prefix %@. We will introduce the usage of these options and Index: dtrace/README =================================================================== diff -u -rbb1e3cc1ea09b7ec55fd421570bee9fa48591e70 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- dtrace/README (.../README) (revision bb1e3cc1ea09b7ec55fd421570bee9fa48591e70) +++ dtrace/README (.../README) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -26,7 +26,7 @@ to the begin of the nxsh script, or add the path to the dtrace invocation (see below) -* If dtrace compliation fails (e.g. "... nsf*:::method-entry does not +* If dtrace compilation fails (e.g. "... nsf*:::method-entry does not match any probes"), start an nxsh in a different window to make the nsf provider and the probes known to the kernel. Index: generic/asm/threaded.c =================================================================== diff -u -r7a5c74031f47cee0058b75eea5131706a050617e -rc4f449cb353be812ba6502ef8e9587e87881f59b --- generic/asm/threaded.c (.../threaded.c) (revision 7a5c74031f47cee0058b75eea5131706a050617e) +++ generic/asm/threaded.c (.../threaded.c) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -25,7 +25,7 @@ * would be interesting to define the instructions in some * e.g. scripting language and to produce different * implementations from the same source to address the - * protability issue. + * portability issue. * * Most probably, one needs a larger program-code with more * instructions to provide more meaningful results. Index: generic/nsfPointer.c =================================================================== diff -u -r8e079b542dd68c7975117266efba2b4701909ce9 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- generic/nsfPointer.c (.../nsfPointer.c) (revision 8e079b542dd68c7975117266efba2b4701909ce9) +++ generic/nsfPointer.c (.../nsfPointer.c) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -301,7 +301,7 @@ *---------------------------------------------------------------------- * Nsf_PointerTypeLookup -- * - * Lookup of type name. If the type name is registed, return the + * Lookup of type name. If the type name is registered, return the * converter or NULL otherwise. * * Results: Index: library/lib/make.tcl =================================================================== diff -u -r09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/lib/make.tcl (.../make.tcl) (revision 09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae) +++ library/lib/make.tcl (.../make.tcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -201,7 +201,7 @@ if {[catch {:create main} errorMsg]} { puts stderr "*** $errorMsg" - # Exit sliently, alltough we are leaving from an active stack + # Exit silently, alltough we are leaving from an active stack # frame. ::nsf::configure debug 0 exit -1 Index: library/lib/nx-pp.tcl =================================================================== diff -u -rac12ae106c22725367fef28c79ceec8900b366a6 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/lib/nx-pp.tcl (.../nx-pp.tcl) (revision ac12ae106c22725367fef28c79ceec8900b366a6) +++ library/lib/nx-pp.tcl (.../nx-pp.tcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -10,7 +10,7 @@ # package require nx::pp # set html [nx::pp render { your script }] # -# Desinged to be usable from asciidoc like gnu source-highligt, +# Designed to be usable from asciidoc like gnu source-highligt, # ignores options. # # Gustaf Neumann, Dez 2010 Index: library/mongodb/tests/nsf-gridfs.test =================================================================== diff -u -r65669db80721267f5715c0990b626dabfc6c9cd1 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/mongodb/tests/nsf-gridfs.test (.../nsf-gridfs.test) (revision 65669db80721267f5715c0990b626dabfc6c9cd1) +++ library/mongodb/tests/nsf-gridfs.test (.../nsf-gridfs.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -28,7 +28,7 @@ ? {set mongoConn [::mongo::connect]} mongoc_client_t:0 # -# Open a GridFS in the mongo datbase "myfs" and use the usual prefix +# Open a GridFS in the mongo database "myfs" and use the usual prefix # "fs", such GridFS names the collections "fs.chunks" and "fs.files". # ? {set gridFS [::mongo::gridfs::open $mongoConn myfs fs]} mongoc_gridfs_t:0 Index: library/mongodb/tests/nx-mongo.test =================================================================== diff -u -ra6b73475646ce8805e0a7ed54b27ecacca9d5cbf -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/mongodb/tests/nx-mongo.test (.../nx-mongo.test) (revision a6b73475646ce8805e0a7ed54b27ecacca9d5cbf) +++ library/mongodb/tests/nx-mongo.test (.../nx-mongo.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -54,7 +54,7 @@ # # The insert operation of above can be achieved with less typing via -# the conveniance method "insert": +# the convenience method "insert": # ? { nx::mongo::db is_oid [Person insert -name Stefan -projects {nsf nx}]} 1 ? { nx::mongo::db is_oid [Person insert -name Joe -projects abc -age 23]} 1 Index: library/mongodb/tests/nx-reference-many.test =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/mongodb/tests/nx-reference-many.test (.../nx-reference-many.test) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ library/mongodb/tests/nx-reference-many.test (.../nx-reference-many.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -36,7 +36,7 @@ ###################################################################### # The second approach to implement references to other objects via an # property pointing to the object ids of other objects. This is -# similar to the classical datbase approach. When the object is +# similar to the classical database approach. When the object is # fetched, the application developer has to care about # fetching/dereferencing the referenced objects. # Index: library/mongodb/tests/nx-reference-one.test =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/mongodb/tests/nx-reference-one.test (.../nx-reference-one.test) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ library/mongodb/tests/nx-reference-one.test (.../nx-reference-one.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -39,7 +39,7 @@ ###################################################################### # The second approach to implement references to other objects via an # property pointing to the object id of an other object. This is the -# classical datbase approach. When the object is fetched, the +# classical database approach. When the object is fetched, the # application developer has to care about fetching/dereferencing the # referenced object. # Index: library/nx/nx.tcl =================================================================== diff -u -r03bde7ee87099accfec6d374b78df464d596d768 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/nx/nx.tcl (.../nx.tcl) (revision 03bde7ee87099accfec6d374b78df464d596d768) +++ library/nx/nx.tcl (.../nx.tcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -637,7 +637,7 @@ return -code error \ "[self]: object does not have an instance variable '$name'" } - # call explicitly the per-object variant of "info::slotobejcts" + # call explicitly the per-object variant of "info::slotobjects" set slot [: ::nsf::methods::object::info::slotobjects -type ::nx::Slot $name] if {$slot ne ""} { @@ -1860,7 +1860,7 @@ ::nx::VariableSlot protected method setterRedefinedOptions {} { # # In the :trace = "set" case, the slot will be set via the trace - # triggered from the direct assingment. Otherwise, when the + # triggered from the direct assignment. Otherwise, when the # "value=set" method is provided, tell nsf ot call it (e.g. in # configure). # @@ -1987,7 +1987,7 @@ } ::nx::VariableSlot public method parameter {} { - # This is a shortend "lightweight" version of "getParameterSpec" + # This is a shortened "lightweight" version of "getParameterSpec" # returning less (implicit) details. used e.g. by "info variable parameter" set options [:getParameterOptions -withMultiplicity true] set spec [:namedParameterSpec -map-private "" ${:name} $options] Index: library/serialize/serializer.tcl =================================================================== diff -u -r09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/serialize/serializer.tcl (.../serializer.tcl) (revision 09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae) +++ library/serialize/serializer.tcl (.../serializer.tcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -618,7 +618,7 @@ :method extraMethodProperties {o perObject m} { # - # Perserve special method properties like "debug" and + # Preserve special method properties like "debug" and # "deprecated" for arbitrary kind of methods via # "nsf::method::property" calls. # @@ -719,7 +719,7 @@ # # The aliasedCmd is fully qualified and could be a method # handle or a primitive cmd. For a primitive cmd, we have no - # alias dependency. If the cmd is registed on an object, we + # alias dependency. If the cmd is registered on an object, we # report the dependency. # set regObj [::nsf::method::registered $aliasedCmd] @@ -885,7 +885,7 @@ set evalList [:collectVars -serializeSlot $serializeSlot $o $s] if {$serializeSlot} { - # Slots need to be explicitely initialized to ensure + # Slots need to be explicitly initialized to ensure # __invalidateobjectparameter to be called lappend evalList :init } Index: library/xotcl/apps/comm/webserver.xotcl =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/apps/comm/webserver.xotcl (.../webserver.xotcl) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ library/xotcl/apps/comm/webserver.xotcl (.../webserver.xotcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -91,7 +91,7 @@ } @ Httpd h2 { - description "Web server with basic authentication using the specialied worker"} + description "Web server with basic authentication using the specialized worker"} if {[info exists env(USER)]} { set USER "$env(USER)" Index: library/xotcl/doc/tutorial.html =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/doc/tutorial.html (.../tutorial.html) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ library/xotcl/doc/tutorial.html (.../tutorial.html) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -4127,12 +4127,12 @@ the specified command (callee) is take at invocation time (should only be done for (builtin) commands inplemented in C), and -default provides a means for providing default methods when -none are specifed.

+none are specified.

Each of the arguments after the method name (including callee) can be be substituted an invocation time, or they are taken literally. The arguments to be substituted are starting always -with a percent sign. These arguemnts can be %self, +with a percent sign. These arguments can be %self, %proc, %1, %argclindex, or % followed by a Tcl command, and it can be prefixed with a positional prefix %@. We will introduce the usage of these options and Index: library/xotcl/library/comm/Httpd.xotcl =================================================================== diff -u -rb34996b24ea334963e83aadda66384680a6f8ce5 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/library/comm/Httpd.xotcl (.../Httpd.xotcl) (revision b34996b24ea334963e83aadda66384680a6f8ce5) +++ library/xotcl/library/comm/Httpd.xotcl (.../Httpd.xotcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -142,7 +142,7 @@ my destroy } else { #my showMsg "!EOF in http/$version ???" - # we close the conneciton actively (e.g. forced by an error) + # we close the connection actively (e.g. forced by an error) [self]::connection flush #puts stderr "DESTROY----this line should never show up" my destroy Index: library/xotcl/library/lib/mixinStrategy.xotcl =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/library/lib/mixinStrategy.xotcl (.../mixinStrategy.xotcl) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ library/xotcl/library/lib/mixinStrategy.xotcl (.../mixinStrategy.xotcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -11,7 +11,7 @@ mixin-classes, where only one kind of a family of conformant mixins should be registered. <@p> - Naming convertions for strategies: + Naming conventions for strategies: All strategies must follow the naming convention 'kind=implementation'. Examples are the persistency strategy 'eager' specfied as 'persistent=eager' or the persistency strategy 'lazy' (specified as Index: library/xotcl/library/xotcl2.tcl =================================================================== diff -u -r09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision 09b4bca7c8d5c44f6be0b2c04ebfcdb7a58fd5ae) +++ library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -163,7 +163,7 @@ # @method ::xotcl::Object#cleanup # - # TODO: this is a method not used in the Next Scripting Langauge. This + # TODO: this is a method not used in the Next Scripting Language. This # method is just called via recreate, so everything necessary can be # performed there as well. However, it is available for backward # compatibility available in XOTcl 2.0 Index: library/xotcl/tests/slottest.xotcl =================================================================== diff -u -r353fdf460e5124d48f9ebe0f37e23abe51494b38 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision 353fdf460e5124d48f9ebe0f37e23abe51494b38) +++ library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -710,7 +710,7 @@ ? {::nsf::method::property o obar debug} 1 } -nx::test case nx-retuns+serialize { +nx::test case nx-returns+serialize { ::xotcl::Class create Context ? {Context instproc default_form_loader {arg} -returns integer { Index: library/xotcl/tests/speedtest.xotcl =================================================================== diff -u -reea18b07ddfc917545d48ab6a272c0bfb9656f07 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- library/xotcl/tests/speedtest.xotcl (.../speedtest.xotcl) (revision eea18b07ddfc917545d48ab6a272c0bfb9656f07) +++ library/xotcl/tests/speedtest.xotcl (.../speedtest.xotcl) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -474,7 +474,7 @@ # -cmd {c2 o f 10} -expected 11 -count 5000 \ # -post {c2 destroy} -nx::test new -msg {dispatch subobject directy via [self]} \ +nx::test new -msg {dispatch subobject directly via [self]} \ -pre {C c2; C c2::o; c2::o proc f a {incr a}; c2 proc t a {[self]::o f $a}} \ -cmd {c2 t 12} -expected 13 -count 5000 \ -post {c2 destroy} Index: man/xowish.1 =================================================================== diff -u -rcaba76f5ac2943f5a3dfd33550cb578132f40c80 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- man/xowish.1 (.../xowish.1) (revision caba76f5ac2943f5a3dfd33550cb578132f40c80) +++ man/xowish.1 (.../xowish.1) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -77,7 +77,7 @@ structures, like design patterns. .PP Dynamic Component Loading XOTcl -integrates the Tcl package loading with architectrual support for +integrates the Tcl package loading with architectural support for integration with object-oriented constructs. Moreover, it provides tracking/tracing of component loading. .PP Index: tests/class-method.test =================================================================== diff -u -r371cc41c32db500cb1d5bcab139ef65299ef4d6c -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/class-method.test (.../class-method.test) (revision 371cc41c32db500cb1d5bcab139ef65299ef4d6c) +++ tests/class-method.test (.../class-method.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -2,7 +2,7 @@ package require nx::test # -# The first test series without the conveniance layer +# The first test series without the convenience layer # nx::test case class-methods-0 { nx::Class create M1 @@ -21,7 +21,7 @@ } # -# require the conveniance layer +# require the convenience layer # and make it verbose # package require nx::class-method Index: tests/destroy.test =================================================================== diff -u -r371cc41c32db500cb1d5bcab139ef65299ef4d6c -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/destroy.test (.../destroy.test) (revision 371cc41c32db500cb1d5bcab139ef65299ef4d6c) +++ tests/destroy.test (.../destroy.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -804,7 +804,7 @@ # -# The following tests the deletion order triggering implict +# The following tests the deletion order triggering implicit # deletions. This caused a crash in nsf 2.0b2. # package req nx::serializer Index: tests/disposition.test =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/disposition.test (.../disposition.test) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ tests/disposition.test (.../disposition.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -937,7 +937,7 @@ # // Parameter (alias) dispatch // # # In contrast to an ordinary dispatch, a parameter dispatch results - # in a different callstack structure, due to the interferring + # in a different callstack structure, due to the interfering # configure(): # # N+5 |:CscFrame @Type(ENSEMBLE)| <-- foo (leaf) Index: tests/interp.test =================================================================== diff -u -r3e47756fecd604b68064d75fbdba27c5ab1dee42 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/interp.test (.../interp.test) (revision 3e47756fecd604b68064d75fbdba27c5ab1dee42) +++ tests/interp.test (.../interp.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -27,7 +27,7 @@ $i eval [list package req nx] ? {$i eval [list info commands ::nx::Object]} ::nx::Object # - # Tcl's hiding mechansim only applies to objects/classes in the + # Tcl's hiding mechanism only applies to objects/classes in the # top-level namespace. So any non-globally namespaced ones and # nested objects are not concerned ... # Index: tests/parameters.test =================================================================== diff -u -r9bd2c31a189868ce6564005d7b5a7cf9e0dc81a5 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/parameters.test (.../parameters.test) (revision 9bd2c31a189868ce6564005d7b5a7cf9e0dc81a5) +++ tests/parameters.test (.../parameters.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -285,7 +285,7 @@ ? {C eval :__object_configureparameter} \ "-mixins:mixinreg,slot=::nx::Class::slot::mixins,slotset,method=class-mixin,0..n {-superclasses:class,alias,method=::nsf::methods::class::superclass,1..n ::nx::Object} -filters:filterreg,slot=::nx::Class::slot::filters,slotset,method=class-filter,0..n -object-mixins:mixinreg,slot=::nx::Object::slot::object-mixins,slotset,method=object-mixin,0..n -object-filters:filterreg,slot=::nx::Object::slot::object-filters,slotset,method=object-filter,0..n -class:class,alias,method=::nsf::methods::object::class __initblock:cmd,optional,nodashalnum" - #### TOOD: remove or add + #### TODO: remove or add #? {c1 eval :__object_configureparameter} \ # "::c1: unable to dispatch method '__objectparameter'" } @@ -475,7 +475,7 @@ D public method foo {m:integer,0..n} { return $m } - ? {d1 foo ""} "" "emtpy list" + ? {d1 foo ""} "" "empty list" ? {d1 foo 1} "1" "single value" ? {d1 foo {1 2}} "1 2" "multiple values" ? {d1 foo {1 a 2}} \ @@ -485,7 +485,7 @@ D public method foo {m:object,0..n} { return $m } - ? {d1 foo ""} "" "emtpy list" + ? {d1 foo ""} "" "empty list" ? {d1 foo o} "o" "single value" ? {d1 foo {o d1 x}} \ {invalid value in "o d1 x": expected object but got "x" for parameter "m"} \ @@ -1193,7 +1193,7 @@ error "expected false but got $value" } # Note that this converter does NOT return a value; it converts all - # values into emtpy strings. + # values into empty strings. } ? {::nsf::is -complain mType,slot=::tmpObj,0..* {1 0}} \ {invalid value in "1 0": expected false but got 1} \ Index: tests/plain-object-method.test =================================================================== diff -u -r5a162b098b6a9550218646d470b274769bda8da1 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/plain-object-method.test (.../plain-object-method.test) (revision 5a162b098b6a9550218646d470b274769bda8da1) +++ tests/plain-object-method.test (.../plain-object-method.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -15,7 +15,7 @@ } # -# require the conveniance layer +# require the convenience layer # and make it verbose # package require nx::plain-object-method Index: tests/properties.test =================================================================== diff -u -r8f64a468d8bb53f2ba317c8c738c2d3a24243980 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/properties.test (.../properties.test) (revision 8f64a468d8bb53f2ba317c8c738c2d3a24243980) +++ tests/properties.test (.../properties.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -946,7 +946,7 @@ # Bar create b1 Foo property y - # access obejctparamter indirectly via configure + # access objectparameter indirectly via configure ? {b1 configure -y 2} "" } Index: tests/returns.test =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/returns.test (.../returns.test) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ tests/returns.test (.../returns.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -44,7 +44,7 @@ # query the returns value ? {::nsf::method::property C lappend returns} integer - # reset it to emtpy + # reset it to empty ? {::nsf::method::property C lappend returns ""} "" ? {::nsf::method::property C bar-ok1 returns ""} "" ? {::nsf::method::property C bar-ok2 returns ""} "" @@ -186,7 +186,7 @@ # query the returns value ? {::nsf::method::property C lappend returns} integer - # reset it to emtpy + # reset it to empty ? {::nsf::method::property C lappend returns ""} "" c1 eval {set :l e1} @@ -332,7 +332,7 @@ # query the returns value ? {::nsf::method::property C lappend returns} integer - # reset it to emtpy + # reset it to empty ? {::nsf::method::property C lappend returns ""} "" ? {::nsf::method::property C bar-ok1 returns ""} "" ? {::nsf::method::property C bar-ok2 returns ""} "" Index: tests/tcl86.test =================================================================== diff -u -rf934951db464db1a6f39ac98290ecde17a466cd7 -rc4f449cb353be812ba6502ef8e9587e87881f59b --- tests/tcl86.test (.../tcl86.test) (revision f934951db464db1a6f39ac98290ecde17a466cd7) +++ tests/tcl86.test (.../tcl86.test) (revision c4f449cb353be812ba6502ef8e9587e87881f59b) @@ -186,7 +186,7 @@ nx::Class create ATeam -superclass Enumerator { # # Overload "each" to show overloading. Here, we simply capitalize - # the memebers in the "each" method. + # the members in the "each" method. # :public method each {var body} { while 1 {