pretty print multi demension array keys
[nedit-bw.git] / array-key-array-iterator.patch
blobfcee16b8c8c5c91605f6cd85afb0b1f70432c240
1 Subject: array key array iteration
3 Adds a new syntax to iterate over the keys of an array, where the key is
4 assigned to an array:
6 for (keys[expr] in array)
7 { ... }
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)
18 { ... }
20 ---
22 doc/help.etx | 14 ++
23 source/interpret.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
24 source/ops.h | 3
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
36 +**
37 +** Before: Prog-> next, ...
38 +** TheStack-> [val], next, ...
39 +** After: Prog-> next, ... (unchanged)
40 +** TheStack-> [next], ...
41 +*/
42 +static int popStack(void)
44 + DataValue val;
46 + DISASM_RT(PC-1, 1);
47 + STACKDUMP(1, 4);
49 + POP(val);
51 + return STAT_OK;
54 +/*
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, ...
66 +*/
67 +static int beginArrayIterArray(void)
69 + Symbol *iterator;
70 + DataValue *iteratorValPtr;
71 + DataValue arrayVal;
72 + int nDims;
74 + DISASM_RT(PC-1, 2);
75 + STACKDUMP(2, 3);
77 + iterator = PC->sym;
78 + PC++;
80 + POP(arrayVal);
81 + PEEK_INT(nDims, 0);
83 + if (iterator->type == LOCAL_SYM) {
84 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
85 + }
86 + else {
87 + return(execError("bad temporary iterator: %s", iterator->name));
88 + }
90 + if (nDims < 0) {
91 + return(execError("bad multi dimension", NULL));
92 + }
94 + iteratorValPtr->tag = INT_TAG;
95 + if (arrayVal.tag != ARRAY_TAG) {
96 + return(execError("can't iterate non-array", NULL));
97 + }
99 + iteratorValPtr->val.arrayPtr = arrayIterateFirst(&arrayVal);
101 + return(STAT_OK);
104 +static int countDim(const char *key)
106 + int nDims = 0;
107 + size_t seplen = strlen(ARRAY_DIM_SEP);
108 + const char *sep = key;
110 + do {
111 + nDims++;
112 + sep = strstr(sep, ARRAY_DIM_SEP);
113 + if (sep)
114 + sep += seplen;
115 + } while (sep);
117 + return nDims;
120 +/* maybe use splitMS? */
121 +static Boolean splitKeyIntoArray(const char *key, DataValue *keyArr)
123 + int nDims = 0;
124 + size_t seplen = strlen(ARRAY_DIM_SEP);
125 + size_t keylen;
126 + const char *sep = key;
127 + const char *nextsep = key;
128 + DataValue keyVal;
130 + do {
131 + nextsep = strstr(sep, ARRAY_DIM_SEP);
132 + if (nextsep) {
133 + keylen = nextsep - sep;
134 + } else {
135 + keylen = strlen(sep);
138 + keyVal.tag = STRING_TAG;
139 + if (!AllocNStringNCpy(&keyVal.val.str, sep, keylen)) {
140 + return False;
143 + if (!ArrayInsert(keyArr, AllocStringOfNumber(nDims), &keyVal)) {
144 + return False;
147 + nDims++;
148 + sep = nextsep;
149 + if (sep)
150 + sep += seplen;
151 + } while (sep);
153 + return True;
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)
165 + Symbol *iterator;
166 + Symbol *keyArraySym;
167 + Symbol *valSym;
168 + DataValue *iteratorValPtr;
169 + DataValue *keyArrayPtr;
170 + DataValue *valPtr;
171 + SparseArrayEntry *thisEntry;
172 + Inst *branchAddr;
173 + int withVal;
174 + int nDims, d;
175 + Boolean keyFound = False;
177 + DISASM_RT(PC-1, 4);
178 + STACKDUMP(1, 4);
180 + withVal = PC->value;
181 + PC++;
183 + PEEK_INT(nDims, 0);
185 + keyArraySym = PC->sym;
186 + PC++;
188 + if (withVal) {
189 + valSym = PC->sym;
190 + PC++;
193 + iterator = PC->sym;
194 + PC++;
195 + branchAddr = PC + PC->value;
196 + PC++;
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);
204 + else {
205 + return(execError("can't assign to: %s", keyArraySym->name));
207 + keyArrayPtr->tag = ARRAY_TAG;
208 + keyArrayPtr->val.arrayPtr = NULL;
210 + if (withVal) {
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;
217 + else {
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);
226 + else {
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 */
234 + if (nDims > 0) {
235 + int thisDim = countDim(thisEntry->key);
237 + if (nDims != thisDim) {
238 + /* try next */
239 + thisEntry = arrayIterateNext(thisEntry);
240 + continue;
244 + keyFound = True;
246 + /* set keys */
247 + if (!splitKeyIntoArray(thisEntry->key, keyArrayPtr)) {
248 + return(execError("can't split key: %s", thisEntry->key));
251 + if (withVal) {
252 + /* set value */
253 + *valPtr = thisEntry->value;
256 + /* advance iterator */
257 + thisEntry = arrayIterateNext(thisEntry);
258 + break;
260 + iteratorValPtr->val.arrayPtr = thisEntry;
262 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
263 + PC = branchAddr;
266 + return STAT_OK;
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);
275 ++i;
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);
281 ++i;
283 @@ -4040,6 +4269,27 @@ static void disasmInternal(Inst *inst, i
284 i += 5;
287 + else if (j == OP_ARRAY_ITER_ARRAY) {
288 + if (!inst[i+1].value) {
289 + /* without val */
290 + printd(" %s[] = %s++ end-loop=(%+d) %p",
291 + inst[i+2].sym->name,
292 + inst[i+3].sym->name,
293 + inst[i+4].value,
294 + &inst[i+4] + inst[i+4].value);
295 + i += 4;
297 + else {
298 + /* with val */
299 + printd(" %s[]=%s = %s++ end-loop=(%+d) %p",
300 + inst[i+2].sym->name,
301 + inst[i+3].sym->name,
302 + inst[i+4].sym->name,
303 + inst[i+5].value,
304 + &inst[i+5] + inst[i+5].value);
305 + i += 5;
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);
321 + ADD_SYM(iterSym);
322 + ADD_OP(OP_ARRAY_ITER_ARRAY);
323 + ADD_IMMED(0); /* without val symbol */
324 + ADD_SYM($4);
325 + ADD_SYM(iterSym);
326 + ADD_BR_OFF(0);
328 + blank block {
329 + ADD_OP(OP_BRANCH);
330 + ADD_BR_OFF($10+2);
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);
338 + ADD_SYM(iterSym);
339 + ADD_OP(OP_ARRAY_ITER_ARRAY);
340 + ADD_IMMED(1); /* with val symbol */
341 + ADD_SYM($4);
342 + ADD_SYM($9);
343 + ADD_SYM(iterSym);
344 + ADD_BR_OFF(0);
346 + blank block {
347 + ADD_OP(OP_BRANCH);
348 + ADD_BR_OFF($12+2);
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 ')'
357 | arrconstr
360 +numexpropt: blank {
361 + ADD_OP(OP_PUSH_IMMED);
362 + ADD_IMMED(0);
364 + | numexpr
367 while: WHILE blank {
368 $$ = GetPC(); StartLoopAddrList();
370 diff --quilt old/doc/help.etx new/doc/help.etx
371 --- old/doc/help.etx
372 +++ new/doc/help.etx
373 @@ -2345,6 +2345,20 @@ Macro Language
375 does work.
377 + There is also a special array iterator for multi-dimensional arrays:
379 + for (keyArray[nDim] in myArray)
380 + <body>
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)
389 + <body>
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
395 --- old/source/ops.h
396 +++ 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) */