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
-
-
-
-
-
-
-
-
-
-
-
-
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
+
+
+
+
+
+
+
+
+
+
+
+
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
:
-
-
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 method autoname
-
- :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
-
-
-
-
% 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
- }
-
- :public method delete {item:object} {
- :items delete $item
- $item destroy
- }
-
-}
-
Create an instance of OrderedContainer
…
-
-
-
% OrderedContainer create container2 -memberClass ::C
-::container2
-
-
-
-
% 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 {
-
-
- :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
- }
-
- :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
-
-
-
-
% 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
-
-
-
-
% 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
:
+
+
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 method autoname
+
+ :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
+
+
+
+
% 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
+ }
+
+ :public method delete {item:object} {
+ :items delete $item
+ $item destroy
+ }
+
+}
+
Create an instance of OrderedContainer
…
+
+
+
% OrderedContainer create container2 -memberClass ::C
+::container2
+
+
+
+
% 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 {
+
+
+ :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
+ }
+
+ :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
+
+
+
+
% 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
+
+
+
+
% 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.
-
-
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
-
-
-
-
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
-
-
-
-
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
-
-
-
-
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 …
-
-
--
-
-and we call the methods again:
-
-
-
-
-
-
-
-
::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 ….
-
-
--
-
-and we call the methods again:
-
-
-
-
-
-
-
-
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
-
-
-
-
::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.
+
+
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
+
+
+
+
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
+
+
+
+
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
+
+
+
+
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 …
+
+
+-
+
+and we call the methods again:
+
+
+
+
+
+
+
+
::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 ….
+
+
+-
+
+and we call the methods again:
+
+
+
+
+
+
+
+
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
+
+
+
+
::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 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:
-
-
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 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:
+
+
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.
-
-
-
The class Empty
does not provide any structural or behaviora
-features on behalf of future instances, they will remain empty.
-
-
Provide one instance of Empty
to add an object variable to …
-
-
-
-
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
-
-
c) Is there a Tcl variable managed by the variable slot?
-
-
-
-
A second instance of Empty
has no such capability: foo
-
-
a) Is there any Tcl variable, one named foo
? No …
-
-
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 …
-
-
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.
+
+
+
The class Empty
does not provide any structural or behaviora
+features on behalf of future instances, they will remain empty.
+
+
Provide one instance of Empty
to add an object variable to …
+
+
+
+
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
+
+
c) Is there a Tcl variable managed by the variable slot?
+
+
+
+
A second instance of Empty
has no such capability: foo
+
+
a) Is there any Tcl variable, one named foo
? No …
+
+
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 …
+
+
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
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+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
+
+
+
+
+
+
+
+
+
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.
-
-
-
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.
+
+
+
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
-
-
-
-
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
+
+
+
+
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 {
-
-
- :property delegatee:object
-
-
- :public method operation {} {
- if {[info exists :delegatee]} {
- ${:delegatee} operation
- } else {
- return "default implementatiton"
- }
- }
-}
-
-nx::Class create Delegatee {
-
-
- :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 {
+
+
+ :property delegatee:object
+
+
+ :public method operation {} {
+ if {[info exists :delegatee]} {
+ ${:delegatee} operation
+ } else {
+ return "default implementatiton"
+ }
+ }
+}
+
+nx::Class create Delegatee {
+
+
+ :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.
-
-
-
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.
+
+
+
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.
-
-
-
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.
+
+
+
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:
-
-
-
-
-
% set c [Circle new -x 3.0 -y 4.0 -radius 5.0]
-
-
-
Change the radius of the copied circle:
-
-
-
% $d configure -radius 1.5
-
-
-
-
% $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:
+
+
+
+
+
% set c [Circle new -x 3.0 -y 4.0 -radius 5.0]
+
+
+
Change the radius of the copied circle:
+
+
+
% $d configure -radius 1.5
+
+
+
+
% $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:
-
-
-
-
% 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 ===========
-
-
-
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:
+
+
+
+
% 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 ===========
+
+
+
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 {
- :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
-
-
-
-
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 {
- :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 {
+ :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
+
+
+
+
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 {
+ :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
-
-
-
-
-
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
-
- for {set i 0} {$i < 9} {incr i} {lappend positions $i}
- :variable positions $positions
-
- :public method load {data} {
- 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} {
- 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} {
- }
-
- :method set {x y value:integer,0..1} {
- if {$value<1 || $value>9} {
- set :board($x,$y) {}
- } else {
- set :board($x,$y) $value
- }
- }
- :method get {x y} {
- return [set :board($x,$y)]
- }
-
- :method getRow {x y} {
- return [lmap x ${:positions} {:get $x $y}]
- }
- :method getCol {x y} {
- return [lmap y ${:positions} {:get $x $y}]
- }
-
- :method getRegion {x y} {
- 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 [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}]
- }
-
- :public method solve {} {
- 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]
- }
-
- :create ruleOnlyChoice {
- :object method Solve {hSudoku x y choices} {
- if {[llength $choices] == 1} {
- return $choices
- } else {
- return 0
- }
- }
- }
-
- :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
- }
- }
-
- :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
- }
- }
-
- :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
+
+
+
+
+
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
+
+ for {set i 0} {$i < 9} {incr i} {lappend positions $i}
+ :variable positions $positions
+
+ :public method load {data} {
+ 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} {
+ 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} {
+ }
+
+ :method set {x y value:integer,0..1} {
+ if {$value<1 || $value>9} {
+ set :board($x,$y) {}
+ } else {
+ set :board($x,$y) $value
+ }
+ }
+ :method get {x y} {
+ return [set :board($x,$y)]
+ }
+
+ :method getRow {x y} {
+ return [lmap x ${:positions} {:get $x $y}]
+ }
+ :method getCol {x y} {
+ return [lmap y ${:positions} {:get $x $y}]
+ }
+
+ :method getRegion {x y} {
+ 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 [expr {81-[llength [lsearch -all -inline [join [:dump]] {}]]}]
+ }
+
+ :public method solve {} {
+ 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]
+ }
+
+ :create ruleOnlyChoice {
+ :object method Solve {hSudoku x y choices} {
+ if {[llength $choices] == 1} {
+ return $choices
+ } else {
+ return 0
+ }
+ }
+ }
+
+ :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
+ }
+ }
+
+ :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
+ }
+ }
+
+ :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.
-
-
-
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
-}
-
-
-
-
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.
+
+
+
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
+}
+
+
+
+
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
-
-
-
-
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
+
+
+
+
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
-
- :public method each {var block} {
- foreach member ${:members} {
- uplevel [list set $var $member]
- uplevel $block
- }
- }
-
- :public method map {var block} {
- set result [list]
- :each $var {
- uplevel [list set $var [set $var]]
- lappend result [uplevel $block]
- }
- return $result
- }
-
- :public method count {} {
- return [llength ${:members}]
- }
-
- :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 {
- :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 {
- :module Enumerable
-
- :public method each {var block} {
- foreach member ${:members} {
- uplevel [list set $var $member-[string length $member]]
- uplevel $block
- }
- }
-
- :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
+
+ :public method each {var block} {
+ foreach member ${:members} {
+ uplevel [list set $var $member]
+ uplevel $block
+ }
+ }
+
+ :public method map {var block} {
+ set result [list]
+ :each $var {
+ uplevel [list set $var [set $var]]
+ lappend result [uplevel $block]
+ }
+ return $result
+ }
+
+ :public method count {} {
+ return [llength ${:members}]
+ }
+
+ :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 {
+ :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 {
+ :module Enumerable
+
+ :public method each {var block} {
+ foreach member ${:members} {
+ uplevel [list set $var $member-[string length $member]]
+ uplevel $block
+ }
+ }
+
+ :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)
-
-
-
-
-
-
-
-
-
-
-
-
-
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} {
- 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} {
- 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} {
- 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 {
- 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)
+
+
+
+
+
+
+
+
+
+
+
+
+
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} {
+ 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} {
+ 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} {
+ 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 {
+ 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.
-
-
-
-
-
-
-
-
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.
+
+
+
+
+
+
+
+
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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.
-
-
--
-
-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
-
-
-
-
-
-
-
-
-
-
--
-
-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]}
-
-
-
-
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
-}
-
-
-
-
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]
- }
-}
-
-
-
-
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.
+
+
+-
+
+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
+
+
+
+
+
+
+
+
+
+
+-
+
+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]}
+
+
+
+
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
+}
+
+
+
+
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]
+ }
+}
+
+
+
+
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.
-
-
-
-
-
-
-
-
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.
+
+
+
+
+
+
+
+
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.
-
-
-
-
-
-
-
-
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
- }
- }
-
-
-
-
SpreadSheet create x {
- # populate with some values
- :cell 0,0 Spread1
- :cell 1,0 47
- :cell 2,1 11
- }
-
-
-
-
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.
+
+
+
+
+
+
+
+
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
+ }
+ }
+
+
+
+
SpreadSheet create x {
+ # populate with some values
+ :cell 0,0 Spread1
+ :cell 1,0 47
+ :cell 2,1 11
+ }
+
+
+
+
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
-
--
-
-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
-
-
-
-
Fig 13: tReadStream and tWriteStream as composite traits
-
In this example, traits are used to extend classes and other traits
-(called then composite traits).
-
-
Create a Trait called tPositionableStream
-
-
-
nx::Trait create tPositionableStream {
- :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}
-
-
- :requiredMethods position
- :requiredVariables collection
-}
-
Create a composite trait called tReadStream
based on the trait
-tPositionableStream
:
-
-
-
nx::Trait create tReadStream {
- :public method on {collection} {set :collection $collection; :setToStart}
- :public method next {} {
- if {[:atEnd]} {return ""} else {
- set r [lindex ${:collection} ${:position}]
- :nextPosition
- return $r
- }
- }
-
- :requiredMethods {setToStart atEnd nextPosition}
-
- :require trait tPositionableStream
-}
-
Create a composite trait called tWriteStream
based on the trait
-tPositionableStream
:
-
-
-
nx::Trait create tWriteStream {
- :public method on {collection} {set :collection $collection; :setToEnd}
- :public method nextPut {element} {
- lappend :collection $element
- :nextPosition
- return ""
- }
-
- :requiredMethods {setToEnd nextPosition}
-
- :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
+
+-
+
+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
+
+
+
+
Fig 13: tReadStream and tWriteStream as composite traits
+
In this example, traits are used to extend classes and other traits
+(called then composite traits).
+
+
Create a Trait called tPositionableStream
+
+
+
nx::Trait create tPositionableStream {
+ :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}
+
+
+ :requiredMethods position
+ :requiredVariables collection
+}
+
Create a composite trait called tReadStream
based on the trait
+tPositionableStream
:
+
+
+
nx::Trait create tReadStream {
+ :public method on {collection} {set :collection $collection; :setToStart}
+ :public method next {} {
+ if {[:atEnd]} {return ""} else {
+ set r [lindex ${:collection} ${:position}]
+ :nextPosition
+ return $r
+ }
+ }
+
+ :requiredMethods {setToStart atEnd nextPosition}
+
+ :require trait tPositionableStream
+}
+
Create a composite trait called tWriteStream
based on the trait
+tPositionableStream
:
+
+
+
nx::Trait create tWriteStream {
+ :public method on {collection} {set :collection $collection; :setToEnd}
+ :public method nextPut {element} {
+ lappend :collection $element
+ :nextPosition
+ return ""
+ }
+
+ :requiredMethods {setToEnd nextPosition}
+
+ :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
-
--
-
-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
-
-
-
-
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 {
- :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}
-
-
- :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
+
+-
+
+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
+
+
+
+
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 {
+ :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}
+
+
+ :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
-
-
-
-
-
+
+
+
+
+
+
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:
+
+-
+
+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.
+
+
+-
+
+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.
+
+
+-
+
+It is possible to use NX alias
to register methods
+ under arbitrary names for arbitrary objects or classes.
+
+
+-
+
+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.
+
+
+-
+
+One can invoke in NX fully qualified methods to invoke
+ methods outside the precedence path.
+
+
+-
+
+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.
+
+
+-
+
+One can use in NX the same interface to query (introspect)
+ C-implemented and scripted methods/commands.
+
+
+
+
+-
+
+Orthogonal Parameterization:
+ The Next Scripting Language provides an orthogonal framework for
+ parametrization of methods and objects.
+
+
+-
+
+In NX, the same argument parser is used for
+
+
+
+-
+
+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).
+
+
+-
+
+While XOTcl 1 supported only non-positional arguments at the
+ begin of the argument list, these can be used now at arbitrary
+ positions.
+
+
+
+
+-
+
+Value Checking:
+
+
+-
+
+The Next Scripting Language supports checking of the input
+ parmeters and the return values of scripted and C-implemented
+ methods and commands.
+
+
+-
+
+NX provides a set of predefined checkers (like e.g. integer
,
+ boolean
, object
, …) which can be extended by the
+ applications.
+
+
+-
+
+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
.
+
+
+-
+
+Value Checking can be turned on/off globally or on the
+ method/command level.
+
+
+
+
+-
+
+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).
+
+
+-
+
+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
.
+
+
+-
+
+Profiling Support: The Next Scripting Language provides now two
+ forms of profiling
+
+
+
+-
+
+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
+
+
+-
+
+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.
+
+
+
+
+
+Table 1. Comparison of the Number of Predefined Methods in NX and XOTcl
+
+
+
+
+
+ |
+NX |
+XOTcl |
+
+
+
+
+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 {
+
+
+ :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
+ }
+} |
+
+
+
+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
+
+ xotcl::Class C1
+ C1 instproc foo {} {puts "hello world"}
+
+ package require 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 |
+
+
+
+
+ |
+ |
+
+
+ |
+
+
+ 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 |
+
+
+
+
+
+
+
+Class C
+C instproc foo args {...}
+C proc bar args {...} |
+
+
+
+Class create C {
+ :method foo args {...}
+ :object method bar args {...}
+}
+
+
+
+Class create C
+C method foo args {...}
+C object method bar args {...} |
+
+
+
+
+
+Object o
+o set x 1
+o proc foo args {...} |
+
+
+
+Object create o {
+ set :x 1
+ :object method foo args {...}
+}
+
+
+
+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 |
+
+
+
+
+ |
+ |
+
+
+
+
+
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 ... |
+
+
+
+Class create C {
+ :forward f1 ...
+ :object forward f2 ...
+}
+
+Object create o {
+ :object forward f3 ...
+} |
+
+
+
+
+
+Class C
+C instparametercmd p1
+C parametercmd p2
+
+Object o
+o parametercmd p3 |
+
+
+
+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 |
+
+
+
+
+ |
+
+
+
+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 |
+
+
+
+
+ |
+
+
+ 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 |
+
+
+
+
+ |
+
+
+ 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 ;s1 bar 3 4 ;s1 baz 3 4 ; |
+
+
+
+
+
+
+
2.2.4. Method Deletion
+
NX provides an explicit delete
method for the deletion of methods.
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+
+
+ /cls/ instproc foo {} {}
+/obj/ proc foo {} {} |
+
+
+ /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 ; o baz ;}
+Object o
+o proc baz {} {...} |
+
+
+ Class create C {
+ :method foo args {...}
+ :method bar args {
+ :foo 1 2 3 ; o baz ; }
+}
+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 {
+ set a 1
+ my instvar b
+ set b 2
+ set ::c 3
+} |
+
+
+ Class create C {
+ :method foo args {...}
+ set a 1
+ set :b 2
+ set ::c 3
+ }
+} |
+
+
+
+
+ ... instproc ... {
+ my set /varName/ ?value?
+} |
+
+
+
+... method ... {
+ set :/newVar/ ?value?
+} |
+
+
+
+
+ ... instproc ... {
+ my instvar /varName/
+ set /varName/ ?value?
+} |
+
+
+
+... method ... {
+ ::nx::var import [self] /varName/
+ set /varName/ ?value?
+} |
+
+
+
+
+ ... instproc ... {
+ set /varName/ [my set /otherVar/]
+} |
+
+
+
+... method ... {
+ set /varName/ [set :/otherVar/]
+}
+
+
+ ... method ... {
+ set /newVar/ ${:/otherVar/}
+} |
+
+
+
+
+ ... instproc ... {
+ my exists /varName/
+} |
+
+
+
+... 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? |
+
+
+
+/obj/ eval [list set :/varName/ ?value?] |
+
+
+
+
+ set /varName/ [/obj/ set /otherVar/] |
+
+
+
+set /varName/ [/obj/ eval {set :/otherVar/}] |
+
+
+
+
+ ... instproc ... {
+ /obj/ instvar /varName/
+ set /varName/ ?value?
+} |
+
+
+
+... method ... {
+ ::nx::var import /obj/ /varName/
+ set /varName/ ?value?
+} |
+
+
+ |
+
+
+
+/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 |
+
+
+
+
+
+
+
+Class Foo
+Foo instproc init args {
+ instvar x y
+ set x 1
+ set y 2
+}
+
+Foo f1
+
+ |
+
+
+
+Class create Foo {
+ :variable x 1
+ :variable y 2
+}
+
+Foo create f1
+
+ |
+
+
+
+
+
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 |
+
+
+
+
+ |
+
+
+
+
+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
+
+-
+
+one can specify the property as a non-positional parameter upon
+ creation of the object,
+
+
+-
+
+one can query the value via the method cget
, and
+
+
+-
+
+one can modify the value of the underlying variable via the method
+ configure
.
+
+
+
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+
+
+
+Class Foo -parameter {a {b 1}}
+
+Foo f1 -a 0
+
+
+puts [f1 b]
+
+f1 b 100 |
+
+
+
+Class create Foo {
+ :property a
+ :property {b 1}
+}
+
+Foo create f1 -a 0
+
+
+puts [f1 cget -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 |
+
+
+
+
+
+
+
+Class create Foo -parameter {a {b1}}
+
+puts [f1 b]
+
+f1 a 100
+
+ |
+
+
+
+Class create Foo {
+ :variable -accessor public a
+ :property -accessor public {b 1}
+}
+
+puts [f1 b get]
+
+f1 a set 100
+
+f1 a unset |
+
+
+
+
+
Similar to variable
, properties can be defined in NX on the class
+and on the object level.
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+ |
+
+
+
+Class create Foo {
+ ...
+ :object property cp 101
+}
+
+
+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 |
+
+
+
+
+ |
+
+
+
+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 |
+
+
+
+
+ |
+
+
+
+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 |
+
+
+
+
+ |
+
+
+
+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:
+
+-
+
+Create a slot object, which can be specified in more detail
+ using the init-block of the slot object
+
+
+-
+
+Create a parameter definition for the initialization of the
+ object (usable via a non-positional parameter during object
+ creation), and
+
+
+-
+
+register optionally an accessor function (setter), for which the usual
+ protection levels (public
, protected
or private
) can be used.
+
+
+
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+
+
+
+Class Foo -slots {
+ Attribute a
+ Attribute b -default 1
+}
+
+Foo f1 -a 0
+
+ |
+
+
+
+Class create Foo {
+ :property a
+ :property {b 1}
+}
+
+Foo create f1 -a 0
+
+ |
+
+
+
+
+
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 |
+
+
+
+
+
+
+
+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"
+ }
+ }
+ }
+ }
+} |
+
+
+
+Class create Person {
+ :property -accessor public sex:sex,convert {
+
+ :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 |
+
+
+
+
+ |
+
+
+
+/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 |
+
+
+
+
+
+
+
+Class C
+C instproc foo {
+ -x:integer
+ -y:required
+ -z
+ a
+ b
+} {
+ }
+C create c1
+
+c1 foo -x 1 -y a 2 3 |
+
+
+
+Class create C {
+ :public method foo {
+ -x:integer
+ -y:required
+ -z
+ a
+ b
+ } {
+ }
+ :create c1
+}
+c1 foo -x 1 -y a 2 3 |
+
+
+ |
+
+
+
+Class create C {
+ :public method m1 {a b -x:integer -y} {
+ }
+
+ :public method m2 {a:integer b:boolean} {
+ }
+
+ :public method set {varName value:optional} {
+ }
+
+ :public method m3 {-objs:object,1..n c:class,0..1} {
+ }
+
+ } |
+
+
+
+
+
+
+
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 |
+
+
+
+
+ |
+
+
+
+Class create C {
+
+ :method foo -returns integer {-x:integer} {
+ }
+
+ :alias incr -returns integer ::incr
+
+ :forward ++ -returns integer ::expr 1 +
+
+ :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?
+
+/cls/ instmixin |
+
+
+
+/cls/ mixins add|set|clear ...
+/cls/ mixins guard /mixin/ ?condition?
+/cls/ configure -mixin ...
+
+/cls/ mixins get
+/cls/ cget -mixins
+
+/cls/ mixins classes |
+
+
+
+
+ /obj/ mixin ...
+/obj/ mixinguard /mixin/ ?condition?
+
+/obj/ mixin |
+
+
+
+/obj/ object mixins add|set|clear ...
+/obj/ object mixins guard /mixin/ ?condition?
+/obj/ configure -object-mixins ...
+
+/obj/ object mixins get
+/obj/ cget -object-mixin
+
+/cls/ mixins classes |
+
+
+
+
+
+
+
2.5.2. Register Filters and Filter Guards
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+
+
+ /cls/ instfilter ...
+/cls/ instfilterguard /filter/ ?condition?
+
+/cls/ instfilter |
+
+
+
+/cls/ filters add|set|clear ...
+/cls/ filters guard /filter/ ?condition?
+/cls/ configure -filters ...
+
+/cls/ filters get
+/cls/ cget -filters
+
+/cls/ filters methods |
+
+
+
+
+ /obj/ filter ...
+/obj/ filterguard /filter/ ?condition? |
+
+
+
+/obj/ object filters add|set|clear ...
+/obj/ object filters guard /filter/ ?condition?
+/obj/ configure -object-filters ...
+
+/cls/ object filters get
+/obj/ cget -object-filters
+
+/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? |
+
+
+ |
+
+
+ /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? |
+
+
+ |
+
+
+ /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?
+ |
+
+
+ |
+
+
+ /obj/ info lookup methods -source application ... ?pattern?
+ |
+
+
+ |
+ |
+
+
+ |
+
+
+
+/obj/ info lookup slots ?-type ...? ?-source ...? ?pattern?
+
+ |
+
+
+
+
+ /obj/ info filters -order ?-guards? ?pattern?
+
+/obj/ info mixins -heritage ?-guards? ?pattern? |
+
+
+ /obj/ info lookup filters ?-guards? ?pattern?
+
+/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/
+ |
+
+
+
+
+ /obj/ filtersearch /methodName/ |
+
+
+ /obj/ info lookup filter /methodName/
+ |
+
+
+
+
+
+
+
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 |
+
+
+
+
+ |
+
+
+ /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/ |
+ |
+
+
+
+
+ /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 |
+
+
+
+
+ |
+
+
+ /cls/ info method syntax /methodName/
+/obj/ info object method syntax /methodName/ |
+
+
+
+
+
+
+
+
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 |
+
+
+
+
+ |
+
+
+
+/cls/ info lookup parameters create ?/pattern/?
+
+
+/cls/ info lookup syntax create ?/pattern/?
+
+
+/obj/ info lookup parameters configure ?pattern?
+
+
+/obj/ info lookup syntax 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 |
+
+
+
+
+ |
+
+
+
+/cls/ info variables ?pattern?
+/obj/ info object variables ?pattern?
+
+
+/obj/ info lookup variables /pattern/
+
+
+/obj/ info variable definition /handle/
+/obj/ info variable name /handle/
+/obj/ info variable parameter /handle/ |
+
+
+
+
+
+
+
2.6.10. List Slots
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+ |
+
+
+
+/cls/ info slots \
+ ?-type value? ?-closure? ?-source value? ?pattern?
+/obj/ info object slots ?-type ...? ?pattern?
+
+
+/obj/ info lookup slots \
+ ?-type ...? ?-source ... ?pattern?
+
+
+/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 |
+
+
+
+
+ |
+
+
+ /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 |
+
+
+
+
+ |
+
+
+ /cls/ info method handle /methodName/
+/obj/ info object method handle /methodName/
+/cls/ info method registrationhandle /methodName/
+/obj/ info object method registrationhandle /methodName/
+/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 |
+
+
+
+
+ |
+
+
+ /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? |
+
+
+
+/cls/ info mixinof -scope object ?-closure? \
+ ?pattern? |
+
+
+
+
+ /cls/ info instmixinof ?-closure? ?pattern? |
+
+
+
+/cls/ info mixinof -scope class ?-closure? \
+ ?pattern? |
+
+
+ |
+
+
+
+/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/ info has type /sometype/ |
+
+
+ |
+
+
+ /obj/ info has mixin /cls/ |
+
+
+ |
+
+
+ /obj/ has type ::nx::Class
+
+::nsf::is class /obj/ |
+
+
+ |
+
+
+ expr {[/cls/ info heritage ::nx::Class] ne ""}
+
+::nsf::is metaclass /obj/ |
+
+
+ |
+
+
+ ::nsf::is baseclass /obj/ |
+
+
+ |
+ |
+
+
+
+
+ /obj/ object::exists /obj/ |
+
+
+ ::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 |
+
+
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+
+
+
+
+
+
2.7. Other Predefined Methods
+
+
+
+
+
+
+XOTcl |
+Next Scripting Language |
+
+
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+
+
+
+
+
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/ |
+
+
+ |
+
+
+ ::nsf::method::assertion /obj/ check |
+
+
+
+
+ /obj/ invar /conditions/ |
+
+
+ ::nsf::method::assertion /obj/ object-invar /conditions/ |
+
+
+ |
+
+
+ ::nsf::method::assertion /obj/ object-invar |
+
+
+
+
+ /cls/ instinvar /conditions/ |
+
+
+ ::nsf::method::assertion /cls/ class-invar /conditions/ |
+
+
+ |
+
+
+ ::nsf::method::assertion /cls/ class-invar |
+
+
+
+
+ /cls/ invar /conditions/ |
+
+
+ ::nsf::method::assertion /cls/ object-invar /conditions/ |
+
+
+ |
+
+
+ ::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
).
+
+
+
+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.
+
+
+
+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::Class create Foo
+Foo create 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
:
+
+
+
::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
-
-
-
-
-
+
+
+
+
+
+
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).
+
+
+
+
+
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.
+
+
+
+
nx::Class create Stack {
+
+
+ :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
+
+
+
package require nx
+
+nx::Class create Stack {
+
+ ....
+}
+
+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.
+
+
+
+
+
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.
+
+
+
+
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
.
+
+
+
+
+
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.
+
+
+
+
nx::Class create Safety {
+
+
+ :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.
+
+
+
+
+
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
+
+
+
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.
+
+
+
+
+
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 {
+
+
+ :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 {
+
+
+ :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.
+
+
+
+
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.
+
+
+
+
+
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 {...}
+ set a 1
+ set :b 2
+ 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.
+
+
+
+
+
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.
+
+
+
+
nx::Class create Person {
+ :property name:required
+ :property birthday
+}
+
+nx::Class create Student -superclass Person {
+ :property matnr:required
+ :property {oncampus:boolean true}
+}
+
+Person create p1 -name Bob
+Student create s1 -name Susan -matnr 4711
+
+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
+ }
+
+Derived create d1
+
+
+
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
+ }
+}
+
+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
+
+
+
nx::Class create Dog {
+
+ :public method bark {} {
+ puts "[self] Bark, bark, bark."
+ }
+}
+
+Dog create fido
+
+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}
+}
+
+Dog create fido
+
+fido::tail length get
+
+fido::tail length set 10
+
+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}
+}
+
+Dog create fido
+
+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.
+
+
+
+
nx::Class create Dog {
+ :public method bark {} { puts "[self] Bark, bark, bark." }
+
+ :public alias warn [:info method handle bark]
+ }
+
+Dog create fido
+
+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 {
+
+ :public method foo {} {
+ return [:helper]
+ }
+
+ :method helper {} {
+ return 1
+ }
+}
+
+Foo create f1
+
+f1 foo
+
+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 ;s1 bar 3 4 ;s1 helper 3 4 ;
+
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
+
+
+
nx::Class create C {
+ :property -accessor public {x c}
+}
+
+nx::Class create D -superclass C {
+ :property -accessor private {x d}
+ :public method bar {p} {return [: -local $p get]}
+}
+
+D create d1
+puts [d1 x get] ;puts [d1 bar x] ;
+
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
+}
+
+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}
+ }
+}
+
+c1 bar
+
+c1 foo
+
+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 {
+ :public object method bar {} {return 2}
+ :public method foo {} {return 1}
+
+ :create c1
+}
+
+C bar
+
+c1 foo
+
+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 {
+
+
+ :public method "string length" {x} {....}
+ :public method "string tolower" {x} {...}
+ :public method "string info" {x} {...}
+ :create c1
+}
+
+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]"
+ }
+ }
+}
+
+d1 foo
+
+d1 info precedence
+
+
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 object mixins add M1
+
+C mixins add M2
+
+d1 foo
+
+d1 info precedence
+
+
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 public object method bar {} {
+ puts [:foo]
+ puts [: -local foo]
+ puts [: -intrinsic foo]
+ puts [: -system foo]
+}
+
+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.
+
+
+
+
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 {
+
+ :public object method foo {x y} {
+ puts "x=$x y=$y"
+ }
+
+ :public object method bar {-x -y} {
+ puts "x=$x y=$y"
+ }
+
+ :public object method baz {-x -y a} {
+ puts "x? [info exists x] y? [info exists y] a=$a"
+ }
+}
+
+o1 foo 1 2
+
+o1 bar -y 3 -x 1
+o1 bar -x 1 -y 3
+
+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 {
+
+ :public object method foo {x:required y:optional} {
+ puts "x=$x y? [info exists y]"
+ }
+
+ :public object method bar {-x:required -y:optional} {
+ puts "x=$x y? [info exists y]"
+ }
+}
+
+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 {
+
+ :public object method foo {{x 1} {y 2}} {
+ puts "x=$x y=$y"
+ }
+
+ :public object method bar {{-x 10} {-y 20}} {
+ puts "x=$x y=$y"
+ }
+}
+
+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.
+
+
+
+
+
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 {
+
+ :public object method foo {x:integer o:object,optional} {
+ puts "x=$x o? [info exists o]"
+ }
+
+ :public object method bar {{-x:integer 10} {-verbose:boolean false}} {
+ puts "x=$x verbose=$verbose"
+ }
+}
+
+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
+
+
+
nx::Class create Person
+nx::Class create Project
+
+nx::Object create o5 {
+ :public object method work {
+ -person:object,type=Person
+ -project:object,type=Project
+ } {
+ }
+}
+
+Person create gustaf
+Project create nx
+
+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
+
+
+
::nx::Slot method type=groupsize {name value} {
+ if {$value < 1 || $value > 6} {
+ error "Value '$value' of parameter $name is not between 1 and 6"
+ }
+}
+
+::nx::Slot method type=choice {name value arg} {
+ if {$value ni [split $arg |]} {
+ error "Value '$value' of parameter $name not in permissible values $arg"
+ }
+}
+
+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
+
+d1 foo 2
+d1 foo 10
+
+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
+
+
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 {
+
+ :public object method foo {x:integer,0..1} {
+ puts "x=$x"
+ }
+
+ :public object method bar {x:integer,0..n} {
+ puts "x=$x"
+ }
+
+ :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.2. Resolution Order and Next-Path
+
+
+
+
+
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.
+
+
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.
+
+
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
+
+
+
nx::Class create Person {
+ :property name:required
+ :property birthday
+}
+
+nx::Class create Student -superclass Person {
+ :property matnr:required
+ :property {oncampus:boolean true}
+}
+
+Person create p1 -name Bob
+Student create s1 -name Susan -matnr 4711
+
+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.
+
+
+
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).
+
+
+
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.
+
+
+
+
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
+
+
+
+
+
Figure 47. Slot Classes and Objects
+
+
+
+
+
4.3.5. Attribute Slots
+
+
+-
+
+return value checking
+
+
+-
+
+switch
+
+
+-
+
+initcmd …
+
+
+-
+
+subst rules
+
+
+-
+
+converter
+
+
+-
+
+incremental slots
+
+
+
+
+
+
+
+
+
5. Miscellaneous
+
+
+
+
+
5.2. Unknown Handlers
+
NX provides two kinds of unknown handlers:
+
+
+
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"
+ }
+}
+
+o foo 1 2 3
+
+
+
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} {
+ puts "***** __unknown called with <$name>"
+ ::nx::Class create $name
+}
+
+::nsf::object::unknown::add nx {::nx::Class __unknown}
+
+::nx::Object create o {
+
+ :object mixins add M
+
+ puts [:info object mixins]
+ }
+
The Next Scripting Framework allows to add, query, delete and list unknown handlers.
+
Listing 50: Unknown Handler registration
+
+
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 {