Index: generic/predefined.xotcl =================================================================== diff -u -rf4e75c452cf99c87ad8705c954cb9548652873fa -r5a0750dc422574bc5ae91d9b58c64b8f5713d405 --- generic/predefined.xotcl (.../predefined.xotcl) (revision f4e75c452cf99c87ad8705c954cb9548652873fa) +++ generic/predefined.xotcl (.../predefined.xotcl) (revision 5a0750dc422574bc5ae91d9b58c64b8f5713d405) @@ -565,14 +565,16 @@ } ::xotcl::RelationSlot protected method delete_value {obj prop old value} { if {[string first * $value] > -1 || [string first \[ $value] > -1} { - # string contains meta characters + # value contains globbing meta characters if {${:elementtype} ne "" && ![string match ::* $value]} { - # prefix string with ::, since all object names have leading :: + # prefix glob pattern with ::, since all object names have leading :: set value ::$value } return [lsearch -all -not -glob -inline $old $value] } elseif {${:elementtype} ne ""} { + # value contains no globbing meta characters, but elementtype is given if {[string first :: $value] == -1} { + # get fully qualified name if {![::xotcl::is $value object]} { error "$value does not appear to be an object" } Index: generic/xotcl.c =================================================================== diff -u -r48d5751e9aeb6a4f388f6531a9248c1847b22cae -r5a0750dc422574bc5ae91d9b58c64b8f5713d405 --- generic/xotcl.c (.../xotcl.c) (revision 48d5751e9aeb6a4f388f6531a9248c1847b22cae) +++ generic/xotcl.c (.../xotcl.c) (revision 5a0750dc422574bc5ae91d9b58c64b8f5713d405) @@ -6181,10 +6181,43 @@ *clientData = (char *)ObjStr(objPtr); return TCL_OK; } +enum stringTypeIdx {StringTypeAlnum, StringTypeAlpha, StringTypeAscii, StringTypeBoolean, StringTypeControl, + StringTypeDigit, StringTypeDouble, StringTypeFalse,StringTypeGraph, StringTypeInteger, + StringTypeLower, StringTypePrint, StringTypePunct, StringTypeSpace, StringTypeTrue, + StringTypeUpper, StringTypeWordchar, StringTypeXdigit }; +static CONST char *stringTypeOpts[] = {"alnum", "alpha", "ascii", "boolean", "control", + "digit", "double", "false", "graph", "integer", + "lower", "print", "punct", "space", "true", + "upper", "wordchar", "xdigit", NULL}; + static int convertToTclobj(Tcl_Interp *interp, Tcl_Obj *objPtr, XOTclParam CONST *pPtr, ClientData *clientData) { - *clientData = (ClientData)objPtr; - return TCL_OK; + Tcl_Obj *objv[3]; + int result; + + if (pPtr->converterArg) { + /*fprintf(stderr, "convertToStringType %s (must be %s)\n", ObjStr(objPtr), ObjStr(pPtr->converterArg));*/ + + objv[1] = pPtr->converterArg; + objv[2] = objPtr; + + result = XOTclCallCommand(interp, XOTE_IS, 3, objv); + if (result == TCL_OK) { + int success; + Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &success); + if (success == 1) { + *clientData = (ClientData)objPtr; + } else { + result = XOTclVarErrMsg(interp, "expected ", ObjStr(pPtr->converterArg), + " but got \"", ObjStr(objPtr), "\"", NULL); + } + } + } else { + *clientData = (ClientData)objPtr; + result = TCL_OK; + } + return result; } + static int convertToNothing(Tcl_Interp *interp, Tcl_Obj *objPtr, XOTclParam CONST *pPtr, ClientData *clientData) { return TCL_OK; } @@ -6319,6 +6352,7 @@ paramPtr->type = typeName; return TCL_OK; } + static int ParamOptionParse(Tcl_Interp *interp, char *option, int length, int disallowedOptions, XOTclParam *paramPtr) { int result = TCL_OK; @@ -6370,27 +6404,45 @@ paramPtr->converterArg = Tcl_NewStringObj(option+5, length-5); INCR_REF_COUNT(paramPtr->converterArg); } else { - XOTclObject *paramObj; - Tcl_Obj *checker; - XOTclClass *pcl; - Tcl_Command cmd; + int i, found = -1; + + for (i=0; stringTypeOpts[i]; i++) { + /* Do not allow abbreviations, so the additional strlen checks + for a full match */ + if (strncmp(option, stringTypeOpts[i], length) == 0 && strlen(stringTypeOpts[i]) == length) { + found = i; + break; + } + } + if (found > -1) { + /* converter is stringType */ + result = ParamOptionSetConverter(interp, paramPtr, "stringtype", convertToTclobj); + paramPtr->converterArg = Tcl_NewStringObj(stringTypeOpts[i], -1); + INCR_REF_COUNT(paramPtr->converterArg); + } else { + /* converter defined via method */ + XOTclObject *paramObj; + Tcl_Obj *checker; + XOTclClass *pcl; + Tcl_Command cmd; - result = GetObjectFromObj(interp, XOTclGlobalObjects[XOTE_METHOD_PARAMETER_SLOT_OBJ], ¶mObj); - if (result != TCL_OK) - return result; + result = GetObjectFromObj(interp, XOTclGlobalObjects[XOTE_METHOD_PARAMETER_SLOT_OBJ], ¶mObj); + if (result != TCL_OK) + return result; + + checker = ParamCheckObj(interp, option, length); + INCR_REF_COUNT(checker); + cmd = ObjectFindMethod(interp, paramObj, ObjStr(checker), &pcl); - checker = ParamCheckObj(interp, option, length); - INCR_REF_COUNT(checker); - cmd = ObjectFindMethod(interp, paramObj, ObjStr(checker), &pcl); - - if (cmd == NULL) { - fprintf(stderr, "**** could not find checker method %s defined on %s\n", - ObjStr(checker), objectName(paramObj)); - paramPtr->flags |= XOTCL_ARG_CURRENTLY_UNKNOWN; - /* TODO: for the time being, we do not return an error here */ + if (cmd == NULL) { + fprintf(stderr, "**** could not find checker method %s defined on %s\n", + ObjStr(checker), objectName(paramObj)); + paramPtr->flags |= XOTCL_ARG_CURRENTLY_UNKNOWN; + /* TODO: for the time being, we do not return an error here */ + } + result = ParamOptionSetConverter(interp, paramPtr, "usertype", convertViaCmd); + paramPtr->converterName = checker; } - result = ParamOptionSetConverter(interp, paramPtr, "usertype", convertViaCmd); - paramPtr->converterName = checker; } if ((paramPtr->flags & disallowedOptions)) { Index: generic/xotclInt.h =================================================================== diff -u -r05f4b42f7615ea410b7ac33093f5b8382ce7e8c5 -r5a0750dc422574bc5ae91d9b58c64b8f5713d405 --- generic/xotclInt.h (.../xotclInt.h) (revision 05f4b42f7615ea410b7ac33093f5b8382ce7e8c5) +++ generic/xotclInt.h (.../xotclInt.h) (revision 5a0750dc422574bc5ae91d9b58c64b8f5713d405) @@ -512,7 +512,7 @@ XOTE_METHOD, XOTE_OBJECT, XOTE_SETTER, XOTE_GUARD_OPTION, XOTE___UNKNOWN__, /* Patly redefined Tcl commands; leave them together at the end */ - XOTE_EXPR, XOTE_FORMAT, XOTE_INFO, XOTE_INTERP, XOTE_RENAME, XOTE_SUBST + XOTE_EXPR, XOTE_FORMAT, XOTE_INFO, XOTE_INTERP, XOTE_IS, XOTE_RENAME, XOTE_SUBST } XOTclGlobalNames; #if !defined(XOTCL_C) extern char *XOTclGlobalStrings[]; @@ -535,7 +535,7 @@ "method", "object", "setter", "-guard", "__unknown__", /* tcl commands */ - "expr", "format", "info", "interp", "rename", "subst", + "expr", "format", "info", "interp", "::tcl::string::is", "rename", "subst", }; #endif Index: generic/xotclShadow.c =================================================================== diff -u -r28acb2d7bddbbf6c82e6c516d7706f9429e05e6b -r5a0750dc422574bc5ae91d9b58c64b8f5713d405 --- generic/xotclShadow.c (.../xotclShadow.c) (revision 28acb2d7bddbbf6c82e6c516d7706f9429e05e6b) +++ generic/xotclShadow.c (.../xotclShadow.c) (revision 5a0750dc422574bc5ae91d9b58c64b8f5713d405) @@ -131,6 +131,7 @@ #endif rc |= XOTclReplaceCommand(interp, XOTE_FORMAT, 0, initialized); rc |= XOTclReplaceCommand(interp, XOTE_INTERP, 0, initialized); + rc |= XOTclReplaceCommand(interp, XOTE_IS, 0, initialized); /* for the following commands, we have to add our own semantics */ rc |= XOTclReplaceCommand(interp, XOTE_RENAME, XOTcl_RenameObjCmd, initialized); Index: tests/parameters.xotcl =================================================================== diff -u -r48d5751e9aeb6a4f388f6531a9248c1847b22cae -r5a0750dc422574bc5ae91d9b58c64b8f5713d405 --- tests/parameters.xotcl (.../parameters.xotcl) (revision 48d5751e9aeb6a4f388f6531a9248c1847b22cae) +++ tests/parameters.xotcl (.../parameters.xotcl) (revision 5a0750dc422574bc5ae91d9b58c64b8f5713d405) @@ -12,7 +12,6 @@ ####################################################### Test case valuecheck Test parameter count 10000 -#Test parameter count 10 Object create o1 Class create C -parameter {a {b:boolean} {c 1}} @@ -34,6 +33,11 @@ # do not allow "currently unknown" user defined types in valuecheck ? {::xotcl::valuecheck in1 aaa} {invalid value constraints "in1"} +? {::xotcl::valuecheck lower c} 1 "lower case char" +? {::xotcl::valuecheck lower abc} 1 "lower case chars" +? {::xotcl::valuecheck lower Abc} 0 "no lower case chars" +? {string is lower abc} 1 "tcl command 'string is lower'" + # # parameter options # required @@ -55,6 +59,7 @@ # method NO YES YES YES NO NO/POSSIBLE YES # # relation NO YES NO YES NO NO YES +# stringtype YES YES NO NO YES YES YES # # switch NO NO NO NO NO YES YES # integer YES YES NO NO YES YES YES @@ -226,6 +231,14 @@ {Parameter option 'relation' not allowed} \ "don't allow relation option as method parameter" +? {D method foo {a:double} {return $a}} \ + {::xotcl::classes::D::foo} \ + "allow 'string is XXXX' for argument checking" +? {d1 foo 1} 1 "check int as double" +? {d1 foo 1.1} 1.1 "check double as double" +? {d1 foo 1.1a} {expected double but got "1.1a"} "check non-double as double" +? {D info method parameter foo} a:double + ####################################################### # non required positional arguments ####################################################### @@ -245,7 +258,7 @@ ? {d1 foo 1} "1-0-0-1" "omit optional arguments" ####################################################### -# non required positional arguments +# multivalued arguments ####################################################### Test case multivalued Object create o @@ -581,6 +594,7 @@ C create c1 -mixin M Object create o +puts stderr ===== Class create ParamTest -parameter { o:object c:class @@ -589,6 +603,8 @@ m:metaclass mix:mixin,arg=M b:baseclass + u:upper + us:upper,multivalued {x:object,multivalued {o}} } @@ -604,6 +620,8 @@ ? {parameterFromSlot ParamTest d1} "d1:object,type=::C" ? {parameterFromSlot ParamTest mix} "mix:mixin,arg=M" ? {parameterFromSlot ParamTest x} "x:object,multivalued o" +? {parameterFromSlot ParamTest u} "u:upper" +? {parameterFromSlot ParamTest us} "us:upper,multivalued" ? {ParamTest create p -o o} ::p ? {ParamTest create p -o xxx} \ @@ -621,6 +639,10 @@ ? {ParamTest create p -d o} \ {expected object of type ::C but got "o"} \ "o not of type ::C" +? {ParamTest create p -u A} ::p +? {ParamTest create p -u c1} {expected upper but got "c1"} +? {ParamTest create p -us {A B}} ::p +? {p us add C end} "A B C" # TODO: naming "type" and "mixin" not perfect. # maybe "type" => "hastype"