Tcl Source Code

Artifact [affb0e9623]
Login

Artifact affb0e9623a454b6ef8ee82926e8fbe5e833d06c:

Attachment "apply4.patch" to ticket [944803ffff] added by msofer 2006-01-19 05:46:13.
Index: generic/tclBasic.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclBasic.c,v
retrieving revision 1.187
diff -u -r1.187 tclBasic.c
--- generic/tclBasic.c	11 Jan 2006 17:34:53 -0000	1.187
+++ generic/tclBasic.c	18 Jan 2006 22:24:13 -0000
@@ -105,6 +105,7 @@
      */
 
     {"append",		Tcl_AppendObjCmd,	TclCompileAppendCmd,	1},
+    {"apply",	        Tcl_ApplyObjCmd,        NULL,                   1},
     {"array",		Tcl_ArrayObjCmd,	NULL,			1},
     {"binary",		Tcl_BinaryObjCmd,	NULL,			1},
     {"break",		Tcl_BreakObjCmd,	TclCompileBreakCmd,	1},
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.264
diff -u -r1.264 tclInt.h
--- generic/tclInt.h	27 Dec 2005 20:14:09 -0000	1.264
+++ generic/tclInt.h	18 Jan 2006 22:24:13 -0000
@@ -2240,6 +2240,9 @@
 MODULE_SCOPE int	Tcl_AppendObjCmd(ClientData clientData,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *CONST objv[]);
+MODULE_SCOPE int	Tcl_ApplyObjCmd(ClientData clientData,
+			    Tcl_Interp *interp, int objc,
+			    Tcl_Obj *CONST objv[]);
 MODULE_SCOPE int	Tcl_ArrayObjCmd(ClientData clientData,
 			    Tcl_Interp *interp, int objc,
 			    Tcl_Obj *CONST objv[]);
Index: generic/tclProc.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclProc.c,v
retrieving revision 1.83
diff -u -r1.83 tclProc.c
--- generic/tclProc.c	13 Dec 2005 22:43:18 -0000	1.83
+++ generic/tclProc.c	18 Jan 2006 22:24:13 -0000
@@ -6,6 +6,7 @@
  *
  * Copyright (c) 1987-1993 The Regents of the University of California.
  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
+ * Copyright (c) 2004-2006 Miguel Sofer
  *
  * See the file "license.terms" for information on usage and redistribution of
  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -20,6 +21,8 @@
  * Prototypes for static functions in this file
  */
 
+static int              ObjInterpProcEx(ClientData clientData,register Tcl_Interp *interp,
+	                    int objc, Tcl_Obj *CONST objv[], int skip);
 static void		ProcBodyDup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr);
 static void		ProcBodyFree(Tcl_Obj *objPtr);
 static int		ProcessProcResultCode(Tcl_Interp *interp,
@@ -131,6 +134,9 @@
 
     if (TclCreateProc(interp, nsPtr, procName, objv[2], objv[3],
 	    &procPtr) != TCL_OK) {
+	Tcl_AddErrorInfo(interp, "\n    (creating proc \"");
+	Tcl_AddErrorInfo(interp, procName);
+	Tcl_AddErrorInfo(interp, "\")");
 	return TCL_ERROR;
     }
 
@@ -372,8 +378,7 @@
 	}
 	if ((fieldCount == 0) || (*fieldValues[0] == 0)) {
 	    ckfree((char *) fieldValues);
-	    Tcl_AppendResult(interp, "procedure \"", procName,
-		    "\" has argument with no name", NULL);
+	    Tcl_AppendResult(interp, "argument with no name", NULL);
 	    goto procError;
 	}
 
@@ -397,16 +402,16 @@
 		} while (*q != '\0');
 		q--;
 		if (*q == ')') { /* we have an array element */
-		    Tcl_AppendResult(interp, "procedure \"", procName,
-			    "\" has formal parameter \"", fieldValues[0],
-			    "\" that is an array element", NULL);
+		    Tcl_AppendResult(interp, "formal parameter \"",
+			    fieldValues[0],
+			    "\" is an array element", NULL);
 		    ckfree((char *) fieldValues);
 		    goto procError;
 		}
 	    } else if ((*p == ':') && (*(p+1) == ':')) {
-		Tcl_AppendResult(interp, "procedure \"", procName,
-			"\" has formal parameter \"", fieldValues[0],
-			"\" that is not a simple name", NULL);
+		Tcl_AppendResult(interp, "formal parameter \"",
+			fieldValues[0],
+			"\" is not a simple name", NULL);
 		ckfree((char *) fieldValues);
 		goto procError;
 	    }
