Index: TODO =================================================================== diff -u -r06f89c385bcc658773eb4dcda50e56abba2eb656 -r3cb6a6a8f1e33e63abeec25b3c36231702af6fe2 --- TODO (.../TODO) (revision 06f89c385bcc658773eb4dcda50e56abba2eb656) +++ TODO (.../TODO) (revision 3cb6a6a8f1e33e63abeec25b3c36231702af6fe2) @@ -1021,6 +1021,8 @@ - replaced several occurrences of "eval" in nx.tcl and xotcl2.tcl +- implemented parameter option "allowempty" +- extended regression test TODO: - nameing @@ -1053,6 +1055,15 @@ package require nx::test namespace import ::nx::* + * parameter option allowempty is a little longish; we should + choose probably a shorter name + + Object create o { + :method foo {x:integer,allowempty y:integer os:object,multivalued,allowempty} { + return $x + } + } + - documentation - documentationssytem - langref (xotcl 2.0 + next-scripting) @@ -1192,7 +1203,6 @@ - info valuecheck? - turn off optionally value checking - get rid of "::xotcl::is"? -- value checker (allowempty) # TODO (optimization): optimizer can improve parameter checking: # (a) simple approach: make scripted setter methods on domain Index: generic/xotcl.c =================================================================== diff -u -r033c63d771af5253b0e94c2a9c1c6a94df40242e -r3cb6a6a8f1e33e63abeec25b3c36231702af6fe2 --- generic/xotcl.c (.../xotcl.c) (revision 033c63d771af5253b0e94c2a9c1c6a94df40242e) +++ generic/xotcl.c (.../xotcl.c) (revision 3cb6a6a8f1e33e63abeec25b3c36231702af6fe2) @@ -5387,6 +5387,9 @@ if ((pPtr->flags & XOTCL_ARG_SUBST_DEFAULT)) { ParamDefsFormatOption(interp, nameStringObj, "substdefault", &colonWritten, &first); } + if ((pPtr->flags & XOTCL_ARG_ALLOW_EMPTY)) { + ParamDefsFormatOption(interp, nameStringObj, "allowempty", &colonWritten, &first); + } if ((pPtr->flags & XOTCL_ARG_INITCMD)) { ParamDefsFormatOption(interp, nameStringObj, "initcmd", &colonWritten, &first); } else if ((pPtr->flags & XOTCL_ARG_METHOD)) { @@ -6424,6 +6427,8 @@ paramPtr->flags &= ~XOTCL_ARG_REQUIRED; } else if (strncmp(option, "substdefault", 12) == 0) { paramPtr->flags |= XOTCL_ARG_SUBST_DEFAULT; + } else if (strncmp(option, "allowempty", 10) == 0) { + paramPtr->flags |= XOTCL_ARG_ALLOW_EMPTY; } else if (strncmp(option, "initcmd", 7) == 0) { paramPtr->flags |= XOTCL_ARG_INITCMD; } else if (strncmp(option, "method", 6) == 0) { @@ -9450,7 +9455,14 @@ for (i=0; iconverter)(interp, ov[i], pPtr, clientData, &elementObjPtr); + const char *valueString = ObjStr(ov[i]); + + if (pPtr->flags & XOTCL_ARG_ALLOW_EMPTY && *valueString == '\0') { + result = convertToString(interp, ov[i], pPtr, clientData, &elementObjPtr); + } else { + result = (*pPtr->converter)(interp, ov[i], pPtr, clientData, &elementObjPtr); + } + if (result == TCL_OK) { Tcl_ListObjAppendElement(interp, *outObjPtr, elementObjPtr); } else { @@ -9477,20 +9489,32 @@ int objc, i; Tcl_Obj **ov; + /* + * In the multivalued case, we have either to check a list of + * values or to build a new list of values (in case, the converter + * normalizes the values). + */ result = Tcl_ListObjGetElements(interp, objPtr, &objc, &ov); if (result != TCL_OK) { return result; } /* - Default assumption: outObjPtr is not modified, in cases where - necessary, we switch to the helper function - */ + * Default assumption: outObjPtr is not modified, in cases where + * necessary, we switch to the helper function + */ *outObjPtr = objPtr; for (i=0; iconverter)(interp, ov[i], pPtr, clientData, &elementObjPtr); + const char *valueString = ObjStr(ov[i]); + + if (pPtr->flags & XOTCL_ARG_ALLOW_EMPTY && *valueString == '\0') { + result = convertToString(interp, ov[i], pPtr, clientData, &elementObjPtr); + } else { + result = (*pPtr->converter)(interp, ov[i], pPtr, clientData, &elementObjPtr); + } + if (result == TCL_OK) { if (ov[i] != elementObjPtr) { /* @@ -9514,7 +9538,12 @@ } } } else { - result = (*pPtr->converter)(interp, objPtr, pPtr, clientData, outObjPtr); + const char *valueString = ObjStr(objPtr); + if (pPtr->flags & XOTCL_ARG_ALLOW_EMPTY && *valueString == '\0') { + result = convertToString(interp, objPtr, pPtr, clientData, outObjPtr); + } else { + result = (*pPtr->converter)(interp, objPtr, pPtr, clientData, outObjPtr); + } } return result; } Index: generic/xotclInt.h =================================================================== diff -u -rf3cb5afe6aa1b6761b4a9909058f64ff7d64ab92 -r3cb6a6a8f1e33e63abeec25b3c36231702af6fe2 --- generic/xotclInt.h (.../xotclInt.h) (revision f3cb5afe6aa1b6761b4a9909058f64ff7d64ab92) +++ generic/xotclInt.h (.../xotclInt.h) (revision 3cb6a6a8f1e33e63abeec25b3c36231702af6fe2) @@ -366,8 +366,9 @@ #define XOTCL_ARG_NOARG 0x0004 #define XOTCL_ARG_CURRENTLY_UNKNOWN 0x0008 #define XOTCL_ARG_SUBST_DEFAULT 0x0010 -#define XOTCL_ARG_INITCMD 0x0020 -#define XOTCL_ARG_METHOD 0x0040 +#define XOTCL_ARG_ALLOW_EMPTY 0x0020 +#define XOTCL_ARG_INITCMD 0x0040 +#define XOTCL_ARG_METHOD 0x0080 #define XOTCL_ARG_RELATION 0x0100 #define XOTCL_ARG_SWITCH 0x0200 #define XOTCL_ARG_HAS_DEFAULT 0x1000 Index: tests/parameters.tcl =================================================================== diff -u -r033c63d771af5253b0e94c2a9c1c6a94df40242e -r3cb6a6a8f1e33e63abeec25b3c36231702af6fe2 --- tests/parameters.tcl (.../parameters.tcl) (revision 033c63d771af5253b0e94c2a9c1c6a94df40242e) +++ tests/parameters.tcl (.../parameters.tcl) (revision 3cb6a6a8f1e33e63abeec25b3c36231702af6fe2) @@ -901,23 +901,40 @@ ? {o foo 3} 4 } + ####################################################### +# allow empty values +####################################################### +Test case allow-empty { + + Object create o1 + Object create o2 + Object create o3 + + Object create o { + :method foo {x:integer,allowempty y:integer os:object,multivalued,allowempty} { + return $x + } + } + + ? {o foo 1 2 {o1 o2}} 1 "all values specified" + ? {o foo "" 2 {o1 o2}} "" "first is empty" + ? {o foo 1 "" {o1 o2}} {expected integer but got "" for parameter y} "second is empty" + ? {o foo 1 2 {}} 1 "empty list, does not require allowempty" + ? {o foo 1 2 {o1 "" o2}} 1 "list contains empty value" + + ? {o info method parameter foo} "x:integer,allowempty y:integer os:object,multivalued,allowempty" + + o method foo {x:integer,allowempty y:integer os:object,multivalued} {return $x} + ? {o foo 1 2 {o1 "" o2}} {invalid value in "o1 "" o2": expected object but got "" for parameter os} \ + "list contains empty value" + +} +####################################################### # slot specific converter ####################################################### Test case slot-specfic-converter { - # Class create Person - # Person slots { - # ::nx::Attribute create sex -type "sex" { - # :method type=sex {name value} { - # #puts stderr "[current] slot specific converter" - # switch -glob $value { - # m* {return m} - # f* {return f} - # default {error "expected sex but got $value"} - # } - # } - # } - # } + Class create Person { :attribute sex { :type "sex" @@ -931,6 +948,7 @@ } } } + Person create p1 -sex male ? {p1 sex} m Person method foo {s:sex,slot=::Person::slot::sex} {return $s}