Diff
Not logged in

Differences From Artifact [3bbddbac86]:

To Artifact [00ffe5a931]:


1
2
3
4
5
6
7
8

9
10
11
12
13

14
15
16
17
18
19
20
21
22


23
24
25
26
27
28
29
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32








+




-
+









+
+







/*
 * tclProc.c --
 *
 *	This file contains routines that implement Tcl procedures, including
 *	the "proc" and "uplevel" commands.
 *
 * 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.
 *
 * RCS: @(#) $Id: tclProc.c,v 1.84 2006/01/23 11:01:59 msofer Exp $
 * RCS: @(#) $Id: tclProc.c,v 1.85 2006/02/01 19:26:02 dgp Exp $
 */

#include "tclInt.h"
#include "tclCompile.h"

/*
 * 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,
			    char *procName, int nameLen, int returnCode);
static int		TclCompileNoOp(Tcl_Interp *interp, Tcl_Parse *parsePtr,
			    struct CompileEnv *envPtr);
static void		InitCompiledLocals(Tcl_Interp *interp,
127
128
129
130
131
132
133



134
135
136
137
138
139
140
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146







+
+
+








    /*
     * Create the data structure to represent the procedure.
     */

    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;
    }

    /*
     * Now create a command for the procedure. This will initially be in the
     * current namespace unless the procedure's name included namespace
     * qualifiers. To create the new command in the right namespace, we
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
374
375
376
377
378
379
380

381

382
383
384
385
386
387
388







-
+
-







	    Tcl_AppendResult(interp,
		    "too many fields in argument specifier \"",
		    argArray[i], "\"", NULL);
	    goto procError;
	}
	if ((fieldCount == 0) || (*fieldValues[0] == 0)) {
	    ckfree((char *) fieldValues);
	    Tcl_AppendResult(interp, "procedure \"", procName,
	    Tcl_AppendResult(interp, "argument with no name", NULL);
		    "\" has argument with no name", NULL);
	    goto procError;
	}

	nameLength = strlen(fieldValues[0]);
	if (fieldCount == 2) {
	    valueLength = strlen(fieldValues[1]);
	} else {
393
394
395
396
397
398
399
400
401
402



403
404
405
406
407
408
409



410
411
412
413
414
415
416
398
399
400
401
402
403
404



405
406
407
408
409
410
411



412
413
414
415
416
417
418
419
420
421







-
-
-
+
+
+




-
-
-
+
+
+







	    if (*p == '(') {
		CONST char *q = p;
		do {
		    q++;
		} 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;
	    }
	    p++;
	}

	if (precompiled) {
1109
1110
1111
1112
1113
1114
1115
















1116
1117
1118
1119
1120
1121
1122
1123

1124
1125
1126
1127
1128
1129
1130
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








+







				 * 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. */
{

    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;
    register Var *varPtr;
    register CompiledLocal *localPtr;
    char *procName;
    int nameLen, localCt, numArgs, argCt, i, imax, result;
    Var *compiledLocals;
    Tcl_Obj *CONST *argObjs;

    /*
     * Get the procedure's name.
     */

    procName = Tcl_GetStringFromObj(objv[0], &nameLen);

1179
1180
1181
1182
1183
1184
1185
1186


1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197

1198
1199
1200
1201
1202
1203

1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217

1218
1219
1220
1221
1222
1223
1224
1201
1202
1203
1204
1205
1206
1207

1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219

1220
1221
1222
1223
1224
1225

1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239

1240
1241
1242
1243
1244
1245
1246
1247







-
+
+










-
+





-
+













-
+







     * Match and assign the call's actual parameters to the procedure's formal
     * arguments. The formal arguments are described by the first numArgs
     * entries in both the Proc structure's local variable list and the call
     * frame's local variable array.
     */

    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 */
    argObjs = &objv[skip];
    varPtr = framePtr->compiledLocals;
    localPtr = procPtr->firstLocalPtr;
    if (numArgs == 0) {
	if (argCt) {
	    goto incorrectArgs;
	} else {
	    goto runProc;
	}
    }
    imax = ((argCt < numArgs - 1)? argCt : (numArgs - 1));
    for (i = 1; i <= imax; i++) {
    for (i = 0; i < imax; i++) {
	/*
	 * "Normal" arguments; last formal is special, depends on it being
	 * 'args'.
	 */

	Tcl_Obj *objPtr = objv[i];
	Tcl_Obj *objPtr = argObjs[i];

	varPtr->value.objPtr = objPtr;
	Tcl_IncrRefCount(objPtr);	/* local var is a reference */
	varPtr->name = localPtr->name;
	varPtr->nsPtr = NULL;
	varPtr->hPtr = NULL;
	varPtr->refCount = 0;
	varPtr->tracePtr = NULL;
	varPtr->searchPtr = NULL;
	varPtr->flags = localPtr->flags;
	varPtr++;
	localPtr = localPtr->nextPtr;
    }
    for (; i < numArgs; i++) {
    for (; i < (numArgs - 1); i++) {
	/*
	 * This loop is entered if argCt < (numArgs-1). Set default values;
	 * last formal is special.
	 */

	if (localPtr->defValuePtr != NULL) {
	    Tcl_Obj *objPtr = localPtr->defValuePtr;
1241
1242
1243
1244
1245
1246
1247
1248

1249
1250
1251
1252

1253
1254
1255
1256
1257
1258
1259
1264
1265
1266
1267
1268
1269
1270

1271
1272
1273
1274

1275
1276
1277
1278
1279
1280
1281
1282







-
+



-
+








    /*
     * When we get here, the last formal argument remains to be defined:
     * localPtr and varPtr point to the last argument to be initialized.
     */

    if (localPtr->flags & VAR_IS_ARGS) {
	Tcl_Obj *listPtr = Tcl_NewListObj(objc-numArgs, &(objv[numArgs]));
	Tcl_Obj *listPtr = Tcl_NewListObj(argCt-i, &(argObjs[i]));
	varPtr->value.objPtr = listPtr;
	Tcl_IncrRefCount(listPtr);	/* local var is a reference */
    } else if (argCt == numArgs) {
	Tcl_Obj *objPtr = objv[numArgs];
	Tcl_Obj *objPtr = argObjs[i];
	varPtr->value.objPtr = objPtr;
	Tcl_IncrRefCount(objPtr);	/* local var is a reference */
    } else if ((argCt < numArgs) && (localPtr->defValuePtr != NULL)) {
	Tcl_Obj *objPtr = localPtr->defValuePtr;
	varPtr->value.objPtr = objPtr;
	Tcl_IncrRefCount(objPtr);	/* local var is a reference */
    } else {
1275
1276
1277
1278
1279
1280
1281
1282

1283
1284
1285
1286
1287
1288
1289
1298
1299
1300
1301
1302
1303
1304

1305
1306
1307
1308
1309
1310
1311
1312







-
+








	desiredObjs = (Tcl_Obj **)
		ckalloc(sizeof(Tcl_Obj *) * (unsigned)(numArgs+1));

#ifdef AVOID_HACKS_FOR_ITCL
	desiredObjs[0] = objv[0];
#else
	desiredObjs[0] = Tcl_NewListObj(1, objv);
	desiredObjs[0] = Tcl_NewListObj(skip, objv);
#endif /* AVOID_HACKS_FOR_ITCL */

	localPtr = procPtr->firstLocalPtr;
	for (i=1 ; i<=numArgs ; i++) {
	    TclNewObj(argObj);
	    if (localPtr->defValuePtr != NULL) {
		Tcl_AppendStringsToObj(argObj, "?", localPtr->name, "?", NULL);
1860
1861
1862
1863
1864
1865
1866


























































































































































































































1867
1868
1869
1870
1871
1872
1873
1874
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








	    TclEmitOpcode(INST_POP, envPtr);
	}
    }
    envPtr->currStackDepth = savedStackDepth;
    TclEmitPush(TclRegisterNewLiteral(envPtr, "", 0), envPtr);
    return TCL_OK;
}

/*
 * 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 = NULL;
    Tcl_Obj *lambdaPtr, *nsObjPtr, *errPtr;
    int result;
    Command cmd;
    Tcl_Namespace *nsPtr;

#define JOE_EXTENSION 0
#if JOE_EXTENSION
    Tcl_Obj *elemPtr;
    int numElem;
#endif
    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;

#if JOE_EXTENSION
/*
 * Joe English's suggestion to allow cmdNames to function as lambdas. Requires
 * also making tclCmdNameType non-static in tclObj.c
 *
 */ 
    } else if ((lambdaPtr->typePtr == &tclCmdNameType)
	    || (TCL_OK == (Tcl_ListObjGetElements(interp, lambdaPtr, &numElem, &elemPtr))
		    && (numElem == 1))) {
	return Tcl_EvalObjv(interp, objc-1, objv+1, 0);
#endif
    }

    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
 * fill-column: 78
 * End:
 */