@@ -1113,6 +1118,22 @@
 				 * procedure. */
     Tcl_Obj *CONST objv[])	/* Argument value objects. */
 {
+
+    return ObjInterpProcEx(clientData, interp, objc, objv, /*skip*/ 1);
+}
+	
+static int
+ObjInterpProcEx(
+    ClientData clientData, 	/* Record describing procedure to be
+				 * interpreted. */
+    register Tcl_Interp *interp,/* Interpreter in which procedure was
+				 * invoked. */
+    int objc,			/* Count of number of arguments to this
+				 * procedure. */
+    Tcl_Obj *CONST objv[],	/* Argument value objects. */
+    int skip)                   /* Number of initial arguments to be skipped,
+				 * ie, words in the "command name" */ 
+{
     register Proc *procPtr = (Proc *) clientData;
     Namespace *nsPtr = procPtr->cmdPtr->nsPtr;
     CallFrame *framePtr, **framePtrPtr;
@@ -1183,7 +1204,7 @@
      */
 
     numArgs = procPtr->numArgs;
-    argCt = objc-1; /* set it to the number of args to the proc */
+    argCt = objc-skip; /* set it to the number of args to the proc */
     varPtr = framePtr->compiledLocals;
     localPtr = procPtr->firstLocalPtr;
     if (numArgs == 0) {
@@ -1194,7 +1215,7 @@
 	}
     }
     imax = ((argCt < numArgs - 1)? argCt : (numArgs - 1));
