CVS rebase
[nedit-bw.git] / anonArrayNamedArgs7.diff
blob33f0b58ecdb64d38dfa8a64d53e8027a031c2a8e
1 Allow inline construction of anonymous arrays and "named arguments"
3 Available as a patch:
4 http://sourceforge.net/tracker/index.php?func=detail&aid=1592340&group_id=11005&atid=311005
5 [ 1592340 ] Array literals and named arguments
6 anonArrayNamedArgs3.diff 2006-11-16 02:34
8 This patch allows arrays to be created in-line as anonymous entities. For
9 example:
10 my_array = { \
11 1, 2, 3, # assigns elements [0] to [2] \
12 ["word"] = "val", # assigns element ["word"] \
13 , # don't assign to [3] \
14 4, # assigns element [4] = 4 \
15 [7] = 7, 8, # assigns elements [7] and [8] \
16 ["sub"] = { "a", "b" } # assigns an array to element ["sub"] \
18 The anonymous array is available in contexts where a whole array is needed;
19 for example:
20 val0 = { [0] = "zero"}[0]
21 my_array += { [val0] = 4 + 5 - 9 }
23 The patch also allows macro functions to be called with array element style
24 assignments. These cannot use simple numeric or numeric string indices, but
25 multidimensional numeric indices are allowed.
27 The array element style ("named") arguments are inserted into the called
28 routine's $args array, and accessible using array syntax. Simple argument
29 expressions are counted from the first ($1), with numeric index 1 in $args,
30 up to $n_args. Note that $args[] and $n_args may now be different since
31 the former will include the count of "named" arguments.
33 For example:
34 define returnArgs {
35 return $args
37 a = returnArgs(1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi")
38 b = { , 1, 2, 3, 4, 5, [6,7]=8, ["hello"] = "hi" }
39 Here the arrays a and b are equal. (Note the comma at the start of b's
40 assigned array.)
41 But:
42 a = returnArgs(1, [2]="no") # fails: name for arg cannot be numeric
44 Finally, calling a built-in macro function always makes sure an extra
45 argument is present at argList[nArgs] - in calls using positional values
46 only, this argument will have a tag value of NO_TAG. If "named" arguments
47 have been passed, the argument will have tag ARRAY_TAG, and the named
48 arguments be present in the array, indexed by name. (The positional
49 arguments will not be in this array but can be added to it readily.)
50 Use the ArrayGet() function or iterate over entries as required.
52 ---
54 doc/help.etx | 100 +++++++++-
55 source/interpret.c | 502 ++++++++++++++++++++++++++++++++++++++++++++---------
56 source/interpret.h | 6
57 source/parse.y | 97 +++++++++-
58 4 files changed, 608 insertions(+), 97 deletions(-)
60 diff --quilt old/doc/help.etx new/doc/help.etx
61 --- old/doc/help.etx
62 +++ new/doc/help.etx
63 @@ -2045,10 +2045,26 @@ Macro Language
65 function_name(arg1, arg2, ...)
67 - where arg1, arg2, etc. represent the argument values which are passed to
68 - the routine being called. A function or subroutine call can be on a line by
69 - itself, as above, or if it returns a value, can be invoked within a character
70 - or numeric expression:
71 + where arg1, arg2, etc. represent the arguments which are passed to
72 + the routine being called. Arguments can be one of two kinds: positional or
73 + named. A positional argument is passed as an expression in the argument
74 + list. A named argument is passed by indicating the name as an array key,
75 + between square brackets, followed by the "=" operator and an expression
76 + for the argument's value. Named arguments can use any valid array key value
77 + as long as it cannot be converted into an integer value.
79 + For example, the call
81 + result = func(["prompt"] = "Available values are:", \
82 + a, b, c, d, e, f, \
83 + ["buttons"] = { "OK", "Change", "Cancel" })
85 + provides six positional arguments (with the values of variables a, b, c, d, e
86 + and f), and the named arguments "prompt", with a string value, and "buttons"
87 + with an array value.
89 + A function or subroutine call can be on a line by itself, as above, or if it
90 + returns a value, can be invoked within a character or numeric expression:
92 a = fn1(b, c) + fn2(d)
93 dialog("fn3 says: " fn3())
94 @@ -2095,11 +2111,14 @@ Macro Language
95 the autoload macro file, cf. Preferences_. Macro files can be loaded with
96 File -> Load Macro File or with the load_macro_file() action.
98 - The arguments with which a user-defined subroutine or function was invoked,
99 - are presented as $1, $2, ... , $9 or $args[expr], where expr can be evaluated
100 - to an integer from 1 to the number of arguments. The number of arguments can
101 - be read from $n_args or $args[]. The array $args[expr] is the only way to
102 - access arguments beyond the first 9.
103 + Within the body of a user-defined subroutine or function, the first nine
104 + positional arguments can be retrieved using the identifiers $1, $2, ... , $9.
105 + Both positional and named arguments can be accessed using the $args array:
106 + if the key is numeric, the corresponding positional argument (numbered from 1)
107 + can be retrieved; otherwise the key is a name, and the name argument's value
108 + is retrieved. The identifier $n_args provides the number of positional
109 + arguments passed to the function. You can test for the presence of named
110 + arguments in the $args array using the "in" operator.
112 To return a value from a subroutine, and/or to exit from the subroutine
113 before the end of the subroutine body, use the return statement:
114 @@ -2238,7 +2257,7 @@ Macro Language
115 All of the above operators are array only, meaning both the left and right
116 sides of the operator must be arrays. The results are also arrays.
118 - Array keys can also contain multiple dimensions:
119 + Array keys can contain multiple "dimensions":
121 x[1, 1, 1] = "string"
123 @@ -2283,6 +2302,67 @@ Macro Language
125 does work.
127 + Note that if an array contains a value that is itself an array, you can
128 + apply the index operator more than once. For example
130 + subarray["a"] = "value"
131 + mainarray[1] = subarray
133 + for (i in mainarray) {
134 + if ("a" in mainarray[i])
135 + value_a = mainarray[i]["a"]
136 + ...
139 +4>Array Initializing Expressions
141 + You can build arrays using array expressions. These are contained in braces,
142 + "{" and "}", and contain a list of possibly empty value assignments.
143 + For example
145 + myarray = { ["a"] = "first", \
146 + ["col" colno] = x * 5, \
147 + [x, y] = 2 * func() }
149 + If the keys are numeric (or convertible to plian integers) and in increasing
150 + sequence, only the first is required; thus
152 + myarray = { [5] = a, b, c, \
153 + [1,2] = "2-D key", \
154 + ["20"] = d, e }
156 + creates entries with keys "5", "6", "7", "20", "21" and ("1" $sub_sep "2").
157 + If no key value is given for the first entry, "0" is used. If you want to skip
158 + a value in a sequence, just provide an empty value, thus
160 + myarray = { a, b, , c }
162 + creates entries with keys "0", "1" and "3". The entry for key "2" is not
163 + created.
165 + If a later array entry has the same key value as an earlier one, the later
166 + value overwrites the earlier one. For example
168 + myarray = { a, b, c, [1] = d, e }
170 + overwrites the myarray["1"] entry, initialized with the value of b, with the
171 + value of d. Similarly the myarray["2"] entry is overwritten.
173 + You can use array initializing expressions as part of other expressions. They
174 + can be passed as arguments to functions:
176 + result = func({ ["message"] = "The value is", \
177 + ["value"] = 4, "OK" })
179 + Or you can use them to add to arrays, as in
181 + myarray += { [newkey] = newvalue }
183 + The built-in variable $empty_array evaluates to an empty array. You can also
184 + build an empty array using array initializing expressions as follows:
186 + myarray = {}
188 3>Looping and Conditionals
190 NEdit supports looping constructs: for and while, and conditional statements:
191 diff --quilt old/source/interpret.c new/source/interpret.c
192 --- old/source/interpret.c
193 +++ new/source/interpret.c
194 @@ -122,6 +122,7 @@ static int fetchRetVal(void);
195 static int branch(void);
196 static int branchTrue(void);
197 static int branchFalse(void);
198 +static int branchIf(Boolean trueOrFalse);
199 static int branchNever(void);
200 static int arrayRef(void);
201 static int arrayAssign(void);
202 @@ -130,6 +131,16 @@ static int beginArrayIter(void);
203 static int arrayIter(void);
204 static int inArray(void);
205 static int deleteArrayElement(void);
206 +static int anonArrayOpen(void);
207 +static int anonArraySkip(void);
208 +static int anonArrayNextVal(void);
209 +static int anonArrayIndexVal(void);
210 +static int anonArrayClose(void);
211 +static int namedArg1(void);
212 +static int namedArgN(void);
213 +static int namedArg1orN(Boolean isFirst);
214 +static int swapTop2(void);
215 +static int makeArrayKeyFromArgs(int nArgs, char **keyString, int leaveParams);
216 static void freeSymbolTable(Symbol *symTab);
217 static int errCheck(const char *s);
218 static int execError(const char *s1, const char *s2);
219 @@ -216,17 +227,20 @@ static int (*OpFns[N_OPS])() = {returnNo
220 assign, callSubroutine, fetchRetVal, branch, branchTrue, branchFalse,
221 branchNever, arrayRef, arrayAssign, beginArrayIter, arrayIter, inArray,
222 deleteArrayElement, pushArraySymVal,
223 - arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray};
224 + arrayRefAndAssignSetup, pushArgVal, pushArgCount, pushArgArray,
225 + anonArrayOpen, anonArraySkip, anonArrayNextVal, anonArrayIndexVal,
226 + anonArrayClose, namedArg1, namedArgN, swapTop2,
227 + };
229 -/* Stack-> symN-sym0(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ... */
230 -#define FP_ARG_ARRAY_INDEX (-1)
231 -#define FP_ARG_COUNT_INDEX (-2)
232 -#define FP_FUNCTION_NAME (-3) /* !! */
233 -#define FP_SYMBOL_TABLE (-4) /* !! */
234 -#define FP_OLD_FP_INDEX (-5)
235 -#define FP_RET_PC_INDEX (-6)
236 +/* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
237 +#define FP_ARG_COUNT_INDEX (-1)
238 +#define FP_FUNCTION_NAME (-2) /* !! */
239 +#define FP_SYMBOL_TABLE (-3) /* !! */
240 +#define FP_OLD_FP_INDEX (-4)
241 +#define FP_RET_PC_INDEX (-5)
242 +#define FP_ARG_ARRAY_INDEX (-6)
244 -#define FP_TO_ARGS_DIST (0 - FP_RET_PC_INDEX) /* should be 0 - (above index) */
245 +#define FP_TO_ARGS_DIST (0 - FP_ARG_ARRAY_INDEX) /* should be 0 - (above index) */
247 #define FP_GET_ITEM(xFrameP,xIndex) (*(xFrameP + xIndex))
248 #define FP_GET_ARG_ARRAY_CACHE(xFrameP) (FP_GET_ITEM(xFrameP, FP_ARG_ARRAY_INDEX))
249 @@ -238,6 +252,13 @@ static int (*OpFns[N_OPS])() = {returnNo
250 #define FP_GET_SYM_N(xFrameP,xN) (FP_GET_ITEM(xFrameP, xN))
251 #define FP_GET_SYM_VAL(xFrameP,xSym) (FP_GET_SYM_N(xFrameP, xSym->value.val.n))
253 +#define PUSH_CHECK_TOO_MUCH(n) \
254 + (StackP + (n) > &TheStack[STACK_SIZE])
256 +#define PUSH_CHECK(n) \
257 + if (PUSH_CHECK_TOO_MUCH(n)) \
258 + return execError(StackOverflowMsg, "");
261 ** Initialize macro language global variables. Must be called before
262 ** any macros are even parsed, because the parser uses action routine
263 @@ -409,7 +430,7 @@ void SwapCode(Inst *start, Inst *boundar
264 do { register Inst t, *l = L, *h = H - 1; \
265 while (l < h) { t = *h; *h-- = *l; *l++ = t; } } while (0)
266 /* double-reverse method: reverse elements of both parts then whole lot */
267 - /* eg abcdefABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> DCBAedcba */
268 + /* eg abcdeABCD -1-> edcbaABCD -2-> edcbaDCBA -3-> ABCDabcde */
269 reverseCode(start, boundary); /* 1 */
270 reverseCode(boundary, end); /* 2 */
271 reverseCode(start, end); /* 3 */
272 @@ -488,7 +509,8 @@ int ExecuteMacro(WindowInfo *window, Pro
273 static DataValue noValue = {NO_TAG, {0}};
274 Symbol *s;
275 int i;
277 + int haveNamedArgs;
279 /* Create an execution context (a stack, a stack pointer, a frame pointer,
280 and a program counter) which will retain the program state across
281 preemption and resumption of execution */
282 @@ -500,9 +522,16 @@ int ExecuteMacro(WindowInfo *window, Pro
283 context->runWindow = window;
284 context->focusWindow = window;
286 + haveNamedArgs = (nArgs < 0);
287 + if (haveNamedArgs)
288 + nArgs = -nArgs;
290 /* Push arguments and call information onto the stack */
291 for (i=0; i<nArgs; i++)
292 - *(context->stackP++) = args[i];
293 + *(context->stackP++) = args[i];
295 + if (!haveNamedArgs)
296 + *(context->stackP++) = noValue; /* cached arg array */
298 context->stackP->val.subr = NULL; /* return PC */
299 context->stackP->tag = NO_TAG;
300 @@ -519,12 +548,10 @@ int ExecuteMacro(WindowInfo *window, Pro
301 context->stackP->val.str.len = strlen(context->stackP->val.str.rep);
302 context->stackP++;
304 - context->stackP->tag = NO_TAG; /* nArgs */
305 - context->stackP->val.n = nArgs;
306 + context->stackP->tag = INT_TAG; /* nArgs */
307 + context->stackP->val.n = nArgs - haveNamedArgs;
308 context->stackP++;
310 - *(context->stackP++) = noValue; /* cached arg array */
312 context->frameP = context->stackP;
314 /* Initialize and make room on the stack for local variables */
315 @@ -597,12 +624,16 @@ int ContinueMacro(RestartData *continuat
319 +** Set up a new stack frame, with no caller arguments, in the current context,
320 +** and set up execution for Program *prog.
322 ** If a macro is already executing, and requests that another macro be run,
323 ** this can be called instead of ExecuteMacro to run it in the same context
324 ** as if it were a subroutine. This saves the caller from maintaining
325 ** separate contexts, and serializes processing of the two macros without
326 ** additional work.
328 +/* TODO: this function should really return a status (if fails PUSH_CHECK) */
329 void RunMacroAsSubrCall(Program *prog)
331 Symbol *s;
332 @@ -610,6 +641,10 @@ void RunMacroAsSubrCall(Program *prog)
334 /* See subroutine "callSubroutine" for a description of the stack frame
335 for a subroutine call */
336 + /* if (PUSH_CHECK_TOO_MUCH(4)) return MACRO_ERROR; */
338 + *(StackP++) = noValue; /* cached arg array */
340 StackP->tag = NO_TAG;
341 StackP->val.inst = PC; /* return PC */
342 StackP++;
343 @@ -627,18 +662,18 @@ void RunMacroAsSubrCall(Program *prog)
344 StackP->val.str.len = strlen(StackP->val.str.rep);
345 StackP++;
347 - StackP->tag = NO_TAG; /* nArgs */
348 + StackP->tag = INT_TAG; /* nArgs */
349 StackP->val.n = 0;
350 StackP++;
352 - *(StackP++) = noValue; /* cached arg array */
354 FrameP = StackP;
355 PC = prog->code;
356 for (s = prog->localSymList; s != NULL; s = s->next) {
357 + /* if (PUSH_CHECK_TOO_MUCH(1)) return MACRO_ERROR; */
358 FP_GET_SYM_VAL(FrameP, s) = noValue;
359 StackP++;
361 + /* return MACRO_DONE? MACRO_PREEMPT? MACRO_TIME_LIMIT? */
364 void FreeRestartData(RestartData *context)
365 @@ -1356,12 +1391,15 @@ static int pushArgArray(void)
366 if (resultArray->tag != ARRAY_TAG) {
367 resultArray->tag = ARRAY_TAG;
368 resultArray->val.arrayPtr = ArrayNew();
371 + /* load arguments from positional arg list if not already done */
372 + if (nArgs && !ArrayGet(resultArray, longAsStr(argNum + 1), &argVal)) {
373 for (argNum = 0; argNum < nArgs; ++argNum) {
374 argVal = FP_GET_ARG_N(FrameP, argNum);
375 if (!ArrayInsert(resultArray, AllocStringOfNumber(argNum + 1),
376 &argVal)) {
377 - return(execError("array insertion failure", NULL));
378 + return(execError("argument array insertion failure", NULL));
382 @@ -1417,6 +1455,274 @@ static int pushArraySymVal(void)
386 +** create an anonymous array and next index number value (0) on the stack (for
387 +** array construction expressions)
389 +** Before: Prog-> [next], ...
390 +** TheStack-> next, ...
391 +** After: Prog-> [next], ...
392 +** TheStack-> [empty-array, 0], next, ...
394 +static int anonArrayOpen(void)
396 + DataValue dataVal;
398 + DISASM_RT(PC-1, 1);
399 + STACKDUMP(0, 3);
401 + /* make an empty array */
402 + dataVal.tag = ARRAY_TAG;
403 + dataVal.val.arrayPtr = ArrayNew();
405 + /* push the default next index value first */
406 + PUSH_INT(0)
408 + /* and the empty array */
409 + PUSH(dataVal)
411 + return STAT_OK;
415 +** cause the auto-incrementing next index number value to increase without
416 +** actually creating an entry in the anonymous array (for array construction
417 +** expressions)
419 +** Before: Prog-> [next], ...
420 +** TheStack-> [anon-array, next-index], next, ...
421 +** After: Prog-> [next], ...
422 +** TheStack-> [anon-array, next-index+1], next, ...
424 +static int anonArraySkip(void)
426 + DataValue anonArray;
427 + int nextIndex;
429 + DISASM_RT(PC-1, 1);
430 + STACKDUMP(2, 3);
432 + POP(anonArray)
433 + POP_INT(nextIndex)
435 + /* we need to increment the index for next time */
436 + ++nextIndex;
438 + /* push the default next index value first, then the array */
439 + PUSH_INT(nextIndex)
440 + PUSH(anonArray)
442 + return STAT_OK;
446 +** add an entry to the anonymous array at the stack head, using the numeric
447 +** index just below that; restack the incremented index and anonymous array
448 +** (for array construction expressions)
450 +** Before: Prog-> [next], ...
451 +** TheStack-> [expr, anon-array, next-index], next, ...
452 +** After: Prog-> [next], ...
453 +** TheStack-> [anon-array, next-index+1], next, ...
455 +static int anonArrayNextVal(void)
457 + DataValue exprVal, anonArray;
458 + int nextIndex;
459 + char numString[TYPE_INT_STR_SIZE(int)];
461 + DISASM_RT(PC-1, 1);
462 + STACKDUMP(3, 3);
464 + POP(exprVal)
465 + POP(anonArray)
466 + POP_INT(nextIndex)
468 + sprintf(numString, "%d", nextIndex);
469 + if (!ArrayInsert(&anonArray, AllocStringCpy(numString), &exprVal)) {
470 + return(execError("array insertion failure", NULL));
473 + /* we need to increment the index for next time */
474 + ++nextIndex;
476 + /* push the default next index value first, then the array */
477 + PUSH_INT(nextIndex)
478 + PUSH(anonArray)
480 + return STAT_OK;
485 +** Before: Prog-> [nDim], next, ...
486 +** TheStack-> [expr, indnDim, ... ind1, anon-array, next-index], next, ...
487 +** After: Prog-> nDim, [next], ...
488 +** TheStack-> [anon-array, new-next-index], next, ...
490 +static int anonArrayIndexVal(void)
492 + int errNum;
493 + char *keyString = NULL;
494 + DataValue exprVal, anonArray;
495 + int nextIndex, index;
496 + int nDim;
498 + nDim = PC->value;
499 + PC++;
501 + DISASM_RT(PC-2, 2);
502 + STACKDUMP(nDim+3, 3);
504 + POP(exprVal)
506 + /* the next nDim stack entries form the index */
507 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
508 + if (errNum != STAT_OK) {
509 + return errNum;
512 + POP(anonArray)
513 + POP_INT(nextIndex)
515 + /* if our index is numeric (or can be converted to a number) we must
516 + change the next index value */
517 + if (nDim == 1 && StringToNum(keyString, &index)) {
518 + nextIndex = index + 1;
521 + if (!ArrayInsert(&anonArray, keyString, &exprVal)) {
522 + return(execError("array insertion failure", NULL));
525 + /* push the default next index value first, then the array */
526 + PUSH_INT(nextIndex)
527 + PUSH(anonArray)
529 + return STAT_OK;
533 +** finish building an anonymous array by removing the next index number value
534 +** from the stack (for array construction expressions)
536 +** Before: Prog-> [next], ...
537 +** TheStack-> [anon-array, next-index], next, ...
538 +** After: Prog-> [next], ...
539 +** TheStack-> [anon-array], next, ...
541 +static int anonArrayClose(void)
543 + DataValue anonArray;
544 + DataValue next_index;
546 + DISASM_RT(PC-1, 1);
547 + STACKDUMP(2, 3);
549 + /* remove top two elements */
550 + POP(anonArray)
551 + POP(next_index)
552 + /* put back the array content */
553 + PUSH(anonArray)
555 + return STAT_OK;
559 +** create an $args array for the named arg with index of nDim elements, and
560 +** value expr; leave result on top of stack
562 +** Before: Prog-> [nDim], next, ...
563 +** TheStack-> [expr, indnDim, ... ind1], argN-arg1, next, ...
564 +** After: Prog-> nDim, [next], ...
565 +** TheStack-> args, argN-arg1, next, ...
567 +static int namedArg1(void)
569 + return namedArg1orN(True);
573 +** add the named arg with index of nDim elements, and value expr to the $args
574 +** array at the top of the stack
576 +** Before: Prog-> [nDim], next, ...
577 +** TheStack-> [expr, indnDim, ... ind1, args], argN-arg1, next, ...
578 +** After: Prog-> nDim, [next], ...
579 +** TheStack-> [args], argN-arg1, next, ...
581 +static int namedArgN()
583 + return namedArg1orN(False);
587 +** implementation for namedArg1(), namedArgN()
589 +static int namedArg1orN(Boolean isFirst)
591 + int errNum;
592 + char *keyString = NULL;
593 + DataValue exprVal, argsArray;
594 + int nDim, index;
596 + nDim = (PC++)->value;
598 + DISASM_RT(PC-2, 2);
599 + STACKDUMP(nDim + (isFirst ? 2 : 1), 3);
601 + POP(exprVal)
603 + /* the next nDim stack entries form the index */
604 + errNum = makeArrayKeyFromArgs(nDim, &keyString, 0);
605 + if (errNum != STAT_OK) {
606 + return errNum;
609 + /* if our index is numeric (or can be converted to a number) we must
610 + change the next index value */
611 + if (nDim == 1 && StringToNum(keyString, &index)) {
612 + return execError("named argument name must not be numeric", NULL);
615 + if (isFirst) {
616 + /* make a new empty array */
617 + argsArray.tag = ARRAY_TAG;
618 + argsArray.val.arrayPtr = NULL;
620 + else {
621 + /* use the array at the top of the stack */
622 + POP(argsArray)
625 + if (!ArrayInsert(&argsArray, keyString, &exprVal)) {
626 + return(execError("named argument insertion failure", NULL));
629 + /* and (re)push the array */
630 + PUSH(argsArray)
632 + return STAT_OK;
636 +** exchange top two values on the stack
638 +static int swapTop2(void)
640 + DataValue dv1, dv2;
642 + DISASM_RT(PC-1, 1);
643 + STACKDUMP(2, 3);
645 + POP(dv1)
646 + POP(dv2)
647 + PUSH(dv1)
648 + PUSH(dv2)
650 + return STAT_OK;
654 ** assign top value to next symbol
656 ** Before: Prog-> [symbol], next, ...
657 @@ -2052,7 +2358,9 @@ static int concat(void)
659 ** Call a subroutine or function (user defined or built-in). Args are the
660 ** subroutine's symbol, and the number of arguments which have been pushed
661 -** on the stack.
662 +** on the stack. If this value is less than zero, use the absolute value,
663 +** but note that the last one is already the $args array so don't set aside
664 +** space for that.
666 ** For a macro subroutine, the return address, frame pointer, number of
667 ** arguments and space for local variables are added to the stack, and the
668 @@ -2060,11 +2368,11 @@ static int concat(void)
669 ** arguments are popped off the stack, and the routine is just called.
671 ** Before: Prog-> [subrSym], nArgs, next, ...
672 -** TheStack-> argN-arg1, next, ...
673 +** TheStack-> argArray?, argN-arg1, next, ...
674 ** After: Prog-> next, ... -- (built-in called subr)
675 ** TheStack-> retVal?, next, ...
676 ** or: Prog-> (in called)next, ... -- (macro code called subr)
677 -** TheStack-> symN-sym1(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
678 +** TheStack-> symN-sym1(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
680 static int callSubroutine(void)
682 @@ -2073,14 +2381,18 @@ static int callSubroutine(void)
683 static DataValue noValue = {NO_TAG, {0}};
684 Program *prog;
685 char *errMsg;
687 + int haveNamedArgs;
689 sym = PC->sym;
690 PC++;
691 nArgs = PC->value;
692 PC++;
695 + haveNamedArgs = (nArgs < 0);
696 + nArgs = (haveNamedArgs) ? -nArgs - 1 : nArgs;
698 DISASM_RT(PC-3, 3);
699 - STACKDUMP(nArgs, 3);
700 + STACKDUMP(nArgs + haveNamedArgs, 3);
703 ** If the subroutine is built-in, call the built-in routine
704 @@ -2088,13 +2400,16 @@ static int callSubroutine(void)
705 if (sym->type == C_FUNCTION_SYM) {
706 DataValue result;
708 + if (!haveNamedArgs)
709 + PUSH(noValue) /* push dummy named arg array */
711 /* "pop" stack back to the first argument in the call stack */
712 - StackP -= nArgs;
713 + StackP -= nArgs + 1;
715 /* Call the function and check for preemption */
716 PreemptRequest = False;
717 - if (!sym->value.val.subr(FocusWindow, StackP,
718 - nArgs, &result, &errMsg))
719 + /* NB nArgs < 0 implies presence of named args array in last position */
720 + if (!sym->value.val.subr(FocusWindow, StackP, nArgs, &result, &errMsg))
721 return execError(errMsg, sym->name);
722 if (PC->func == fetchRetVal) {
723 if (result.tag == NO_TAG) {
724 @@ -2114,38 +2429,42 @@ static int callSubroutine(void)
725 ** values which are already there.
727 if (sym->type == MACRO_FUNCTION_SYM) {
728 - prog = sym->value.val.prog;
729 + PUSH_CHECK(3 + !haveNamedArgs)
731 - StackP->tag = NO_TAG; /* return PC */
732 - StackP->val.inst = PC;
733 - StackP++;
735 - StackP->tag = NO_TAG; /* old FrameP */
736 - StackP->val.dataval = FrameP;
737 - StackP++;
739 - StackP->tag = NO_TAG;
740 - StackP->val.sym = prog->localSymList; /* symbol table */
741 - StackP++;
743 - StackP->tag = STRING_TAG;
744 - StackP->val.str.rep = sym->name; /* function name */
745 - StackP->val.str.len = strlen(sym->name);
746 - StackP++;
748 - StackP->tag = NO_TAG; /* nArgs */
749 - StackP->val.n = nArgs;
750 - StackP++;
751 + prog = sym->value.val.prog;
753 + if (!haveNamedArgs)
754 + *(StackP++) = noValue; /* push dummy named arg array */
756 + StackP->tag = NO_TAG; /* return PC */
757 + StackP->val.inst = PC;
758 + StackP++;
760 - *(StackP++) = noValue; /* cached arg array */
761 + StackP->tag = NO_TAG; /* old FrameP */
762 + StackP->val.dataval = FrameP;
763 + StackP++;
765 - FrameP = StackP;
766 - PC = prog->code;
767 - for (s = prog->localSymList; s != NULL; s = s->next) {
768 - FP_GET_SYM_VAL(FrameP, s) = noValue;
769 - StackP++;
771 - return STAT_OK;
772 + StackP->tag = NO_TAG;
773 + StackP->val.sym = prog->localSymList; /* symbol table */
774 + StackP++;
776 + StackP->tag = STRING_TAG;
777 + StackP->val.str.rep = sym->name; /* function name */
778 + StackP->val.str.len = strlen(sym->name);
779 + StackP++;
781 + StackP->tag = NO_TAG; /* nArgs */
782 + StackP->val.n = nArgs;
783 + StackP++;
785 + FrameP = StackP;
786 + PC = prog->code;
787 + for (s = prog->localSymList; s != NULL; s = s->next) {
788 + PUSH_CHECK(1)
789 + FP_GET_SYM_VAL(FrameP, s) = noValue;
790 + StackP++;
792 + return STAT_OK;
796 @@ -2157,7 +2476,13 @@ static int callSubroutine(void)
797 XKeyEvent key_event;
798 Display *disp;
799 Window win;
802 + if (haveNamedArgs) {
803 + return execError(
804 + "%s action routine called with named argument array",
805 + sym->name);
808 /* Create a fake event with a timestamp suitable for actions which need
809 timestamps, a marker to indicate that the call was from a macro
810 (to stop shell commands from putting up their own separate banner) */
811 @@ -2216,7 +2541,7 @@ static int returnVal(void)
813 ** Return from a subroutine call
814 ** Before: Prog-> [next], ...
815 -** TheStack-> retVal?, ...(FP), argArray, nArgs, oldFP, retPC, argN-arg1, next, ...
816 +** TheStack-> retVal?, ...(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ...
817 ** After: Prog-> next, ..., (in caller)[FETCH_RET_VAL?], ...
818 ** TheStack-> retVal?, next, ...
820 @@ -2293,34 +2618,26 @@ static int branch(void)
822 static int branchTrue(void)
824 - int value;
825 - Inst *addr;
827 - DISASM_RT(PC-1, 2);
828 - STACKDUMP(1, 3);
830 - POP_INT(value)
831 - addr = PC + PC->value;
832 - PC++;
834 - if (value)
835 - PC = addr;
836 - return STAT_OK;
837 + return branchIf(True);
839 static int branchFalse(void)
841 + return branchIf(False);
843 +static int branchIf(Boolean trueOrFalse)
845 int value;
846 Inst *addr;
849 DISASM_RT(PC-1, 2);
850 STACKDUMP(1, 3);
852 POP_INT(value)
853 addr = PC + PC->value;
854 PC++;
856 - if (!value)
857 - PC = addr;
859 + if (!value == !trueOrFalse)
860 + PC = addr;
861 return STAT_OK;
864 @@ -3224,7 +3541,15 @@ static void disasmInternal(Inst *inst, i
865 "ARRAY_REF_ASSIGN_SETUP", /* arrayRefAndAssignSetup */
866 "PUSH_ARG", /* $arg[expr] */
867 "PUSH_ARG_COUNT", /* $arg[] */
868 - "PUSH_ARG_ARRAY" /* $arg */
869 + "PUSH_ARG_ARRAY", /* $arg */
870 + "ARRAY_OPEN", /* anonArrayOpen: "{...}" */
871 + "ARRAY_SKIP", /* anonArraySkip: "{ , ...}" */
872 + "ARRAY_NEXT_VAL", /* anonArrayNextVal: "{ expr }" */
873 + "ARRAY_INDEX_VAL", /* anonArrayIndexVal: "{ [i]=expr }" */
874 + "ARRAY_CLOSE", /* anonArrayClose: "{...}" */
875 + "NAMED_ARG1", /* namedArg1: "fn([...]=..., ...)" */
876 + "NAMED_ARGN", /* namedArgN: "fn(..., [...]=...)" */
877 + "SWAP_TOP2", /* swapTop2: cf namedArgN */
879 int i, j;
881 @@ -3254,7 +3579,14 @@ static void disasmInternal(Inst *inst, i
882 ++i;
884 else if (j == OP_SUBR_CALL) {
885 - printd("%s (%d arg)", inst[i+1].sym->name, inst[i+2].value);
886 + int args = inst[i+2].value;
887 + printd("%s ", inst[i+1].sym->name);
888 + if (args < 0) {
889 + printd("%d+args[] (%d)", -args - 1, args);
891 + else {
892 + printd("%d args", args);
894 i += 2;
896 else if (j == OP_BEGIN_ARRAY_ITER) {
897 @@ -3268,8 +3600,12 @@ static void disasmInternal(Inst *inst, i
898 inst[i+3].value, &inst[i+3] + inst[i+3].value);
899 i += 3;
901 - else if (j == OP_ARRAY_REF || j == OP_ARRAY_DELETE ||
902 - j == OP_ARRAY_ASSIGN) {
903 + else if (j == OP_ARRAY_REF ||
904 + j == OP_ARRAY_DELETE ||
905 + j == OP_ARRAY_ASSIGN ||
906 + j == OP_ANONARRAY_INDEX_VAL ||
907 + j == OP_NAMED_ARG1 ||
908 + j == OP_NAMED_ARGN) {
909 printd("nDim=%d", inst[i+1].value);
910 ++i;
912 @@ -3305,7 +3641,7 @@ static void disasm(Inst *inst, int nInst
913 #endif /* #ifdef DEBUG_DISASSEMBLER */
915 #ifdef DEBUG_STACK /* for run-time stack dumping */
916 -#define STACK_DUMP_ARG_PREFIX "Arg"
917 +#define STACK_DUMP_ARG_PREFIX " $"
918 static void stackdumpframe(DataValue *arrow, DataValue *outpt, DataValue *fp,
919 DataValue *sp, char topMark)
921 @@ -3362,12 +3698,12 @@ static void stackdumpframe(DataValue *ar
922 printd("%4.4s", leadIn);
923 printd("%8p%c", dv, topMark);
924 switch (offset) {
925 - case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
926 case FP_ARG_COUNT_INDEX: pos = "NArgs"; break; /* num. arguments */
927 case FP_FUNCTION_NAME: pos = "FnName"; break;
928 case FP_SYMBOL_TABLE: pos = "FnSyms"; break;
929 case FP_OLD_FP_INDEX: pos = "OldFP"; break;
930 case FP_RET_PC_INDEX: pos = "RetPC"; break;
931 + case FP_ARG_ARRAY_INDEX: pos = "args[]"; break; /* argument array */
932 default:
933 if (offset < -FP_TO_ARGS_DIST &&
934 offset >= -FP_TO_ARGS_DIST - nArgs)
935 diff --quilt old/source/parse.y new/source/parse.y
936 --- old/source/parse.y
937 +++ new/source/parse.y
938 @@ -67,7 +67,7 @@ static int nextSymIsField = 0;
939 %token <sym> NUMBER STRING SYMBOL FIELD
940 %token DELETE ARG_LOOKUP
941 %token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN
942 -%type <nArgs> arglistopt arglist catlist
943 +%type <nArgs> arglistopt arglist catlist fnarglsopt fnarglist fnarg
944 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
945 %type <sym> evalsym
946 %type <oper> operassign incrdecr
947 @@ -246,7 +246,7 @@ simpstmt: /* simple variable assignmen
948 ADD_OP(OP_ARRAY_ASSIGN); ADD_IMMED(1);
950 /* function call */
951 - | SYMBOL '(' arglistopt ')' {
952 + | SYMBOL '(' fnarglsopt ')' {
953 ADD_OP(OP_SUBR_CALL);
954 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
956 @@ -272,6 +272,59 @@ arglist: blank expr bla
957 catlist: numexpr %prec CONCAT { $$ = 1; }
958 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
960 +fnarg: expr {
961 + $$ = 0;
963 + | '[' arglist ']' '=' expr {
964 + $$ = $2; /* how many index elements to read? */
966 + | dot field '=' expr {
967 + $$ = 1; /* how many index elements to read? */
970 +fnarglsopt: blank { $$ = 0; }
971 + | fnarglist { $$ = $1; }
973 +fnarglist: blank fnarg blank {
974 + if ($2 > 0) {
975 + /* named argument code already knows about index length (see
976 + rule for arg: above); it needs to be assembled into an
977 + array, which must be created. */
978 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($2);
979 + $$ = -1; /* negative single arg for named arg array */
981 + else {
982 + /* a normal positional argument - leave value on stack */
983 + $$ = 1;
986 + | fnarglist ',' blank fnarg blank {
987 + if ($4 > 0) {
988 + /* named arg: $4 == how many indices to process */
989 + if ($1 >= 0) {
990 + /* first named arg: create the array */
991 + ADD_OP(OP_NAMED_ARG1); ADD_IMMED($4);
992 + $$ = -($1 + 1); /* make arg count negative */
994 + else {
995 + /* another named arg: add to array */
996 + ADD_OP(OP_NAMED_ARGN); ADD_IMMED($4);
997 + $$ = $1; /* no new positional args */
1000 + else {
1001 + /* positional arg */
1002 + if ($1 < 0) {
1003 + ADD_OP(OP_SWAP_TOP2); /* keep arg array as last */
1004 + $$ = $1 - 1;
1006 + else {
1007 + $$ = $1 + 1; /* no named args yet */
1013 expr: catlist {
1014 if ($1 > 1) {
1015 ADD_OP(OP_CONCAT); ADD_IMMED($1);
1016 @@ -311,17 +364,54 @@ arrayexpr: numexpr {
1017 $$ = GetPC();
1020 + /* anonymous arrays eg: array = { , "hi", .a=2, ["b"]="three" } */
1021 +arrconstr0: '{' {
1022 + /* create an empty array into which to add things */
1023 + ADD_OP(OP_ANONARRAY_OPEN);
1026 +arrconstr: arrconstr0 arrlistopt '}' {
1027 + /* we're done: the array is complete */
1028 + ADD_OP(OP_ANONARRAY_CLOSE);
1031 +arrlistopt: arrlist
1033 +arrlist: arrentry
1034 + | arrlist ',' arrentry
1036 +arrentry: blank {
1037 + /* missing entry will skip an index value */
1038 + ADD_OP(OP_ANONARRAY_SKIP);
1040 + | blank expr blank {
1041 + /* make a suitable index >= 0 and add expr there */
1042 + ADD_OP(OP_ANONARRAY_NEXT_VAL);
1044 + | blank '[' arglist ']' blank '=' blank expr blank {
1045 + /* build the index from arglistopt and add expr there */
1046 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
1047 + ADD_IMMED($3);
1049 + | blank dot field blank '=' blank expr blank {
1050 + /* build the index from arglistopt and add expr there */
1051 + ADD_OP(OP_ANONARRAY_INDEX_VAL);
1052 + ADD_IMMED(1);
1055 numexpr: '(' blank expr blank ')'
1056 | NUMBER { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
1057 | STRING { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
1058 | SYMBOL { ADD_OP(OP_PUSH_SYM); ADD_SYM($1); }
1059 - | SYMBOL '(' arglistopt ')' {
1060 + | SYMBOL '(' fnarglsopt ')' {
1061 ADD_OP(OP_SUBR_CALL);
1062 ADD_SYM(PromoteToGlobal($1)); ADD_IMMED($3);
1063 ADD_OP(OP_FETCH_RET_VAL);
1065 + /*
1066 | ARG_LOOKUP '[' blank numexpr blank ']' { ADD_OP(OP_PUSH_ARG); }
1067 | ARG_LOOKUP '[' blank ']' { ADD_OP(OP_PUSH_ARG_COUNT); }
1068 + */
1069 | ARG_LOOKUP { ADD_OP(OP_PUSH_ARG_ARRAY); }
1070 | numexpr '[' arglistopt ']' {
1071 ADD_OP(OP_ARRAY_REF); ADD_IMMED($3);
1072 @@ -403,6 +493,7 @@ or: OR {
1073 dot: '.' %prec '.' {
1074 nextSymIsField = 1;
1076 + | arrconstr
1078 blank: /* nothing */
1079 | blank '\n'
1080 diff --quilt old/source/interpret.h new/source/interpret.h
1081 --- old/source/interpret.h
1082 +++ new/source/interpret.h
1083 @@ -48,7 +48,11 @@ enum operations {OP_RETURN_NO_VAL, OP_RE
1084 OP_BRANCH_TRUE, OP_BRANCH_FALSE, OP_BRANCH_NEVER, OP_ARRAY_REF,
1085 OP_ARRAY_ASSIGN, OP_BEGIN_ARRAY_ITER, OP_ARRAY_ITER, OP_IN_ARRAY,
1086 OP_ARRAY_DELETE, OP_PUSH_ARRAY_SYM, OP_ARRAY_REF_ASSIGN_SETUP, OP_PUSH_ARG,
1087 - OP_PUSH_ARG_COUNT, OP_PUSH_ARG_ARRAY, N_OPS};
1088 + OP_PUSH_ARG_COUNT, OP_PUSH_ARG_ARRAY,
1089 + OP_ANONARRAY_OPEN, OP_ANONARRAY_SKIP, OP_ANONARRAY_NEXT_VAL,
1090 + OP_ANONARRAY_INDEX_VAL, OP_ANONARRAY_CLOSE,
1091 + OP_NAMED_ARG1, OP_NAMED_ARGN, OP_SWAP_TOP2,
1092 + N_OPS};
1094 enum typeTags {NO_TAG, INT_TAG, STRING_TAG, ARRAY_TAG};