update drag-move to v1.5-beta
[nedit-bw.git] / extend-for-key-in-array-syntax.patch
blob0db2c26482049cc72115616e12f620cf6cffa63a
1 Subject: provide key/value iterator for arrays
3 Lets you iterate over the keys and the associated values from a array, like:
5 for (key = value in array)
6 { ... }
8 ---
10 doc/help.etx | 5 ++
11 source/interpret.c | 101 ++++++++++++++++++++++++++++++++++++++---------------
12 source/ops.h | 2 -
13 source/parse.y | 75 ++++++++++++++++++++++++++++++++++++---
14 4 files changed, 148 insertions(+), 35 deletions(-)
16 diff --quilt old/source/interpret.c new/source/interpret.c
17 --- old/source/interpret.c
18 +++ new/source/interpret.c
19 @@ -3371,9 +3371,9 @@ static int arrayRefAndAssignSetup(void)
21 ** setup symbol values for array iteration in interpreter
23 -** Before: Prog-> [iter], ARRAY_ITER, iterVar, iter, endLoopBranch, next, ...
24 +** Before: Prog-> [iter], ARRAY_ITER, withVal, iterVarKey(, iterVarVal), iter, endLoopBranch, next, ...
25 ** TheStack-> [arrayVal], next, ...
26 -** After: Prog-> iter, [ARRAY_ITER], iterVar, iter, endLoopBranch, next, ...
27 +** After: Prog-> iter, [ARRAY_ITER], withVal, iterVarKey(, iterVarVal), iter, endLoopBranch, next, ...
28 ** TheStack-> [next], ...
29 ** Where:
30 ** iter is a symbol which gives the position of the iterator value in
31 @@ -3411,21 +3411,23 @@ static int beginArrayIter(void)
35 -** copy key to symbol if node is still valid, marked bad by a color of -1
36 -** then move iterator to next node
37 +** copy key and value to symbols if node is still valid, marked bad by a color
38 +** of -1 then move iterator to next node
39 ** this allows iterators to progress even if you delete any node in the array
40 ** except the item just after the current key
42 -** Before: Prog-> iter, ARRAY_ITER, [iterVar], iter, endLoopBranch, next, ...
43 +** Before: Prog-> iter, ARRAY_ITER, [withVal], iterVarKey(, iterVarVal), iter, endLoopBranch, next, ...
44 ** TheStack-> [next], ...
45 -** After: Prog-> iter, ARRAY_ITER, iterVar, iter, endLoopBranch, [next], ...
46 +** After: Prog-> iter, ARRAY_ITER, withVal, iterVarKey(, iterVarVal), iter, endLoopBranch, [next], ...
47 ** TheStack-> [next], ... (unchanged)
48 -** Where:
49 +** Where:
50 ** iter is a symbol which gives the position of the iterator value in
51 -** the stack frame (set up by BEGIN_ARRAY_ITER); that value refers
52 +** the stack frame (set up by OP_BEGIN_ARRAY_ITER); that value refers
53 ** to the array and a position within it
54 -** iterVal is the programmer-visible symbol which will take the current
55 +** iterVarKey is the programmer-visible symbol which will take the current
56 ** key value
57 +** iterVarVal is the programmer-visible symbol which will take the current
58 +** entry value (only if withVal is true)
59 ** endLoopBranch is the instruction offset to the instruction following the
60 ** loop (measured from itself)
61 ** arrayVal is the data value holding the array in question
62 @@ -3435,32 +3437,54 @@ static int beginArrayIter(void)
63 static int arrayIter(void)
65 Symbol *iterator;
66 - Symbol *item;
67 + Symbol *keySym;
68 + Symbol *valSym;
69 DataValue *iteratorValPtr;
70 - DataValue *itemValPtr;
71 + DataValue *keyValPtr;
72 + DataValue *valPtr;
73 SparseArrayEntry *thisEntry;
74 Inst *branchAddr;
75 + int withVal;
77 DISASM_RT(PC-1, 4);
78 - STACKDUMP(0, 3);
79 + STACKDUMP(0, 4);
81 - item = PC->sym;
82 + withVal = PC->value;
83 + PC++;
84 + keySym = PC->sym;
85 PC++;
86 + if (withVal) {
87 + valSym = PC->sym;
88 + PC++;
89 + }
90 iterator = PC->sym;
91 PC++;
92 branchAddr = PC + PC->value;
93 PC++;
95 - if (item->type == LOCAL_SYM) {
96 - itemValPtr = &FP_GET_SYM_VAL(FrameP, item);
97 + if (keySym->type == LOCAL_SYM) {
98 + keyValPtr = &FP_GET_SYM_VAL(FrameP, keySym);
100 - else if (item->type == GLOBAL_SYM) {
101 - itemValPtr = &(item->value);
102 + else if (keySym->type == GLOBAL_SYM) {
103 + keyValPtr = &(keySym->value);
105 else {
106 - return(execError("can't assign to: %s", item->name));
107 + return(execError("can't assign to: %s", keySym->name));
109 + keyValPtr->tag = NO_TAG;
111 + if (withVal) {
112 + if (valSym->type == LOCAL_SYM) {
113 + valPtr = &FP_GET_SYM_VAL(FrameP, valSym);
115 + else if (valSym->type == GLOBAL_SYM) {
116 + valPtr = &(valSym->value);
118 + else {
119 + return(execError("can't assign to: %s", valSym->name));
121 + valPtr->tag = NO_TAG;
123 - itemValPtr->tag = NO_TAG;
125 if (iterator->type == LOCAL_SYM) {
126 iteratorValPtr = &FP_GET_SYM_VAL(FrameP, iterator);
127 @@ -3471,10 +3495,17 @@ static int arrayIter(void)
129 thisEntry = iteratorValPtr->val.arrayPtr;
130 if (thisEntry && thisEntry->nodePtrs.color != -1) {
131 - itemValPtr->tag = STRING_TAG;
132 - itemValPtr->val.str.rep = thisEntry->key;
133 - itemValPtr->val.str.len = strlen(thisEntry->key);
135 + /* set key */
136 + keyValPtr->tag = STRING_TAG;
137 + keyValPtr->val.str.rep = thisEntry->key;
138 + keyValPtr->val.str.len = strlen(thisEntry->key);
140 + if (withVal) {
141 + /* set value */
142 + *valPtr = thisEntry->value;
145 + /* advance iterator */
146 iteratorValPtr->val.arrayPtr = arrayIterateNext(thisEntry);
148 else {
149 @@ -3989,11 +4020,25 @@ static void disasmInternal(Inst *inst, i
150 ++i;
152 else if (j == OP_ARRAY_ITER) {
153 - printd(" %s = %s++ end-loop=(%+d) %p",
154 - inst[i+1].sym->name,
155 - inst[i+2].sym->name,
156 - inst[i+3].value, &inst[i+3] + inst[i+3].value);
157 - i += 3;
158 + if (!inst[i+1].value) {
159 + /* without val */
160 + printd(" %s = %s++ end-loop=(%+d) %p",
161 + inst[i+2].sym->name,
162 + inst[i+3].sym->name,
163 + inst[i+4].value,
164 + &inst[i+4] + inst[i+4].value);
165 + i += 4;
167 + else {
168 + /* with val */
169 + printd(" %s=%s = %s++ end-loop=(%+d) %p",
170 + inst[i+2].sym->name,
171 + inst[i+3].sym->name,
172 + inst[i+4].sym->name,
173 + inst[i+5].value,
174 + &inst[i+5] + inst[i+5].value);
175 + i += 5;
178 else if (j == OP_ARRAY_REF ||
179 j == OP_ARRAY_DELETE ||
180 diff --quilt old/source/parse.y new/source/parse.y
181 --- old/source/parse.y
182 +++ new/source/parse.y
183 @@ -68,6 +68,7 @@ int yyparse(void);
184 static int follow(char expect, int yes, int no);
185 static int follow2(char expect1, int yes1, char expect2, int yes2, int no);
186 static int follow_non_whitespace(char expect, int yes, int no);
187 +static int eq_look_ahead(void);
188 static Symbol *matchesActionRoutine(char **inPtr);
189 static int scanString(void);
191 @@ -107,7 +108,7 @@ typedef struct LVinst {
192 %token <sym> STRING SYMBOL FIELD
193 %token <num> NUMBER
194 %token DELETE ARG_LOOKUP
195 -%token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN DEFINE TYPEOF
196 +%token IF WHILE DO ELSE FOR BREAK CONTINUE RETURN DEFINE TYPEOF KEYVAL
197 %type <num> arglistopt arglist catlist fnarglsopt fnarglist fnarg
198 %type <inst> cond comastmts comastmtlst for while do else and or arrayexpr mark
199 %type <sym> evalsym
200 @@ -238,14 +239,37 @@ stmt: ';' blank
202 | for '(' blank SYMBOL IN blank arrayexpr blank ')' {
203 Symbol *iterSym = InstallIteratorSymbol();
204 - ADD_OP(OP_BEGIN_ARRAY_ITER); ADD_SYM(iterSym);
205 - ADD_OP(OP_ARRAY_ITER); ADD_SYM($4); ADD_SYM(iterSym); ADD_BR_OFF(0);
206 + ADD_OP(OP_BEGIN_ARRAY_ITER);
207 + ADD_SYM(iterSym);
208 + ADD_OP(OP_ARRAY_ITER);
209 + ADD_IMMED(0); /* without val symbol */
210 + ADD_SYM($4);
211 + ADD_SYM(iterSym);
212 + ADD_BR_OFF(0);
214 blank block {
215 - ADD_OP(OP_BRANCH); ADD_BR_OFF($7+2);
216 - SET_BR_OFF($7+5, GetPC());
217 + ADD_OP(OP_BRANCH);
218 + ADD_BR_OFF($7+2);
219 + SET_BR_OFF($7+6, GetPC());
220 FillLoopAddrs(GetPC(), $7+2);
222 + | for '(' blank SYMBOL KEYVAL SYMBOL IN blank arrayexpr blank ')' {
223 + Symbol *iterSym = InstallIteratorSymbol();
224 + ADD_OP(OP_BEGIN_ARRAY_ITER);
225 + ADD_SYM(iterSym);
226 + ADD_OP(OP_ARRAY_ITER);
227 + ADD_IMMED(1); /* with val symbol */
228 + ADD_SYM($4);
229 + ADD_SYM($6);
230 + ADD_SYM(iterSym);
231 + ADD_BR_OFF(0);
233 + blank block {
234 + ADD_OP(OP_BRANCH);
235 + ADD_BR_OFF($9+2);
236 + SET_BR_OFF($9+7, GetPC());
237 + FillLoopAddrs(GetPC(), $9+2);
239 | BREAK stmtend blank {
240 ADD_OP(OP_BRANCH); ADD_BR_OFF(0);
241 if (AddBreakAddr(GetPC()-1)) {
242 @@ -901,7 +925,7 @@ static int yylex(void)
243 switch (result) {
244 case '>': return follow('=', GE, GT);
245 case '<': return follow('=', LE, LT);
246 - case '=': return follow('=', EQ, '=');
247 + case '=': return eq_look_ahead();
248 case '!': return follow('=', NE, NOT);
249 case '+': return follow2('+', INCR, '=', ADDEQ, '+');
250 case '-': return follow2('-', DECR, '=', SUBEQ, '-');
251 @@ -964,6 +988,45 @@ static int follow_non_whitespace(char ex
255 +static int eq_look_ahead(void)
257 + char *savedInPtr = InPtr;
259 + /* check "==" */
260 + if (*InPtr == '=') {
261 + ++InPtr;
262 + return EQ;
265 + /* skip any whitespace */
266 + skipWhitespace();
268 + /* now a symbol */
269 + if (!isalpha((unsigned char)*InPtr) && *InPtr != '$') {
270 + InPtr = savedInPtr;
271 + return '=';
274 + /* skip remaining symbol chars */
275 + while (isalnum((unsigned char)*InPtr) || *InPtr == '_') {
276 + ++InPtr;
279 + /* skip any whitespace */
280 + skipWhitespace();
282 + /* now "in" */
283 + if (InPtr[0] == 'i' && InPtr[1] == 'n' &&
284 + (!isalnum((unsigned char)InPtr[2]) && InPtr[2] != '_')) {
285 + InPtr = savedInPtr;
286 + return KEYVAL;
289 + /* not the '=' in a key=val for loop */
290 + InPtr = savedInPtr;
291 + return '=';
295 ** Look (way) ahead for hyphenated routine names which begin at inPtr. A
296 ** hyphenated name is allowed if it is pre-defined in the global symbol
297 diff --quilt old/doc/help.etx new/doc/help.etx
298 --- old/doc/help.etx
299 +++ new/doc/help.etx
300 @@ -2258,6 +2258,11 @@ Macro Language
301 for (aKey in x)
302 <body>
304 + Or, to get also the corresponding value for the key:
306 + for (aKey = theVal in x)
307 + <body>
309 Elements can be removed from an array using the delete command:
311 delete x[3] # deletes element with key 3
312 diff --quilt old/source/ops.h new/source/ops.h
313 --- old/source/ops.h
314 +++ new/source/ops.h
315 @@ -39,7 +39,7 @@ OP(BRANCH_NEVER, branchNever)
316 OP(ARRAY_REF, arrayRef) /* N */ /* pop(kN..k1,a), push(a[k1..kN]) */
317 OP(ARRAY_ASSIGN, arrayAssign) /* N */ /* pop(v,kN..k1,a), a[k1..kN]=v */
318 OP(BEGIN_ARRAY_ITER, beginArrayIter) /* it */ /* pop(a), it=a.begin */
319 -OP(ARRAY_ITER, arrayIter) /* k,it,pc */ /* it ? (k.v=it.k, it++) : PC = pc */
320 +OP(ARRAY_ITER, arrayIter) /*w,k[,v],it,pc*/ /* it?(k.v=it.k,(w?v.v=it.v:),it++):PC=pc */
321 OP(IN_ARRAY, inArray) /* pop(a,k), push(a[k]?1:0) */
322 OP(ARRAY_DELETE, deleteArrayElement) /*N*/ /* N>0 ? (pop(kN..k1,a), del(a[k])) : (pop(a), delall(a)) */
323 OP(PUSH_ARRAY_SYM, pushArraySymVal) /*s,i*/ /* if i: s.v=ary()), push(s.v) */