import parse-define.patch
[nedit-bw.git] / multi-dim-key-iterator.patch
bloba54157bfd088b0eed6667edc9154dd244e39a88d
1 ---
3 doc/help.etx | 15 +++
4 source/interpret.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++
5 source/interpret.h | 1
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,
27 unpackArrayToArgs,
28 typeOfIn, typeOfOut,
29 arrayIndex,
30 + arrayMultiIter,
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)
37 PC = branchAddr;
39 return(STAT_OK);
42 +static int countDim(const char *key)
44 + int nDims = 0;
45 + size_t seplen = strlen(ARRAY_DIM_SEP);
46 + const char *sep = key;
48 + do {
49 + nDims++;
50 + sep = strstr(sep, ARRAY_DIM_SEP);
51 + if (sep)
52 + sep += seplen;
53 + } while (sep);
55 + return nDims;
58 +static Boolean splitKey(const char *key, DataValue **keys)
60 + int nDims = 0;
61 + size_t seplen = strlen(ARRAY_DIM_SEP);
62 + size_t keylen;
63 + const char *sep = key;
64 + const char *nextsep = key;
66 + do {
67 + nextsep = strstr(sep, ARRAY_DIM_SEP);
68 + if (nextsep) {
69 + keylen = nextsep - sep;
70 + } else {
71 + keylen = strlen(sep);
72 + }
74 + keys[nDims]->tag = STRING_TAG;
75 + if (!AllocNStringNCpy(&keys[nDims]->val.str, sep, keylen)) {
76 + return False;
77 + }
79 + nDims++;
80 + sep = nextsep;
81 + if (sep)
82 + sep += seplen;
83 + } while (sep);
85 + return True;
88 +/*
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
93 +**
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)
98 +** Where:
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)
114 + Symbol *iterator;
115 + Symbol **itemKeys;
116 + Symbol *itemVal;
117 + DataValue *iteratorValPtr;
118 + DataValue **itemVarKeyPtrs;
119 + DataValue *itemVarValPtr;
120 + SparseArrayEntry *thisEntry;
121 + Inst *branchAddr;
122 + int withVal;
123 + int nDims, d;
124 + Boolean keyFound = False;
126 + DISASM_RT(PC-1, 4);
127 + STACKDUMP(0, 4);
129 + withVal = PC->value;
130 + PC++;
131 + nDims = PC->value;
132 + PC++;
134 + itemKeys = malloc(nDims * sizeof(*itemKeys));
135 + itemVarKeyPtrs = malloc(nDims * sizeof(*itemVarKeyPtrs));
137 + for (d = 0; d < nDims; d++) {
138 + itemKeys[d] = PC->sym;
139 + PC++;
142 + if (withVal) {
143 + itemVal = PC->sym;
144 + PC++;
147 + iterator = PC->sym;
148 + PC++;
149 + branchAddr = PC + PC->value;
150 + PC++;
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);
159 + else {
160 + const char *name = itemKeys[d]->name;
161 + free(itemKeys);
162 + free(itemVarKeyPtrs);
163 + return(execError("can't assign to: %s", name));
165 + itemVarKeyPtrs[d]->tag = NO_TAG;
168 + if (withVal) {
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);
175 + else {
176 + free(itemKeys);
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);
186 + else {
187 + free(itemKeys);
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) {
198 + /* try next */
199 + thisEntry = arrayIterateNext(thisEntry);
200 + continue;
203 + keyFound = True;
205 + /* set keys */
206 + if (!splitKey(thisEntry->key, itemVarKeyPtrs)) {
207 + free(itemKeys);
208 + free(itemVarKeyPtrs);
209 + return(execError("can't split key: %s", thisEntry->key));
212 + if (withVal) {
213 + /* set value */
214 + *itemVarValPtr = thisEntry->value;
217 + /* advance iterator */
218 + thisEntry = arrayIterateNext(thisEntry);
219 + break;
221 + iteratorValPtr->val.arrayPtr = thisEntry;
223 + if (!keyFound && (!thisEntry || thisEntry->nodePtrs.color == -1)) {
224 + PC = branchAddr;
227 + free(itemKeys);
228 + free(itemVarKeyPtrs);
230 + return(STAT_OK);
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 */
246 int i, j;
247 static size_t opLen;
249 if (!opLen) {
250 @@ -4021,10 +4215,42 @@ static void disasmInternal(Inst *inst, i
251 inst[i+5].value,
252 &inst[i+5] + inst[i+5].value);
253 i += 5;
256 + else if (j == OP_ARRAY_MULTI_ITER) {
257 + int nDim = inst[i+2].value, d;
258 + if (!inst[i+1].value) {
259 + /* without val */
260 + printd(" [");
261 + for (d = 0; d < nDim; d++) {
262 + printd("%s%s",
263 + 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);
270 + i += nDim+4;
272 + else {
273 + /* with val */
274 + printd(" [");
275 + for (d = 0; d < nDim; d++) {
276 + printd("%s%s",
277 + 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);
285 + i += nDim+5;
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,
299 OP_UNPACKTOARGS,
300 OP_TYPEOF_IN, OP_TYPEOF_OUT,
301 OP_ARRAY_INDEX,
302 + OP_ARRAY_MULTI_ITER,
303 N_OPS};
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;
322 +} SymBlk;
324 +typedef struct SymListTag {
325 + SymBlk *head, *tail;
326 + int nSyms;
327 +} SymList;
331 %union {
332 + SymList symlist;
333 Symbol *sym;
334 Inst *inst;
335 int nArgs;
336 enum operations oper;
337 AccumulatorData *acc;
338 @@ -98,10 +111,11 @@ static int nextSymIsField = 0;
339 %token <acc> DEFINE
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
343 %type <sym> evalsym
344 +%type <symlist> symlist
345 %type <oper> operassign incrdecr
346 %token <oper> '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ ANDEQ OREQ
347 %token <oper> INCR DECR
349 %nonassoc IF_NO_ELSE
350 @@ -241,10 +255,65 @@ stmt: ';' blank
351 ADD_OP(OP_BRANCH);
352 ADD_BR_OFF($9+2);
353 SET_BR_OFF($9+7, GetPC());
354 FillLoopAddrs(GetPC(), $9+2);
356 + | for '(' blank '[' symlist ']' IN blank arrayexpr blank ')' {
357 + Symbol *iterSym = InstallIteratorSymbol();
358 + int i = 0;
359 + ADD_OP(OP_BEGIN_ARRAY_ITER);
360 + ADD_SYM(iterSym);
361 + ADD_OP(OP_ARRAY_MULTI_ITER);
362 + ADD_IMMED(0); /* without val symbol */
363 + ADD_IMMED($5.nSyms);
364 + do {
365 + ADD_SYM($5.head->syms[i % SYMBLK_SIZE]);
366 + i++;
367 + if (i % SYMBLK_SIZE == 0 || i == $5.nSyms) {
368 + SymBlk *nextsymblk = $5.head->next;
369 + free($5.head);
370 + $5.head = nextsymblk;
372 + } while (i < $5.nSyms);
373 + $5.tail = $5.head;
374 + ADD_SYM(iterSym);
375 + ADD_BR_OFF(0);
377 + blank block {
378 + ADD_OP(OP_BRANCH);
379 + ADD_BR_OFF($9+2);
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();
385 + int i = 0;
386 + ADD_OP(OP_BEGIN_ARRAY_ITER);
387 + ADD_SYM(iterSym);
388 + ADD_OP(OP_ARRAY_MULTI_ITER);
389 + ADD_IMMED(1); /* with val symbol */
390 + ADD_IMMED($5.nSyms);
391 + do {
392 + ADD_SYM($5.head->syms[i % SYMBLK_SIZE]);
393 + i++;
394 + if (i % SYMBLK_SIZE == 0 || i == $5.nSyms) {
395 + SymBlk *nextsymblk = $5.head->next;
396 + free($5.head);
397 + $5.head = nextsymblk;
399 + } while (i < $5.nSyms);
400 + $5.tail = $5.head;
401 + ADD_SYM($8);
402 + ADD_SYM(iterSym);
403 + ADD_BR_OFF(0);
405 + blank block {
406 + ADD_OP(OP_BRANCH);
407 + ADD_BR_OFF($11+2);
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;
428 + $$.nSyms = 1;
430 + | symlist ',' blank SYMBOL blank {
431 + /* copy all from $1 to $$, and clear $1 */
432 + $$.head = $1.head;
433 + $$.tail = $1.tail;
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;
441 + $$.tail = symblk;
443 + $$.tail->syms[$$.nSyms % SYMBLK_SIZE] = $4;
444 + $$.nSyms++;
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
454 --- old/doc/help.etx
455 +++ 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)
462 - {..}
463 + <body>
465 + There is also a special array iterator for multi-dimensional arrays:
467 + for ([dim1Key, ..., dimNKey] in myArray)
468 + <body>
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)
476 + <body>
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"