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 | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
25 source/parse.y | 42 +++++++++
26 4 files changed, 289 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 @@ -1888,10 +1888,30 @@ static int arrayIndex(void)
37 +** pop a value from the stack
39 +** Before: Prog-> next, ...
40 +** TheStack-> [val], next, ...
41 +** After: Prog-> next, ... (unchanged)
42 +** TheStack-> [next], ...
44 +static int popStack(void)
57 ** copy the top value of the stack
58 ** Before: TheStack-> value, next, ...
59 ** After: TheStack-> value, value, next, ...
61 static int dupStack(void)
62 @@ -3742,10 +3762,202 @@ static int arrayMultiIter(void)
68 +** Before: Prog-> [iter], ARRAY_MULTI_ITER_ARRAY, withVal, iterVarKeyArray(, iterVarVal), iter, endLoopBranch, next, ...
69 +** TheStack-> [arrayVal], nDims, next, ...
70 +** After: Prog-> iter, [ARRAY_MULTI_ITER_ARRAY], withVal, iterVarKeyArray(, iterVarVal), iter, endLoopBranch, next, ...
71 +** TheStack-> [nDims], next, ...
73 +static int beginArrayMultiIterArray(void)
76 + DataValue *iteratorValPtr;
89 + if (iterator->type == LOCAL_SYM) {
90 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
93 + return(execError("bad temporary iterator: %s", iterator->name));
97 + return(execError("bad multi dimension", NULL));
100 + iteratorValPtr->tag = INT_TAG;
101 + if (arrayVal.tag != ARRAY_TAG) {
102 + return(execError("can't iterate non-array", NULL));
105 + iteratorValPtr->val.arrayPtr = arrayIterateFirst(&arrayVal);
110 +static Boolean splitKeyArr(const char *key, DataValue *keyArr)
113 + size_t seplen = strlen(ARRAY_DIM_SEP);
115 + const char *sep = key;
116 + const char *nextsep = key;
120 + nextsep = strstr(sep, ARRAY_DIM_SEP);
122 + keylen = nextsep - sep;
124 + keylen = strlen(sep);
127 + keyVal.tag = STRING_TAG;
128 + if (!AllocNStringNCpy(&keyVal.val.str, sep, keylen)) {
132 + if (!ArrayInsert(keyArr, AllocStringOfNumber(nDims), &keyVal)) {
147 +** Before: Prog-> iter, ARRAY_MULTI_ITER_ARRAY, [withVal], iterVarKeyArray, (, iterVarVal), iter, endLoopBranch, next, ...
148 +** TheStack-> [nDims], next, ...
149 +** After: Prog-> iter, ARRAY_MULTI_ITER_ARRAY, withVal, iterVarKeyArray, (, iterVarVal), iter, endLoopBranch, [next], ...
150 +** TheStack-> [nDims], next, ... (unchanged)
152 +static int arrayMultiIterArray(void)
155 + Symbol *keyArraySym;
157 + DataValue *iteratorValPtr;
158 + DataValue *keyArrayPtr;
160 + SparseArrayEntry *thisEntry;
164 + Boolean keyFound = False;
166 + DISASM_RT(PC-1, 4);
169 + withVal = PC->value;
172 + PEEK_INT(nDims, 0);
174 + keyArraySym = PC->sym;
182 + iterator = PC->sym;
184 + branchAddr = PC + PC->value;
187 + if (keyArraySym->type == LOCAL_SYM) {
188 + keyArrayPtr = &FP_GET_SYM_VAL(FrameP, keyArraySym);
190 + else if (keyArraySym->type == GLOBAL_SYM) {
191 + keyArrayPtr = &(keyArraySym->value);
194 + return(execError("can't assign to: %s", keyArraySym->name));
196 + keyArrayPtr->tag = ARRAY_TAG;
197 + keyArrayPtr->val.arrayPtr = NULL;
200 + if (valSym->type == LOCAL_SYM) {
201 + valPtr = &FP_GET_SYM_VAL(FrameP, valSym);
203 + else if (valSym->type == GLOBAL_SYM) {
204 + valPtr = &(valSym->value);
207 + return(execError("can't assign to: %s", valSym->name));
209 + valPtr->tag = NO_TAG;
212 + if (iterator->type == LOCAL_SYM) {
213 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
216 + return(execError("bad temporary iterator: %s", iterator->name));
219 + thisEntry = iteratorValPtr->val.arrayPtr;
220 + while (thisEntry && thisEntry->nodePtrs.color != -1) {
222 + /* check if this is an nDims key, but only for non zero dims */
224 + int thisDim = countDim(thisEntry->key);
226 + if (nDims != thisDim) {
228 + thisEntry = arrayIterateNext(thisEntry);
236 + if (!splitKeyArr(thisEntry->key, keyArrayPtr)) {
237 + return(execError("can't split key: %s", thisEntry->key));
242 + *valPtr = thisEntry->value;
245 + /* advance iterator */
246 + thisEntry = arrayIterateNext(thisEntry);
249 + iteratorValPtr->val.arrayPtr = thisEntry;
251 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
253 + /*POP_INT(nDims);*/
260 ** determine if a key or keys exists in an array
261 ** if the left argument is a string or integer a single check is performed
262 ** if the key exists, 1 is pushed onto the stack, otherwise 0
263 ** if the left argument is an array 1 is pushed onto the stack if every key
264 ** in the left array exists in the right array, otherwise 0
265 @@ -4217,11 +4429,12 @@ static void disasmInternal(Inst *inst, i
267 else if (j == OP_SUBR_CALL_STACKED_N) {
268 printd(" %s args[] (?)", inst[i+1].sym->name);
271 - else if (j == OP_BEGIN_ARRAY_ITER) {
272 + else if (j == OP_BEGIN_ARRAY_ITER ||
273 + j == OP_BEGIN_ARRAY_MULTI_ITER_ARRAY) {
274 printd(" %s in", inst[i+1].sym->name);
277 else if (j == OP_ARRAY_ITER) {
278 if (!inst[i+1].value) {
279 @@ -4274,10 +4487,31 @@ static void disasmInternal(Inst *inst, i
280 inst[i+nDim+5].value,
281 &inst[i+nDim+5] + inst[i+nDim+5].value);
285 + else if (j == OP_ARRAY_MULTI_ITER_ARRAY) {
286 + if (!inst[i+1].value) {
288 + printd(" %s[] = %s++ end-loop=(%+d) %8p",
289 + inst[i+2].sym->name,
290 + inst[i+3].sym->name,
292 + &inst[i+4] + inst[i+4].value);
297 + printd(" %s[]=%s = %s++ end-loop=(%+d) %8p",
298 + inst[i+2].sym->name,
299 + inst[i+3].sym->name,
300 + inst[i+4].sym->name,
302 + &inst[i+5] + inst[i+5].value);
306 else if (j == OP_ARRAY_REF ||
307 j == OP_ARRAY_DELETE ||
308 j == OP_ARRAY_ASSIGN ||
309 j == OP_ANONARRAY_INDEX_VAL ||
310 j == OP_NAMED_ARG1 ||
311 diff --quilt old/source/parse.y new/source/parse.y
312 --- old/source/parse.y
313 +++ new/source/parse.y
314 @@ -320,10 +320,45 @@ stmt: ';' blank
317 SET_BR_OFF($11+7+$5.nSyms, GetPC());
318 FillLoopAddrs(GetPC(), $11+2);
320 + | for '(' blank SYMBOL '[' numexpropt ']' IN blank arrayexpr blank ')' {
321 + Symbol *iterSym = InstallIteratorSymbol();
322 + ADD_OP(OP_BEGIN_ARRAY_MULTI_ITER_ARRAY);
324 + ADD_OP(OP_ARRAY_MULTI_ITER_ARRAY);
325 + ADD_IMMED(0); /* without val symbol */
333 + SET_BR_OFF($10+6, GetPC());
334 + FillLoopAddrs(GetPC(), $10+2);
337 + | for '(' blank SYMBOL '[' numexpropt ']' KEYVAL SYMBOL IN blank arrayexpr blank ')' {
338 + Symbol *iterSym = InstallIteratorSymbol();
339 + ADD_OP(OP_BEGIN_ARRAY_MULTI_ITER_ARRAY);
341 + ADD_OP(OP_ARRAY_MULTI_ITER_ARRAY);
342 + ADD_IMMED(1); /* with val symbol */
351 + SET_BR_OFF($12+7, GetPC());
352 + FillLoopAddrs(GetPC(), $12+2);
355 | BREAK stmtend blank {
356 ADD_OP(OP_BRANCH); ADD_BR_OFF(0);
357 if (AddBreakAddr(GetPC()-1)) {
358 yyerror("break outside loop"); YYERROR;
360 @@ -668,10 +703,17 @@ numexpr: '(' blank expr blank ')'
361 | keyinexpr NOT IN blank numexpr %prec IN {
368 + ADD_OP(OP_PUSH_IMMED);
374 | blank '[' arglist ']' blank {
375 /* build the index from arglist and push to stack */
376 ADD_OP(OP_ARRAY_INDEX);
378 diff --quilt old/doc/help.etx new/doc/help.etx
381 @@ -2349,10 +2349,19 @@ Macro Language
382 Also with the associated value for the multi-dimensional key:
384 for ([dim1Key, ..., dimNKey] = theVal in myArray)
387 + As a last iterator variant, you can use this syntax:
389 + for (keyArray[nDim] in myArray)
392 + where this loop iterates over all keys with dimension nDim and puts the
393 + dimension keys into the array keyArray (starting with 0). If nDim equals
394 + 0 or is missing (i.e. keyArray[]) all keys are iterated.
396 Note that if an array contains a value that is itself an array, you can
397 apply the index operator more than once. For example
399 subarray["a"] = "value"
400 mainarray[1] = subarray
401 diff --quilt old/source/ops.h new/source/ops.h
405 /* op name function arguments operation */
406 OP(RETURN_NO_VAL, returnNoVal) /* rewind */
407 OP(RETURN, returnVal) /* pop(ret), rewind, push(ret) */
408 OP(PUSH_SYM, pushSymVal) /* sym */ /* push(sym.v) */
409 OP(PUSH_IMMED, pushImmed) /* immed */ /* push(immed) */
410 +OP(POP, popStack) /* pop(v) */
411 OP(DUP, dupStack) /* pop(v), push(v,v) */
412 OP(ADD, add) /* pop(v2,v1), push(v1 + v2) */
413 OP(SUB, subtract) /* pop(v2,v1), push(v1 - v2) */
414 OP(MUL, multiply) /* pop(v2,v1), push(v1 * v2) */
415 OP(DIV, divide) /* pop(v2,v1), push(v1 / v2) */
416 @@ -39,10 +40,12 @@ OP(BRANCH_NEVER, branchNever)
417 OP(ARRAY_REF, arrayRef) /* N */ /* pop(kN..k1,a), push(a[k1..kN]) */
418 OP(ARRAY_ASSIGN, arrayAssign) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v */
419 OP(BEGIN_ARRAY_ITER, beginArrayIter) /* it */ /* pop(a), it=a.begin */
420 OP(ARRAY_ITER, arrayIter) /*w,k[,v],it,pc*/ /* it?(k.v=it.k,(w?v.v=it.v:),it++):PC=pc */
421 OP(ARRAY_MULTI_ITER, arrayMultiIter) /*w,N,k1..kN[,v],it,pc*/ /* while it: (if dim(it.k)==N: ([k1..kN].v=it.k,(w?v.v=it.v:),break), it++), if notfound: PC=pc */
422 +OP(BEGIN_ARRAY_MULTI_ITER_ARRAY, beginArrayMultiIterArray) /*it*/ /* */
423 +OP(ARRAY_MULTI_ITER_ARRAY, arrayMultiIterArray) /*w,k[v],pc*/ /* top(N) */
424 OP(IN_ARRAY, inArray) /* pop(a,k), push(a[k]?1:0) */
425 OP(ARRAY_INDEX, arrayIndex) /* N */ /* pop(kN..k1), push([kN..k1]) */
426 OP(ARRAY_DELETE, deleteArrayElement) /*N*/ /* N>0 ? (pop(kN..k1,a), del(a[k])) : (pop(a), delall(a)) */
427 OP(PUSH_ARRAY_SYM, pushArraySymVal) /*s,i*/ /* if i: s.v=ary()), push(s.v) */
428 OP(ARRAY_REF_ASSIGN_SETUP, arrayRefAndAssignSetup) /*op,N*/ /* pop(v,kN..a), a[k1..kN] op= v */