1 Subject: array key array iteration
3 Adds a new syntax to iterate over the keys of an array, where the key is
6 for (keys[expr] in array)
9 expr should be an non-negative integer which gives the dimension to iterate.
10 I.e. if expr is 10, only keys with 10 dimensions are iterated. If expr is 0,
11 or missing, all keys are iterated.
13 The key is split-up and assigned to array elements starting with 0.
15 The key/value iteration is also supported:
17 for (keys[expr] = val in array)
23 source/interpret.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
25 source/parse.y | 42 ++++++++
26 4 files changed, 310 insertions(+), 1 deletion(-)
28 diff --quilt old/source/interpret.c new/source/interpret.c
29 --- old/source/interpret.c
30 +++ new/source/interpret.c
31 @@ -1879,6 +1879,26 @@ static int assign(void)
35 +** pop a value from the stack
37 +** Before: Prog-> next, ...
38 +** TheStack-> [val], next, ...
39 +** After: Prog-> next, ... (unchanged)
40 +** TheStack-> [next], ...
42 +static int popStack(void)
55 ** copy the top value of the stack
56 ** Before: TheStack-> value, next, ...
57 ** After: TheStack-> value, value, next, ...
58 @@ -3515,6 +3535,214 @@ static int arrayIter(void)
62 +** Before: Prog-> [iter], ARRAY_ITER_ARRAY, withVal, keyArraySym(, valSym), iter, endLoopBranch, next, ...
63 +** TheStack-> [arrayVal], nDims, next, ...
64 +** After: Prog-> iter, [ARRAY_ITER_ARRAY], withVal, keyArraySym(, valSym), iter, endLoopBranch, next, ...
65 +** TheStack-> [nDims], next, ...
67 +static int beginArrayIterArray(void)
70 + DataValue *iteratorValPtr;
83 + if (iterator->type == LOCAL_SYM) {
84 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
87 + return(execError("bad temporary iterator: %s", iterator->name));
91 + return(execError("bad multi dimension", NULL));
94 + iteratorValPtr->tag = INT_TAG;
95 + if (arrayVal.tag != ARRAY_TAG) {
96 + return(execError("can't iterate non-array", NULL));
99 + iteratorValPtr->val.arrayPtr = arrayIterateFirst(&arrayVal);
104 +static int countDim(const char *key)
107 + size_t seplen = strlen(ARRAY_DIM_SEP);
108 + const char *sep = key;
112 + sep = strstr(sep, ARRAY_DIM_SEP);
120 +/* maybe use splitMS? */
121 +static Boolean splitKeyIntoArray(const char *key, DataValue *keyArr)
124 + size_t seplen = strlen(ARRAY_DIM_SEP);
126 + const char *sep = key;
127 + const char *nextsep = key;
131 + nextsep = strstr(sep, ARRAY_DIM_SEP);
133 + keylen = nextsep - sep;
135 + keylen = strlen(sep);
138 + keyVal.tag = STRING_TAG;
139 + if (!AllocNStringNCpy(&keyVal.val.str, sep, keylen)) {
143 + if (!ArrayInsert(keyArr, AllocStringOfNumber(nDims), &keyVal)) {
158 +** Before: Prog-> iter, ARRAY_ITER_ARRAY, [withVal], keyArraySym, (, valSym), iter, endLoopBranch, next, ...
159 +** TheStack-> [nDims], next, ...
160 +** After: Prog-> iter, ARRAY_ITER_ARRAY, withVal, keyArraySym, (, valSym), iter, endLoopBranch, [next], ...
161 +** TheStack-> [nDims], next, ... (unchanged)
163 +static int arrayIterArray(void)
166 + Symbol *keyArraySym;
168 + DataValue *iteratorValPtr;
169 + DataValue *keyArrayPtr;
171 + SparseArrayEntry *thisEntry;
175 + Boolean keyFound = False;
177 + DISASM_RT(PC-1, 4);
180 + withVal = PC->value;
183 + PEEK_INT(nDims, 0);
185 + keyArraySym = PC->sym;
193 + iterator = PC->sym;
195 + branchAddr = PC + PC->value;
198 + if (keyArraySym->type == LOCAL_SYM) {
199 + keyArrayPtr = &FP_GET_SYM_VAL(FrameP, keyArraySym);
201 + else if (keyArraySym->type == GLOBAL_SYM) {
202 + keyArrayPtr = &(keyArraySym->value);
205 + return(execError("can't assign to: %s", keyArraySym->name));
207 + keyArrayPtr->tag = ARRAY_TAG;
208 + keyArrayPtr->val.arrayPtr = NULL;
211 + if (valSym->type == LOCAL_SYM) {
212 + valPtr = &FP_GET_SYM_VAL(FrameP, valSym);
214 + else if (valSym->type == GLOBAL_SYM) {
215 + valPtr = &valSym->value;
218 + return(execError("can't assign to: %s", valSym->name));
220 + valPtr->tag = NO_TAG;
223 + if (iterator->type == LOCAL_SYM) {
224 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
227 + return(execError("bad temporary iterator: %s", iterator->name));
230 + thisEntry = iteratorValPtr->val.arrayPtr;
231 + while (thisEntry && thisEntry->nodePtrs.color != -1) {
233 + /* check if this is an nDims key, but only if requested */
235 + int thisDim = countDim(thisEntry->key);
237 + if (nDims != thisDim) {
239 + thisEntry = arrayIterateNext(thisEntry);
247 + if (!splitKeyIntoArray(thisEntry->key, keyArrayPtr)) {
248 + return(execError("can't split key: %s", thisEntry->key));
253 + *valPtr = thisEntry->value;
256 + /* advance iterator */
257 + thisEntry = arrayIterateNext(thisEntry);
260 + iteratorValPtr->val.arrayPtr = thisEntry;
262 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
270 ** determine if a key or keys exists in an array
271 ** if the left argument is a string or integer a single check is performed
272 ** if the key exists, 1 is pushed onto the stack, otherwise 0
273 @@ -4015,7 +4243,8 @@ static void disasmInternal(Inst *inst, i
274 printd(" %s args[] (?)", inst[i+1].sym->name);
277 - else if (j == OP_BEGIN_ARRAY_ITER) {
278 + else if (j == OP_BEGIN_ARRAY_ITER ||
279 + j == OP_BEGIN_ARRAY_ITER_ARRAY) {
280 printd(" %s in", inst[i+1].sym->name);
283 @@ -4040,6 +4269,27 @@ static void disasmInternal(Inst *inst, i
287 + else if (j == OP_ARRAY_ITER_ARRAY) {
288 + if (!inst[i+1].value) {
290 + printd(" %s[] = %s++ end-loop=(%+d) %8p",
291 + inst[i+2].sym->name,
292 + inst[i+3].sym->name,
294 + &inst[i+4] + inst[i+4].value);
299 + printd(" %s[]=%s = %s++ end-loop=(%+d) %8p",
300 + inst[i+2].sym->name,
301 + inst[i+3].sym->name,
302 + inst[i+4].sym->name,
304 + &inst[i+5] + inst[i+5].value);
308 else if (j == OP_ARRAY_REF ||
309 j == OP_ARRAY_DELETE ||
310 j == OP_ARRAY_ASSIGN ||
311 diff --quilt old/source/parse.y new/source/parse.y
312 --- old/source/parse.y
313 +++ new/source/parse.y
314 @@ -270,6 +270,41 @@ stmt: ';' blank
315 SET_BR_OFF($9+7, GetPC());
316 FillLoopAddrs(GetPC(), $9+2);
318 + | for '(' blank SYMBOL '[' numexpropt ']' IN blank arrayexpr blank ')' {
319 + Symbol *iterSym = InstallIteratorSymbol();
320 + ADD_OP(OP_BEGIN_ARRAY_ITER_ARRAY);
322 + ADD_OP(OP_ARRAY_ITER_ARRAY);
323 + ADD_IMMED(0); /* without val symbol */
331 + SET_BR_OFF($10+6, GetPC());
332 + FillLoopAddrs(GetPC(), $10+2);
333 + ADD_OP(OP_POP); /* remove nDim from stack */
335 + | for '(' blank SYMBOL '[' numexpropt ']' KEYVAL SYMBOL IN blank arrayexpr blank ')' {
336 + Symbol *iterSym = InstallIteratorSymbol();
337 + ADD_OP(OP_BEGIN_ARRAY_ITER_ARRAY);
339 + ADD_OP(OP_ARRAY_ITER_ARRAY);
340 + ADD_IMMED(1); /* with val symbol */
349 + SET_BR_OFF($12+7, GetPC());
350 + FillLoopAddrs(GetPC(), $12+2);
351 + ADD_OP(OP_POP); /* remove nDim from stack */
353 | BREAK stmtend blank {
354 ADD_OP(OP_BRANCH); ADD_BR_OFF(0);
355 if (AddBreakAddr(GetPC()-1)) {
356 @@ -714,6 +749,13 @@ numexpr: '(' blank expr blank ')'
361 + ADD_OP(OP_PUSH_IMMED);
368 $$ = GetPC(); StartLoopAddrList();
370 diff --quilt old/doc/help.etx new/doc/help.etx
373 @@ -2345,6 +2345,20 @@ Macro Language
377 + There is also a special array iterator for multi-dimensional arrays:
379 + for (keyArray[nDim] in myArray)
382 + where this loop iterates over all keys with dimension nDim and puts the
383 + dimension keys into the array keyArray (starting with 0). If nDim equals
384 + 0 or is missing (i.e. keyArray[]) all keys will be iterated.
386 + Also with the associated value for the multi-dimensional key:
388 + for (keyArray[nDim] = theVal in myArray)
391 Note that if an array contains a value that is itself an array, you can
392 apply the index operator more than once. For example
394 diff --quilt old/source/ops.h new/source/ops.h
397 @@ -7,6 +7,7 @@ OP(RETURN_NO_VAL, returnNoVal)
398 OP(RETURN, returnVal) /* pop(ret), rewind, push(ret) */
399 OP(PUSH_SYM, pushSymVal) /* sym */ /* push(sym.v) */
400 OP(PUSH_IMMED, pushImmed) /* immed */ /* push(immed) */
401 +OP(POP, popStack) /* pop(v) */
402 OP(DUP, dupStack) /* pop(v), push(v,v) */
403 OP(ADD, add) /* pop(v2,v1), push(v1 + v2) */
404 OP(SUB, subtract) /* pop(v2,v1), push(v1 - v2) */
405 @@ -40,6 +41,8 @@ OP(ARRAY_REF, arrayRef)
406 OP(ARRAY_ASSIGN, arrayAssign) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v */
407 OP(BEGIN_ARRAY_ITER, beginArrayIter) /* it */ /* pop(a), it=a.begin */
408 OP(ARRAY_ITER, arrayIter) /*w,k[,v],it,pc*/ /* it?(k.v=it.k,(w?v.v=it.v:),it++):PC=pc */
409 +OP(BEGIN_ARRAY_ITER_ARRAY, beginArrayIterArray) /*it*/ /* */
410 +OP(ARRAY_ITER_ARRAY, arrayIterArray) /*w,ka[,v],it,pc*/ /* top(N),while it: (if dim(it.k)==N: (ka.v=split(it.k),(w?v.v=it.v:),return),it++), PC=pc */
411 OP(IN_ARRAY, inArray) /* pop(a,k), push(a[k]?1:0) */
412 OP(ARRAY_DELETE, deleteArrayElement) /*N*/ /* N>0 ? (pop(kN..k1,a), del(a[k])) : (pop(a), delall(a)) */
413 OP(PUSH_ARRAY_SYM, pushArraySymVal) /*s,i*/ /* if i: s.v=ary()), push(s.v) */