add cast to XtCalloc() call
[nedit-bw.git] / multi-dim-key-iterator-arr.patch
blob092e51beb5f3a13e1337c862dfbb3f108b5d8fb8
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 | 9 ++
23 source/interpret.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
24 source/ops.h | 3
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)
33 return STAT_OK;
37 +** pop a value from the stack
38 +**
39 +** Before: Prog-> next, ...
40 +** TheStack-> [val], next, ...
41 +** After: Prog-> next, ... (unchanged)
42 +** TheStack-> [next], ...
43 +*/
44 +static int popStack(void)
46 + DataValue val;
48 + DISASM_RT(PC-1, 1);
49 + STACKDUMP(1, 4);
51 + POP(val);
53 + return STAT_OK;
56 +/*
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 @@ -3744,10 +3764,202 @@ static int arrayMultiIter(void)
64 return(STAT_OK);
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, ...
72 +*/
73 +static int beginArrayMultiIterArray(void)
75 + Symbol *iterator;
76 + DataValue *iteratorValPtr;
77 + DataValue arrayVal;
78 + int nDims;
80 + DISASM_RT(PC-1, 2);
81 + STACKDUMP(2, 3);
83 + iterator = PC->sym;
84 + PC++;
86 + POP(arrayVal);
87 + PEEK_INT(nDims, 0);
89 + if (iterator->type == LOCAL_SYM) {
90 + iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
91 + }
92 + else {
93 + return(execError("bad temporary iterator: %s", iterator->name));
94 + }
96 + if (nDims < 0) {
97 + return(execError("bad multi dimension", NULL));
98 + }
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);
107 + return(STAT_OK);
110 +static Boolean splitKeyArr(const char *key, DataValue *keyArr)
112 + int nDims = 0;
113 + size_t seplen = strlen(ARRAY_DIM_SEP);
114 + size_t keylen;
115 + const char *sep = key;
116 + const char *nextsep = key;
117 + DataValue keyVal;
119 + do {
120 + nextsep = strstr(sep, ARRAY_DIM_SEP);
121 + if (nextsep) {
122 + keylen = nextsep - sep;
123 + } else {
124 + keylen = strlen(sep);
127 + keyVal.tag = STRING_TAG;
128 + if (!AllocNStringNCpy(&keyVal.val.str, sep, keylen)) {
129 + return False;
132 + if (!ArrayInsert(keyArr, AllocStringOfNumber(nDims), &keyVal)) {
133 + return False;
136 + nDims++;
137 + sep = nextsep;
138 + if (sep)
139 + sep += seplen;
140 + } while (sep);
142 + return True;
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)
154 + Symbol *iterator;
155 + Symbol *keyArraySym;
156 + Symbol *valSym;
157 + DataValue *iteratorValPtr;
158 + DataValue *keyArrayPtr;
159 + DataValue *valPtr;
160 + SparseArrayEntry *thisEntry;
161 + Inst *branchAddr;
162 + int withVal;
163 + int nDims, d;
164 + Boolean keyFound = False;
166 + DISASM_RT(PC-1, 4);
167 + STACKDUMP(1, 4);
169 + withVal = PC->value;
170 + PC++;
172 + PEEK_INT(nDims, 0);
174 + keyArraySym = PC->sym;
175 + PC++;
177 + if (withVal) {
178 + valSym = PC->sym;
179 + PC++;
182 + iterator = PC->sym;
183 + PC++;
184 + branchAddr = PC + PC->value;
185 + PC++;
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);
193 + else {
194 + return(execError("can't assign to: %s", keyArraySym->name));
196 + keyArrayPtr->tag = ARRAY_TAG;
197 + keyArrayPtr->val.arrayPtr = NULL;
199 + if (withVal) {
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);
206 + else {
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);
215 + else {
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 */
223 + if (nDims > 0) {
224 + int thisDim = countDim(thisEntry->key);
226 + if (nDims != thisDim) {
227 + /* try next */
228 + thisEntry = arrayIterateNext(thisEntry);
229 + continue;
233 + keyFound = True;
235 + /* set keys */
236 + if (!splitKeyArr(thisEntry->key, keyArrayPtr)) {
237 + return(execError("can't split key: %s", thisEntry->key));
240 + if (withVal) {
241 + /* set value */
242 + *valPtr = thisEntry->value;
245 + /* advance iterator */
246 + thisEntry = arrayIterateNext(thisEntry);
247 + break;
249 + iteratorValPtr->val.arrayPtr = thisEntry;
251 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
252 + PC = branchAddr;
253 + /*POP_INT(nDims);*/
256 + return(STAT_OK);
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 @@ -4219,11 +4431,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);
269 ++i;
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);
275 ++i;
277 else if (j == OP_ARRAY_ITER) {
278 if (!inst[i+1].value) {
279 @@ -4276,10 +4489,31 @@ static void disasmInternal(Inst *inst, i
280 inst[i+nDim+5].value,
281 &inst[i+nDim+5] + inst[i+nDim+5].value);
282 i += nDim+5;
285 + else if (j == OP_ARRAY_MULTI_ITER_ARRAY) {
286 + if (!inst[i+1].value) {
287 + /* without val */
288 + printd(" %s[] = %s++ end-loop=(%+d) %8p",
289 + inst[i+2].sym->name,
290 + inst[i+3].sym->name,
291 + inst[i+4].value,
292 + &inst[i+4] + inst[i+4].value);
293 + i += 4;
295 + else {
296 + /* with val */
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,
301 + inst[i+5].value,
302 + &inst[i+5] + inst[i+5].value);
303 + i += 5;
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
315 ADD_OP(OP_BRANCH);
316 ADD_BR_OFF($11+2);
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);
323 + ADD_SYM(iterSym);
324 + ADD_OP(OP_ARRAY_MULTI_ITER_ARRAY);
325 + ADD_IMMED(0); /* without val symbol */
326 + ADD_SYM($4);
327 + ADD_SYM(iterSym);
328 + ADD_BR_OFF(0);
330 + blank block {
331 + ADD_OP(OP_BRANCH);
332 + ADD_BR_OFF($10+2);
333 + SET_BR_OFF($10+6, GetPC());
334 + FillLoopAddrs(GetPC(), $10+2);
335 + ADD_OP(OP_POP);
337 + | for '(' blank SYMBOL '[' numexpropt ']' KEYVAL SYMBOL IN blank arrayexpr blank ')' {
338 + Symbol *iterSym = InstallIteratorSymbol();
339 + ADD_OP(OP_BEGIN_ARRAY_MULTI_ITER_ARRAY);
340 + ADD_SYM(iterSym);
341 + ADD_OP(OP_ARRAY_MULTI_ITER_ARRAY);
342 + ADD_IMMED(1); /* with val symbol */
343 + ADD_SYM($4);
344 + ADD_SYM($9);
345 + ADD_SYM(iterSym);
346 + ADD_BR_OFF(0);
348 + blank block {
349 + ADD_OP(OP_BRANCH);
350 + ADD_BR_OFF($12+2);
351 + SET_BR_OFF($12+7, GetPC());
352 + FillLoopAddrs(GetPC(), $12+2);
353 + ADD_OP(OP_POP);
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 {
362 ADD_OP(OP_IN_ARRAY);
363 ADD_OP(OP_NOT);
367 +numexpropt: blank {
368 + ADD_OP(OP_PUSH_IMMED);
369 + ADD_IMMED(0);
371 + | numexpr
373 keyinexpr: numexpr
374 | blank '[' arglist ']' blank {
375 /* build the index from arglist and push to stack */
376 ADD_OP(OP_ARRAY_INDEX);
377 ADD_IMMED($3);
378 diff --quilt old/doc/help.etx new/doc/help.etx
379 --- old/doc/help.etx
380 +++ 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)
385 <body>
387 + As a last iterator variant, you can use this syntax:
389 + for (keyArray[nDim] in myArray)
390 + <body>
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
402 --- old/source/ops.h
403 +++ new/source/ops.h
404 @@ -5,10 +5,11 @@
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 */