CVS update
[nedit-bw.git] / anonArrayNamedArgs7.diff
blob66773b3dec57435ddccc8f10237966a59dba3a81
1 From: Tony Balinskt <ajbj@free.fr>
2 Subject: Allow inline construction of anonymous arrays and "named arguments"
4 Available as a patch:
6 http://sourceforge.net/tracker/index.php?func=detail&aid=1592340&group_id=11005&atid=311005
7 [ 1592340 ] Array literals and named arguments
8 anonArrayNamedArgs3.diff 2006-11-16 02:34
10 This patch allows arrays to be created in-line as anonymous entities. For
11 example:
13 my_array = { \
14 1, 2, 3, # assigns elements [0] to [2] \
15 ["word"] = "val", # assigns element ["word"] \
16 , # don't assign to [3] \
17 4, # assigns element [4] = 4 \
18 [7] = 7, 8, # assigns elements [7] and [8] \
19 ["sub"] = { "a", "b" } # assigns an array to element ["sub"] \
22 The anonymous array is available in contexts where a whole array is needed;
23 for example:
25 val0 = { [0] = "zero"}[0]
26 my_array += { [val0] = 4 + 5 - 9 }
28 The patch also allows macro functions to be called with array element style
29 assignments. These cannot use simple numeric or numeric string indices, but
30 multidimensional numeric indices are allowed.
32 The array element style ("named") arguments are inserted into the called
33 routine's $args array, and accessible using array syntax. Simple argument
34 expressions are counted from the first ($1), with numeric index 1 in $args,
35 up to $n_args. Note that $args[] and $n_args may now be different since the
36 former will include the count of "named" arguments.
38 For example:
40 define returnArgs {
41 return $args
43 a = returnArgs(1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi")
44 b = { , 1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi" }
46 Here the arrays a and b are equal. (Note the comma at the start of b's
47 assigned array.) But:
49 a = returnArgs(1, [2]="no") # fails: name for arg cannot be numeric
51 Finally, calling a built-in macro function always makes sure an extra
52 argument is present at argList[nArgs] - in calls using positional values
53 only, this argument will have a tag value of NO_TAG. If "named" arguments
54 have been passed, the argument will have tag ARRAY_TAG, and the named
55 arguments be present in the array, indexed by name. (The positional
56 arguments will not be in this array but can be added to it readily.) Use the
57 ArrayGet() function or iterate over entries as required.
59 ---
61 doc/help.etx | 100 +++++++++++-
62 source/interpret.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++-------
63 source/ops.h | 8 +
64 source/parse.y | 101 ++++++++++++-
65 4 files changed, 555 insertions(+), 65 deletions(-)
67 diff --quilt old/doc/help.etx new/doc/help.etx
68 --- old/doc/help.etx
69 +++ new/doc/help.etx
70 @@ -2045,10 +2045,26 @@ Macro Language
72 function_name(arg1, arg2, ...)
74 - where arg1, arg2, etc. represent the argument values which are passed to
75 - the routine being called. A function or subroutine call can be on a line by
76 - itself, as above, or if it returns a value, can be invoked within a character
77 - or numeric expression:
78 + where arg1, arg2, etc. represent the arguments which are passed to
79 + the routine being called. Arguments can be one of two kinds: positional or
80 + named. A positional argument is passed as an expression in the argument
81 + list. A named argument is passed by indicating the name as an array key,
82 + between square brackets, followed by the "=" operator and an expression
83 + for the argument's value. Named arguments can use any valid array key value
84 + as long as it cannot be converted into an integer value.
86 + For example, the call
88 + result = func(["prompt"] = "Available values are:", \
89 + a, b, c, d, e, f, \
90 + ["buttons"] = { "OK", "Change", "Cancel" })
92 + provides six positional arguments (with the values of variables a, b, c, d, e
93 + and f), and the named arguments "prompt", with a string value, and "buttons"
94 + with an array value.
96 + A function or subroutine call can be on a line by itself, as above, or if it
97 + returns a value, can be invoked within a character or numeric expression:
99 a = fn1(b, c) + fn2(d)
100 dialog("fn3 says: " fn3())
101 @@ -2095,11 +2111,14 @@ Macro Language
102 the autoload macro file, cf. Preferences_. Macro files can be loaded with
103 File -> Load Macro File or with the load_macro_file() action.
105 - The arguments with which a user-defined subroutine or function was invoked,
106 - are presented as $1, $2, ... , $9 or $args[expr], where expr can be evaluated
107 - to an integer from 1 to the number of arguments. The number of arguments can
108 - be read from $n_args or $args[]. The array $args[expr] is the only way to
109 - access arguments beyond the first 9.
110 + Within the body of a user-defined subroutine or function, the first nine
111 + positional arguments can be retrieved using the identifiers $1, $2, ... , $9.
112 + Both positional and named arguments can be accessed using the $args array:
113 + if the key is numeric, the corresponding positional argument (numbered from 1)
114 + can be retrieved; otherwise the key is a name, and the name argument's value
115 + is retrieved. The identifier $n_args provides the number of positional
116 + arguments passed to the function. You can test for the presence of named
117 + arguments in the $args array using the "in" operator.
119 To return a value from a subroutine, and/or to exit from the subroutine
120 before the end of the subroutine body, use the return statement:
121 @@ -2238,7 +2257,7 @@ Macro Language
122 All of the above operators are array only, meaning both the left and right
123 sides of the operator must be arrays. The results are also arrays.
125 - Array keys can also contain multiple dimensions:
126 + Array keys can contain multiple "dimensions":
128 x[1, 1, 1] = "string"
130 @@ -2283,6 +2302,67 @@ Macro Language
132 does work.
134 + Note that if an array contains a value that is itself an array, you can
135 + apply the index operator more than once. For example
137 + subarray["a"] = "value"
138 + mainarray[1] = subarray
140 + for (i in mainarray) {
141 + if ("a" in mainarray[i])
142 + value_a = mainarray[i]["a"]
143 + ...
146 +4>Array Initializing Expressions
148 + You can build arrays using array expressions. These are contained in braces,
149 + "{" and "}", and contain a list of possibly empty value assignments.
150 + For example
152 + myarray = { ["a"] = "first", \
153 + ["col" colno] = x * 5, \
154 + [x, y] = 2 * func() }
156 + If the keys are numeric (or convertible to plain integers) and in increasing
157 + sequence, only the first is required; thus
159 + myarray = { [5] = a, b, c, \
160 + [1,2] = "2-D key", \
161 + ["20"] = d, e }
163 + creates entries with keys "5", "6", "7", "20", "21" and ("1" $sub_sep "2").
164 + If no key value is given for the first entry, "0" is used. If you want to skip
165 + a value in a sequence, just provide an empty value, thus
167 + myarray = { a, b, , c }
169 + creates entries with keys "0", "1" and "3". The entry for key "2" is not
170 + created.
172 + If a later array entry has the same key value as an earlier one, the later
173 + value overwrites the earlier one. For example
175 + myarray = { a, b, c, [1] = d, e }
177 + overwrites the myarray["1"] entry, initialized with the value of b, with the
178 + value of d. Similarly the myarray["2"] entry is overwritten.
180 + You can use array initializing expressions as part of other expressions. They
181 + can be passed as arguments to functions:
183 + result = func({ ["message"] = "The value is", \
184 + ["value"] = 4, "OK" })
186 + Or you can use them to add to arrays, as in
188 + myarray += { [newkey] = newvalue }
190 + The built-in variable $empty_array evaluates to an empty array. You can also
191 + build an empty array using array initializing expressions as follows:
193 + myarray = {}
195 3>Looping and Conditionals
197 NEdit supports looping constructs: for and while, and conditional statements:
198 diff --quilt old/source/interpret.c new/source/interpret.c
199 --- old/source/interpret.c
200 +++ new/source/interpret.c
201 @@ -86,9 +86,12 @@ static void restoreContext(RestartData *
202 #include "ops.h"
203 #undef OP
204 static int returnValOrNone(int valOnStack);
205 +static int branchIf(Boolean trueOrFalse);
206 +static int namedArg1orN(Boolean isFirst);
208 static int concatenateNwithSep(int nVals, const char *sep, char **result,
209 int leaveParams);
210 +static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams);
211 static void freeSymbolTable(Symbol *symTab);
212 static int errCheck(const char *s);
213 static int execError(const char *s1, const char *s2);
214 @@ -177,16 +180,16 @@ static int (*OpFns[])() = {
215 #undef OP
218 -/* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
219 -#define FP_ARG_ARRAY_INDEX (-1)
220 -#define FP_ARG_COUNT_INDEX (-2)
221 -#define FP_FUNCTION_NAME (-3) /* !! */
222 -#define FP_SYMBOL_TABLE (-4) /* !! */
223 -#define FP_OLD_FP_INDEX (-5)
224 -#define FP_RET_PC_INDEX (-6)
225 -#define FP_PROG_INDEX (-7)
226 +/* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
227 +#define FP_ARG_COUNT_INDEX (-1)
228 +#define FP_FUNCTION_NAME (-2) /* !! */
229 +#define FP_SYMBOL_TABLE (-3) /* !! */
230 +#define FP_OLD_FP_INDEX (-4)
231 +#define FP_RET_PC_INDEX (-5)
232 +#define FP_PROG_INDEX (-6)
233 +#define FP_ARG_ARRAY_INDEX (-7)
235 -#define FP_TO_ARGS_DIST (0 - FP_PROG_INDEX) /* should be 0 - (above index) */
236 +#define FP_TO_ARGS_DIST (0 - FP_ARG_ARRAY_INDEX) /* should be 0 - (above index) */
238 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
239 #define FP_GET_ARG_ARRAY(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_INDEX))
240 @@ -384,7 +387,7 @@ void SwapCode(Inst *start, Inst *boundar
241 do { register Inst t, *l = L, *h = H - 1; \
242 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
243 /* double-reverse method: reverse elements of both parts then whole lot */
244 - /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
245 + /* eg abcdeABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> ABCDabcde */
246 reverseCode(start, boundary); /* 1 */
247 reverseCode(boundary, end); /* 2 */
248 reverseCode(start, end); /* 3 */
249 @@ -453,8 +456,9 @@ void FillLoopAddrs(Inst *breakAddr, Inst
250 ** helper function to setup the next frame
252 static void setupFrame(DataValue **frameP, DataValue **stackP,
253 - Inst **pc, Program *prog, int nArgs, DataValue *args, int passProg,
254 - const char *name)
255 + Inst **pc, Program *prog,
256 + int nArgs, DataValue *args, DataValue argArray,
257 + int passProg, const char *name)
259 static DataValue noValue = {NO_TAG, {0}};
260 int i;
261 @@ -473,6 +477,9 @@ static void setupFrame(DataValue **frame
262 nArgs = -nArgs;
265 + /* cached arg array */
266 + *((*stackP)++) = argArray;
268 /* prog to free, if requested */
269 (*stackP)->tag = NO_TAG;
270 (*stackP)->val.prog = passProg ? prog : NULL;
271 @@ -500,13 +507,10 @@ static void setupFrame(DataValue **frame
272 (*stackP)++;
274 /* nArgs */
275 - (*stackP)->tag = NO_TAG;
276 + (*stackP)->tag = INT_TAG;
277 (*stackP)->val.n = nArgs;
278 (*stackP)++;
280 - /* cached arg array */
281 - *((*stackP)++) = noValue;
283 *frameP = *stackP;
285 /* Initialize and make room on the stack for local variables */
286 @@ -558,10 +562,11 @@ int ExecuteMacro(WindowInfo *window, Pro
287 DataValue *result, RestartData **continuation, char **msg)
289 RestartData *context;
290 - static DataValue noValue = {NO_TAG, {0}};
291 + static DataValue argArray = {NO_TAG, {0}};
292 Symbol *s;
293 int i;
295 + int haveNamedArgs;
297 /* Create an execution context (a stack, a stack pointer, a frame pointer,
298 and a program counter) which will retain the program state across
299 preemption and resumption of execution */
300 @@ -572,10 +577,17 @@ int ExecuteMacro(WindowInfo *window, Pro
301 context->runWindow = window;
302 context->focusWindow = window;
304 + haveNamedArgs = (nArgs < 0);
305 + if (haveNamedArgs) {
306 + nArgs = -nArgs - 1;
307 + argArray = args[nArgs];
310 /* pre-set the return PC to NULL */
311 context->pc = NULL;
312 setupFrame(&context->frameP, &context->stackP, &context->pc,
313 - prog, nArgs, args, False, prog->name ? prog->name : "<exec-macro>");
314 + prog, nArgs, args, argArray, False,
315 + prog->name ? prog->name : "<exec-macro>");
317 /* Begin execution, return on error or preemption */
318 return ContinueMacro(context, result, msg);
319 @@ -651,7 +663,9 @@ int ContinueMacro(RestartData *continuat
321 void RunMacroAsSubrCall(Program *prog)
323 - setupFrame(&FrameP, &StackP, &PC, prog, 0, NULL, True,
324 + static DataValue noValue = {NO_TAG, {0}};
326 + setupFrame(&FrameP, &StackP, &PC, prog, 0, NULL, noValue, True,
327 prog->name ? prog->name : "<run-macro>");
330 @@ -1448,12 +1462,15 @@ static int pushArgArray(void)
331 if (resultArray->tag != ARRAY_TAG) {
332 resultArray->tag = ARRAY_TAG;
333 resultArray->val.arrayPtr = ArrayNew();
336 + /* load arguments from positional arg list if not already done */
337 + if (nArgs && !ArrayGet(resultArray, longAsStr(argNum + 1), &argVal)) {
338 for (argNum = 0; argNum < nArgs; ++argNum) {
339 argVal = FP_GET_ARG_N(FrameP, argNum);
340 if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
341 &argVal)) {
342 - return(execError("array insertion failure", NULL));
343 + return(execError("argument array insertion failure", NULL));
347 @@ -1509,6 +1526,274 @@ static int pushArraySymVal(void)
351 +** create an anonymous array and next index number value (0) on the stack (for
352 +** array construction expressions)
354 +** Before: Prog-> [next], ...
355 +** TheStack-> next, ...
356 +** After: Prog-> [next], ...
357 +** TheStack-> [empty-array, 0], next, ...
359 +static int anonArrayOpen(void)
361 + DataValue dataVal;
363 + DISASM_RT(PC-1, 1);
364 + STACKDUMP(0, 3);
366 + /* make an empty array */
367 + dataVal.tag = ARRAY_TAG;
368 + dataVal.val.arrayPtr = ArrayNew();
370 + /* push the default next index value first */
371 + PUSH_INT(0);
373 + /* and the empty array */
374 + PUSH(dataVal);
376 + return STAT_OK;
380 +** cause the auto-incrementing next index number value to increase without
381 +** actually creating an entry in the anonymous array (for array construction
382 +** expressions)
384 +** Before: Prog-> [next], ...
385 +** TheStack-> [anon-array, next-index], next, ...
386 +** After: Prog-> [next], ...
387 +** TheStack-> [anon-array, next-index+1], next, ...
389 +static int anonArraySkip(void)
391 + DataValue anonArray;
392 + int nextIndex;
394 + DISASM_RT(PC-1, 1);
395 + STACKDUMP(2, 3);
397 + POP(anonArray);
398 + POP_INT(nextIndex);
400 + /* we need to increment the index for next time */
401 + ++nextIndex;
403 + /* push the default next index value first, then the array */
404 + PUSH_INT(nextIndex);
405 + PUSH(anonArray);
407 + return STAT_OK;
411 +** add an entry to the anonymous array at the stack head, using the numeric
412 +** index just below that; restack the incremented index and anonymous array
413 +** (for array construction expressions)
415 +** Before: Prog-> [next], ...
416 +** TheStack-> [expr, anon-array, next-index], next, ...
417 +** After: Prog-> [next], ...
418 +** TheStack-> [anon-array, next-index+1], next, ...
420 +static int anonArrayNextVal(void)
422 + DataValue exprVal, anonArray;
423 + int nextIndex;
424 + char numString[TYPE_INT_STR_SIZE(int)];
426 + DISASM_RT(PC-1, 1);
427 + STACKDUMP(3, 3);
429 + POP(exprVal);
430 + POP(anonArray);
431 + POP_INT(nextIndex);
433 + sprintf(numString, "%d", nextIndex);
434 + if (!ArrayInsert(&anonArray, AllocStringCpy(numString), &exprVal)) {
435 + return(execError("array insertion failure", NULL));
438 + /* we need to increment the index for next time */
439 + ++nextIndex;
441 + /* push the default next index value first, then the array */
442 + PUSH_INT(nextIndex);
443 + PUSH(anonArray);
445 + return STAT_OK;
450 +** Before: Prog-> [nDim], next, ...
451 +** TheStack-> [expr, indnDim, ... ind1, anon-array, next-index], next, ...
452 +** After: Prog-> nDim, [next], ...
453 +** TheStack-> [anon-array, new-next-index], next, ...
455 +static int anonArrayIndexVal(void)
457 + int errNum;
458 + char *keyString = NULL;
459 + DataValue exprVal, anonArray;
460 + int nextIndex, index;
461 + int nDim;
463 + nDim = PC->value;
464 + PC++;
466 + DISASM_RT(PC-2, 2);
467 + STACKDUMP(nDim+3, 3);
469 + POP(exprVal);
471 + /* the next nDim stack entries form the index */
472 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
473 + if (errNum != STAT_OK) {
474 + return errNum;
477 + POP(anonArray);
478 + POP_INT(nextIndex);
480 + /* if our index is numeric (or can be converted to a number) we must
481 + change the next index value */
482 + if (nDim == 1 && StringToNum(keyString, &index)) {
483 + nextIndex = index + 1;
486 + if (!ArrayInsert(&anonArray, keyString, &exprVal)) {
487 + return(execError("array insertion failure", NULL));
490 + /* push the default next index value first, then the array */
491 + PUSH_INT(nextIndex);
492 + PUSH(anonArray);
494 + return STAT_OK;
498 +** finish building an anonymous array by removing the next index number value
499 +** from the stack (for array construction expressions)
501 +** Before: Prog-> [next], ...
502 +** TheStack-> [anon-array, next-index], next, ...
503 +** After: Prog-> [next], ...
504 +** TheStack-> [anon-array], next, ...
506 +static int anonArrayClose(void)
508 + DataValue anonArray;
509 + DataValue next_index;
511 + DISASM_RT(PC-1, 1);
512 + STACKDUMP(2, 3);
514 + /* remove top two elements */
515 + POP(anonArray);
516 + POP(next_index);
517 + /* put back the array content */
518 + PUSH(anonArray);
520 + return STAT_OK;
524 +** create an $args array for the named arg with index of nDim elements, and
525 +** value expr; leave result on top of stack
527 +** Before: Prog-> [nDim], next, ...
528 +** TheStack-> [expr, indnDim, ... ind1], argN-arg1, next, ...
529 +** After: Prog-> nDim, [next], ...
530 +** TheStack-> args, argN-arg1, next, ...
532 +static int namedArg1(void)
534 + return namedArg1orN(True);
538 +** add the named arg with index of nDim elements, and value expr to the $args
539 +** array at the top of the stack
541 +** Before: Prog-> [nDim], next, ...
542 +** TheStack-> [expr, indnDim, ... ind1, args], argN-arg1, next, ...
543 +** After: Prog-> nDim, [next], ...
544 +** TheStack-> [args], argN-arg1, next, ...
546 +static int namedArgN()
548 + return namedArg1orN(False);
552 +** implementation for namedArg1(), namedArgN()
554 +static int namedArg1orN(Boolean isFirst)
556 + int errNum;
557 + char *keyString = NULL;
558 + DataValue exprVal, argsArray;
559 + int nDim, index;
561 + nDim = (PC++)->value;
563 + DISASM_RT(PC-2, 2);
564 + STACKDUMP(nDim + (isFirst ? 2 : 1), 3);
566 + POP(exprVal);
568 + /* the next nDim stack entries form the index */
569 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
570 + if (errNum != STAT_OK) {
571 + return errNum;
574 + /* if our index is numeric (or can be converted to a number) we must
575 + change the next index value */
576 + if (nDim == 1 && StringToNum(keyString, &index)) {
577 + return execError("named argument name must not be numeric", NULL);
580 + if (isFirst) {
581 + /* make a new empty array */
582 + argsArray.tag = ARRAY_TAG;
583 + argsArray.val.arrayPtr = NULL;
585 + else {
586 + /* use the array at the top of the stack */
587 + POP(argsArray);
590 + if (!ArrayInsert(&argsArray, keyString, &exprVal)) {
591 + return(execError("named argument insertion failure", NULL));
594 + /* and (re)push the array */
595 + PUSH(argsArray);
597 + return STAT_OK;
601 +** exchange top two values on the stack
603 +static int swapTop2(void)
605 + DataValue dv1, dv2;
607 + DISASM_RT(PC-1, 1);
608 + STACKDUMP(2, 3);
610 + POP(dv1);
611 + POP(dv2);
612 + PUSH(dv1);
613 + PUSH(dv2);
615 + return STAT_OK;
619 ** assign top value to next symbol
621 ** Before: Prog-> [symbol], next, ...
622 @@ -2144,7 +2429,9 @@ static int concat(void)
624 ** Call a subroutine or function (user defined or built-in). Args are the
625 ** subroutine's symbol, and the number of arguments which have been pushed
626 -** on the stack.
627 +** on the stack. If this value is less than zero, use the absolute value,
628 +** but note that the last one is already the $args array so don't set aside
629 +** space for that.
631 ** For a macro subroutine, the return address, frame pointer, number of
632 ** arguments and space for local variables are added to the stack, and the
633 @@ -2152,27 +2439,35 @@ static int concat(void)
634 ** arguments are popped off the stack, and the routine is just called.
636 ** Before: Prog-> [subrSym], nArgs, next, ...
637 -** TheStack-> argN-arg1, next, ...
638 +** TheStack-> argArray?, argN-arg1, next, ...
639 ** After: Prog-> next, ... -- (built-in called subr)
640 ** TheStack-> retVal?, next, ...
641 ** or: Prog-> (in called)next, ... -- (macro code called subr)
642 -** TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
643 +** TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
645 static int callSubroutine(void)
647 Symbol *sym, *s;
648 int i, nArgs;
649 static DataValue noValue = {NO_TAG, {0}};
650 + DataValue argArray = noValue;
651 Program *prog;
652 char *errMsg;
654 + int haveNamedArgs;
656 sym = PC->sym;
657 PC++;
658 nArgs = PC->value;
659 PC++;
662 + haveNamedArgs = (nArgs < 0);
663 + if (haveNamedArgs) {
664 + nArgs = -nArgs - 1;
665 + POP(argArray);
668 DISASM_RT(PC-3, 3);
669 - STACKDUMP(nArgs, 3);
670 + STACKDUMP(nArgs + haveNamedArgs, 3);
673 ** If the subroutine is built-in, call the built-in routine
674 @@ -2180,8 +2475,10 @@ static int callSubroutine(void)
675 if (sym->type == C_FUNCTION_SYM) {
676 DataValue result;
678 + PUSH(argArray); /* push dummy named arg array */
680 /* "pop" stack back to the first argument in the call stack */
681 - StackP -= nArgs;
682 + StackP -= nArgs + 1;
684 /* Call the function and check for preemption */
685 PreemptRequest = False;
686 @@ -2209,7 +2506,8 @@ static int callSubroutine(void)
687 prog = sym->value.val.prog;
688 prog->refcount++;
689 /* -nArgs means 'arguments are on stack' */
690 - setupFrame(&FrameP, &StackP, &PC, prog, -nArgs, NULL, True, sym->name);
691 + setupFrame(&FrameP, &StackP, &PC, prog, -nArgs, NULL, argArray,
692 + True, sym->name);
693 return STAT_OK;
696 @@ -2222,7 +2520,13 @@ static int callSubroutine(void)
697 XKeyEvent key_event;
698 Display *disp;
699 Window win;
702 + if (haveNamedArgs) {
703 + return execError(
704 + "%s action routine called with named argument array",
705 + sym->name);
708 /* Create a fake event with a timestamp suitable for actions which need
709 timestamps, a marker to indicate that the call was from a macro
710 (to stop shell commands from putting up their own separate banner) */
711 @@ -2283,7 +2587,7 @@ static int returnVal(void)
713 ** Return from a subroutine call
714 ** Before: Prog-> [next], ...
715 -** TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
716 +** TheStack-> retVal?, ...(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
717 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
718 ** TheStack-> retVal?, next, ...
720 @@ -2349,34 +2653,26 @@ static int branch(void)
722 static int branchTrue(void)
724 - int value;
725 - Inst *addr;
727 - DISASM_RT(PC-1, 2);
728 - STACKDUMP(1, 3);
730 - POP_INT(value);
731 - addr = PC + PC->value;
732 - PC++;
734 - if (value)
735 - PC = addr;
736 - return STAT_OK;
737 + return branchIf(True);
739 static int branchFalse(void)
741 + return branchIf(False);
743 +static int branchIf(Boolean trueOrFalse)
745 int value;
746 Inst *addr;
749 DISASM_RT(PC-1, 2);
750 STACKDUMP(1, 3);
752 POP_INT(value);
753 addr = PC + PC->value;
754 PC++;
756 - if (!value)
757 - PC = addr;
759 + if (!value == !trueOrFalse)
760 + PC = addr;
761 return STAT_OK;
764 @@ -3372,7 +3668,14 @@ static void disasmInternal(Inst *inst, i
765 ++i;
767 else if (j == OP_SUBR_CALL) {
768 - printd("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
769 + int args = inst[i+2].value;
770 + printd("%s ", inst[i+1].sym->name);
771 + if (args < 0) {
772 + printd("%d+args[] (%d)", -args - 1, args);
774 + else {
775 + printd("%d args", args);
777 i += 2;
779 else if (j == OP_BEGIN_ARRAY_ITER) {
780 @@ -3386,8 +3689,12 @@ static void disasmInternal(Inst *inst, i
781 inst[i+3].value, &inst[i+3] + inst[i+3].value);
782 i += 3;
784 - else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
785 - j == OP_ARRAY_ASSIGN) {
786 + else if (j == OP_ARRAY_REF ||
787 + j == OP_ARRAY_DELETE ||
788 + j == OP_ARRAY_ASSIGN ||
789 + j == OP_ANONARRAY_INDEX_VAL ||
790 + j == OP_NAMED_ARG1 ||
791 + j == OP_NAMED_ARGN) {
792 printd("nDim=%d", inst[i+1].value);
793 ++i;
795 @@ -3481,13 +3788,13 @@ static void stackdumpframe(DataValue *ar
796 printd("%4.4s", leadIn);
797 printd("%8p%c", dv, topMark);
798 switch (offset) {
799 - case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
800 case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* num. arguments */
801 case FP_FUNCTION_NAME: pos = "FnName"; break;
802 case FP_SYMBOL_TABLE: pos = "FnSyms"; break;
803 case FP_OLD_FP_INDEX: pos = "OldFP"; break;
804 case FP_RET_PC_INDEX: pos = "RetPC"; break;
805 case FP_PROG_INDEX: pos = "Prog"; break;
806 + case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
807 default:
808 if (offset < -FP_TO_ARGS_DIST &&
809 offset >= -FP_TO_ARGS_DIST - nArgs)
810 diff --quilt old/source/parse.y new/source/parse.y
811 --- old/source/parse.y
812 +++ new/source/parse.y
813 @@ -95,7 +95,7 @@ static int nextSymIsField = 0;
814 %token DELETE ARG_LOOKUP
815 %token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN
816 %token <acc> DEFINE
817 -%type <num> arglistopt arglist catlist
818 +%type <num> arglistopt arglist catlist fnarglsopt fnarglist fnarg
819 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
820 %type <sym> evalsym
821 %type <oper> operassign incrdecr
822 @@ -322,7 +322,7 @@ simpstmt: /* simple variable assignmen
823 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED(1);
825 /* function call */
826 - | SYMBOL '(' arglistopt ')' {
827 + | SYMBOL '(' fnarglsopt ')' {
828 ADD_OP(OP_SUBR_CALL);
829 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
831 @@ -353,6 +353,60 @@ catlist: numexpr %prec CONC
832 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
835 +/* function argument lists */
836 +fnarg: expr {
837 + $$ = 0;
839 + | '[' arglist ']' '=' expr {
840 + $$ = $2; /* how many index elements to read? */
842 + | dot field '=' expr {
843 + $$ = 1; /* how many index elements to read? */
846 +fnarglsopt: blank { $$ = 0; }
847 + | fnarglist { $$ = $1; }
849 +fnarglist: blank fnarg blank {
850 + if ($2 > 0) {
851 + /* named argument code already knows about index length (see
852 + rule for arg: above); it needs to be assembled into an
853 + array, which must be created. */
854 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($2);
855 + $$ = -1; /* negative single arg for named arg array */
857 + else {
858 + /* a normal positional argument - leave value on stack */
859 + $$ = 1;
862 + | fnarglist ',' blank fnarg blank {
863 + if ($4 > 0) {
864 + /* named arg: $4 == how many indices to process */
865 + if ($1 >= 0) {
866 + /* first named arg: create the array */
867 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($4);
868 + $$ = -($1 + 1); /* make arg count negative */
870 + else {
871 + /* another named arg: add to array */
872 + ADD_OP(OP_NAMED_ARGN); ADD_IMMED($4);
873 + $$ = $1; /* no new positional args */
876 + else {
877 + /* positional arg */
878 + if ($1 < 0) {
879 + ADD_OP(OP_SWAP_TOP2); /* keep arg array as last */
880 + $$ = $1 - 1;
882 + else {
883 + $$ = $1 + 1; /* no named args yet */
889 expr: catlist {
890 if ($1 > 1) {
891 ADD_OP(OP_CONCAT); ADD_IMMED($1);
892 @@ -397,17 +451,57 @@ arrayexpr: numexpr {
896 +/* anonymous arrays eg: array = { , "hi", .a=2, ["b"]="three" } */
897 +arrconstr0: '{' {
898 + /* create an empty array into which to add things */
899 + ADD_OP(OP_ANONARRAY_OPEN);
902 +arrconstr: arrconstr0 arrlistopt '}' {
903 + /* we're done: the array is complete */
904 + ADD_OP(OP_ANONARRAY_CLOSE);
907 +arrlistopt: arrlist
909 +arrlist: arrentry
910 + | arrlist ',' arrentry
912 +arrentry: blank {
913 + /* missing entry will skip an index value */
914 + ADD_OP(OP_ANONARRAY_SKIP);
916 + | blank expr blank {
917 + /* make a suitable index >= 0 and add expr there */
918 + ADD_OP(OP_ANONARRAY_NEXT_VAL);
920 + | blank '[' arglist ']' blank '=' blank expr blank {
921 + /* build the index from arglistopt and add expr there */
922 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
923 + ADD_IMMED($3);
925 + | blank dot field blank '=' blank expr blank {
926 + /* build the index from arglistopt and add expr there */
927 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
928 + ADD_IMMED(1);
932 numexpr: '(' blank expr blank ')'
933 | NUMBER { ADD_OP(OP_PUSH_IMMED); ADD_IMMED($1); }
934 | STRING { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
935 | SYMBOL { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
936 - | SYMBOL '(' arglistopt ')' {
937 + | SYMBOL '(' fnarglsopt ')' {
938 ADD_OP(OP_SUBR_CALL);
939 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
940 ADD_OP(OP_FETCH_RET_VAL);
942 + /* this doesn't work for $args["string"]:
943 | ARG_LOOKUP '[' blank numexpr blank ']' { ADD_OP(OP_PUSH_ARG); }
944 + */
945 + /* this doesn't work if $args contains non-argnum indices
946 | ARG_LOOKUP '[' blank ']' { ADD_OP(OP_PUSH_ARG_COUNT); }
947 + */
948 | ARG_LOOKUP { ADD_OP(OP_PUSH_ARG_ARRAY); }
949 | numexpr '[' arglistopt ']' {
950 ADD_OP(OP_ARRAY_REF); ADD_IMMED($3);
951 @@ -452,6 +546,7 @@ numexpr: '(' blank expr blank ')'
952 ADD_OP(OP_IN_ARRAY);
953 ADD_OP(OP_NOT);
955 + | arrconstr
958 while: WHILE blank {
959 diff --quilt old/source/ops.h new/source/ops.h
960 --- old/source/ops.h
961 +++ new/source/ops.h
962 @@ -47,3 +47,11 @@ OP(ARRAY_REF_ASSIGN_SETUP, arrayRefAndAs
963 OP(PUSH_ARG, pushArgVal) /* pop(num), push($num) */
964 OP(PUSH_ARG_COUNT, pushArgCount) /* push($n_args) */
965 OP(PUSH_ARG_ARRAY, pushArgArray) /* push($args) */
966 +OP(ANONARRAY_OPEN, anonArrayOpen) /* push(0), push(ary()) */
967 +OP(ANONARRAY_SKIP, anonArraySkip) /* pop(a,n), push(n+1,a) */
968 +OP(ANONARRAY_NEXT_VAL, anonArrayNextVal) /* pop(v,a,n); a[n++]=v, push(n,a) */
969 +OP(ANONARRAY_INDEX_VAL, anonArrayIndexVal) /*N*/ /* pop(v,kN..k1,a,n), a[k1..kN]=v, push(isNum(k1)?num(k1)+1:n,a) */
970 +OP(ANONARRAY_CLOSE, anonArrayClose) /* pop(a,n), push(a) */
971 +OP(NAMED_ARG1, namedArg1) /* N */ /* pop(v,kN..k1), a=ary(), a[k1..kN]=v, push(a) */
972 +OP(NAMED_ARGN, namedArgN) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v, push(a) */
973 +OP(SWAP_TOP2, swapTop2) /* pop(v1,v2), push(v1,v2) */