Index: TODO =================================================================== diff -u -rb876f2df8715159b566727e3c240b5bcca7bacab -rbd1cce484140aaf66113cf647f060ae48d32b24f --- TODO (.../TODO) (revision b876f2df8715159b566727e3c240b5bcca7bacab) +++ TODO (.../TODO) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -2259,16 +2259,38 @@ with SYSTEM_MALLOC (own modified version of tclThreadAlloc.c) - fixed memory leak (namespace names and structures) +- nx.tcl: + * full rewrite of slot machinerie, much simpler structure + * relation handling via parameter aliases instread of pseudo converter + * mixinclass SlotOptimizer removed + * new class BootStrapAttributeSlot + +- ConvertToRelation() and handling of parametertype "relation" +- Make CompiledColonVarFetch() more robust in case of + half initialized objects (create vartable on the fly if needed) +- allow empty parameter options in parameter parser +- removed nsf::parametersfromslots (became simple, part of objectparameter now) +- removed hardcoded objectparameter (attributes, volatile and noinit) +- updated regression test + TODO: - object parameter type forward: - regression test - reduce verbosity - get rid of eager tcd creation -- toParameterSpec: - simplify, maybe different methods per class? +- slotmachinerie 2 + - update class diagrams + - naming "incremental", "reconfigure" + - check: + slottest/t.023: 3.40 mms, o1 class + Warning: Arguments '{::M ::xotcl::Object}' to constructor of object ::__unknown are most likely not processed + - check, if substdefault/default (ObjectParameter) could work with e.g. alias; otherwise, move substdefault down + we could use ::nx::Object as e.g. default superclass + - should we deactivate add/delete for non-multivalued cases? + - doc: NextScriptingLanguage/index.html: Index: generic/nsf.c =================================================================== diff -u -rb876f2df8715159b566727e3c240b5bcca7bacab -rbd1cce484140aaf66113cf647f060ae48d32b24f --- generic/nsf.c (.../nsf.c) (revision b876f2df8715159b566727e3c240b5bcca7bacab) +++ generic/nsf.c (.../nsf.c) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -2651,7 +2651,19 @@ HashVarFree(var); } - varTablePtr = object->nsPtr ? Tcl_Namespace_varTablePtr(object->nsPtr) : object->varTablePtr; + if (object->nsPtr) { + varTablePtr = Tcl_Namespace_varTablePtr(object->nsPtr); + } else if (object->varTablePtr) { + varTablePtr = object->varTablePtr; + } else { + /* + * In most situations, we have a varTablePtr through the clauses + * above. However, if someone redefines e.g. the method + * "configure" or "objectparameter", we might find an object with + * an still empty varTable, since these are lazy initiated. + */ + varTablePtr = object->varTablePtr = VarHashTableCreate(); + } assert(varTablePtr); resVarInfo->lastObject = object; @@ -8652,17 +8664,6 @@ } static int -ConvertToRelation(Tcl_Interp *UNUSED(interp), Tcl_Obj *objPtr, NsfParam CONST *UNUSED(pPtr), - ClientData *clientData, Tcl_Obj **outObjPtr) { - /* NsfRelationCmd is the real setter, which checks the values - according to the relation type (Class, List of Class, list of - filters; we treat it here just like a tclobj */ - *clientData = (ClientData)objPtr; - *outObjPtr = objPtr; - return TCL_OK; -} - -static int ConvertToParameter(Tcl_Interp *interp, Tcl_Obj *objPtr, NsfParam CONST *pPtr, ClientData *clientData, Tcl_Obj **outObjPtr) { CONST char *value = ObjStr(objPtr); @@ -8915,11 +8916,6 @@ result = ParamOptionSetConverter(interp, paramPtr, "class", ConvertToClass); paramPtr->flags |= NSF_ARG_BASECLASS; - } else if (strncmp(option, "relation", 8) == 0) { - result = ParamOptionSetConverter(interp, paramPtr, "relation", ConvertToRelation); - paramPtr->flags |= NSF_ARG_RELATION; - /*paramPtr->type = "tclobj";*/ - } else if (strncmp(option, "parameter", 9) == 0) { result = ParamOptionSetConverter(interp, paramPtr, "parameter", ConvertToParameter); @@ -9034,10 +9030,13 @@ /* skip space from end */ for (end = l; end>0 && isspace((int)argString[end-1]); end--); /* process last option */ - result = ParamOptionParse(interp, argString, start, end-start, disallowedFlags, paramPtr); - if (result != TCL_OK) { - goto param_error; + if (end-start > 0) { + result = ParamOptionParse(interp, argString, start, end-start, disallowedFlags, paramPtr); + if (result != TCL_OK) { + goto param_error; + } } + } else { /* no ':', the whole arg is the name, we have not options */ NEW_STRING(paramPtr->name, argString, length); @@ -16518,36 +16517,6 @@ continue; } - /* previous code to handle relations */ - if (paramPtr->converter == ConvertToRelation) { - ClientData relIdx; - Tcl_Obj *relationObj = paramPtr->converterArg ? paramPtr->converterArg : paramPtr->nameObj, - *outObjPtr; - CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); - - /* - * Execute relation cmd in the context above the object frame, - * since the object frame changes the current namespace as - * well. References to classes with implicit namespaces might - * fail otherwise. - */ - Tcl_Interp_varFramePtr(interp) = varFramePtr->callerPtr; - result = ConvertToRelationtype(interp, relationObj, paramPtr, &relIdx, &outObjPtr); - - if (result == TCL_OK) { - result = NsfRelationCmd(interp, object, PTR2INT(relIdx), newValue); - } - Tcl_Interp_varFramePtr(interp) = varFramePtr; - - if (result != TCL_OK) { - Nsf_PopFrameObj(interp, framePtr); - ParseContextRelease(&pc); - goto configure_exit; - } - /* done with relation handling */ - continue; - } - /* special setter for init commands */ if (paramPtr->flags & (NSF_ARG_INITCMD|NSF_ARG_ALIAS|NSF_ARG_FORWARD)) { CallFrame *varFramePtr = Tcl_Interp_varFramePtr(interp); Index: library/nx/nx.tcl =================================================================== diff -u -raedc1032110ff312eab8b83878d10a9e6ae401e7 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- library/nx/nx.tcl (.../nx.tcl) (revision aedc1032110ff312eab8b83878d10a9e6ae401e7) +++ library/nx/nx.tcl (.../nx.tcl) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -628,10 +628,10 @@ } else { set properties [string range $value [expr {$colonPos+1}] end] set name [string range $value 0 [expr {$colonPos -1}]] + set useArgFor arg foreach property [split $properties ,] { - if {$property in [list "required" "multivalued" "allowempty" \ - "convert" "nosetter"]} { - if {$property eq "convert"} { + if {$property in [list "required" "convert" "nosetter" "substdefault" "noarg"]} { + if {$property in "convert" } { set class [:requireClass ::nx::Attribute $class] } lappend opts -$property 1 @@ -641,16 +641,16 @@ if {![string match ::* $type]} {set type ::$type} } elseif {[string match arg=* $property]} { set argument [string range $property 4 end] - lappend opts -arg $argument + lappend opts -$useArgFor $argument } elseif {$property eq "optional"} { lappend opts -required 0 } elseif {$property in [list "alias" "forward"]} { set class [:requireClass ::nx::ObjectParameterSlot $class] lappend opts -disposition $property set class [:requireClass ::nx::ObjectParameterSlot $class] - } elseif {[regexp {([01])[.][.]([1n*])} $property _ lower upper]} { - if {$lower eq "0"} {lappend opts -allowempty 1} - if {$upper ne "1"} {lappend opts -multivalued 1} + set useArgFor methodname + } elseif {[regexp {([01])[.][.]([1n*])} $property _ minOccurance maxOccurance]} { + lappend opts -multiplicity $property } else { set type $property } @@ -693,7 +693,8 @@ MetaSlot create ::nx::MethodParameterSlot ::nsf::relation MethodParameterSlot superclass Slot - # create an object for dispatching + # Create an object for dispatching method parameter specific value + # checkers MethodParameterSlot create ::nx::methodParameterSlot # use low level interface for defining slot values. Normally, this is @@ -703,7 +704,8 @@ foreach att $definitions { if {[llength $att]>1} {foreach {att default} $att break} set slotObj [::nx::slotObj $class $att] - ::nx::ObjectParameterSlot create $slotObj + #puts stderr "::nx::BootStrapAttributeSlot create $slotObj" + ::nx::BootStrapAttributeSlot create $slotObj if {[info exists default]} { ::nsf::setvar $slotObj default $default unset default @@ -728,6 +730,7 @@ set value $default } ::nsf::setvar $i $att $value + #puts stderr "::nsf::setvar $i $att $value (second round)" } } unset default @@ -738,287 +741,212 @@ ::nsf::invalidateobjectparameter $class } + ObjectParameterSlot public method namedParameterSpec {{-prefix -} name options} { + # + # Build a pos/nonpos parameter specification from name and option list + # + if {[llength $options]>0} { + return $prefix${name}:[join $options ,] + } else { + return $prefix${name} + } + } + ############################################ # Define slots for slots ############################################ + # + # We would like to have attribute slots during bootstrap to + # configure the slots itself (e.g. a relation slot object). This is + # however a chicken/egg problem, so we use a very simple class for + # defining slots for slots, called BootStrapAttributeSlot. + # + MetaSlot create ::nx::BootStrapAttributeSlot + ::nsf::relation BootStrapAttributeSlot superclass ObjectParameterSlot + + BootStrapAttributeSlot public method getParameterSpec {} { + # + # Bootstrap version of getParameter spec. Just bare essentials. + # + set options [list] + if {[info exists :default]} { + if {[string match {*\[*\]*} ${:default}]} { + append options substdefault + } + return [list [list [:namedParameterSpec [namespace tail [self]] $options]] ${:default}] + } + return [list [:namedParameterSpec [namespace tail [self]] $options]] + } + + BootStrapAttributeSlot protected method init {args} { + # + # Empty constructor; do nothing, intentionally without "next" + # + } + + ##################################### + # configure nx::Slot + ##################################### createBootstrapAttributeSlots ::nx::Slot { - {name} - {multivalued false} - {required false} - default - type } + ##################################### + # configure nx::ObjectParameterSlot + ##################################### + createBootstrapAttributeSlots ::nx::ObjectParameterSlot { {name "[namespace tail [::nsf::self]]"} - {methodname} {domain "[lindex [regexp -inline {^(.*)::slot::[^:]+$} [::nsf::self]] 1]"} - {defaultmethods {get assign}} {manager "[::nsf::self]"} {per-object false} - {arg} + {methodname} + {forwardername} + {defaultmethods {get assign}} {nosetter true} - {disposition} + {noarg} + {disposition alias} + {required false} + {substdefault false} } - # maybe add the following slots at some later time here - # defaultcmd - # valuecmd - # valuechangedcmd - - ::nsf::alias ObjectParameterSlot get ::nsf::setvar - ::nsf::alias ObjectParameterSlot assign ::nsf::setvar - - ObjectParameterSlot public method add {obj prop value {pos 0}} { - if {![set :multivalued]} { - error "Property $prop of [set :domain]->$obj ist not multivalued" - } - if {[::nsf::existsvar $obj $prop]} { - ::nsf::setvar $obj $prop [linsert [::nsf::setvar $obj $prop] $pos $value] - } else { - ::nsf::setvar $obj $prop [list $value] - } - } - - ObjectParameterSlot public method delete {-nocomplain:switch obj prop value} { - set old [::nsf::setvar $obj $prop] - set p [lsearch -glob $old $value] - if {$p>-1} {::nsf::setvar $obj $prop [lreplace $old $p $p]} else { - error "$value is not a $prop of $obj (valid are: $old)" - } - } - + # TODO: check, if substdefault/default could work with e.g. alias; otherwise, move substdefault down + # + # Default unknown handler for all slots + # ObjectParameterSlot protected method unknown {method args} { + # + # Report just application specific methods not starting with "__" + # set methods [list] - foreach m [::nsf::dispatch [::nsf::self] ::nsf::methods::object::info::lookupmethods] { - if {[::nsf::dispatch Object ::nsf::methods::object::info::lookupmethods $m] ne ""} continue + foreach m [::nsf::dispatch [::nsf::self] ::nsf::methods::object::info::lookupmethods -source application] { if {[string match __* $m]} continue lappend methods $m } error "Method '$method' unknown for slot [::nsf::self]; valid are: {[lsort $methods]}" } - + + ObjectParameterSlot protected method init {args} { + # + # Provide a default depending on :name for :methodname. When slot + # objects are created, invalidate the object parameters to reflect + # the changes + # + if {![info exists :methodname]} { + set :methodname ${:name} + } + if {[::nsf::is class ${:domain}]} { + ::nsf::invalidateobjectparameter ${:domain} + } + # + # plain object parameter have currently no setter/forwarder + # + } + ObjectParameterSlot public method destroy {} { - #puts stderr DESTROY-[info exists :domain] + # + # When slot objects are destroyed, invalidate the object + # parameters to reflect the changes + # if {[info exists :domain] && ${:domain} ne "" && [::nsf::is class ${:domain}]} { ::nsf::invalidateobjectparameter ${:domain} } ::nsf::next } - - ObjectParameterSlot protected method init {args} { - if {${:domain} eq ""} { - set :domain [::nsf::current callingobject] + + ObjectParameterSlot protected method makeForwarder {} { + # + # Build forwarder from the source object class ($domain) to the slot + # to delegate read and update operations + # + # intended to be called on RelationSlot or AttributeSlot + # + if {![info exists :forwardername]} { + set :forwardername ${:methodname} } - if {${:domain} ne ""} { - if {![info exists :methodname]} { - set :methodname ${:name} - } - if {[::nsf::is class ${:domain}]} { - ::nsf::invalidateobjectparameter ${:domain} - } - if {${:per-object} && [info exists :default] } { - ::nsf::setvar ${:domain} ${:name} ${:default} - } - if {[info exists :nosetter]} { - #puts stderr "Do not register forwarder ${:domain} ${:name}" - return - } - #puts stderr "ObjectParameterSlot [::nsf::self] init, forwarder on ${:domain} <$args> ${:per-object}" - ::nsf::forward ${:domain} \ - {*}[expr {${:per-object} ? "-per-object" : ""}] \ - ${:name} \ - ${:manager} \ - [list %1 [${:manager} defaultmethods]] %self \ - ${:methodname} - } + #puts stderr [list ::nsf::forward ${:domain} \ + {*}[expr {${:per-object} ? "-per-object" : ""}] \ + ${:name} ${:manager} [list %1 [${:manager} defaultmethods]] %self \ + ${:forwardername}] + ::nsf::forward ${:domain} \ + {*}[expr {${:per-object} ? "-per-object" : ""}] \ + ${:name} \ + ${:manager} \ + [list %1 [${:manager} defaultmethods]] %self \ + ${:forwardername} } + ObjectParameterSlot protected method getParameterOptions {} { + # + # Obtain a list of parameter options from slot object + # + set options ${:disposition} + if {${:name} ne ${:methodname}} {lappend options arg=${:methodname}} + if {${:required}} {lappend options required} + if {[info exists :noarg] && ${:noarg}} {lappend options noarg} + return $options + } + + ObjectParameterSlot public method getParameterSpec {} { + # + # Get a full object parmeter specification from slot object + # + return [list [:namedParameterSpec ${:name} [:getParameterOptions]]] + } + + ################################################################# # We have no working objectparameter yet, since it requires a # minimal slot infrastructure to build object parameters from - # slots. The above definitions should be sufficient. We provide the - # definition here before we refine the slot definitions. + # slots. The above definitions should be sufficient as a basis for + # object parameters. We provide the definition here before we refine + # the slot definitions. # - # Invalidate previously defined object parameter. - + # Invalidate previously defined object parameter (built with the + # empty objectparameter definition. + # ::nsf::invalidateobjectparameter MetaSlot - # Provide the a slot based mechanism for building an object - # configuration interface from slot definitions - - ObjectParameterSlot public method toParameterSpec {{name:substdefault ${:name}}} { - set objparamdefinition $name - set methodparamdefinition "" - set objopts [list] - set methodopts [list] - set type "" - - if {[info exists :required] && ${:required}} { - lappend objopts required - lappend methodopts required - } - - if {[info exists :type]} { - if {[string match ::* ${:type}]} { - set type [expr {[::nsf::is metaclass ${:type}] ? "class" : "object"}] - lappend objopts type=${:type} - lappend methodopts type=${:type} - } else { - set type ${:type} - } - } - # TODO: remove multivalued check on relations by handling multivalued - # not in relation, but in the converters - set objUpper 1 - set methodUpper 1 - set objLower 1 - set methodLower 1 - if {[info exists :multivalued] && ${:multivalued}} { - if {!([info exists :type] && ${:type} eq "relation")} { - #lappend objopts multivalued - set objUpper * - } else { - #puts stderr "ignore multivalued for $name in relation" - } - } - if {[info exists :allowempty]} { - set objLower 0 - set methodLower 0 - } - if {$objLower != 1 || $objUpper != 1} { - lappend objopts "$objLower..$objUpper" - } - if {$methodLower != 1 || $methodUpper != 1} { - lappend methodopts "$methodLower..$methodUpper" - } - - if {[info exists :arg]} { - set prefix [expr {$type eq "object" || $type eq "class" ? "type" : "arg"}] - lappend objopts $prefix=${:arg} - if {![info exists :disposition]} { - lappend methodopts $prefix=${:arg} - } - } - foreach att {convert} { - if {[info exists :$att]} { - lappend objopts $att - lappend methodopts $att - } - } - if {[info exists :default]} { - set arg ${:default} - # deactivated for now: || [string first {$} $arg] > -1 - if {[string match {*\[*\]*} $arg] - && $type ne "substdefault"} { - lappend objopts substdefault - } - } elseif {[info exists :initcmd]} { - set arg ${:initcmd} - lappend objopts initcmd - } - if {[info exists :methodname]} { - if {${:methodname} ne ${:name}} { - lappend objopts arg=${:methodname} - lappend methodopts arg=${:methodname} - #puts stderr "..... setting arg for methodname: [::nsf::self] has arg arg=${:methodname}" - } - } - if {$type ne ""} { - set objopts [linsert $objopts 0 $type] - # Never add "substdefault" to methodopts, since these are for - # provided values, not for defaults. - if {$type ne "substdefault"} {set methodopts [linsert $methodopts 0 $type]} - } - - if {[info exists :disposition]} { - set objopts [linsert $objopts 0 ${:disposition}] - } elseif {$type ni [list "" "boolean" "integer" "object" "class" \ - "metaclass" "baseclass" "parameter" \ - "alnum" "alpha" "ascii" "control" "digit" "double" \ - "false" "graph" "lower" "print" "punct" "space" "true" \ - "wideinteger" "wordchar" "xdigit" ]} { - #puts stderr "adding slot for type $type" - lappend objopts slot=[::nsf::self] - } - - if {[llength $objopts] > 0} { - #append objparamdefinition :[join $objopts ,] - set objparamdefinition [list $name:[join $objopts ,]] - } - if {[llength $methodopts] > 0} { - set methodparamdefinition [join $methodopts ,] - } - if {[info exists arg]} { - lappend objparamdefinition $arg - } - #puts stderr "*** [::nsf::current method] ${name} returns [list oparam $objparamdefinition mparam $methodparamdefinition]" - return [list oparam $objparamdefinition mparam $methodparamdefinition] - } - - proc ::nsf::parametersfromslots {object} { + Object protected method objectparameter {{lastparameter __initcmd:initcmd,optional}} { + #puts stderr "... objectparameter for [::nsf::self]" set parameterdefinitions [list] - foreach slot [::nsf::dispatch $object ::nsf::methods::object::info::lookupslots -type ::nx::Slot] { - # Skip some slots for xotcl; - # TODO: maybe different parametersfromslots for xotcl? - if {[::nsf::is class ::xotcl::Object] - && [::nsf::dispatch $object ::nsf::methods::object::info::hastype ::xotcl::Object] && - ([$slot name] eq "mixin" || [$slot name] eq "filter") - } continue - array set "" [$slot toParameterSpec] - # insert dash carefully to first element ... - # TODO we should do this already in toParameterSpec, but setter uses oparam without the dash - set param [list -[lindex $(oparam) 0]] - if {[llength $(oparam)] > 1} {lappend param [lindex $(oparam) 1]} - lappend parameterdefinitions $param + foreach slot [nsf::dispatch [self] ::nsf::methods::object::info::lookupslots -type ::nx::Slot] { + lappend parameterdefinitions [$slot getParameterSpec] } - return $parameterdefinitions - } - - Object protected method objectparameter {{lastparameter __initcmd:initcmd,optional}} { - #puts stderr "... objectparameter [::nsf::self]" - set parameterdefinitions [::nsf::parametersfromslots [::nsf::self]] - if {[::nsf::is class [::nsf::self]]} { - lappend parameterdefinitions -attributes:alias -# {{-object-mixin:forward,arg=::nsf::relation %self %proc}} \ -# {{-object-filter:forward,arg=::nsf::relation %self %proc}} - } - - # {{-F:forward,arg=%self foo %1 a b c %method}} - lappend parameterdefinitions \ - -noinit:alias,arg=::nsf::methods::object::noinit,noarg \ - -volatile:alias,noarg \ - {*}$lastparameter + lappend parameterdefinitions {*}$lastparameter #puts stderr "*** parameter definition for [::nsf::self]: $parameterdefinitions" return $parameterdefinitions } - } + namespace eval ::nx { ############################################ - # RelationSlot + # class nx::RelationSlot ############################################ MetaSlot create ::nx::RelationSlot + ::nsf::relation RelationSlot superclass ObjectParameterSlot + createBootstrapAttributeSlots ::nx::RelationSlot { {elementtype ::nx::Class} - {multivalued true} - {type relation} - {nosetter} + {nosetter false} } - ::nsf::relation RelationSlot superclass ObjectParameterSlot - ::nsf::alias RelationSlot assign ::nsf::relation - - RelationSlot protected method init {} { - if {${:type} ne "relation"} { - error "RelationSlot requires type == \"relation\"" - } ::nsf::next + if {!${:nosetter}} { + :makeForwarder + } } + # + # create methods for slot operations assign/get/add/delete + # + ::nsf::alias RelationSlot assign ::nsf::relation + RelationSlot protected method delete_value {obj prop old value} { + # + # helper method for the delete operation + # if {[string first * $value] > -1 || [string first \[ $value] > -1} { # value contains globbing meta characters if {${:elementtype} ne "" && ![string match ::* $value]} { @@ -1064,13 +992,11 @@ } RelationSlot public method add {obj prop value {pos 0}} { - if {![set :multivalued]} { - error "Property $prop of ${:domain}->$obj ist not multivalued" - } set oldSetting [::nsf::relation $obj $prop] # use uplevel to avoid namespace surprises uplevel [list ::nsf::relation $obj $prop [linsert $oldSetting $pos $value]] } + RelationSlot public method delete {-nocomplain:switch obj prop value} { uplevel [list ::nsf::relation $obj $prop [:delete_value $obj $prop [::nsf::relation $obj $prop] $value]] } @@ -1080,48 +1006,36 @@ ############################################ proc register_system_slots {os} { - ::nx::RelationSlot create ${os}::Class::slot::superclass - ::nsf::alias ${os}::Class::slot::superclass assign ::nsf::relation - - #::nx::RelationSlot create ${os}::Object::slot::class -multivalued false - #::nsf::alias ${os}::Object::slot::class assign ::nsf::relation + # method "class" is a plain forwarder to relation (no slot) ::nsf::forward ${os}::Object class ::nsf::relation %self class - ::nx::RelationSlot create ${os}::Object::slot::mixin \ - -methodname object-mixin + # all other relation cmds are defined as slots + ::nx::RelationSlot create ${os}::Class::slot::superclass + ::nx::RelationSlot create ${os}::Object::slot::mixin \ + -forwardername object-mixin ::nx::RelationSlot create ${os}::Object::slot::filter -elementtype "" \ - -methodname object-filter + -forwardername object-filter - ::nx::RelationSlot create ${os}::Class::slot::mixin -methodname class-mixin - + ::nx::RelationSlot create ${os}::Class::slot::mixin \ + -forwardername class-mixin ::nx::RelationSlot create ${os}::Class::slot::filter -elementtype "" \ - -methodname class-filter + -forwardername class-filter - # Create two conveniance slots to allow configuration of - # object-slots for classes via object-mixins # - # Approach 1: create RelationSlot with nosetter + # Create two convenience object parameters to allow configuration + # of per-object mixins and filters for classes. # - #::nx::RelationSlot create ${os}::Class::slot::object-mixin -nosetter 1 - #::nx::RelationSlot create ${os}::Class::slot::object-filter -elementtype "" -nosetter 1 - - # - # Approach 2: use parameter forwarder - # - #::nx::ObjectParameterSlot create ${os}::Class::slot::object-mixin \ - # -disposition forward -arg "::nsf::relation %self %proc" - #::nx::ObjectParameterSlot create ${os}::Class::slot::object-filter \ - # -disposition forward -arg "::nsf::relation %self %proc" - - # - # Approach 3: use parameter alias - # ::nx::ObjectParameterSlot create ${os}::Class::slot::object-mixin \ - -disposition alias -arg "::nsf::classes::nx::Object::mixin" + -methodname "::nsf::classes::nx::Object::mixin" ::nx::ObjectParameterSlot create ${os}::Class::slot::object-filter \ - -disposition alias -arg "::nsf::classes::nx::Object::filter" + -methodname "::nsf::classes::nx::Object::filter" + ::nx::ObjectParameterSlot create ${os}::Class::slot::attributes + ::nx::ObjectParameterSlot create ${os}::Object::slot::noinit \ + -methodname ::nsf::methods::object::noinit -noarg true + ::nx::ObjectParameterSlot create ${os}::Object::slot::volatile -noarg true + # # Define method "guard" for mixin- and filter-slots of Object and Class # @@ -1168,34 +1082,145 @@ MetaSlot create ::nx::Attribute -superclass ::nx::ObjectParameterSlot createBootstrapAttributeSlots ::nx::Attribute { - allowempty - convert - incremental + {arg} + {convert false} + {default} + {incremental} + {multiplicity 1..1} + {nosetter false} + {type} + initcmd valuecmd defaultcmd valuechangedcmd - nosetter } - Attribute method __default_from_cmd {obj cmd var sub op} { - #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" - ::nsf::dispatch $obj -frame object \ - ::trace remove variable $var $op [list [::nsf::self] [::nsf::current method] $obj $cmd] - ::nsf::setvar $obj $var [$obj eval $cmd] + ::nx::Attribute protected method checkInstVar {} { + if {${:per-object} && [info exists :default] } { + if {![::nsf::existsvar ${:domain} ${:name}]} { + ::nsf::setvar ${:domain} ${:name} ${:default} + } + } } - Attribute method __value_from_cmd {obj cmd var sub op} { - #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" - ::nsf::setvar $obj $var [$obj eval $cmd] + + ::nx::Attribute protected method getParameterOptions {{-withMultiplicity 0} {-withSubstdefault 0}} { + set options "" + if {[info exists :type]} { + if {[string match ::* ${:type}]} { + lappend options [expr {[::nsf::is metaclass ${:type}] ? "class" : "object"}] type=${:type} + } else { + lappend options ${:type} + if {${:type} ni [list "" "boolean" "integer" "object" "class" \ + "metaclass" "baseclass" "parameter" \ + "alnum" "alpha" "ascii" "control" "digit" "double" \ + "false" "graph" "lower" "print" "punct" "space" "true" \ + "wideinteger" "wordchar" "xdigit" ]} { + lappend options slot=[::nsf::self] + } + } + } + if {${:required}} {lappend options required} + if {${:convert}} {lappend options convert} + if {$withMultiplicity && [info exists :multiplicity] && ${:multiplicity} ne "1..1"} { + lappend options ${:multiplicity} + } + if {$withSubstdefault && [info exists :substdefault] && ${:substdefault}} { + lappend options substdefault + } + #puts stderr "*** getParameterOptions [self] return $options" + return $options } - Attribute method __value_changed_cmd {obj cmd var sub op} { - # puts stderr "**************************" - # puts "valuechanged obj=$obj cmd=$cmd, var=$var, op=$op, ...\n$obj exists $var -> [::nsf::setvar $obj $var]" - eval $cmd + + ::nx::Attribute public method getParameterSpec {} { + set options [:getParameterOptions -withMultiplicity true -withSubstdefault true] + if {[info exists :initcmd]} { + lappend options initcmd + return [list [:namedParameterSpec ${:name} $options] ${:initcmd}] + + } elseif {[info exists :default]} { + # deactivated for now: || [string first {$} ${:default}] > -1 + if {[string match {*\[*\]*} ${:default}]} { + lappend options substdefault + } + return [list [:namedParameterSpec ${:name} $options] ${:default}] + } else { + return [list [:namedParameterSpec ${:name} $options]] + } } - Attribute protected method init {} { - # Do first ordinary slot initialization - ::nsf::next + + ::nx::Attribute protected method isMultivalued {} { + return [string match {*..[n*]} ${:multiplicity}] + } + + ::nx::Attribute protected method needsForwarder {} { + # + # We just forward, when + # * "assign" and "add" are still untouched, or + # * or incremental is specified + # + if {[:info lookup method assign] ne "::nsf::classes::nx::Attribute::assign"} {return 1} + if {[:info lookup method add] ne "::nsf::classes::nx::Attribute::add"} {return 1} + if {![info exists :incremental]} {return 0} + #if {![:isMultivalued]} {return 0} + #puts stderr "[self] ismultivalued" + return 1 + } + + ::nx::Attribute protected method makeAccessor {} { + if {${:nosetter}} { + #puts stderr "Do not register forwarder ${:domain} ${:name}" + return + } + if {[:needsForwarder]} { + :makeForwarder + :makeIncrementalOperations + } else { + :makeSetter + } + } + + ::nx::Attribute public method reconfigure {} { + puts stderr "*** Should we reconfigure [self]???" + :makeAccessor + } + + ::nx::Attribute protected method init {} { + next + :checkInstVar + :makeAccessor + :handleTraces + } + + ::nx::Attribute protected method makeSetter {} { + set options [:getParameterOptions -withMultiplicity true] + set setterParam ${:name} + if {[llength $options]>0} {append setterParam :[join $options ,]} + #puts stderr [list ::nsf::setter ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] $setterParam] + ::nsf::setter ${:domain} {*}[expr {${:per-object} ? "-per-object" : ""}] $setterParam + } + + ::nx::Attribute protected method makeIncrementalOperations {} { + set options [:getParameterOptions -withMultiplicity true] + set body {::nsf::setvar $obj $var $value} + lappend options slot=[::nsf::self] + + if {[:info lookup method assign] eq "::nsf::classes::nx::Attribute::assign"} { + puts stderr ":public method assign [list obj var [:namedParameterSpec -prefix {} value $options]] $body" + :public method assign [list obj var [:namedParameterSpec -prefix {} value $options]] $body + } + if {[:isMultivalued] && [:info lookup method add] eq "::nsf::classes::nx::Attribute::add"} { + set options_single [:getParameterOptions] + lappend options_single slot=[::nsf::self] + puts stderr ":public method add [list obj prop [:namedParameterSpec -prefix {} value $options_single] {pos 0}] {::nsf::next}" + :public method add [list obj prop [:namedParameterSpec -prefix {} value $options_single] {pos 0}] {::nsf::next} + } else { + # TODO should we deactivate add/delete? + } + } + + ::nx::Attribute protected method handleTraces {} { + # essentially like before set __initcmd "" set trace {::nsf::dispatch [::nsf::self] -frame object ::trace} # There might be already default values registered on the @@ -1215,26 +1240,6 @@ append __initcmd "$trace add variable [list ${:name}] write \ \[list [::nsf::self] __value_changed_cmd \[::nsf::self\] [list [set :valuechangedcmd]]\]" } - - array set "" [:toParameterSpec ${:name}] - #puts stderr "Attribute.init valueParam for [::nsf::self] is $(mparam)" - if {$(mparam) ne ""} { - if {[info exists :multivalued] && ${:multivalued}} { - # set variable "body" to minimize problems with spacing, since - # the body is literally compared by the slot optimizer. - set body {::nsf::setvar $obj $var $value} - :public method assign [list obj var value:$(mparam),1..*,slot=[::nsf::self]] \ - $body - - #puts stderr "adding add method for [::nsf::self] with value:$(mparam)" - :public method add [list obj prop value:$(mparam),slot=[::nsf::self] {pos 0}] { - ::nsf::next - } - } else { - set body {::nsf::setvar $obj $var $value} - :public method assign [list obj var value:$(mparam),slot=[::nsf::self]] $body - } - } if {$__initcmd ne ""} { if {${:per-object}} { ${:domain} eval $__initcmd @@ -1243,66 +1248,112 @@ } } + # + # implementation of forwarder operations: assign get add delete + # + ::nsf::alias Attribute get ::nsf::setvar + ::nsf::alias Attribute assign ::nsf::setvar + + Attribute public method add {obj prop value {pos 0}} { + if {![:isMultivalued]} { + puts stderr "... vars [[self] info vars] // [[self] eval {set :multiplicity}]" + error "Property $prop of [set :domain] ist not multivalued" + } + if {[::nsf::existsvar $obj $prop]} { + ::nsf::setvar $obj $prop [linsert [::nsf::setvar $obj $prop] $pos $value] + } else { + ::nsf::setvar $obj $prop [list $value] + } + } + + Attribute public method delete {-nocomplain:switch obj prop value} { + set old [::nsf::setvar $obj $prop] + set p [lsearch -glob $old $value] + if {$p>-1} {::nsf::setvar $obj $prop [lreplace $old $p $p]} else { + error "$value is not a $prop of $obj (valid are: $old)" + } + } + + # + # implementation of trace commands + # + Attribute method __default_from_cmd {obj cmd var sub op} { + #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" + ::nsf::dispatch $obj -frame object \ + ::trace remove variable $var $op [list [::nsf::self] [::nsf::current method] $obj $cmd] + ::nsf::setvar $obj $var [$obj eval $cmd] + } + Attribute method __value_from_cmd {obj cmd var sub op} { + #puts "GETVAR [::nsf::current method] obj=$obj cmd=$cmd, var=$var, op=$op" + ::nsf::setvar $obj $var [$obj eval $cmd] + } + Attribute method __value_changed_cmd {obj cmd var sub op} { + # puts stderr "**************************" + # puts "valuechanged obj=$obj cmd=$cmd, var=$var, op=$op, ...\n$obj exists $var -> [::nsf::setvar $obj $var]" + eval $cmd + } + + ################################################################## # Define a mixin class for optimizing slots ################################################################## - Class create ::nx::Attribute::Optimizer { + # Class create ::nx::Attribute::Optimizer { - :public method method args {set r [::nsf::next]; :optimize; return $r} - :public method forward args {set r [::nsf::next]; :optimize; return $r} - :protected method init args {set r [::nsf::next]; :optimize; return $r} + # :public method method args {set r [::nsf::next]; :optimize; return $r} + # :public method forward args {set r [::nsf::next]; :optimize; return $r} + # :protected method init args {set r [::nsf::next]; :optimize; return $r} - :public method optimize {} { - #puts stderr "OPTIMIZER ${:name} incremental -[info exists :incremental]" - if {![info exists :methodname]} {return} - if {${:per-object}} { - set perObject -per-object - set infokind object - } else { - set perObject "" - set infokind class - } - if {[::nsf::dispatch ${:domain} ::nsf::methods::${infokind}::info::method handle ${:name}] ne ""} { - #puts stderr "OPTIMIZER RESETTING ${:domain} slot ${:name}" - ::nsf::forward ${:domain} {*}$perObject ${:name} \ - ${:manager} \ - [list %1 [${:manager} defaultmethods]] %self \ - ${:methodname} - } - #puts "*** stderr OPTIMIZER incremental [info exists :incremental] def '[set :defaultmethods]' nosetter [info exists :nosetter]" - if {[info exists :incremental] && ${:incremental}} return - if {[info exists :nosetter]} return - if {[set :defaultmethods] ne {get assign}} return + # :public method optimize {} { + # #puts stderr "OPTIMIZER ${:name} incremental -[info exists :incremental]" + # if {![info exists :methodname]} {return} + # if {${:per-object}} { + # set perObject -per-object + # set infokind object + # } else { + # set perObject "" + # set infokind class + # } + # if {[::nsf::dispatch ${:domain} ::nsf::methods::${infokind}::info::method handle ${:name}] ne ""} { + # #puts stderr "OPTIMIZER RESETTING ${:domain} slot ${:name}" + # ::nsf::forward ${:domain} {*}$perObject ${:name} \ + # ${:manager} \ + # [list %1 [${:manager} defaultmethods]] %self \ + # ${:methodname} + # } + # #puts "*** stderr OPTIMIZER incremental [info exists :incremental] def '[set :defaultmethods]' nosetter [info exists :nosetter]" + # if {[info exists :incremental] && ${:incremental}} return + # if {[info exists :nosetter]} return + # if {[set :defaultmethods] ne {get assign}} return - # - # Check, if the definition of "assign" and "get" are still the - # defaults. If this is not the case, we cannot replace them with - # the plain setters. - # - set assignInfo [:info method definition [:info lookup method assign]] - #puts stderr "OPTIMIZER assign=$assignInfo//[lindex $assignInfo end]//[:info precedence]" - if {$assignInfo ne "::nx::ObjectParameterSlot public alias assign ::nsf::setvar" && - [lindex $assignInfo end] ne {::nsf::setvar $obj $var $value} } return - #if {$assignInfo ne "::nx::ObjectParameterSlot public alias assign ::nsf::setvar"} return + # # + # # Check, if the definition of "assign" and "get" are still the + # # defaults. If this is not the case, we cannot replace them with + # # the plain setters. + # # + # set assignInfo [:info method definition [:info lookup method assign]] + # #puts stderr "OPTIMIZER assign=$assignInfo//[lindex $assignInfo end]//[:info precedence]" + # if {$assignInfo ne "::nx::ObjectParameterSlot public alias assign ::nsf::setvar" && + # [lindex $assignInfo end] ne {::nsf::setvar $obj $var $value} } return + # #if {$assignInfo ne "::nx::ObjectParameterSlot public alias assign ::nsf::setvar"} return - set getInfo [:info method definition [:info lookup method get]] - if {$getInfo ne "::nx::ObjectParameterSlot public alias get ::nsf::setvar"} return + # set getInfo [:info method definition [:info lookup method get]] + # if {$getInfo ne "::nx::ObjectParameterSlot public alias get ::nsf::setvar"} return - array set "" [:toParameterSpec ${:name}] - if {$(mparam) ne ""} { - set setterParam [lindex $(oparam) 0] - # never pass substdefault to setter - regsub -all ,substdefault $setterParam "" setterParam - #puts stderr "setterParam=$setterParam, op=$(oparam)" - } else { - set setterParam ${:name} - } - ::nsf::setter ${:domain} {*}$perObject $setterParam - #puts stderr "::nsf::setter ${:domain} {*}$perObject $setterParam" - } - } - # register the optimizer per default - Attribute mixin add Attribute::Optimizer + # array set "" [:toParameterSpec ${:name}] + # if {$(mparam) ne ""} { + # set setterParam [lindex $(oparam) 0] + # # never pass substdefault to setter + # regsub -all ,substdefault $setterParam "" setterParam + # #puts stderr "setterParam=$setterParam, op=$(oparam)" + # } else { + # set setterParam ${:name} + # } + # ::nsf::setter ${:domain} {*}$perObject $setterParam + # #puts stderr "::nsf::setter ${:domain} {*}$perObject $setterParam" + # } + # } + # # register the optimizer per default + # Attribute mixin add Attribute::Optimizer ################################################################## # Define method "attribute" for convenience @@ -1427,11 +1478,6 @@ } } - # TODO: This is the slots method.... remove it for now. - # - #Class forward slots %self contains \ - # -object {%::nsf::dispatch [::nsf::self] -objframe ::subst [::nsf::self]::slot} - ################################################################## # copy/move implementation ################################################################## Index: library/xotcl/library/xotcl2.tcl =================================================================== diff -u -r28fd214e129bc6c2384a2ef587a2be8b480c7248 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision 28fd214e129bc6c2384a2ef587a2be8b480c7248) +++ library/xotcl/library/xotcl2.tcl (.../xotcl2.tcl) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -363,13 +363,15 @@ Object instproc self {} {::xotcl::self} # - # objectparameter definition, backwards upward compatible. We use + # Method objectparameter, backwards upward compatible. We use # here the definition of parametersfromslots from nx.tcl # ::xotcl::Object instproc objectparameter {} { - set parameterdefinitions [::nsf::parametersfromslots [self]] + set parameterdefinitions [list] + foreach slot [::nsf::dispatch [self] ::nsf::methods::object::info::lookupslots -type ::nx::Slot] { + lappend parameterdefinitions [$slot getParameterSpec] + } lappend parameterdefinitions args - #puts stderr "*** parameter definition for [self]: $parameterdefinitions" return $parameterdefinitions } @@ -395,22 +397,20 @@ # if not specified ${os}::Object alloc ${os}::Class::slot ${os}::Object alloc ${os}::Object::slot - ::nx::RelationSlot create ${os}::Class::slot::superclass ::nsf::alias ${os}::Class::slot::superclass assign ::nsf::relation - ::nx::RelationSlot create ${os}::Object::slot::class -multivalued false + ::nx::RelationSlot create ${os}::Object::slot::class ::nsf::alias ${os}::Object::slot::class assign ::nsf::relation - ::nx::RelationSlot create ${os}::Object::slot::mixin \ - -methodname object-mixin + -forwardername object-mixin ::nx::RelationSlot create ${os}::Object::slot::filter \ - -methodname object-filter \ + -forwardername object-filter \ -elementtype "" ::nx::RelationSlot create ${os}::Class::slot::instmixin \ - -methodname class-mixin + -forwardername class-mixin ::nx::RelationSlot create ${os}::Class::slot::instfilter \ - -methodname class-filter \ + -forwardername class-filter \ -elementtype "" } register_system_slots ::xotcl Index: library/xotcl/tests/slottest.xotcl =================================================================== diff -u -rc13b31f42883e5f30ca6fd505efd0267c2c30ea8 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision c13b31f42883e5f30ca6fd505efd0267c2c30ea8) +++ library/xotcl/tests/slottest.xotcl (.../slottest.xotcl) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -157,14 +157,14 @@ } Class publishes -slots { - Role create written_by -references Person -multivalued true - Role create has_published -references Paper -multivalued true + Role create written_by -references Person -multiplicity 0..n + Role create has_published -references Paper -multiplicity 0..n } Class Project -slots { Attribute create name Role create manager -references Person - Role create member -references Person -multivalued true + Role create member -references Person -multiplicity 0..n } puts [Person serialize] @@ -266,7 +266,7 @@ # maybe work directly on ::xotcl::Attribute would be nicer, when # ::xotcl::Attribute would be true alias for ::nx::Attribute ... -::nx::Attribute mixin delete ::nx::Attribute::Optimizer +#::nx::Attribute mixin delete ::nx::Attribute::Optimizer Class C1 -parameter {a {b 10} {c "Hello World"}} C1 c1 -a 1 @@ -283,7 +283,7 @@ Attribute create c -default "Hello World" } C2 c2 -a 1 -? {c2 procsearch a} "::C2 instforward a" +? {c2 procsearch a} "::C2 instparametercmd a" ? {c2 a} 1 ? {c2 b} 10 ? {c2 c} "Hello World" @@ -292,7 +292,7 @@ ? {c2 a} 1 "new indirect parametercmd" ? {c2 a 1} 1 "new indirect parametercmd" -::nx::Attribute mixin add ::nx::Attribute::Optimizer +#::nx::Attribute mixin add ::nx::Attribute::Optimizer Class C3 -slots { Attribute create a @@ -334,8 +334,7 @@ ? {a0 procsearch f3} "::a0 proc f3" ? {a0 procsearch f4} "::a0 forward f4" ? {a0 procsearch set} "::xotcl::Object instcmd set" -#? {A slot foo info lookup method assign} "::nsf::classes::nx::ObjectParameterSlot::assign" -? {A::slot::foo info lookup method assign} "::nsf::classes::nx::ObjectParameterSlot::assign" +? {A::slot::foo info lookup method assign} "::nsf::classes::nx::Attribute::assign" # redefine setter for foo of class A #A slot foo method assign {domain var val} ... @@ -372,7 +371,7 @@ Class Person -slots { Attribute create name Attribute create age -default 0 - Attribute create projects -default {} -multivalued true -incremental true -allowempty true + Attribute create projects -default {} -multiplicity 0..n -incremental true } Person p1 -name "Gustaf" @@ -416,7 +415,7 @@ } Person slots { - Attribute create projects -default "" -multivalued true -incremental true -type ::Project -allowempty true + Attribute create projects -default "" -multiplicity 0..n -incremental true -type ::Project Attribute create salary -type integer } @@ -504,7 +503,7 @@ Class create A -slots { Attribute create foo -default 1 { - :public method assign { domain var value} { + :public method assign {domain var value} { if {$value < 0 || $value > 99} { error "$value is not in the range of 0 .. 99" } @@ -517,9 +516,10 @@ ? {a1 foo 10} 10 ? {a1 foo 20} 20 ? {a1 foo} 20 +? {a1 foo -1} "-1 is not in the range of 0 .. 99" ? {catch {a1 foo -1}} 1 -? {catch {a1 foo 100}} 1 -? {catch {a1 foo 99}} 0 +? {a1 foo 100} "100 is not in the range of 0 .. 99" +? {a1 foo 99} 99 set x [Object new -set x 1 -contains { Object new -set x 1.1 Index: tests/alias.test =================================================================== diff -u -r8bfe3ac01fe62c1917587fc5fe76952d3ff8f397 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- tests/alias.test (.../alias.test) (revision 8bfe3ac01fe62c1917587fc5fe76952d3ff8f397) +++ tests/alias.test (.../alias.test) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -6,10 +6,10 @@ Test parameter count 10 Test case alias-preliminaries { - # The system methods of Object are either alias or forwarders - ? {lsort [::nx::ObjectParameterSlot info methods -methodtype alias]} {assign get} - ? {::nx::ObjectParameterSlot info method definition get} \ - "::nx::ObjectParameterSlot public alias get ::nsf::setvar" + # The system methods of nx::Attribute are either alias or forwarders + ? {lsort [::nx::Attribute info methods -methodtype alias]} {assign get} + ? {::nx::Attribute info method definition get} \ + "::nx::Attribute public alias get ::nsf::setvar" # define an alias and retrieve its definition set cmd "::nx::Object public alias set ::set" Index: tests/info-method.test =================================================================== diff -u -r28fd214e129bc6c2384a2ef587a2be8b480c7248 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- tests/info-method.test (.../info-method.test) (revision 28fd214e129bc6c2384a2ef587a2be8b480c7248) +++ tests/info-method.test (.../info-method.test) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -234,7 +234,7 @@ } D create d1 - ? {D info lookup slots} "::nx::Class::slot::object-mixin ::nx::Class::slot::mixin ::nx::Class::slot::superclass ::nx::Class::slot::object-filter ::nx::Class::slot::filter" + ? {D info lookup slots} "::nx::Class::slot::object-mixin ::nx::Class::slot::mixin ::nx::Class::slot::superclass ::nx::Class::slot::object-filter ::nx::Class::slot::filter ::nx::Class::slot::attributes ::nx::Object::slot::volatile ::nx::Object::slot::noinit" ? {D info slots} "::D::slot::b ::D::slot::a2 ::D::slot::c" ? {::nx::Object info method parameter info} "" } Index: tests/method-modifiers.test =================================================================== diff -u -rcacb1074477ab383bffc999a68e741ef11211de3 -rbd1cce484140aaf66113cf647f060ae48d32b24f --- tests/method-modifiers.test (.../method-modifiers.test) (revision cacb1074477ab383bffc999a68e741ef11211de3) +++ tests/method-modifiers.test (.../method-modifiers.test) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -277,6 +277,7 @@ ? {c1 d} "::c1: unable to dispatch method 'd'" ? {C A 2} 2 + ? {C A} 2 ? {C B} B2 ? {C C} C2 ? {C D} "Method 'D' unknown for ::C. Consider '::C create D ' instead of '::C D '" Index: tests/parameters.test =================================================================== diff -u -rbc86c5d5cba70c6f5db2b184c143a7dde00cca4c -rbd1cce484140aaf66113cf647f060ae48d32b24f --- tests/parameters.test (.../parameters.test) (revision bc86c5d5cba70c6f5db2b184c143a7dde00cca4c) +++ tests/parameters.test (.../parameters.test) (revision bd1cce484140aaf66113cf647f060ae48d32b24f) @@ -249,10 +249,10 @@ C create c1 ? {C eval {:objectparameter}} \ - "-object-mixin:alias,arg=::nsf::classes::nx::Object::mixin -mixin:relation,arg=class-mixin,slot=::nx::Class::slot::mixin -superclass:relation,slot=::nx::Class::slot::superclass -object-filter:alias,arg=::nsf::classes::nx::Object::filter -filter:relation,arg=class-filter,slot=::nx::Class::slot::filter -attributes:alias -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-object-mixin:alias,arg=::nsf::classes::nx::Object::mixin -mixin:alias -superclass:alias -object-filter:alias,arg=::nsf::classes::nx::Object::filter -filter:alias -attributes:alias -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg __initcmd:initcmd,optional" ? {c1 eval {:objectparameter}} \ - "-a -b:boolean {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-a -b:boolean {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" } ####################################################### @@ -265,13 +265,13 @@ c1 class Object ? {c1 eval :objectparameter} \ - "-mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" Class create D -superclass C -attributes {d:required} D create d1 -d 100 ? {d1 eval :objectparameter} \ - "-d:required -a -b:boolean {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-d:required -a -b:boolean {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" } ####################################################### @@ -287,27 +287,27 @@ Class create M2 -attributes {b2} D mixin M ? {d1 eval :objectparameter} \ - "-b -m1 -m2 -d:required -a {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" \ + "-b -m1 -m2 -d:required -a {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" \ "mixin added" M mixin M2 ? {d1 eval :objectparameter} \ - "-b2 -b -m1 -m2 -d:required -a {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" \ + "-b2 -b -m1 -m2 -d:required -a {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" \ "transitive mixin added" D mixin "" #we should have again the old interface ? {d1 eval :objectparameter} \ - "-d:required -a -b:boolean {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-d:required -a -b:boolean {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" C mixin M ? {d1 eval :objectparameter} \ - "-b2 -b -m1 -m2 -d:required -a {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" \ + "-b2 -b -m1 -m2 -d:required -a {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" \ "mixin added" C mixin "" #we should have again the old interface ? {d1 eval :objectparameter} \ - "-d:required -a -b:boolean {-c 1} -mixin:relation,arg=object-mixin,slot=::nx::Object::slot::mixin -filter:relation,arg=object-filter,slot=::nx::Object::slot::filter -noinit:alias,arg=::nsf::methods::object::noinit,noarg -volatile:alias,noarg __initcmd:initcmd,optional" + "-d:required -a -b:boolean {-c 1} -volatile:alias,noarg -noinit:alias,arg=::nsf::methods::object::noinit,noarg -mixin:alias -filter:alias __initcmd:initcmd,optional" } ####################################################### @@ -366,10 +366,6 @@ {expected class but got "D11" for parameter "-class"} \ "pass non-existing class" - ? {D public method foo {a:relation} {}} \ - {Parameter option 'relation' not allowed} \ - "don't allow relation option as method parameter" - ? {D public method foo {a:double} {return $a}} \ {::nsf::classes::D::foo} \ "allow 'string is XXXX' for argument checking" @@ -428,7 +424,7 @@ ? {d1 foo {o d1 x}} \ {invalid value in "o d1 x": expected object but got "x" for parameter "m"} \ "multiple values" - + Class create Foo -attributes { {ints:integer,1..*} } @@ -438,7 +434,7 @@ # make slot incremental Foo::slot::ints eval { set :incremental 1 - :optimize + :reconfigure } Foo create foo -ints {1 2} @@ -842,19 +838,17 @@ # TODO: we have no good interface for querying the slot notation for parameters proc ::parameterFromSlot {class objectparameter} { set slot ${class}::slot::$objectparameter - array set "" [$slot toParameterSpec $objectparameter] - return $(oparam) + return [$slot getParameterSpec] } - ? {::parameterFromSlot ParamTest o} "o:object" - ? {::parameterFromSlot ParamTest c} "c:class" - ? {::parameterFromSlot ParamTest c1} "c1:class,type=::MC" - ? {::parameterFromSlot ParamTest d} "d:object,type=::C" - ? {::parameterFromSlot ParamTest d1} "d1:object,type=::C" - #? {::parameterFromSlot ParamTest mix} "mix:hasmixin,arg=M,slot=::ParamTest::slot::mix" - ? {::parameterFromSlot ParamTest x} "x:object,1..* o" - ? {::parameterFromSlot ParamTest u} "u:upper,slot=::ParamTest::slot::u" - ? {::parameterFromSlot ParamTest us} "us:upper,1..*,slot=::ParamTest::slot::us" + ? {::parameterFromSlot ParamTest o} "-o:object" + ? {::parameterFromSlot ParamTest c} "-c:class" + ? {::parameterFromSlot ParamTest c1} "-c1:class,type=::MC" + ? {::parameterFromSlot ParamTest d} "-d:object,type=::C" + ? {::parameterFromSlot ParamTest d1} "-d1:object,type=::C" + ? {::parameterFromSlot ParamTest x} "-x:object,1..* o" + ? {::parameterFromSlot ParamTest u} "-u:upper,slot=::ParamTest::slot::u" + ? {::parameterFromSlot ParamTest us} "-us:upper,slot=::ParamTest::slot::us,1..*" ? {ParamTest create p -o o} ::p ? {ParamTest create p -o xxx} \ @@ -889,7 +883,7 @@ {invalid value in "A B c": expected upper but got "c" for parameter "-us"} ParamTest::slot::us eval { set :incremental 1 - :optimize + :reconfigure } ? {ParamTest create p -us {A B}} ::p ? {p us add C end} "A B C" @@ -907,8 +901,8 @@ #} ParamTest eval { :attribute os { - :type object - :multivalued true + :type object + :multiplicity 1..n } } @@ -1087,7 +1081,7 @@ Test parameter count 1000 Test case slot-optimizer { - Class create C -attributes {a b:integer c:integer,multivalued} + Class create C -attributes {a b:integer c:integer,0..n} C create c1 ? {c1 a 1} 1 @@ -1173,7 +1167,6 @@ :attribute c {set :valuechangedcmd { ::nsf::setvar $obj $var 999 }} :create c1 } - ? {c1 a} 4 ? {c1 b} 44 ? {c1 c 5} 999