fix 'nedit file' segfaults
[nedit-bw.git] / arrayReplacesArglist5.diff
blob3575931fd2abd61a6b802400ba3c3ec8e5d75d49
1 Allow all arguments to be passed to a function using a single array value,
2 using the syntax:
4 retval = func( = args)
6 This relies on other patches.
8 Numerically indexed arguments are extracted from the array and pushed on the
9 stack, along with the number of arguments.
11 ---
13 source/interpret.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++-------
14 source/interpret.h | 2
15 source/parse.y | 32 +++++++---
16 3 files changed, 165 insertions(+), 29 deletions(-)
18 diff --quilt old/source/interpret.c new/source/interpret.c
19 --- old/source/interpret.c
20 +++ new/source/interpret.c
21 @@ -62,10 +62,12 @@ static const char CVSID[] = "$Id: interp
23 #ifdef HAVE_DEBUG_H
24 #include "../debug.h"
25 #endif
27 +#include <assert.h>
29 #define PROGRAM_SIZE 4096 /* Maximum program size */
30 #define MAX_ERR_MSG_LEN 256 /* Max. length for error messages */
31 #define LOOP_STACK_SIZE 200 /* (Approx.) Number of break/continue stmts
32 allowed per program */
33 #define INSTRUCTION_LIMIT 100 /* Number of instructions the interpreter is
34 @@ -117,10 +119,12 @@ static int concatenateNwithSep(int nVals
35 int leaveParams);
36 static int concat(void);
37 static int assign(void);
38 static int callSubroutineFromSymbol(Symbol *sym, int nArgs);
39 static int callSubroutine(void);
40 +static int callSubroutineStackedN(void);
41 +static int unpackArrayToArgs(void);
42 static int fetchRetVal(void);
43 static int branch(void);
44 static int branchTrue(void);
45 static int branchFalse(void);
46 static int branchIf(Boolean trueOrFalse);
47 @@ -229,10 +233,12 @@ static int (*OpFns[N_OPS])() = {returnNo
48 branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
49 deleteArrayElement, pushArraySymVal,
50 arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray,
51 anonArrayOpen, anonArraySkip, anonArrayNextVal, anonArrayIndexVal,
52 anonArrayClose, namedArg1, namedArgN, swapTop2,
53 + callSubroutineStackedN,
54 + unpackArrayToArgs,
57 /* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
58 #define FP_ARG_COUNT_INDEX (-1)
59 #define FP_FUNCTION_NAME (-2) /* !! */
60 @@ -656,11 +662,11 @@ void RunMacroAsSubrCall(Program *prog)
62 StackP->tag = NO_TAG;
63 StackP->val.sym = prog->localSymList; /* symbol table */
64 StackP++;
66 - StackP->tag = STRING_TAG;
67 + StackP->tag = STRING_TAG; /* function name */
68 StackP->val.str.rep = prog->name ? prog->name : "<run-macro>";
69 StackP->val.str.len = strlen(StackP->val.str.rep);
70 StackP++;
72 StackP->tag = INT_TAG; /* nArgs */
73 @@ -1347,33 +1353,36 @@ static int pushArgCount(void)
76 static int pushArgArray(void)
78 int nArgs, argNum;
79 - DataValue argVal, *resultArray;
80 + DataValue argVal, *argArray;
81 + Boolean needArgCopy = False;
83 DISASM_RT(PC-1, 1);
84 STACKDUMP(0, 3);
86 nArgs = FP_GET_ARG_COUNT(FrameP);
87 - resultArray = &FP_GET_ARG_ARRAY_CACHE(FrameP);
88 - if (resultArray->tag != ARRAY_TAG) {
89 - resultArray->tag = ARRAY_TAG;
90 - resultArray->val.arrayPtr = ArrayNew();
91 + argArray = &FP_GET_ARG_ARRAY_CACHE(FrameP);
92 + if (argArray->tag != ARRAY_TAG) {
93 + /* we require a real array in the argArray position */
94 + argArray->tag = ARRAY_TAG;
95 + argArray->val.arrayPtr = ArrayNew();
96 + needArgCopy = True;
99 - /* load arguments from positional arg list if not already done */
100 - if (nArgs && !ArrayGet(resultArray, longAsStr(argNum + 1), &argVal)) {
101 + if (needArgCopy || (nArgs && !ArrayGet(argArray, (char *)"1", &argVal))) {
102 + /* load arguments from positional arg list if not already done */
103 for (argNum = 0; argNum < nArgs; ++argNum) {
104 argVal = FP_GET_ARG_N(FrameP, argNum);
105 - if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
106 + if (!ArrayInsert(argArray, AllocStringOfNumber(argNum + 1),
107 &argVal)) {
108 return(execError("argument array insertion failure", NULL));
112 - PUSH(*resultArray);
113 + PUSH(*argArray);
114 return STAT_OK;
118 ** Push an array (by reference) onto the stack
119 @@ -2340,10 +2349,15 @@ static int concat(void)
122 ** For callSubroutine:
123 ** Before: Prog-> [subrSym], nArgs, next, ...
124 ** TheStack-> argArray?, argN-arg1, next, ...
126 +** For callSubroutineStackedN:
127 +** Before: Prog-> [subrSym], next, ...
128 +** TheStack-> nArgs, argArray, argN-arg1, next, ...
130 ** After: Prog-> next, ... -- (built-in called subr)
131 ** TheStack-> retVal?, next, ...
132 ** or: Prog-> (in called)next, ... -- (macro code called subr)
133 ** TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
135 @@ -2363,12 +2377,13 @@ static int callSubroutineFromSymbol(Symb
136 ** If the subroutine is built-in, call the built-in routine
138 if (sym->type == C_FUNCTION_SYM) {
139 DataValue result;
141 - if (!haveNamedArgs)
142 + if (!haveNamedArgs) {
143 PUSH(noValue) /* push dummy named arg array */
146 /* "pop" stack back to the first argument in the call stack */
147 StackP -= nArgs + 1;
149 /* Call the function and check for preemption */
150 @@ -2488,23 +2503,56 @@ static int callSubroutineFromSymbol(Symb
151 ** Stack-> argN-arg1, next, ...
153 ** After: Prog-> next, ... -- (built-in called subr)
154 ** Stack-> retVal?, next, ...
155 ** or: Prog-> (in called)next, ... -- (macro code called subr)
156 -** Stack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
157 +** Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
159 static int callSubroutine(void)
161 Symbol *sym;
162 int nArgs;
163 - int n;
165 sym = PC++->sym;
166 nArgs = PC++->value;
168 DISASM_RT(PC-3, 3);
169 - STACKDUMP(nArgs, 3);
170 + STACKDUMP(nArgs > 0 ? nArgs : -nArgs, 3);
172 + return callSubroutineFromSymbol(sym, nArgs);
176 +** Before: Prog-> [subrSym], next, ...
177 +** Stack-> nArgs, argArray, argN-arg1, next, ...
179 +** After: Prog-> next, ... -- (built-in called subr)
180 +** Stack-> retVal?, next, ...
181 +** or: Prog-> (in called)next, ... -- (macro code called subr)
182 +** Stack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
184 +static int callSubroutineStackedN(void)
186 + Symbol *sym;
187 + int nArgs;
188 + /* this is much like callSubroutine, but we get nArgs off the stack
189 + and it will always be negative since there is always an argArray */
191 + sym = PC++->sym;
193 + PEEK_INT(nArgs, 0)
194 + DISASM_RT(PC-2, 2);
195 + STACKDUMP(-nArgs + 1, 3); /* +1 for stacked nArgs */
197 + POP_INT(nArgs)
199 + assert (nArgs < 0);
201 + if (nArgs >= 0) {
202 + /* should never happen */
203 + return execError("array argument call to %s erroneous", sym->name);
206 return callSubroutineFromSymbol(sym, nArgs);
210 @@ -2514,24 +2562,35 @@ static int callSubroutine(void)
211 ** removeArgs must indicate how many.
214 int OverlayRoutineFromSymbol(Symbol *sym, int nArgs, int removeArgs)
216 - DataValue *stackTop = StackP + nArgs - removeArgs;
217 + DataValue *argArray = StackP + nArgs - removeArgs;
219 + assert (nArgs >= 0);
220 + assert (nArgs - removeArgs >= 0);
222 if (removeArgs > 0) {
223 DataValue *from = StackP + removeArgs;
224 DataValue *to = StackP;
225 int n = nArgs - removeArgs;
227 nArgs = n;
228 + ++n; /* add one for the argArray */
229 while (n--) {
230 *to++ = *from++;
234 - StackP = stackTop;
235 + if (argArray->tag == ARRAY_TAG) {
236 + StackP = argArray + 1;
237 + nArgs = -nArgs - 1;
239 + else {
240 + assert (argArray->tag == NO_TAG);
241 + StackP = argArray;
243 return callSubroutineFromSymbol(sym, nArgs);
247 ** Assumes a valid prog. Wraps the program in a dummy symbol then calls
248 @@ -2542,18 +2601,72 @@ int OverlayRoutineFromSymbol(Symbol *sym
249 int OverlayRoutineFromProg(Program *prog, int nArgs, int removeArgs)
251 Symbol sym;
253 sym.type = MACRO_FUNCTION_SYM;
254 - sym.name = "<overlaid function>";
255 + sym.name = prog->name;
256 sym.value.val.str.rep = (char *)prog;
257 sym.next = NULL;
259 return OverlayRoutineFromSymbol(&sym, nArgs, removeArgs);
263 +** For special call style where the $args array in the called function is
264 +** assigned from an array in the caller (as "calledFunc(=argsArray)"),
265 +** take consecutive elements indexed from 1 and put them on the stack, leaving
266 +** a copy of the actual array at the top of the stack, with the stacked
267 +** arguments removed. Finally, add the negative of the number of arguments
268 +** aplus 1 (for the argArray itself). This operation must be followed
269 +** by OP_SUBR_CALL_STACKED_N (callSubroutineStackedN()).
271 +** The array copy is needed because if/when the $args array is accessed, the
272 +** arguments are copied back to the array, probably in different positions, as
273 +** is the case of a "call(=array)" function, where the first argument is
274 +** removed (the function name) and the others shifted down once. Without a
275 +** copy, this modifies the original array - a pass by reference not allowed in
276 +** the language.
278 +** Before: Prog-> next, ...
279 +** TheStack-> argArray, next, ...
280 +** After: Prog-> next, ...
281 +** TheStack-> -(nArgs+1), argArray, argN-arg1, next, ...
283 +static int unpackArrayToArgs(void)
285 + int nArgs, res;
287 + DataValue dvEntry, dvArray;
289 + DISASM_RT(PC-1, 1);
290 + STACKDUMP(1, 3);
292 + POP(dvEntry)
294 + if (dvEntry.tag != ARRAY_TAG) {
295 + return execError("argument array call made with non-array value", NULL);
297 + res = ArrayCopy(&dvArray, &dvEntry);
298 + if (res != STAT_OK) {
299 + return execError("cannot copy array in array call", NULL);
302 + /* push positional argument entries in the array on the stack */
303 + for (nArgs = 1; ; ++nArgs) {
304 + char *ind = (char *)longAsStr(nArgs);
305 + if (!ArrayGet(&dvArray, ind, &dvEntry))
306 + break;
307 + /* remove them from remaining array */
308 + ArrayDelete(&dvArray, ind);
309 + PUSH(dvEntry)
311 + PUSH(dvArray)
312 + PUSH_INT(-nArgs)
313 + return STAT_OK;
317 ** This should never be executed, returnVal checks for the presence of this
318 ** instruction at the PC to decide whether to push the function's return
319 ** value, then skips over it without executing.
321 static int fetchRetVal(void)
322 @@ -3581,10 +3694,12 @@ static void disasmInternal(Inst *inst, i
323 "ARRAY_INDEX_VAL", /* anonArrayIndexVal: "{ [i]=expr }" */
324 "ARRAY_CLOSE", /* anonArrayClose: "{...}" */
325 "NAMED_ARG1", /* namedArg1: "fn([...]=..., ...)" */
326 "NAMED_ARGN", /* namedArgN: "fn(..., [...]=...)" */
327 "SWAP_TOP2", /* swapTop2: cf namedArgN */
328 + "SUBR_CALL_STACKED_N", /* callSubroutineStackedN */
329 + "UNPACKTOARGS", /* unpackArrayToArgs */
331 int i, j;
333 printd("\n");
334 for (i = 0; i < nInstr; ++i) {
335 @@ -3620,10 +3735,14 @@ static void disasmInternal(Inst *inst, i
336 else {
337 printd("%d args", args);
339 i += 2;
341 + else if (j == OP_SUBR_CALL_STACKED_N) {
342 + printd("%s args[] (?)", inst[i+1].sym->name);
343 + ++i;
345 else if (j == OP_BEGIN_ARRAY_ITER) {
346 printd("%s in", inst[i+1].sym->name);
347 ++i;
349 else if (j == OP_ARRAY_ITER) {
350 @@ -3667,10 +3786,11 @@ static void disasm(Inst *inst, int nInst
352 static int outIsTTY = -1;
353 if (outIsTTY == -1) outIsTTY = isatty(fileno(stdout));
354 if (outIsTTY) { printd("\033[H"); }
355 disasmInternal(inst, nInstr);
356 + if (outIsTTY) { printd("\033[J\n"); }
357 outPrintd();
359 #endif /* #ifdef DEBUG_DISASSEMBLER */
361 #ifdef DEBUG_STACK /* for run-time stack dumping */
362 @@ -3707,11 +3827,11 @@ static void stackdumpframe(DataValue *ar
363 if (len > 27)
364 len = 27;
365 if (len > symLen)
366 symLen = len;
371 /* output instructions between endDv and sp - 1 inclusive */
372 #ifdef DEBUG_STACK_HEADFIRST
373 dv = sp;
374 while (--dv >= endDv)
375 @@ -3754,11 +3874,11 @@ static void stackdumpframe(DataValue *ar
376 break;
378 printd(posFmt, pos);
380 /* local symbol names? */
381 - if (offset < nSyms) {
382 + if (0 <= offset && offset < nSyms) {
383 for (sym = syms; sym != NULL; sym = sym->next) {
384 if (sym->value.val.n == offset) {
385 symName = sym->name;
386 break;
388 @@ -3767,11 +3887,11 @@ static void stackdumpframe(DataValue *ar
389 printd("%-*.*s ", symLen, symLen, symName);
391 if (dv == fnNm && dv->tag == STRING_TAG && dv->val.str.rep)
392 printd("%s", dv->val.str.rep);
393 else
394 - dumpVal(*dv);
395 + dumpVal(*dv);
397 printd("\n");
400 #ifdef DEBUG_STACK_HEADFIRST
401 diff --quilt old/source/interpret.h new/source/interpret.h
402 --- old/source/interpret.h
403 +++ new/source/interpret.h
404 @@ -50,10 +50,12 @@ enum operations {OP_RETURN_NO_VAL, OP_RE
405 OP_ARRAY_DELETE, OP_PUSH_ARRAY_SYM, OP_ARRAY_REF_ASSIGN_SETUP, OP_PUSH_ARG,
406 OP_PUSH_ARG_COUNT, OP_PUSH_ARG_ARRAY,
407 OP_ANONARRAY_OPEN, OP_ANONARRAY_SKIP, OP_ANONARRAY_NEXT_VAL,
408 OP_ANONARRAY_INDEX_VAL, OP_ANONARRAY_CLOSE,
409 OP_NAMED_ARG1, OP_NAMED_ARGN, OP_SWAP_TOP2,
410 + OP_SUBR_CALL_STACKED_N,
411 + OP_UNPACKTOARGS,
412 N_OPS};
414 enum typeTags {NO_TAG, INT_TAG, STRING_TAG, ARRAY_TAG};
416 enum execReturnCodes {MACRO_TIME_LIMIT, MACRO_PREEMPT, MACRO_DONE, MACRO_ERROR};
417 diff --quilt old/source/parse.y new/source/parse.y
418 --- old/source/parse.y
419 +++ new/source/parse.y
420 @@ -66,10 +66,11 @@ static int nextSymIsField = 0;
421 enum operations oper;
423 %token <sym> NUMBER STRING SYMBOL FIELD
424 %token DELETE ARG_LOOKUP
425 %token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN
426 +%type <nArgs> arrlist arrentry
427 %type <nArgs> arglistopt arglist catlist fnarglsopt fnarglist fnarg
428 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
429 %type <sym> evalsym
430 %type <oper> operassign incrdecr
431 %token <oper> '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ ANDEQ OREQ
432 @@ -244,15 +245,11 @@ simpstmt: /* simple variable assignmen
433 | incrdecr blank initarraylv dot field {
434 ADD_OP(OP_ARRAY_REF_ASSIGN_SETUP); ADD_IMMED(0); ADD_IMMED(1);
435 ADD_OP($1);
436 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED(1);
438 - /* function call */
439 - | SYMBOL '(' fnarglsopt ')' {
440 - ADD_OP(OP_SUBR_CALL);
441 - ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
443 + | funccall
446 evalsym: SYMBOL {
447 $$ = $1; ADD_OP(OP_PUSH_SYM); ADD_SYM($1);
449 @@ -262,19 +259,36 @@ comastmts: blank { $$ = G
451 comastmtlst: simpstmt blank { $$ = GetPC(); }
452 | comastmtlst ',' blank simpstmt blank { $$ = GetPC(); }
455 +/* array key argument lists */
456 arglistopt: blank { $$ = 0; }
457 | arglist { $$ = $1; }
459 arglist: blank expr blank { $$ = 1; }
460 | arglist ',' blank expr blank { $$ = $1 + 1; }
463 +/* string concatenation lists */
464 catlist: numexpr %prec CONCAT { $$ = 1; }
465 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
468 +/* function call and its argument lists */
469 +funccall: SYMBOL '(' fnarglsopt ')' {
470 + ADD_OP(OP_SUBR_CALL);
471 + ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
473 + | SYMBOL '(' blank '=' blank expr blank ')' {
474 + /* a single array replaces the argument list */
475 + ADD_OP(OP_UNPACKTOARGS);
476 + ADD_OP(OP_SUBR_CALL_STACKED_N);
477 + ADD_SYM(PromoteToGlobal($1));
481 fnarg: expr {
482 $$ = 0;
484 | '[' arglist ']' '=' expr {
485 $$ = $2; /* how many index elements to read? */
486 @@ -402,17 +416,17 @@ arrentry: blank {
488 numexpr: '(' blank expr blank ')'
489 | NUMBER { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
490 | STRING { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
491 | SYMBOL { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
492 - | SYMBOL '(' fnarglsopt ')' {
493 - ADD_OP(OP_SUBR_CALL);
494 - ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
495 + | funccall {
496 ADD_OP(OP_FETCH_RET_VAL);
498 - /*
499 + /* this doesn't work for $args["string"]:
500 | ARG_LOOKUP '[' blank numexpr blank ']' { ADD_OP(OP_PUSH_ARG); }
501 + */
502 + /* this doesn't work if $args contains non-argnum indices
503 | ARG_LOOKUP '[' blank ']' { ADD_OP(OP_PUSH_ARG_COUNT); }
505 | ARG_LOOKUP { ADD_OP(OP_PUSH_ARG_ARRAY); }
506 | numexpr '[' arglistopt ']' {
507 ADD_OP(OP_ARRAY_REF); ADD_IMMED($3);