1 From: Tony Balinski <ajbj@free.fr>
2 Subject: Allow an array to take the place of function arguments in a call
8 Exactly one array argument is allowed. The content of this array is made
9 available to the called function as $args. The positional arguments, $1 and
10 so on, are copied from the array to the stack, using the corresponding
11 numeric indices, increasing from 1 until there is a break in the sequence.
15 Corrected this patch: resolving inconsistent stacking between ExecuteMacro()
16 or RunMacroAsSubrCall() and callSubroutineFromSymbol(). Things would work
17 unless you had executable statements in a macro file.
19 Another correction handles how the argument array is passed when using the
20 new syntax. Firstly, a copy is passed to the called function (before this
21 would be the actual array); then the unnamed (numbered) arguments are
22 removed from this copy as they are loaded onto the stack. If the called
23 function then accesses "$args" the arguments are copied back into the array
24 copy. This is to allow for further functions which reoder arguments such as
25 call(function_name,arg1,arg2,...) to work when invoked with the new syntax.
29 source/interpret.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++------
31 source/parse.y | 23 +++++---
32 3 files changed, 143 insertions(+), 22 deletions(-)
34 diff --quilt old/source/interpret.c new/source/interpret.c
35 --- old/source/interpret.c
36 +++ new/source/interpret.c
37 @@ -64,6 +64,8 @@ static const char CVSID[] = "$Id: interp
43 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
44 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
45 allowed to execute before preempting and
46 @@ -1453,29 +1455,32 @@ static int pushArgCount(void)
47 static int pushArgArray(void)
50 - DataValue argVal, *resultArray;
51 + DataValue argVal, *argArray;
52 + Boolean needArgCopy = False;
57 nArgs = FP_GET_ARG_COUNT(FrameP);
58 - resultArray = &FP_GET_ARG_ARRAY(FrameP);
59 - if (resultArray->tag != ARRAY_TAG) {
60 - resultArray->tag = ARRAY_TAG;
61 - resultArray->val.arrayPtr = ArrayNew();
62 + argArray = &FP_GET_ARG_ARRAY(FrameP);
63 + if (argArray->tag != ARRAY_TAG) {
64 + /* we require a real array in the argArray position */
65 + argArray->tag = ARRAY_TAG;
66 + argArray->val.arrayPtr = ArrayNew();
70 - /* load arguments from positional arg list if not already done */
71 - if (nArgs && !ArrayGet(resultArray, longAsStr(argNum + 1), &argVal)) {
72 + if (needArgCopy || (nArgs && !ArrayGet(argArray, (char *)"1", &argVal))) {
73 + /* load arguments from positional arg list if not already done */
74 for (argNum = 0; argNum < nArgs; ++argNum) {
75 argVal = FP_GET_ARG_N(FrameP, argNum);
76 - if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
77 + if (!ArrayInsert(argArray, AllocStringOfNumber(argNum + 1),
79 return(execError("argument array insertion failure", NULL));
88 @@ -2446,6 +2451,11 @@ static int concat(void)
89 ** For callSubroutine:
90 ** Before: Prog-> [subrSym], nArgs, next, ...
91 ** TheStack-> argArray?, argN-arg1, next, ...
93 +** For callSubroutineStackedN:
94 +** Before: Prog-> [subrSym], next, ...
95 +** TheStack-> nArgs, argArray, argN-arg1, next, ...
97 ** After: Prog-> next, ... -- (built-in called subr)
98 ** TheStack-> retVal?, next, ...
99 ** or: Prog-> (in called)next, ... -- (macro code called subr)
100 @@ -2568,19 +2578,52 @@ static int callSubroutineFromSymbol(Symb
101 ** After: Prog-> next, ... -- (built-in called subr)
102 ** Stack-> retVal?, next, ...
103 ** or: Prog-> (in called)next, ... -- (macro code called subr)
104 -** Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
105 +** Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
107 static int callSubroutine(void)
117 - STACKDUMP(nArgs, 3);
118 + STACKDUMP(nArgs > 0 ? nArgs : -nArgs, 3);
120 + return callSubroutineFromSymbol(sym, nArgs);
124 +** Before: Prog-> [subrSym], next, ...
125 +** Stack-> nArgs, argArray, argN-arg1, next, ...
127 +** After: Prog-> next, ... -- (built-in called subr)
128 +** Stack-> retVal?, next, ...
129 +** or: Prog-> (in called)next, ... -- (macro code called subr)
130 +** Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
132 +static int callSubroutineStackedN(void)
136 + /* this is much like callSubroutine, but we get nArgs off the stack
137 + and it will always be negative since there is always an argArray */
141 + PEEK_INT(nArgs, 0);
142 + DISASM_RT(PC-2, 2);
143 + STACKDUMP(-nArgs + 1, 3); /* +1 for stacked nArgs */
147 + assert (nArgs < 0);
150 + /* should never happen */
151 + return execError("array argument call to %s erroneous", sym->name);
154 return callSubroutineFromSymbol(sym, nArgs);
156 @@ -2594,7 +2637,10 @@ static int callSubroutine(void)
158 int OverlayRoutineFromSymbol(Symbol *sym, int nArgs, int removeArgs)
160 - DataValue *stackTop = StackP + nArgs - removeArgs;
161 + DataValue *argArray = StackP + nArgs - removeArgs;
163 + assert (nArgs >= 0);
164 + assert (nArgs - removeArgs >= 0);
166 if (removeArgs > 0) {
167 DataValue *from = StackP + removeArgs;
168 @@ -2602,12 +2648,20 @@ int OverlayRoutineFromSymbol(Symbol *sym
169 int n = nArgs - removeArgs;
172 + ++n; /* add one for the argArray */
179 + if (argArray->tag == ARRAY_TAG) {
180 + StackP = argArray + 1;
181 + nArgs = -nArgs - 1;
184 + assert (argArray->tag == NO_TAG);
187 return callSubroutineFromSymbol(sym, nArgs);
190 @@ -2630,6 +2684,60 @@ int OverlayRoutineFromProg(Program *prog
194 +** For special call style where the $args array in the called function is
195 +** assigned from an array in the caller (as "calledFunc(=argsArray)"),
196 +** take consecutive elements indexed from 1 and put them on the stack, leaving
197 +** a copy of the actual array at the top of the stack, with the stacked
198 +** arguments removed. Finally, add the negative of the number of arguments
199 +** aplus 1 (for the argArray itself). This operation must be followed
200 +** by OP_SUBR_CALL_STACKED_N (callSubroutineStackedN()).
202 +** The array copy is needed because if/when the $args array is accessed, the
203 +** arguments are copied back to the array, probably in different positions, as
204 +** is the case of a "call(=array)" function, where the first argument is
205 +** removed (the function name) and the others shifted down once. Without a
206 +** copy, this modifies the original array - a pass by reference not allowed in
209 +** Before: Prog-> next, ...
210 +** TheStack-> argArray, next, ...
211 +** After: Prog-> next, ...
212 +** TheStack-> -(nArgs+1), argArray, argN-arg1, next, ...
214 +static int unpackArrayToArgs(void)
218 + DataValue dvEntry, dvArray;
220 + DISASM_RT(PC-1, 1);
225 + if (dvEntry.tag != ARRAY_TAG) {
226 + return execError("argument array call made with non-array value", NULL);
228 + res = ArrayCopy(&dvArray, &dvEntry);
229 + if (res != STAT_OK) {
230 + return execError("cannot copy array in array call", NULL);
233 + /* push positional argument entries in the array on the stack */
234 + for (nArgs = 1; ; ++nArgs) {
235 + char *ind = (char *)longAsStr(nArgs);
236 + if (!ArrayGet(&dvArray, ind, &dvEntry))
238 + /* remove them from remaining array */
239 + ArrayDelete(&dvArray, ind);
248 ** This should never be executed, returnVal checks for the presence of this
249 ** instruction at the PC to decide whether to push the function's return
250 ** value, then skips over it without executing.
251 @@ -3745,6 +3853,10 @@ static void disasmInternal(Inst *inst, i
255 + else if (j == OP_SUBR_CALL_STACKED_N) {
256 + printd("%s args[] (?)", inst[i+1].sym->name);
259 else if (j == OP_BEGIN_ARRAY_ITER) {
260 printd("%s in", inst[i+1].sym->name);
262 diff --quilt old/source/parse.y new/source/parse.y
263 --- old/source/parse.y
264 +++ new/source/parse.y
265 @@ -327,11 +327,7 @@ simpstmt: /* simple variable assignmen
267 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED(1);
269 - /* function call */
270 - | SYMBOL '(' fnarglsopt ')' {
271 - ADD_OP(OP_SUBR_CALL);
272 - ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
278 @@ -359,6 +355,19 @@ catlist: numexpr %prec CONC
279 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
282 +/* function calls */
283 +funccall: SYMBOL '(' fnarglsopt ')' {
284 + ADD_OP(OP_SUBR_CALL);
285 + ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
287 + | SYMBOL '(' blank '=' blank expr blank ')' {
288 + /* a single array replaces the argument list */
289 + ADD_OP(OP_UNPACKTOARGS);
290 + ADD_OP(OP_SUBR_CALL_STACKED_N);
291 + ADD_SYM(PromoteToGlobal($1));
295 /* function argument lists */
298 @@ -497,9 +506,7 @@ numexpr: '(' blank expr blank ')'
299 | NUMBER { ADD_OP(OP_PUSH_IMMED); ADD_IMMED($1); }
300 | STRING { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
301 | SYMBOL { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
302 - | SYMBOL '(' fnarglsopt ')' {
303 - ADD_OP(OP_SUBR_CALL);
304 - ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
306 ADD_OP(OP_FETCH_RET_VAL);
308 /* this doesn't work for $args["string"]:
309 diff --quilt old/source/ops.h new/source/ops.h
312 @@ -55,3 +55,5 @@ OP(ANONARRAY_CLOSE, anonArrayClos
313 OP(NAMED_ARG1, namedArg1) /* N */ /* pop(v,kN..k1), a=ary(), a[k1..kN]=v, push(a) */
314 OP(NAMED_ARGN, namedArgN) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v, push(a) */
315 OP(SWAP_TOP2, swapTop2) /* pop(v1,v2), push(v1,v2) */
316 +OP(SUBR_CALL_STACKED_N, callSubroutineStackedN) /*s*/ /* pop(N,a,pN..p1), call(s) */
317 +OP(UNPACKTOARGS, unpackArrayToArgs) /* pop(a), push(a[1]..a[N],a,-(N+1)) */