Index: generic/xotcl.c =================================================================== diff -u -rfe19549734064c3a57866e7e47743ec787f647e5 -rc6066a15de738754028991b2b57b8f1d5a1cccaa --- generic/xotcl.c (.../xotcl.c) (revision fe19549734064c3a57866e7e47743ec787f647e5) +++ generic/xotcl.c (.../xotcl.c) (revision c6066a15de738754028991b2b57b8f1d5a1cccaa) @@ -5359,6 +5359,8 @@ ParamDefsFormatOption(interp, nameStringObj, "method", &colonWritten, &first); } else if ((pPtr->flags & XOTCL_ARG_NOARG)) { ParamDefsFormatOption(interp, nameStringObj, "noarg", &colonWritten, &first); + } else if ((pPtr->flags & XOTCL_ARG_MULTIVALUED)) { + ParamDefsFormatOption(interp, nameStringObj, "multivalued", &colonWritten, &first); } innerlist = Tcl_NewListObj(0, NULL); @@ -6313,6 +6315,8 @@ paramPtr->flags |= XOTCL_ARG_INITCMD; } else if (strncmp(option, "method", 6) == 0) { paramPtr->flags |= XOTCL_ARG_METHOD; + } else if (strncmp(option, "multivalued", 11) == 0) { + paramPtr->flags |= XOTCL_ARG_MULTIVALUED; } else if (strncmp(option, "noarg", 5) == 0) { paramPtr->flags |= XOTCL_ARG_NOARG; paramPtr->nrArgs = 0; @@ -8650,7 +8654,7 @@ XOTclSetterMethod(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { XOTclObject *obj = (XOTclObject*)clientData; - if (!obj) return XOTclObjErrType(interp, objv[0], "Object"); + if (!obj) return XOTclObjErrType(interp, objv[0], "object"); if (objc > 2) return XOTclObjErrArgCnt(interp, obj->cmdName, objv[0], "?value?"); return setInstVar(interp, obj, objv[0], objc == 2 ? objv[1] : NULL); } @@ -8923,7 +8927,7 @@ */ #endif - if (!tcd || !tcd->obj) return XOTclObjErrType(interp, objv[0], "Object"); + if (!tcd || !tcd->obj) return XOTclObjErrType(interp, objv[0], "object"); if (tcd->passthrough) { /* two short cuts for simple cases */ /* early binding, cmd *resolved, we have to care only for objscope */ @@ -9439,6 +9443,34 @@ } static int +ArgumentCheck(Tcl_Interp *interp, Tcl_Obj *obj, struct XOTclParam CONST *pPtr, ClientData *clientData) { + int result; + + if (pPtr->flags & XOTCL_ARG_MULTIVALUED) { + int objc, i; + Tcl_Obj **ov; + + result = Tcl_ListObjGetElements(interp, obj, &objc, &ov); + if (result == TCL_OK) { + for (i=0; iconverter)(interp, ov[i], pPtr, clientData); + if (result != TCL_OK) { + Tcl_Obj *resultObj = Tcl_GetObjResult(interp); + INCR_REF_COUNT(resultObj); + XOTclVarErrMsg(interp, "invalid value in \"", ObjStr(obj), + "\": ", ObjStr(resultObj), (char *) NULL); + DECR_REF_COUNT(resultObj); + break; + } + } + } + } else { + result = (*pPtr->converter)(interp, obj, pPtr, clientData); + } + return result; +} + +static int ArgumentDefaults(parseContext *pcPtr, Tcl_Interp *interp, XOTclParam CONST *ifd, int nrParams) { XOTclParam CONST *pPtr; @@ -9488,7 +9520,7 @@ /* Check the default value, unless we have an INITCMD or METHOD */ if ((pPtr->flags & (XOTCL_ARG_INITCMD|XOTCL_ARG_METHOD)) == 0) { - if ((*pPtr->converter)(interp, newValue, pPtr, &checkedData) != TCL_OK) { + if (ArgumentCheck(interp, newValue, pPtr, &checkedData) != TCL_OK) { return TCL_ERROR; } } @@ -9569,7 +9601,7 @@ i, nppPtr->name, ObjStr(objv[p]), nppPtr->nrArgs, nppPtr->flags & XOTCL_ARG_REQUIRED ? "req":"not req", nppPtr->converter); #endif - if ((*nppPtr->converter)(interp, objv[p], nppPtr, + if (ArgumentCheck(interp, objv[p], nppPtr, &pcPtr->clientData[nppPtr-paramPtr]) != TCL_OK) { return TCL_ERROR; } @@ -9624,7 +9656,7 @@ /*fprintf(stderr, "... arg %s req %d converter %p try to set on %d: '%s' convertViaCmd %p\n", pPtr->name, pPtr->flags & XOTCL_ARG_REQUIRED, pPtr->converter, i, ObjStr(objv[o]), convertViaCmd);*/ - if ((*pPtr->converter)(interp, objv[o], pPtr, &pcPtr->clientData[i]) != TCL_OK) { + if (ArgumentCheck(interp, objv[o], pPtr, &pcPtr->clientData[i]) != TCL_OK) { return TCL_ERROR; } @@ -11653,7 +11685,7 @@ if (XOTclObjectIsClass(object)) { cl = (XOTclClass *)object; } else { - return XOTclObjErrType(interp, object->cmdName, "Class"); + return XOTclObjErrType(interp, object->cmdName, "class"); } if (value == NULL) { @@ -11673,7 +11705,7 @@ case RelationtypeSuperclassIdx: if (!XOTclObjectIsClass(object)) - return XOTclObjErrType(interp, object->cmdName, "Class"); + return XOTclObjErrType(interp, object->cmdName, "class"); cl = (XOTclClass *)object; if (value == NULL) { return ListSuperclasses(interp, cl, NULL, 0); @@ -11696,15 +11728,15 @@ XOTclClass *metaClass; if (!XOTclObjectIsClass(object)) - return XOTclObjErrType(interp, object->cmdName, "Class"); + return XOTclObjErrType(interp, object->cmdName, "class"); cl = (XOTclClass *)object; if (value == NULL) { return XOTclVarErrMsg(interp, "metaclass must be specified as third argument", (char *) NULL); } GetClassFromObj(interp, value, &metaClass, 0); - if (!metaClass) return XOTclObjErrType(interp, value, "Class"); + if (!metaClass) return XOTclObjErrType(interp, value, "class"); cl->object.flags |= XOTCL_IS_ROOT_CLASS; metaClass->object.flags |= XOTCL_IS_ROOT_META_CLASS; Index: generic/xotclError.c =================================================================== diff -u -r300e593347cf3f13d62ac4d21299a2278ff83d5e -rc6066a15de738754028991b2b57b8f1d5a1cccaa --- generic/xotclError.c (.../xotclError.c) (revision 300e593347cf3f13d62ac4d21299a2278ff83d5e) +++ generic/xotclError.c (.../xotclError.c) (revision c6066a15de738754028991b2b57b8f1d5a1cccaa) @@ -108,9 +108,9 @@ } extern int -XOTclObjErrType(Tcl_Interp *interp, Tcl_Obj *nm, char *wt) { +XOTclObjErrType(Tcl_Interp *interp, Tcl_Obj *value, char *type) { Tcl_ResetResult(interp); - Tcl_AppendResult(interp,"Invalid argument: cannot convert '",ObjStr(nm), "' to ", - wt, (char *) NULL); + Tcl_AppendResult(interp,"expected ", type, " but got \"", ObjStr(value), "\"", + (char *) NULL); return TCL_ERROR; } Index: generic/xotclInt.h =================================================================== diff -u -r99b9e9e9c78df12e482d16bca08ffeb5998b3b02 -rc6066a15de738754028991b2b57b8f1d5a1cccaa --- generic/xotclInt.h (.../xotclInt.h) (revision 99b9e9e9c78df12e482d16bca08ffeb5998b3b02) +++ generic/xotclInt.h (.../xotclInt.h) (revision c6066a15de738754028991b2b57b8f1d5a1cccaa) @@ -361,6 +361,7 @@ #define XOTCL_ARG_METHOD 0x0008 #define XOTCL_ARG_NOARG 0x0010 #define XOTCL_ARG_RELATION 0x0100 +#define XOTCL_ARG_MULTIVALUED 0x0200 /* method types */ #define XOTCL_METHODTYPE_ALIAS 0x0001 Index: library/lib/test.xotcl =================================================================== diff -u -r7121883918ed2a2591a63630bd465cd1d98eaa26 -rc6066a15de738754028991b2b57b8f1d5a1cccaa --- library/lib/test.xotcl (.../test.xotcl) (revision 7121883918ed2a2591a63630bd465cd1d98eaa26) +++ library/lib/test.xotcl (.../test.xotcl) (revision c6066a15de738754028991b2b57b8f1d5a1cccaa) @@ -68,16 +68,25 @@ :public method call {msg cmd} { if {[:verbose]} {puts stderr "$msg: $cmd"} - namespace eval ${:namespace} $cmd + #if {[catch {namespace eval ${:namespace} $cmd} result]} { + #puts stderr ERROR=$result + #} + #puts stderr "$msg: $cmd => $result" + #return $result + return [namespace eval ${:namespace} $cmd] } :public method run args { if {[info exists :pre]} {:call "pre" ${:pre}} if {![info exists :msg]} {set :msg ${:cmd}} - set r [:call "run" ${:cmd}] + set gotError [catch {:call "run" ${:cmd}} r] if {[info exists :setResult]} {set r [eval [set :setResult]]} if {$r eq ${:expected}} { - if {[info exists :count]} {set c ${:count}} {set c 1000} + if {$gotError} { + set c 1 + } else { + if {[info exists :count]} {set c ${:count}} {set c 1000} + } if {[:verbose]} { puts stderr "running test $c times" } @@ -116,17 +125,6 @@ $t expected $expected $t run } -proc ?? {cmd expected {msg ""}} { - set namespace [uplevel {namespace current}] - #catch {namespace eval $namespace {$cmd}} errorMsg - catch $cmd ::xotcl::test::errorMsg - if {$msg ne ""} { - set t [Test new -cmd {set ::xotcl::test::errorMsg} -msg $msg -namespace $namespace -count 1] - } else { - set t [Test new -cmd {set ::xotcl::test::errorMsg} -namespace $namespace -count 1] - } - $t expected $expected - $t run -} + namespace import ::xotcl::test::* Index: tests/parameters.xotcl =================================================================== diff -u -r120f51260309bbe35ba0f142a25e1b18947e3635 -rc6066a15de738754028991b2b57b8f1d5a1cccaa --- tests/parameters.xotcl (.../parameters.xotcl) (revision 120f51260309bbe35ba0f142a25e1b18947e3635) +++ tests/parameters.xotcl (.../parameters.xotcl) (revision c6066a15de738754028991b2b57b8f1d5a1cccaa) @@ -84,9 +84,9 @@ ? {catch {D create d1 -d 123}} 0 "create d1 with required argument given" ? {catch {D create d1}} 1 "create d1 without required argument given" -?? {D create d1} "::d1 configure: required argument 'd' is missing" "check error msg" +? {D create d1} "::d1 configure: required argument 'd' is missing" "check error msg" -?? {D create d2 -d x -b a} \ +? {D create d2 -d x -b a} \ {expected boolean value but got "a"} \ "create d2 without required argument given" @@ -95,39 +95,39 @@ #if {[info exists x]} {puts stderr x=$x} } -?? {d1 foo} \ +? {d1 foo} \ "::d1 foo: required argument 'r' is missing" \ "call method without a required argument" -?? {d1 foo -r a} \ +? {d1 foo -r a} \ {expected integer but got "a"} \ "required argument is not integer" -?? {d1 foo -r 1} \ +? {d1 foo -r 1} \ {expected integer but got "aaa"} \ "default value is not of type integer" -?? {d1 foo -r 1 -x 1 -object d1} \ +? {d1 foo -r 1 -x 1 -object d1} \ "" \ "pass object" -?? {d1 foo -r 1 -x 1 -object d11} \ - {Invalid argument: cannot convert 'd11' to object} \ +? {d1 foo -r 1 -x 1 -object d11} \ + {expected object but got "d11"} \ "pass non-existing object" -?? {d1 foo -r 1 -x 1 -class D} \ +? {d1 foo -r 1 -x 1 -class D} \ "" \ "pass class" -?? {d1 foo -r 1 -x 1 -class d1} \ - {Invalid argument: cannot convert 'd1' to class} \ +? {d1 foo -r 1 -x 1 -class d1} \ + {expected class but got "d1"} \ "pass object instead of class" -?? {d1 foo -r 1 -x 1 -class D11} \ - {Invalid argument: cannot convert 'D11' to class} \ +? {d1 foo -r 1 -x 1 -class D11} \ + {expected class but got "D11"} \ "pass non-existing class" -?? {D method foo {a:relation} {}} \ +? {D method foo {a:relation} {}} \ {Parameter option 'relation' not allowed} \ "don't allow relation option as method parameter" @@ -149,7 +149,31 @@ ? {d1 foo 1 2} "1-1-0-1" "omit optional argument" ? {d1 foo 1} "1-0-0-1" "omit optional arguments" +# +# non required positional arguments +# +Test case multivalued +Object create o +D method foo {m:integer,multivalued} { + return $m +} +? {d1 foo ""} "" "emtpy list" +? {d1 foo 1} "1" "single value" +? {d1 foo {1 2}} "1 2" "multiple values" +? {d1 foo {1 a 2}} \ + {invalid value in "1 a 2": expected integer but got "a"} \ + "multiple values with wrong value" + +D method foo {m:object,multivalued} { + return $m +} +? {d1 foo ""} "" "emtpy list" +? {d1 foo o} "o" "single value" +? {d1 foo {o d1 x}} \ + {invalid value in "o d1 x": expected object but got "x"} \ + "multiple values" + # # subst default tests # @@ -288,15 +312,15 @@ } d1 foo 1 -?? {d1 foo 10} \ +? {d1 foo 10} \ "Value '10' of parameter a is not between 1 and 3" \ "invalid value" D method foo {a:unknowntype} { puts stderr a=$a } -?? {d1 foo 10} \ +? {d1 foo 10} \ "::xotcl::parameterType: unable to dispatch method 'type=unknowntype'" \ "missing type checker" @@ -312,7 +336,7 @@ } ? {d1 foo a} "a=a" -?? {d1 foo 10} \ +? {d1 foo 10} \ "Value '10' of parameter a not in permissible values a|b|c" \ "invalid value" @@ -322,7 +346,7 @@ ? {d1 foo a good -c b} "a=a,b=good,c=b" ? {d1 foo a good} "a=a,b=good,c=a" -?? {d1 foo b "very good"} \ +? {d1 foo b "very good"} \ "Value 'very good' of parameter b not in permissible values good|bad" \ "invalid value (not included)" @@ -339,12 +363,12 @@ ? {d1 foo 2 -b 4 9} "a=2,b=4,c=9" ? {d1 foo 2 10} "a=2,b=3,c=10" -?? {d1 foo 2 11} \ +? {d1 foo 2 11} \ "Value '11' of parameter c not between 5 and 10" \ "invalid value" # define type twice -?? {D method foo {a:int,range,arg=1-3} {return a=$a}} \ +? {D method foo {a:int,range,arg=1-3} {return a=$a}} \ "Refuse to redefine parameter converter to use usertype" \ "invalid value" @@ -413,36 +437,36 @@ D method foo-type {x:type,arg=::C} {return $x} ? {d1 foo-base ::xotcl2::Object} "::xotcl2::Object" -?? {d1 foo-base C} \ +? {d1 foo-base C} \ "Value 'C' of x is not a baseclass" \ "not a base class" ? {d1 foo-class D} "D" -?? {d1 foo-class xxx} \ - "Invalid argument: cannot convert 'xxx' to class" \ +? {d1 foo-class xxx} \ + {expected class but got "xxx"} \ "not a class" -?? {d1 foo-class o} \ - "Invalid argument: cannot convert 'o' to class" \ +? {d1 foo-class o} \ + {expected class but got "o"} \ "not a class" ? {d1 foo-meta ::xotcl2::Class} "::xotcl2::Class" -?? {d1 foo-meta ::xotcl2::Object} \ +? {d1 foo-meta ::xotcl2::Object} \ "Value '::xotcl2::Object' of x is not a metaclass" \ "not a base class" ? {d1 foo-mixin c1} "c1" -?? {d1 foo-mixin o} \ +? {d1 foo-mixin o} \ "Value 'o' of x has not mixin ::M" \ "does not have mixin M" ? {d1 foo-object o} "o" -?? {d1 foo-object xxx} \ - "Invalid argument: cannot convert 'xxx' to object" \ +? {d1 foo-object xxx} \ + {expected object but got "xxx"} \ "not an object" ? {d1 foo-type d1} "d1" ? {d1 foo-type c1} "c1" -?? {d1 foo-type o} \ +? {d1 foo-type o} \ "Value 'o' of x of not of type ::C" \ "o not of type ::C" @@ -465,12 +489,12 @@ b:baseclass } ? {ParamTest create p -o o} ::p -?? {ParamTest create p -o xxx} \ - "Invalid argument: cannot convert 'xxx' to object" \ +? {ParamTest create p -o xxx} \ + {expected object but got "xxx"} \ "not an object" ? {ParamTest create p -mix c1} ::p -?? {ParamTest create p -mix o} \ +? {ParamTest create p -mix o} \ "Value 'o' of mix has not mixin M" \ "does not have mixin M" @@ -496,8 +520,8 @@ ? {p o o} \ "o" \ "value is an object" -?? {p o xxx} \ - "Invalid argument: cannot convert 'xxx' to object" \ +? {p o xxx} \ + {expected object but got "xxx"} \ "value is not an object" ParamTest slots { @@ -511,8 +535,8 @@ "o c1 d1" \ "value is a list of objects (multiple elements)" -?? {p os {o xxx d1}} \ - "Invalid argument: cannot convert 'xxx' to object" \ +? {p os {o xxx d1}} \ + {expected object but got "xxx"} \ "list with invalid object" ## TODO regression test for type checking, parameter options (initcmd,