-    for (i = 1; i <= imax; i++) {
+    for (i = skip; i <= imax; i++) {
 	/*
 	 * "Normal" arguments; last formal is special, depends on it being
 	 * 'args'.
@@ -1245,7 +1266,9 @@
      */
 
     if (localPtr->flags & VAR_IS_ARGS) {
-	Tcl_Obj *listPtr = Tcl_NewListObj(objc-numArgs, &(objv[numArgs]));
+	int offset = skip-1;
+	Tcl_Obj *listPtr = Tcl_NewListObj(objc-numArgs-offset,
+		&(objv[numArgs+offset]));
 	varPtr->value.objPtr = listPtr;
 	Tcl_IncrRefCount(listPtr);	/* local var is a reference */
     } else if (argCt == numArgs) {
@@ -1866,6 +1889,209 @@
 }
 
 /*
+ * LAMBDA and APPLY implementation
+ *
+ */
+
+static void		DupLambdaInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr,
+			    Tcl_Obj *copyPtr));
+static void		FreeLambdaInternalRep _ANSI_ARGS_((
+    			    Tcl_Obj *objPtr));
+static int		SetLambdaFromAny _ANSI_ARGS_((Tcl_Interp *interp,
+			    Tcl_Obj *objPtr));
+
+Tcl_ObjType lambdaType = {
+    "lambda",				/* name */
+    FreeLambdaInternalRep,	        /* freeIntRepProc */
+    DupLambdaInternalRep,	        /* dupIntRepProc */
+    (Tcl_UpdateStringProc *) NULL,	/* updateStringProc */
+    SetLambdaFromAny			/* setFromAnyProc */
+};
+
+/*
+ * a Lambda Tcl_Obj has the form
+ *
+ *  ptr1 is a *Proc:     pointer to a proc structure
+ *  ptr2 is a *Tcl_Obj:  the lambda's namespace
+ */
+
+static void
+DupLambdaInternalRep(srcPtr, copyPtr)
+    Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */
+    register Tcl_Obj *copyPtr;	/* Object with internal rep to set. */
+{
+    Proc *procPtr = (Proc *) srcPtr->internalRep.twoPtrValue.ptr1;
+    Tcl_Obj *nsObjPtr = (Tcl_Obj *) srcPtr->internalRep.twoPtrValue.ptr2;
+
+    copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
+    copyPtr->internalRep.twoPtrValue.ptr2 = (VOID *) nsObjPtr;
+
+    procPtr->refCount++;
+    Tcl_IncrRefCount(nsObjPtr);
+    copyPtr->typePtr = &lambdaType;
+}
+
+static void
+FreeLambdaInternalRep(objPtr)
+    register Tcl_Obj *objPtr;	/* CmdName object with internal
+				 * representation to free. */
+{
+    Proc *procPtr = (Proc *) objPtr->internalRep.twoPtrValue.ptr1;
+    Tcl_Obj *nsObjPtr = (Tcl_Obj *) objPtr->internalRep.twoPtrValue.ptr2;
+
+    procPtr->refCount--;
+    if (procPtr->refCount == 0) {
+	TclProcCleanupProc(procPtr);
+    }
+    TclDecrRefCount(nsObjPtr);
+}
+
+
+static int
+SetLambdaFromAny(interp, objPtr)
+    Tcl_Interp *interp;		/* Used for error reporting if not NULL. */
+    register Tcl_Obj *objPtr;	/* The object to convert. */
+{
+    char *name;
+    Tcl_Obj *argsPtr, *bodyPtr, *nsObjPtr, **objv, *errPtr;
+    int objc;
+    Proc *procPtr;
+    int result;
+
+    /*
+     * Convert objPtr to list type first; if it cannot be
+     * converted, or if its length is not 2, then it cannot
+     * be converted to lambdaType.
+     */
+
+    result = Tcl_ListObjGetElements(interp, objPtr, &objc, &objv);
+    if ((result != TCL_OK) || ((objc != 2) && (objc != 3))) {
+	errPtr = Tcl_NewStringObj("can't interpret \"",-1);
+	Tcl_IncrRefCount(errPtr);
+	Tcl_AppendObjToObj(errPtr, objPtr);
+	Tcl_AppendToObj(errPtr, "\" as a lambda expression", -1);
+	Tcl_SetObjResult(interp, errPtr);
+	Tcl_DecrRefCount(errPtr);
+	return TCL_ERROR;
+    }
+
+    argsPtr = objv[0];
+    bodyPtr = objv[1];
+
+    /*
+     * Create and initialize the Proc struct. The cmdPtr field is
+     * set to NULL to signal that this is an anonymous function.
+     */
+
+    name = TclGetString(objPtr);
+    
+    if (TclCreateProc(interp, /*ignored nsPtr*/ NULL, name, argsPtr,
+		bodyPtr, &procPtr) != TCL_OK) {
+	Tcl_AddErrorInfo(interp, "\n    (parsing lambda expression \"");
+	Tcl_AddErrorInfo(interp, name);
+	Tcl_AddErrorInfo(interp, "\")");
+        return TCL_ERROR;
+    }
+    procPtr->refCount++;
+    procPtr->cmdPtr = (Command *) NULL;
+
+    /*
+     * Set the namespace for this lambda: given by objv[2] understood
+     * as a global reference, or else global per default.
+     */
+    
+    nsObjPtr = Tcl_NewStringObj("::", 2);
+    Tcl_IncrRefCount(nsObjPtr);    
+
+    if (objc == 3) {
+	Tcl_AppendObjToObj(nsObjPtr, objv[2]);
+    }
+	    	
+    
+    /*
+     * Free the list internalrep of objPtr - this will free argsPtr, but
+     * bodyPtr retains a reference from the Proc structure. Then finish
+     * the conversion to lambdaType.
+     */
+
+    objPtr->typePtr->freeIntRepProc(objPtr);
+
+    objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) procPtr;
+    objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) nsObjPtr;
+    objPtr->typePtr = &lambdaType;
+    return TCL_OK;
+}
+
+int
+Tcl_ApplyObjCmd(dummy, interp, objc, objv)
+    ClientData dummy;		/* Not used. */
+    Tcl_Interp *interp;		/* Current interpreter. */
+    int objc;			/* Number of arguments. */
+    Tcl_Obj *CONST objv[];	/* Argument objects. */
+{
+    Interp *iPtr = (Interp *) interp;
+    Proc *procPtr;
+    Tcl_Obj *lambdaPtr, *nsObjPtr, *errPtr;
+    int result;
+    Command cmd;
+    Tcl_Namespace *nsPtr;
+    
+    if (objc < 2) {
+	Tcl_WrongNumArgs(interp, 1, objv, "lambdaExpr ?arg1 arg2 ...?");
+	return TCL_ERROR;
+    }
+
+    /*
+     * Set lambdaPtr, convert it to lambdaType in the current
+     * interp if necessary.
+     */
+ 
+    lambdaPtr = objv[1];
+    if (lambdaPtr->typePtr == &lambdaType) {
+	procPtr = (Proc *) lambdaPtr->internalRep.twoPtrValue.ptr1;
+    } else {
+	procPtr = (Proc *) NULL;
+    }
+
+    if ((procPtr == NULL) || (procPtr->iPtr != iPtr)) {
+	result = SetLambdaFromAny(interp, lambdaPtr);
+	if (result != TCL_OK) {
+	    return result;
+	}
+	procPtr = (Proc *) lambdaPtr->internalRep.twoPtrValue.ptr1;
+    }
+    procPtr->cmdPtr = &cmd;
+    
+    /*
+     * Find the namespace where this lambda should run, and
+     * push a call frame for that namespace. Note that
+     * TclObjInterpProc() will pop it.
+     */
+    
+    nsObjPtr = (Tcl_Obj *) lambdaPtr->internalRep.twoPtrValue.ptr2;
+    result  = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr);
+    if (result != TCL_OK) {
+	return result;
+    }
+    if (nsPtr == (Tcl_Namespace *) NULL) {
+	errPtr = Tcl_NewStringObj("cannot find namespace \"",-1);
+	Tcl_IncrRefCount(errPtr);
+	Tcl_AppendObjToObj(errPtr, nsObjPtr);
+	Tcl_AppendToObj(errPtr, "\"", -1);
+	Tcl_SetObjResult(interp, errPtr);
+	Tcl_DecrRefCount(errPtr);
+	return TCL_ERROR;
+    }
+
+    /*
+      cmd = *((Command *) Tcl_GetCommandFromObj(interp, objv[0]));
+    */
+    cmd.nsPtr = (Namespace *) nsPtr;
+
+    return ObjInterpProcEx((ClientData) procPtr, interp, objc, objv, 2);
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
Index: tests/proc-old.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/proc-old.test,v
retrieving revision 1.13
diff -u -r1.13 proc-old.test
--- tests/proc-old.test	29 Oct 2004 15:39:10 -0000	1.13
+++ tests/proc-old.test	18 Jan 2006 22:24:14 -0000
@@ -274,10 +274,10 @@
 } {1 {unmatched open brace in list}}
 test proc-old-5.5 {error conditions} {
     list [catch {proc tproc {{} y} {return foo}} msg] $msg
-} {1 {procedure "tproc" has argument with no name}}
+} {1 {argument with no name}}
 test proc-old-5.6 {error conditions} {
     list [catch {proc tproc {{} y} {return foo}} msg] $msg
-} {1 {procedure "tproc" has argument with no name}}
+} {1 {argument with no name}}
 test proc-old-5.7 {error conditions} {
     list [catch {proc tproc {{x 1 2} y} {return foo}} msg] $msg
 } {1 {too many fields in argument specifier "x 1 2"}}
Index: tests/proc.test
===================================================================
RCS file: /cvsroot/tcl/tcl/tests/proc.test,v
retrieving revision 1.17
diff -u -r1.17 proc.test
--- tests/proc.test	22 Sep 2004 15:48:23 -0000	1.17
+++ tests/proc.test	18 Jan 2006 22:24:14 -0000
@@ -101,12 +101,12 @@
             set z [expr $a(1)+$a(2)]
             puts "$z=z, $a(1)=$a(1)"
         }} msg] $msg
-} {1 {procedure "p" has formal parameter "a(1)" that is an array element}}
+} {1 {formal parameter "a(1)" is an array element}}
 test proc-1.8 {Tcl_ProcObjCmd, check that formal parameter names are simple names} {
     catch {rename p ""}
     list [catch {proc p {b:a b::a} { 
     }} msg] $msg
-} {1 {procedure "p" has formal parameter "b::a" that is not a simple name}}
+} {1 {formal parameter "b::a" is not a simple name}}
 
 test proc-2.1 {TclFindProc, simple proc name and proc not in namespace} {
     catch {namespace delete {expand}[namespace children :: test_ns_*]}