pretty print multi demension array keys
[nedit-bw.git] / anonArrayNamedArgs7.diff
blobb09a2d720a1f29a8a493e31d2db599312576ae77
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 | 409 ++++++++++++++++++++++++++++++++++++++++++++++-------
63 source/ops.h | 8 +
64 source/parse.y | 99 ++++++++++++
65 4 files changed, 552 insertions(+), 64 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,7 +456,8 @@ 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,
254 + Inst **pc, Program *prog,
255 + int nArgs, DataValue *args, DataValue argArray,
256 const char *name)
258 static DataValue noValue = {NO_TAG, {0}};
259 @@ -473,6 +477,9 @@ static void setupFrame(DataValue **frame
260 nArgs = -nArgs;
263 + /* cached arg array */
264 + *((*stackP)++) = argArray;
266 /* prog to free, if requested */
267 (*stackP)->tag = NO_TAG;
268 (*stackP)->val.prog = prog;
269 @@ -500,13 +507,10 @@ static void setupFrame(DataValue **frame
270 (*stackP)++;
272 /* nArgs */
273 - (*stackP)->tag = NO_TAG;
274 + (*stackP)->tag = INT_TAG;
275 (*stackP)->val.n = nArgs;
276 (*stackP)++;
278 - /* cached arg array */
279 - *((*stackP)++) = noValue;
281 *frameP = *stackP;
283 /* Initialize and make room on the stack for local variables */
284 @@ -556,10 +560,17 @@ int ExecuteMacro(WindowInfo *window, Pro
285 DataValue *result, RestartData **continuation, char **msg)
287 RestartData *context;
288 - static DataValue noValue = {NO_TAG, {0}};
289 + static DataValue argArray = {NO_TAG, {0}};
290 Symbol *s;
291 int i;
293 + int haveNamedArgs;
295 + haveNamedArgs = (nArgs < 0);
296 + if (haveNamedArgs) {
297 + nArgs = -nArgs - 1;
298 + argArray = args[nArgs];
301 /* Create an execution context (a stack, a stack pointer, a frame pointer,
302 and a program counter) which will retain the program state across
303 preemption and resumption of execution */
304 @@ -575,7 +586,8 @@ int ExecuteMacro(WindowInfo *window, Pro
305 /* prog will be freed by cller, but by stack also, so inc refcount */
306 prog->refcount++;
307 setupFrame(&context->frameP, &context->stackP, &context->pc,
308 - prog, nArgs, args, prog->name ? prog->name : "<exec-macro>");
309 + prog, nArgs, args, argArray,
310 + prog->name ? prog->name : "<exec-macro>");
312 /* Begin execution, return on error or preemption */
313 return ContinueMacro(context, result, msg);
314 @@ -651,7 +663,9 @@ int ContinueMacro(RestartData *continuat
316 void RunMacroAsSubrCall(Program *prog)
318 - setupFrame(&FrameP, &StackP, &PC, prog, 0, NULL,
319 + static DataValue noValue = {NO_TAG, {0}};
321 + setupFrame(&FrameP, &StackP, &PC, prog, 0, NULL, noValue,
322 prog->name ? prog->name : "<run-macro>");
325 @@ -1448,12 +1462,15 @@ static int pushArgArray(void)
326 if (resultArray->tag != ARRAY_TAG) {
327 resultArray->tag = ARRAY_TAG;
328 resultArray->val.arrayPtr = ArrayNew();
331 + /* load arguments from positional arg list if not already done */
332 + if (nArgs && !ArrayGet(resultArray, longAsStr(argNum + 1), &argVal)) {
333 for (argNum = 0; argNum < nArgs; ++argNum) {
334 argVal = FP_GET_ARG_N(FrameP, argNum);
335 if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
336 &argVal)) {
337 - return(execError("array insertion failure", NULL));
338 + return(execError("argument array insertion failure", NULL));
342 @@ -1509,6 +1526,274 @@ static int pushArraySymVal(void)
346 +** create an anonymous array and next index number value (0) on the stack (for
347 +** array construction expressions)
349 +** Before: Prog-> [next], ...
350 +** TheStack-> next, ...
351 +** After: Prog-> [next], ...
352 +** TheStack-> [empty-array, 0], next, ...
354 +static int anonArrayOpen(void)
356 + DataValue dataVal;
358 + DISASM_RT(PC-1, 1);
359 + STACKDUMP(0, 3);
361 + /* make an empty array */
362 + dataVal.tag = ARRAY_TAG;
363 + dataVal.val.arrayPtr = ArrayNew();
365 + /* push the default next index value first */
366 + PUSH_INT(0);
368 + /* and the empty array */
369 + PUSH(dataVal);
371 + return STAT_OK;
375 +** cause the auto-incrementing next index number value to increase without
376 +** actually creating an entry in the anonymous array (for array construction
377 +** expressions)
379 +** Before: Prog-> [next], ...
380 +** TheStack-> [anon-array, next-index], next, ...
381 +** After: Prog-> [next], ...
382 +** TheStack-> [anon-array, next-index+1], next, ...
384 +static int anonArraySkip(void)
386 + DataValue anonArray;
387 + int nextIndex;
389 + DISASM_RT(PC-1, 1);
390 + STACKDUMP(2, 3);
392 + POP(anonArray);
393 + POP_INT(nextIndex);
395 + /* we need to increment the index for next time */
396 + ++nextIndex;
398 + /* push the default next index value first, then the array */
399 + PUSH_INT(nextIndex);
400 + PUSH(anonArray);
402 + return STAT_OK;
406 +** add an entry to the anonymous array at the stack head, using the numeric
407 +** index just below that; restack the incremented index and anonymous array
408 +** (for array construction expressions)
410 +** Before: Prog-> [next], ...
411 +** TheStack-> [expr, anon-array, next-index], next, ...
412 +** After: Prog-> [next], ...
413 +** TheStack-> [anon-array, next-index+1], next, ...
415 +static int anonArrayNextVal(void)
417 + DataValue exprVal, anonArray;
418 + int nextIndex;
419 + char numString[TYPE_INT_STR_SIZE(int)];
421 + DISASM_RT(PC-1, 1);
422 + STACKDUMP(3, 3);
424 + POP(exprVal);
425 + POP(anonArray);
426 + POP_INT(nextIndex);
428 + sprintf(numString, "%d", nextIndex);
429 + if (!ArrayInsert(&anonArray, AllocStringCpy(numString), &exprVal)) {
430 + return(execError("array insertion failure", NULL));
433 + /* we need to increment the index for next time */
434 + ++nextIndex;
436 + /* push the default next index value first, then the array */
437 + PUSH_INT(nextIndex);
438 + PUSH(anonArray);
440 + return STAT_OK;
445 +** Before: Prog-> [nDim], next, ...
446 +** TheStack-> [expr, indnDim, ... ind1, anon-array, next-index], next, ...
447 +** After: Prog-> nDim, [next], ...
448 +** TheStack-> [anon-array, new-next-index], next, ...
450 +static int anonArrayIndexVal(void)
452 + int errNum;
453 + char *keyString = NULL;
454 + DataValue exprVal, anonArray;
455 + int nextIndex, index;
456 + int nDim;
458 + nDim = PC->value;
459 + PC++;
461 + DISASM_RT(PC-2, 2);
462 + STACKDUMP(nDim+3, 3);
464 + POP(exprVal);
466 + /* the next nDim stack entries form the index */
467 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
468 + if (errNum != STAT_OK) {
469 + return errNum;
472 + POP(anonArray);
473 + POP_INT(nextIndex);
475 + /* if our index is numeric (or can be converted to a number) we must
476 + change the next index value */
477 + if (nDim == 1 && StringToNum(keyString, &index)) {
478 + nextIndex = index + 1;
481 + if (!ArrayInsert(&anonArray, keyString, &exprVal)) {
482 + return(execError("array insertion failure", NULL));
485 + /* push the default next index value first, then the array */
486 + PUSH_INT(nextIndex);
487 + PUSH(anonArray);
489 + return STAT_OK;
493 +** finish building an anonymous array by removing the next index number value
494 +** from the stack (for array construction expressions)
496 +** Before: Prog-> [next], ...
497 +** TheStack-> [anon-array, next-index], next, ...
498 +** After: Prog-> [next], ...
499 +** TheStack-> [anon-array], next, ...
501 +static int anonArrayClose(void)
503 + DataValue anonArray;
504 + DataValue next_index;
506 + DISASM_RT(PC-1, 1);
507 + STACKDUMP(2, 3);
509 + /* remove top two elements */
510 + POP(anonArray);
511 + POP(next_index);
512 + /* put back the array content */
513 + PUSH(anonArray);
515 + return STAT_OK;
519 +** create an $args array for the named arg with index of nDim elements, and
520 +** value expr; leave result on top of stack
522 +** Before: Prog-> [nDim], next, ...
523 +** TheStack-> [expr, indnDim, ... ind1], argN-arg1, next, ...
524 +** After: Prog-> nDim, [next], ...
525 +** TheStack-> args, argN-arg1, next, ...
527 +static int namedArg1(void)
529 + return namedArg1orN(True);
533 +** add the named arg with index of nDim elements, and value expr to the $args
534 +** array at the top of the stack
536 +** Before: Prog-> [nDim], next, ...
537 +** TheStack-> [expr, indnDim, ... ind1, args], argN-arg1, next, ...
538 +** After: Prog-> nDim, [next], ...
539 +** TheStack-> [args], argN-arg1, next, ...
541 +static int namedArgN()
543 + return namedArg1orN(False);
547 +** implementation for namedArg1(), namedArgN()
549 +static int namedArg1orN(Boolean isFirst)
551 + int errNum;
552 + char *keyString = NULL;
553 + DataValue exprVal, argsArray;
554 + int nDim, index;
556 + nDim = (PC++)->value;
558 + DISASM_RT(PC-2, 2);
559 + STACKDUMP(nDim + (isFirst ? 2 : 1), 3);
561 + POP(exprVal);
563 + /* the next nDim stack entries form the index */
564 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
565 + if (errNum != STAT_OK) {
566 + return errNum;
569 + /* if our index is numeric (or can be converted to a number) we must
570 + change the next index value */
571 + if (nDim == 1 && StringToNum(keyString, &index)) {
572 + return execError("named argument name must not be numeric", NULL);
575 + if (isFirst) {
576 + /* make a new empty array */
577 + argsArray.tag = ARRAY_TAG;
578 + argsArray.val.arrayPtr = NULL;
580 + else {
581 + /* use the array at the top of the stack */
582 + POP(argsArray);
585 + if (!ArrayInsert(&argsArray, keyString, &exprVal)) {
586 + return(execError("named argument insertion failure", NULL));
589 + /* and (re)push the array */
590 + PUSH(argsArray);
592 + return STAT_OK;
596 +** exchange top two values on the stack
598 +static int swapTop2(void)
600 + DataValue dv1, dv2;
602 + DISASM_RT(PC-1, 1);
603 + STACKDUMP(2, 3);
605 + POP(dv1);
606 + POP(dv2);
607 + PUSH(dv1);
608 + PUSH(dv2);
610 + return STAT_OK;
614 ** assign top value to next symbol
616 ** Before: Prog-> [symbol], next, ...
617 @@ -2147,7 +2432,9 @@ static int concat(void)
619 ** Call a subroutine or function (user defined or built-in). Args are the
620 ** subroutine's symbol, and the number of arguments which have been pushed
621 -** on the stack.
622 +** on the stack. If this value is less than zero, use the absolute value,
623 +** but note that the last one is already the $args array so don't set aside
624 +** space for that.
626 ** For a macro subroutine, the return address, frame pointer, number of
627 ** arguments and space for local variables are added to the stack, and the
628 @@ -2155,27 +2442,35 @@ static int concat(void)
629 ** arguments are popped off the stack, and the routine is just called.
631 ** Before: Prog-> [subrSym], nArgs, next, ...
632 -** TheStack-> argN-arg1, next, ...
633 +** TheStack-> argArray?, argN-arg1, next, ...
634 ** After: Prog-> next, ... -- (built-in called subr)
635 ** TheStack-> retVal?, next, ...
636 ** or: Prog-> (in called)next, ... -- (macro code called subr)
637 -** TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
638 +** TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
640 static int callSubroutine(void)
642 Symbol *sym, *s;
643 int i, nArgs;
644 static DataValue noValue = {NO_TAG, {0}};
645 + DataValue argArray = noValue;
646 Program *prog;
647 char *errMsg;
649 + int haveNamedArgs;
651 sym = PC->sym;
652 PC++;
653 nArgs = PC->value;
654 PC++;
657 + haveNamedArgs = (nArgs < 0);
658 + if (haveNamedArgs) {
659 + nArgs = -nArgs - 1;
660 + POP(argArray);
663 DISASM_RT(PC-3, 3);
664 - STACKDUMP(nArgs, 3);
665 + STACKDUMP(nArgs + haveNamedArgs, 3);
668 ** If the subroutine is built-in, call the built-in routine
669 @@ -2183,8 +2478,10 @@ static int callSubroutine(void)
670 if (sym->type == C_FUNCTION_SYM) {
671 DataValue result;
673 + PUSH(argArray); /* push dummy named arg array */
675 /* "pop" stack back to the first argument in the call stack */
676 - StackP -= nArgs;
677 + StackP -= nArgs + 1;
679 /* Call the function and check for preemption */
680 PreemptRequest = False;
681 @@ -2212,7 +2509,8 @@ static int callSubroutine(void)
682 prog = sym->value.val.prog;
683 prog->refcount++;
684 /* -nArgs means 'arguments are on stack' */
685 - setupFrame(&FrameP, &StackP, &PC, prog, -nArgs, NULL, sym->name);
686 + setupFrame(&FrameP, &StackP, &PC, prog, -nArgs, NULL, argArray,
687 + sym->name);
688 return STAT_OK;
691 @@ -2225,7 +2523,13 @@ static int callSubroutine(void)
692 XKeyEvent key_event;
693 Display *disp;
694 Window win;
697 + if (haveNamedArgs) {
698 + return execError(
699 + "%s action routine called with named argument array",
700 + sym->name);
703 /* Create a fake event with a timestamp suitable for actions which need
704 timestamps, a marker to indicate that the call was from a macro
705 (to stop shell commands from putting up their own separate banner) */
706 @@ -2286,7 +2590,7 @@ static int returnVal(void)
708 ** Return from a subroutine call
709 ** Before: Prog-> [next], ...
710 -** TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
711 +** TheStack-> retVal?, ...(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
712 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
713 ** TheStack-> retVal?, next, ...
715 @@ -2352,34 +2656,26 @@ static int branch(void)
717 static int branchTrue(void)
719 - int value;
720 - Inst *addr;
722 - DISASM_RT(PC-1, 2);
723 - STACKDUMP(1, 3);
725 - POP_INT(value);
726 - addr = PC + PC->value;
727 - PC++;
729 - if (value)
730 - PC = addr;
731 - return STAT_OK;
732 + return branchIf(True);
734 static int branchFalse(void)
736 + return branchIf(False);
738 +static int branchIf(Boolean trueOrFalse)
740 int value;
741 Inst *addr;
744 DISASM_RT(PC-1, 2);
745 STACKDUMP(1, 3);
747 POP_INT(value);
748 addr = PC + PC->value;
749 PC++;
751 - if (!value)
752 - PC = addr;
754 + if (!value == !trueOrFalse)
755 + PC = addr;
756 return STAT_OK;
759 @@ -3378,7 +3674,14 @@ static void disasmInternal(Inst *inst, i
760 ++i;
762 else if (j == OP_SUBR_CALL) {
763 - printd("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
764 + int args = inst[i+2].value;
765 + printd("%s ", inst[i+1].sym->name);
766 + if (args < 0) {
767 + printd("%d+args[] (%d)", -args - 1, args);
769 + else {
770 + printd("%d args", args);
772 i += 2;
774 else if (j == OP_BEGIN_ARRAY_ITER) {
775 @@ -3392,8 +3695,12 @@ static void disasmInternal(Inst *inst, i
776 inst[i+3].value, &inst[i+3] + inst[i+3].value);
777 i += 3;
779 - else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
780 - j == OP_ARRAY_ASSIGN) {
781 + else if (j == OP_ARRAY_REF ||
782 + j == OP_ARRAY_DELETE ||
783 + j == OP_ARRAY_ASSIGN ||
784 + j == OP_ANONARRAY_INDEX_VAL ||
785 + j == OP_NAMED_ARG1 ||
786 + j == OP_NAMED_ARGN) {
787 printd("nDim=%d", inst[i+1].value);
788 ++i;
790 @@ -3487,13 +3794,13 @@ static void stackdumpframe(DataValue *ar
791 printd("%4.4s", leadIn);
792 printd("%8p%c", dv, topMark);
793 switch (offset) {
794 - case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
795 case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* num. arguments */
796 case FP_FUNCTION_NAME: pos = "FnName"; break;
797 case FP_SYMBOL_TABLE: pos = "FnSyms"; break;
798 case FP_OLD_FP_INDEX: pos = "OldFP"; break;
799 case FP_RET_PC_INDEX: pos = "RetPC"; break;
800 case FP_PROG_INDEX: pos = "Prog"; break;
801 + case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
802 default:
803 if (offset < -FP_TO_ARGS_DIST &&
804 offset >= -FP_TO_ARGS_DIST - nArgs)
805 diff --quilt old/source/parse.y new/source/parse.y
806 --- old/source/parse.y
807 +++ new/source/parse.y
808 @@ -97,7 +97,7 @@ static int nextSymIsField = 0;
809 %token <num> NUMBER
810 %token DELETE ARG_LOOKUP
811 %token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN DEFINE
812 -%type <num> arglistopt arglist catlist
813 +%type <num> arglistopt arglist catlist fnarglsopt fnarglist fnarg
814 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
815 %type <sym> evalsym
816 %type <define> definesym
817 @@ -327,7 +327,7 @@ simpstmt: /* simple variable assignmen
818 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED(1);
820 /* function call */
821 - | SYMBOL '(' arglistopt ')' {
822 + | SYMBOL '(' fnarglsopt ')' {
823 ADD_OP(OP_SUBR_CALL);
824 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
826 @@ -358,6 +358,60 @@ catlist: numexpr %prec CONC
827 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
830 +/* function argument lists */
831 +fnarg: expr {
832 + $$ = 0;
834 + | '[' arglist ']' '=' expr {
835 + $$ = $2; /* how many index elements to read? */
837 + | dot field '=' expr {
838 + $$ = 1; /* how many index elements to read? */
841 +fnarglsopt: blank { $$ = 0; }
842 + | fnarglist { $$ = $1; }
844 +fnarglist: blank fnarg blank {
845 + if ($2 > 0) {
846 + /* named argument code already knows about index length (see
847 + rule for arg: above); it needs to be assembled into an
848 + array, which must be created. */
849 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($2);
850 + $$ = -1; /* negative single arg for named arg array */
852 + else {
853 + /* a normal positional argument - leave value on stack */
854 + $$ = 1;
857 + | fnarglist ',' blank fnarg blank {
858 + if ($4 > 0) {
859 + /* named arg: $4 == how many indices to process */
860 + if ($1 >= 0) {
861 + /* first named arg: create the array */
862 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($4);
863 + $$ = -($1 + 1); /* make arg count negative */
865 + else {
866 + /* another named arg: add to array */
867 + ADD_OP(OP_NAMED_ARGN); ADD_IMMED($4);
868 + $$ = $1; /* no new positional args */
871 + else {
872 + /* positional arg */
873 + if ($1 < 0) {
874 + ADD_OP(OP_SWAP_TOP2); /* keep arg array as last */
875 + $$ = $1 - 1;
877 + else {
878 + $$ = $1 + 1; /* no named args yet */
884 expr: catlist {
885 if ($1 > 1) {
886 ADD_OP(OP_CONCAT); ADD_IMMED($1);
887 @@ -402,17 +456,55 @@ arrayexpr: numexpr {
891 +/* anonymous arrays eg: array = { , "hi", .a=2, ["b"]="three" } */
892 +arrconstr0: '{' {
893 + /* create an empty array into which to add things */
894 + ADD_OP(OP_ANONARRAY_OPEN);
897 +arrconstr: arrconstr0 arrlist '}' {
898 + /* we're done: the array is complete */
899 + ADD_OP(OP_ANONARRAY_CLOSE);
902 +arrlist: arrentry
903 + | arrlist ',' arrentry
905 +arrentry: blank {
906 + /* missing entry will skip an index value */
907 + ADD_OP(OP_ANONARRAY_SKIP);
909 + | blank expr blank {
910 + /* make a suitable index >= 0 and add expr there */
911 + ADD_OP(OP_ANONARRAY_NEXT_VAL);
913 + | blank '[' arglist ']' blank '=' blank expr blank {
914 + /* build the index from arglistopt and add expr there */
915 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
916 + ADD_IMMED($3);
918 + | blank dot field blank '=' blank expr blank {
919 + /* build the index from arglistopt and add expr there */
920 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
921 + ADD_IMMED(1);
925 numexpr: '(' blank expr blank ')'
926 | NUMBER { ADD_OP(OP_PUSH_IMMED); ADD_IMMED($1); }
927 | STRING { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
928 | SYMBOL { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
929 - | SYMBOL '(' arglistopt ')' {
930 + | SYMBOL '(' fnarglsopt ')' {
931 ADD_OP(OP_SUBR_CALL);
932 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
933 ADD_OP(OP_FETCH_RET_VAL);
935 + /* this doesn't work for $args["string"]:
936 | ARG_LOOKUP '[' blank numexpr blank ']' { ADD_OP(OP_PUSH_ARG); }
937 + */
938 + /* this doesn't work if $args contains non-argnum indices
939 | ARG_LOOKUP '[' blank ']' { ADD_OP(OP_PUSH_ARG_COUNT); }
940 + */
941 | ARG_LOOKUP { ADD_OP(OP_PUSH_ARG_ARRAY); }
942 | numexpr '[' arglistopt ']' {
943 ADD_OP(OP_ARRAY_REF); ADD_IMMED($3);
944 @@ -457,6 +549,7 @@ numexpr: '(' blank expr blank ')'
945 ADD_OP(OP_IN_ARRAY);
946 ADD_OP(OP_NOT);
948 + | arrconstr
951 while: WHILE blank {
952 diff --quilt old/source/ops.h new/source/ops.h
953 --- old/source/ops.h
954 +++ new/source/ops.h
955 @@ -47,3 +47,11 @@ OP(ARRAY_REF_ASSIGN_SETUP, arrayRefAndAs
956 OP(PUSH_ARG, pushArgVal) /* pop(num), push($num) */
957 OP(PUSH_ARG_COUNT, pushArgCount) /* push($n_args) */
958 OP(PUSH_ARG_ARRAY, pushArgArray) /* push($args) */
959 +OP(ANONARRAY_OPEN, anonArrayOpen) /* push(0), push(ary()) */
960 +OP(ANONARRAY_SKIP, anonArraySkip) /* pop(a,n), push(n+1,a) */
961 +OP(ANONARRAY_NEXT_VAL, anonArrayNextVal) /* pop(v,a,n); a[n++]=v, push(n,a) */
962 +OP(ANONARRAY_INDEX_VAL, anonArrayIndexVal) /*N*/ /* pop(v,kN..k1,a,n), a[k1..kN]=v, push(isNum(k1)?num(k1)+1:n,a) */
963 +OP(ANONARRAY_CLOSE, anonArrayClose) /* pop(a,n), push(a) */
964 +OP(NAMED_ARG1, namedArg1) /* N */ /* pop(v,kN..k1), a=ary(), a[k1..kN]=v, push(a) */
965 +OP(NAMED_ARGN, namedArgN) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v, push(a) */
966 +OP(SWAP_TOP2, swapTop2) /* pop(v1,v2), push(v1,v2) */