aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2011-06-16 10:27:27 +1000
committerSteve Bennett <steveb@workware.net.au>2011-07-08 05:41:58 +1000
commit4a5e4965e2c208375a77d40b831a07897f80ee50 (patch)
tree8d9ea6efb78a2a884ac347b59b3d48740eda0349
parente3639458879e363cf012a1f7a00fdfab92f0f7ce (diff)
downloadjimtcl-4a5e4965e2c208375a77d40b831a07897f80ee50.zip
jimtcl-4a5e4965e2c208375a77d40b831a07897f80ee50.tar.gz
jimtcl-4a5e4965e2c208375a77d40b831a07897f80ee50.tar.bz2
Better proc optional arg handling
Allows args and optional parameters in any location, in addition to being smaller and faster. Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--jim.c343
-rw-r--r--jim.h20
-rw-r--r--jim_tcl.txt48
-rw-r--r--tests/proc-new.test15
4 files changed, 208 insertions, 218 deletions
diff --git a/jim.c b/jim.c
index cf08246..df2d885 100644
--- a/jim.c
+++ b/jim.c
@@ -3528,28 +3528,32 @@ int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName,
return JIM_OK;
}
-static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
- Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr,
- int leftArity, int optionalArgs, int args, int rightArity)
+static int JimCreateProcedure(Jim_Interp *interp, Jim_Obj *cmdName,
+ Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr)
{
Jim_Cmd *cmdPtr;
Jim_HashEntry *he;
+ int argListLen;
+ int i;
- cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
+ if (JimValidName(interp, "procedure", cmdName) != JIM_OK) {
+ return JIM_ERR;
+ }
+
+ argListLen = Jim_ListLength(interp, argListObjPtr);
+
+ /* Allocate space for both the command pointer and the arg list */
+ cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->isproc = 1;
cmdPtr->u.proc.argListObjPtr = argListObjPtr;
+ cmdPtr->u.proc.argListLen = argListLen;
cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
+ cmdPtr->u.proc.argsPos = -1;
+ cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
Jim_IncrRefCount(argListObjPtr);
Jim_IncrRefCount(bodyObjPtr);
- cmdPtr->u.proc.leftArity = leftArity;
- cmdPtr->u.proc.optionalArgs = optionalArgs;
- cmdPtr->u.proc.args = args;
- cmdPtr->u.proc.rightArity = rightArity;
- cmdPtr->u.proc.staticVars = NULL;
- cmdPtr->u.proc.prevCmd = NULL;
- cmdPtr->inUse = 1;
/* Create the statics hash table. */
if (staticsListObjPtr) {
@@ -3609,6 +3613,59 @@ static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
}
}
+ /* Parse the args out into arglist, validating as we go */
+ /* Examine the argument list for default parameters and 'args' */
+ for (i = 0; i < argListLen; i++) {
+ Jim_Obj *argPtr;
+ Jim_Obj *nameObjPtr;
+ Jim_Obj *defaultObjPtr;
+ int len;
+ int n = 1;
+
+ /* Examine a parameter */
+ Jim_ListIndex(interp, argListObjPtr, i, &argPtr, JIM_NONE);
+ len = Jim_ListLength(interp, argPtr);
+ if (len == 0) {
+ Jim_SetResultString(interp, "procedure has argument with no name", -1);
+ goto err;
+ }
+ if (len > 2) {
+ Jim_SetResultString(interp, "procedure has argument with too many fields", -1);
+ goto err;
+ }
+
+ if (len == 2) {
+ /* Optional parameter */
+ Jim_ListIndex(interp, argPtr, 0, &nameObjPtr, JIM_NONE);
+ Jim_ListIndex(interp, argPtr, 1, &defaultObjPtr, JIM_NONE);
+ }
+ else {
+ /* Required parameter */
+ nameObjPtr = argPtr;
+ defaultObjPtr = NULL;
+ }
+
+
+ if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
+ if (cmdPtr->u.proc.argsPos >= 0) {
+ Jim_SetResultString(interp, "procedure has 'args' specified more than once", -1);
+ goto err;
+ }
+ cmdPtr->u.proc.argsPos = i;
+ }
+ else {
+ if (len == 2) {
+ cmdPtr->u.proc.optArity += n;
+ }
+ else {
+ cmdPtr->u.proc.reqArity += n;
+ }
+ }
+
+ cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
+ cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
+ }
+
/* Add the new command */
/* It may already exist, so we try to delete the old one.
@@ -3618,7 +3675,7 @@ static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
* BUT, if 'local' is in force, instead of deleting the existing
* proc, we stash a reference to the old proc here.
*/
- he = Jim_FindHashEntry(&interp->commands, cmdName);
+ he = Jim_FindHashEntry(&interp->commands, Jim_String(cmdName));
if (he) {
/* There was an old procedure with the same name, this requires
* a 'proc epoch' update. */
@@ -3638,18 +3695,20 @@ static int JimCreateProcedure(Jim_Interp *interp, const char *cmdName,
else {
if (he) {
/* Replace the existing proc */
- Jim_DeleteHashEntry(&interp->commands, cmdName);
+ Jim_DeleteHashEntry(&interp->commands, Jim_String(cmdName));
}
- Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr);
+ Jim_AddHashEntry(&interp->commands, Jim_String(cmdName), cmdPtr);
}
/* Unlike Tcl, set the name of the proc as the result */
- Jim_SetResultString(interp, cmdName, -1);
+ Jim_SetResult(interp, cmdName);
return JIM_OK;
err:
- Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
+ if (cmdPtr->u.proc.staticVars) {
+ Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
+ }
Jim_Free(cmdPtr->u.proc.staticVars);
Jim_DecrRefCount(interp, argListObjPtr);
Jim_DecrRefCount(interp, bodyObjPtr);
@@ -10115,6 +10174,45 @@ static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argVa
return retcode;
}
+/**
+ * Sets the interp result to be an error message indicating the required proc args.
+ */
+static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
+{
+ /* Create a nice error message, consistent with Tcl 8.5 */
+ Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
+ int i;
+
+ for (i = 0; i < cmd->u.proc.argListLen; i++) {
+ Jim_AppendString(interp, argmsg, " ", 1);
+
+ if (i == cmd->u.proc.argsPos) {
+ if (cmd->u.proc.arglist[i].defaultObjPtr) {
+ /* Renamed args */
+ Jim_AppendString(interp, argmsg, "?", 1);
+ Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
+ Jim_AppendString(interp, argmsg, " ...?", -1);
+ }
+ else {
+ /* We have plain args */
+ Jim_AppendString(interp, argmsg, "?argument ...?", -1);
+ }
+ }
+ else {
+ if (cmd->u.proc.arglist[i].defaultObjPtr) {
+ Jim_AppendString(interp, argmsg, "?", 1);
+ Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
+ Jim_AppendString(interp, argmsg, "?", 1);
+ }
+ else {
+ Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
+ }
+ }
+ }
+ Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
+ Jim_FreeNewObj(interp, argmsg);
+}
+
/* Call a procedure implemented in Tcl.
* It's possible to speed-up a lot this function, currently
* the callframes are not cached, but allocated and
@@ -10123,52 +10221,17 @@ static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argVa
*
* This can be fixed just implementing callframes caching
* in JimCreateCallFrame() and JimFreeCallFrame(). */
-int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, const char *filename, int linenr, int argc,
+static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, const char *filename, int linenr, int argc,
Jim_Obj *const *argv)
{
- int i, d, retcode;
Jim_CallFrame *callFramePtr;
- Jim_Obj *argObjPtr;
- Jim_Obj *procname = argv[0];
Jim_Stack *prevLocalProcs;
+ int i, d, retcode, optargs;
/* Check arity */
- if (argc - 1 < cmd->u.proc.leftArity + cmd->u.proc.rightArity ||
- (!cmd->u.proc.args && argc - 1 > cmd->u.proc.leftArity + cmd->u.proc.rightArity + cmd->u.proc.optionalArgs)) {
- /* Create a nice error message, consistent with Tcl 8.5 */
- Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0);
- int arglen = Jim_ListLength(interp, cmd->u.proc.argListObjPtr);
-
- for (i = 0; i < arglen; i++) {
- Jim_Obj *objPtr;
- Jim_ListIndex(interp, cmd->u.proc.argListObjPtr, i, &argObjPtr, JIM_NONE);
-
- Jim_AppendString(interp, argmsg, " ", 1);
-
- if (i < cmd->u.proc.leftArity || i >= arglen - cmd->u.proc.rightArity) {
- Jim_AppendObj(interp, argmsg, argObjPtr);
- }
- else if (i == arglen - cmd->u.proc.rightArity - cmd->u.proc.args) {
- if (Jim_ListLength(interp, argObjPtr) == 1) {
- /* We have plain args */
- Jim_AppendString(interp, argmsg, "?argument ...?", -1);
- }
- else {
- Jim_AppendString(interp, argmsg, "?", 1);
- Jim_ListIndex(interp, argObjPtr, 1, &objPtr, JIM_NONE);
- Jim_AppendObj(interp, argmsg, objPtr);
- Jim_AppendString(interp, argmsg, " ...?", -1);
- }
- }
- else {
- Jim_AppendString(interp, argmsg, "?", 1);
- Jim_ListIndex(interp, argObjPtr, 0, &objPtr, JIM_NONE);
- Jim_AppendObj(interp, argmsg, objPtr);
- Jim_AppendString(interp, argmsg, "?", 1);
- }
- }
- Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procname, argmsg);
- Jim_FreeNewObj(interp, argmsg);
+ if (argc - 1 < cmd->u.proc.reqArity ||
+ (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
+ JimSetProcWrongArgs(interp, argv[0], cmd);
return JIM_ERR;
}
@@ -10191,77 +10254,42 @@ int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, const char *filename, int
Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
interp->framePtr = callFramePtr;
- /* Simplify arg counting */
- argv++;
- argc--;
-
- /* Set arguments */
+ /* How many optional args are available */
+ optargs = (argc - 1 - cmd->u.proc.reqArity);
- /* Assign in this order:
- * leftArity required args.
- * rightArity required args (but actually do it last for simplicity)
- * optionalArgs optional args
- * remaining args into 'args' if 'args'
- */
+ /* Step 'i' along the actual args, and step 'd' along the formal args */
+ i = 1;
+ for (d = 0; d < cmd->u.proc.argListLen; d++) {
+ Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr;
+ if (d == cmd->u.proc.argsPos) {
+ /* assign $args */
+ int argsLen = 0;
+ if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) {
+ argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity);
+ }
+ Jim_Obj *listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen);
- /* Note that 'd' steps along the arg list, whilst argc/argv follow the supplied args */
+ /* It is possible to rename args. */
+ if (cmd->u.proc.arglist[d].defaultObjPtr) {
+ nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
+ }
+ retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
+ if (retcode != JIM_OK) {
+ goto badargset;
+ }
- /* leftArity required args */
- for (d = 0; d < cmd->u.proc.leftArity; d++) {
- Jim_ListIndex(interp, cmd->u.proc.argListObjPtr, d, &argObjPtr, JIM_NONE);
- retcode = JimSetProcArg(interp, argObjPtr, *argv++);
- if (retcode != JIM_OK) {
- goto badargset;
+ i += argsLen;
+ continue;
}
- argc--;
- }
-
- /* Shorten our idea of the number of supplied args */
- argc -= cmd->u.proc.rightArity;
-
- /* optionalArgs optional args */
- for (i = 0; i < cmd->u.proc.optionalArgs; i++) {
- Jim_Obj *nameObjPtr;
- Jim_Obj *valueObjPtr;
- Jim_ListIndex(interp, cmd->u.proc.argListObjPtr, d++, &argObjPtr, JIM_NONE);
-
- /* The name is the first element of the list */
- Jim_ListIndex(interp, argObjPtr, 0, &nameObjPtr, JIM_NONE);
- if (argc) {
- valueObjPtr = *argv++;
- argc--;
+ /* Optional or required? */
+ if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
+ retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
}
else {
- /* No more values, so use default */
- /* The value is the second element of the list */
- Jim_ListIndex(interp, argObjPtr, 1, &valueObjPtr, JIM_NONE);
+ /* Ran out, so use the default */
+ retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
}
- Jim_SetVariable(interp, nameObjPtr, valueObjPtr);
- }
-
- /* Any remaining args go to 'args' */
- if (cmd->u.proc.args) {
- Jim_Obj *listObjPtr = Jim_NewListObj(interp, argv, argc);
-
- /* Get the 'args' name from the procedure args */
- Jim_ListIndex(interp, cmd->u.proc.argListObjPtr, d, &argObjPtr, JIM_NONE);
-
- /* It is possible to rename args. */
- i = Jim_ListLength(interp, argObjPtr);
- if (i == 2) {
- Jim_ListIndex(interp, argObjPtr, 1, &argObjPtr, JIM_NONE);
- }
-
- Jim_SetVariable(interp, argObjPtr, listObjPtr);
- argv += argc;
- d++;
- }
-
- /* rightArity required args */
- for (i = 0; i < cmd->u.proc.rightArity; i++) {
- Jim_ListIndex(interp, cmd->u.proc.argListObjPtr, d++, &argObjPtr, JIM_NONE);
- retcode = JimSetProcArg(interp, argObjPtr, *argv++);
if (retcode != JIM_OK) {
goto badargset;
}
@@ -10307,7 +10335,7 @@ badargset:
else if (retcode == JIM_ERR) {
interp->addStackTrace++;
Jim_DecrRefCount(interp, interp->errorProc);
- interp->errorProc = procname;
+ interp->errorProc = argv[0];
Jim_IncrRefCount(interp->errorProc);
}
return retcode;
@@ -12453,87 +12481,16 @@ static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const
/* [proc] */
static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- int argListLen;
- int leftArity, rightArity;
- int i;
- int optionalArgs = 0;
- int args = 0;
-
if (argc != 4 && argc != 5) {
Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
return JIM_ERR;
}
- if (JimValidName(interp, "procedure", argv[1]) != JIM_OK) {
- return JIM_ERR;
- }
-
- argListLen = Jim_ListLength(interp, argv[2]);
- leftArity = 0;
- rightArity = 0;
-
- /* Examine the argument list for default parameters and 'args' */
- for (i = 0; i < argListLen; i++) {
- Jim_Obj *argPtr;
- int len;
-
- /* Examine a parameter */
- Jim_ListIndex(interp, argv[2], i, &argPtr, JIM_NONE);
- len = Jim_ListLength(interp, argPtr);
- if (len == 0) {
- Jim_SetResultString(interp, "procedure has argument with no name", -1);
- return JIM_ERR;
- }
- if (len > 2) {
- Jim_SetResultString(interp, "procedure has argument with too many fields", -1);
- return JIM_ERR;
- }
-
- if (len == 2) {
- /* May be {args newname} */
- Jim_ListIndex(interp, argPtr, 0, &argPtr, JIM_NONE);
- }
-
- if (Jim_CompareStringImmediate(interp, argPtr, "args")) {
- if (args) {
- Jim_SetResultString(interp, "procedure has 'args' specified more than once", -1);
- return JIM_ERR;
- }
- if (rightArity) {
- Jim_SetResultString(interp, "procedure has 'args' in invalid position", -1);
- return JIM_ERR;
- }
- args = 1;
- continue;
- }
-
- /* Does this parameter have a default? */
- if (len == 1) {
- /* A required arg. Is it part of leftArity or rightArity? */
- if (optionalArgs || args) {
- rightArity++;
- }
- else {
- leftArity++;
- }
- }
- else {
- /* Optional arg. Can't be after rightArity */
- if (rightArity || args) {
- Jim_SetResultString(interp, "procedure has optional arg in invalid position", -1);
- return JIM_ERR;
- }
- optionalArgs++;
- }
- }
-
if (argc == 4) {
- return JimCreateProcedure(interp, Jim_String(argv[1]),
- argv[2], NULL, argv[3], leftArity, optionalArgs, args, rightArity);
+ return JimCreateProcedure(interp, argv[1], argv[2], NULL, argv[3]);
}
else {
- return JimCreateProcedure(interp, Jim_String(argv[1]),
- argv[2], argv[3], argv[4], leftArity, optionalArgs, args, rightArity);
+ return JimCreateProcedure(interp, argv[1], argv[2], argv[3], argv[4]);
}
}
diff --git a/jim.h b/jim.h
index 9f1520b..224f50e 100644
--- a/jim.h
+++ b/jim.h
@@ -466,6 +466,8 @@ typedef int (*Jim_CmdProc)(struct Jim_Interp *interp, int argc,
Jim_Obj *const *argv);
typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData);
+
+
/* A command is implemented in C if funcPtr is != NULL, otherwise
* it's a Tcl procedure with the arglist and body represented by the
* two objects referenced by arglistObjPtr and bodyoObjPtr. */
@@ -483,13 +485,17 @@ typedef struct Jim_Cmd {
/* Tcl procedure */
Jim_Obj *argListObjPtr;
Jim_Obj *bodyObjPtr;
- Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */
- int leftArity; /* Required args assigned from the left */
- int optionalArgs; /* Number of optional args (default values) */
- int rightArity; /* Required args assigned from the right */
- int args; /* True if 'args' specified */
- struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */
- int upcall; /* True if proc is currently in upcall */
+ Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */
+ struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */
+ int argListLen; /* Length of argListObjPtr */
+ int reqArity; /* Number of required parameters */
+ int optArity; /* Number of optional parameters */
+ int argsPos; /* Position of 'args', if specified, or -1 */
+ int upcall; /* True if proc is currently in upcall */
+ struct Jim_ProcArg {
+ Jim_Obj *nameObjPtr; /* Name of this arg */
+ Jim_Obj *defaultObjPtr; /* Default value, (or rename for $args) */
+ } *arglist;
} proc;
} u;
} Jim_Cmd;
diff --git a/jim_tcl.txt b/jim_tcl.txt
index cffbae1..dbcb34a 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -55,6 +55,10 @@ The major differences with Tcl 8.5/8.6 are:
RECENT CHANGES
--------------
+Changes between 0.71 and 0.72
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1. Allow 'args' and optional parameters in any position
+
Changes between 0.70 and 0.71
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Allow 'args' to be renamed in procs
@@ -1052,14 +1056,14 @@ Tcl commands.
The 'proc' command is used to create a new Tcl command procedure:
-+*proc* 'name args ?statics? body'+
++*proc* 'name arglist ?statics? body'+
The new command is name *name*, and it replaces any existing command
there may have been by that name. Whenever the new command is
invoked, the contents of *body* will be executed by the Tcl
interpreter.
-*args* specifies the formal arguments to the procedure.
+*arglist* specifies the formal arguments to the procedure.
It consists of a list, possibly empty, of the following
argument specifiers:
@@ -1078,20 +1082,12 @@ argument specifiers:
+*args*+::
Variable Argument - The special name *args*, which is
- assigned all remaining arguments (including none). The
+ assigned all remaining arguments (including none) as a list. The
variable argument may only be specified once. Note that
the syntax +args newname+ may be used to retain the special
behaviour of *args* with a different local name. In this case,
the variable is named *newname* rather than *args*.
-Arguments must be provided in the following order, any of which
-may be omitted:
-
-1. Required Arguments (Left)
-2. Optional Arguments
-3. Variable Argument
-4. Required Arguments (Right)
-
When the command is invoked, a local variable will be created for each of
the formal arguments to the procedure; its value will be the value
of corresponding argument in the invoking command or the argument's
@@ -1102,14 +1098,30 @@ invocation. However, there must be enough actual arguments for all
required arguments, and there must not be any extra actual arguments
(unless the Variable Argument is specified).
-Actual arguments are assigned to formal arguments as follows:
+Actual arguments are assigned to formal arguments as in left-to-right
+order with the following precedence.
+
+1. Required Arguments (including Reference Arguments)
+2. Optional Arguments
+3. Variable Argument
+
+The following example illustrates precedence. Assume a procedure declaration:
+
+ proc p {{a A} args b {c C} d} {...}
+
+This procedure requires at least two arguments, but can accept an unlimited number.
+The following table shows how various numbers of arguments are assigned.
+Values marked as '-' are assigned the default value.
-1. Left Required Arguments are assigned from the left
-2. Right Required Arguments are assigned from the right
-3. Default Arguments are assigned from the left, following the Left Required Arguments.
-4. A list is formed from any remaining arguments, which are then
- are assigned to the 'args' Variable Argument (if specified). The list will be empty
- if there are no remaining arguments.
+[width="40%",frame="topbot",options="header"]
+|==============
+|Number of arguments|a|args|b|c|d
+|2|-|-|1|-|2
+|3|1|-|2|-|3
+|4|1|-|2|3|4
+|5|1|2|3|4|5
+|6|1|2,3|4|5|6
+|==============
When *body* is being executed, variable names normally refer to local
variables, which are created automatically when referenced and deleted
diff --git a/tests/proc-new.test b/tests/proc-new.test
index 9f18f64..8703748 100644
--- a/tests/proc-new.test
+++ b/tests/proc-new.test
@@ -28,6 +28,14 @@ proc hproc {{a aa} args} {
list a $a args $args
}
+proc iproc {{a aa} b {c cc}} {
+ list a $a b $b c $c
+}
+
+proc jproc {args {a aa} b {c cc} d} {
+ list a $a b $b c $c d $d args $args
+}
+
set n 1
foreach {proc params result} {
aproc {} {}
@@ -48,6 +56,13 @@ foreach {proc params result} {
hproc {} {a aa args {}}
hproc {A} {a A args {}}
hproc {A X Y Z} {a A args {X Y Z}}
+ iproc {B} {a aa b B c cc}
+ iproc {A B} {a A b B c cc}
+ iproc {A B C} {a A b B c C}
+ jproc {B D} {a aa b B c cc d D args {}}
+ jproc {A B D} {a A b B c cc d D args {}}
+ jproc {A B C D} {a A b B c C d D args {}}
+ jproc {E F A B C D} {a A b B c C d D args {E F}}
} {
test proc-1.$n "Proc args combos" [list $proc {*}$params] $result
incr n