4 source/interpret.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++
6 source/parse.y | 95 ++++++++++++++++++++++
7 4 files changed, 336 insertions(+), 1 deletion(-)
9 diff --quilt old/source/interpret.c new/source/interpret.c
10 --- old/source/interpret.c
11 +++ new/source/interpret.c
12 @@ -153,10 +153,11 @@ static int arrayEntryCopyToNode(rbTreeNo
13 static int arrayEntryCompare(rbTreeNode *left, rbTreeNode *right);
14 static void arrayDisposeNode(rbTreeNode *src);
15 static SparseArrayEntry *allocateSparseArrayEntry(void);
16 static int typeOfIn(void);
17 static int typeOfOut(void);
18 +static int arrayMultiIter(void);
20 /* is the intepreter in the special typeof() mode? */
21 static int inTypeOfMode;
23 static const char *tagToStr(enum typeTags tag);
24 @@ -244,10 +245,11 @@ static int (*OpFns[N_OPS])() = {returnNo
25 anonArrayClose, namedArg1, namedArgN, swapTop2,
26 callSubroutineStackedN,
33 /* Stack-> symN-sym0(FP), nArgs, oldFP, retPC, argArray, argN-arg1, next, ... */
34 #define FP_ARG_COUNT_INDEX (-1)
35 #define FP_FUNCTION_NAME (-2) /* !! */
36 @@ -3482,10 +3484,201 @@ static int arrayIter(void)
42 +static int countDim(const char *key)
45 + size_t seplen = strlen(ARRAY_DIM_SEP);
46 + const char *sep = key;
50 + sep = strstr(sep, ARRAY_DIM_SEP);
58 +static Boolean splitKey(const char *key, DataValue **keys)
61 + size_t seplen = strlen(ARRAY_DIM_SEP);
63 + const char *sep = key;
64 + const char *nextsep = key;
67 + nextsep = strstr(sep, ARRAY_DIM_SEP);
69 + keylen = nextsep - sep;
71 + keylen = strlen(sep);
74 + keys[nDims]->tag = STRING_TAG;
75 + if (!AllocNStringNCpy(&keys[nDims]->val.str, sep, keylen)) {
89 +** copy key and value to symbols if node is still valid, marked bad by a color
90 +** of -1 then move iterator to next node
91 +** this allows iterators to progress even if you delete any node in the array
92 +** except the item just after the current key
94 +** Before: Prog-> iter, ARRAY_MULTI_ITER, [withVal], nDims, iterVarKey1, ..., iterVarKeynDims, (, iterVarVal), iter, endLoopBranch, next, ...
95 +** TheStack-> [next], ...
96 +** After: Prog-> iter, ARRAY_MULTI_ITER, withVal, nDims, iterVarKey1, ..., iterVarKeynDims, (, iterVarVal), iter, endLoopBranch, [next], ...
97 +** TheStack-> [next], ... (unchanged)
99 +** iter is a symbol which gives the position of the iterator value in
100 +** the stack frame (set up by OP_BEGIN_ARRAY_ITER); that value refers
101 +** to the array and a position within it
102 +** iterVarKey1, ..., iterVarKeynDim is the programmer-visible symbol which
103 +** will take the current key value
104 +** iterVarVal is the programmer-visible symbol which will take the current
105 +** entry value (only if withVal is true)
106 +** endLoopBranch is the instruction offset to the instruction following the
107 +** loop (measured from itself)
108 +** arrayVal is the data value holding the array in question
109 +** The return-to-start-of-loop branch (at the end of the loop) should address
110 +** the ARRAY_MULTI_ITER instruction
112 +static int arrayMultiIter(void)
117 + DataValue *iteratorValPtr;
118 + DataValue **itemVarKeyPtrs;
119 + DataValue *itemVarValPtr;
120 + SparseArrayEntry *thisEntry;
124 + Boolean keyFound = False;
126 + DISASM_RT(PC-1, 4);
129 + withVal = PC->value;
134 + itemKeys = malloc(nDims * sizeof(*itemKeys));
135 + itemVarKeyPtrs = malloc(nDims * sizeof(*itemVarKeyPtrs));
137 + for (d = 0; d < nDims; d++) {
138 + itemKeys[d] = PC->sym;
147 + iterator = PC->sym;
149 + branchAddr = PC + PC->value;
152 + for (d = 0; d < nDims; d++) {
153 + if (itemKeys[d]->type == LOCAL_SYM) {
154 + itemVarKeyPtrs[d] = &FP_GET_SYM_VAL(FrameP, itemKeys[d]);
156 + else if (itemKeys[d]->type == GLOBAL_SYM) {
157 + itemVarKeyPtrs[d] = &(itemKeys[d]->value);
160 + const char *name = itemKeys[d]->name;
162 + free(itemVarKeyPtrs);
163 + return(execError("can't assign to: %s", name));
165 + itemVarKeyPtrs[d]->tag = NO_TAG;
169 + if (itemVal->type == LOCAL_SYM) {
170 + itemVarValPtr = &FP_GET_SYM_VAL(FrameP, itemVal);
172 + else if (itemVal->type == GLOBAL_SYM) {
173 + itemVarValPtr = &(itemVal->value);
177 + free(itemVarKeyPtrs);
178 + return(execError("can't assign to: %s", itemVal->name));
180 + itemVarValPtr->tag = NO_TAG;
183 + if (iterator->type == LOCAL_SYM) {
184 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
188 + free(itemVarKeyPtrs);
189 + return(execError("bad temporary iterator: %s", iterator->name));
192 + thisEntry = iteratorValPtr->val.arrayPtr;
193 + while (thisEntry && thisEntry->nodePtrs.color != -1) {
194 + /* check if this is a nDims key */
195 + int thisDim = countDim(thisEntry->key);
197 + if (nDims != thisDim) {
199 + thisEntry = arrayIterateNext(thisEntry);
206 + if (!splitKey(thisEntry->key, itemVarKeyPtrs)) {
208 + free(itemVarKeyPtrs);
209 + return(execError("can't split key: %s", thisEntry->key));
214 + *itemVarValPtr = thisEntry->value;
217 + /* advance iterator */
218 + thisEntry = arrayIterateNext(thisEntry);
221 + iteratorValPtr->val.arrayPtr = thisEntry;
223 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
228 + free(itemVarKeyPtrs);
234 ** determine if a key or keys exists in an array
235 ** if the left argument is a string or integer a single check is performed
236 ** if the key exists, 1 is pushed onto the stack, otherwise 0
237 ** if the left argument is an array 1 is pushed onto the stack if every key
238 @@ -3944,10 +4137,11 @@ static void disasmInternal(Inst *inst, i
239 "SUBR_CALL_STACKED_N", /* callSubroutineStackedN */
240 "UNPACKTOARGS", /* unpackArrayToArgs */
241 "TYPEOF_IN", /* typeOfIn */
242 "TYPEOF_OUT", /* typeOfOut */
243 "ARRAY_INDEX", /* arrayIndex */
244 + "ARRAY_MULTI_ITER", /* arrayMultiIter */
250 @@ -4021,10 +4215,42 @@ static void disasmInternal(Inst *inst, i
252 &inst[i+5] + inst[i+5].value);
256 + else if (j == OP_ARRAY_MULTI_ITER) {
257 + int nDim = inst[i+2].value, d;
258 + if (!inst[i+1].value) {
261 + for (d = 0; d < nDim; d++) {
264 + inst[i+3+d].sym->name);
266 + printd("] = %s++ end-loop=(%+d) %8p",
267 + inst[i+nDim+3].sym->name,
268 + inst[i+nDim+4].value,
269 + &inst[i+nDim+4] + inst[i+nDim+4].value);
275 + for (d = 0; d < nDim; d++) {
278 + inst[i+3+d].sym->name);
280 + printd("]=%s = %s++ end-loop=(%+d) %8p",
281 + inst[i+nDim+3].sym->name,
282 + inst[i+nDim+4].sym->name,
283 + inst[i+nDim+5].value,
284 + &inst[i+nDim+5] + inst[i+nDim+5].value);
288 else if (j == OP_ARRAY_REF ||
289 j == OP_ARRAY_DELETE ||
290 j == OP_ARRAY_ASSIGN ||
291 j == OP_ANONARRAY_INDEX_VAL ||
292 j == OP_NAMED_ARG1 ||
293 diff --quilt old/source/interpret.h new/source/interpret.h
294 --- old/source/interpret.h
295 +++ new/source/interpret.h
296 @@ -57,10 +57,11 @@ enum operations {OP_RETURN_NO_VAL, OP_RE
297 OP_NAMED_ARG1, OP_NAMED_ARGN, OP_SWAP_TOP2,
298 OP_SUBR_CALL_STACKED_N,
300 OP_TYPEOF_IN, OP_TYPEOF_OUT,
302 + OP_ARRAY_MULTI_ITER,
305 enum typeTags {NO_TAG, INT_TAG, STRING_TAG, ARRAY_TAG};
307 enum execReturnCodes {MACRO_TIME_LIMIT, MACRO_PREEMPT, MACRO_DONE, MACRO_ERROR};
308 diff --quilt old/source/parse.y new/source/parse.y
309 --- old/source/parse.y
310 +++ new/source/parse.y
311 @@ -81,13 +81,26 @@ static int AllowDefine;
313 static int nextSymIsField = 0;
314 /* set to 1 when we don't want a full symbol, just a name (string) for a
315 field name following a '.' */
317 +#define SYMBLK_SIZE ((64 - sizeof(void *)) / sizeof(void *))
319 +typedef struct SymBlkTag {
320 + Symbol *syms[SYMBLK_SIZE];
321 + struct SymBlkTag *next;
324 +typedef struct SymListTag {
325 + SymBlk *head, *tail;
336 enum operations oper;
337 AccumulatorData *acc;
338 @@ -98,10 +111,11 @@ static int nextSymIsField = 0;
340 %type <nArgs> arrlist arrentry
341 %type <nArgs> arglistopt arglist catlist fnarglsopt fnarglist fnarg
342 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
344 +%type <symlist> symlist
345 %type <oper> operassign incrdecr
346 %token <oper> '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ ANDEQ OREQ
347 %token <oper> INCR DECR
350 @@ -241,10 +255,65 @@ stmt: ';' blank
353 SET_BR_OFF($9+7, GetPC());
354 FillLoopAddrs(GetPC(), $9+2);
356 + | for '(' blank '[' symlist ']' IN blank arrayexpr blank ')' {
357 + Symbol *iterSym = InstallIteratorSymbol();
359 + ADD_OP(OP_BEGIN_ARRAY_ITER);
361 + ADD_OP(OP_ARRAY_MULTI_ITER);
362 + ADD_IMMED(0); /* without val symbol */
363 + ADD_IMMED($5.nSyms);
365 + ADD_SYM($5.head->syms[i % SYMBLK_SIZE]);
367 + if (i % SYMBLK_SIZE == 0 || i == $5.nSyms) {
368 + SymBlk *nextsymblk = $5.head->next;
370 + $5.head = nextsymblk;
372 + } while (i < $5.nSyms);
380 + SET_BR_OFF($9+6+$5.nSyms, GetPC());
381 + FillLoopAddrs(GetPC(), $9+2);
383 + | for '(' blank '[' symlist ']' KEYVAL SYMBOL IN blank arrayexpr blank ')' {
384 + Symbol *iterSym = InstallIteratorSymbol();
386 + ADD_OP(OP_BEGIN_ARRAY_ITER);
388 + ADD_OP(OP_ARRAY_MULTI_ITER);
389 + ADD_IMMED(1); /* with val symbol */
390 + ADD_IMMED($5.nSyms);
392 + ADD_SYM($5.head->syms[i % SYMBLK_SIZE]);
394 + if (i % SYMBLK_SIZE == 0 || i == $5.nSyms) {
395 + SymBlk *nextsymblk = $5.head->next;
397 + $5.head = nextsymblk;
399 + } while (i < $5.nSyms);
408 + SET_BR_OFF($11+7+$5.nSyms, GetPC());
409 + FillLoopAddrs(GetPC(), $11+2);
411 | BREAK stmtend blank {
412 ADD_OP(OP_BRANCH); ADD_BR_OFF(0);
413 if (AddBreakAddr(GetPC()-1)) {
414 yyerror("break outside loop"); YYERROR;
416 @@ -355,10 +424,36 @@ arglistopt: blank { $$ = 0; }
418 arglist: blank expr blank { $$ = 1; }
419 | arglist ',' blank expr blank { $$ = $1 + 1; }
422 +/* SYMBOL list for multi dimension array iterator */
423 +symlist: blank SYMBOL blank {
424 + SymBlk *symblk = malloc(sizeof(*symblk));
425 + symblk->syms[0] = $2;
426 + symblk->next = NULL;
427 + $$.head = $$.tail = symblk;
430 + | symlist ',' blank SYMBOL blank {
431 + /* copy all from $1 to $$, and clear $1 */
434 + $$.nSyms = $1.nSyms;
435 + $1.head = $1.tail = NULL;
436 + /* alloc new SymBlk, if current tail is full */
437 + if ($$.nSyms % SYMBLK_SIZE == 0) {
438 + SymBlk *symblk = malloc(sizeof(*symblk));
439 + symblk->next = NULL;
440 + $$.tail->next = symblk;
443 + $$.tail->syms[$$.nSyms % SYMBLK_SIZE] = $4;
448 /* string concatenation lists */
449 catlist: numexpr %prec CONCAT { $$ = 1; }
450 | catlist numexpr %prec CONCAT { $$ = $1 + 1; }
453 diff --quilt old/doc/help.etx new/doc/help.etx
456 @@ -2334,11 +2334,24 @@ Macro Language
457 looking for $sub_sep in the key.
459 If you want to use the 'in' keyword, use this syntax:
461 if ([1,2] in myArray)
465 + There is also a special array iterator for multi-dimensional arrays:
467 + for ([dim1Key, ..., dimNKey] in myArray)
470 + This would only iterate over all keys with N dimensions assigned to the
471 + symbols dim1Key, ..., dimNKey.
473 + Also with the associated value for the multi-dimensional key:
475 + for ([dim1Key, ..., dimNKey] = theVal in myArray)
478 Note that if an array contains a value that is itself an array, you can
479 apply the index operator more than once. For example
481 subarray["a"] = "value"