1 /* -*- c -*- */
2
3 /*
4 * builtins/flowctl.c
5 *
6 * chpp
7 *
8 * Copyright (C) 1997-1998 Mark Probst
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 #include "../memory.h"
30 #include "../list.h"
31 #include "../error.h"
32 #include "../chash.h"
33 #include "../environment.h"
34 #include "../bytecode.h"
35 #include "../jump.h"
36
37 #include "builtins.h"
38
39 void
builtInIf(int numArgs,macroArgument * args,environment * env,outputWriter * ow)40 builtInIf (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
41 {
42 value *cond;
43
44 if (!(numArgs == 2 || numArgs == 3))
45 {
46 issueError(ERRMAC_WRONG_NUM_ARGS, "if");
47 return;
48 }
49
50 cond = bcExecuteIntoValue(args[0].bytecode, env, 0);
51 if (valueBoolValue(cond))
52 bcExecute(args[1].bytecode, env, ow);
53 else if (numArgs == 3)
54 bcExecute(args[2].bytecode, env, ow);
55 }
56
57 void
builtInIfGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)58 builtInIfGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
59 environment *globalEffects)
60 {
61 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
62
63 if (numArgs == 3)
64 {
65 environment *intersection =
66 envNewIntersection((environment*)listNThElementData(globalEffectList, 1),
67 (environment*)listNThElementData(globalEffectList, 2));
68
69 envAddBindings(globalEffects, intersection);
70 }
71 }
72
73 void
builtInCond(int numArgs,macroArgument * args,environment * env,outputWriter * ow)74 builtInCond (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
75 {
76 int i;
77
78 if (!(numArgs % 2 == 0))
79 {
80 issueError(ERRMAC_WRONG_NUM_ARGS, "cond");
81 return;
82 }
83
84 for (i = 0; i < numArgs; i += 2)
85 {
86 if (valueBoolValue(bcExecuteIntoValue(args[i].bytecode, env, 0)))
87 {
88 bcExecute(args[i + 1].bytecode, env, ow);
89 return;
90 }
91 }
92 }
93
94 void
builtInCondGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)95 builtInCondGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
96 environment *globalEffects)
97 {
98 if (numArgs > 0)
99 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
100 }
101
102 void
builtInCase(int numArgs,macroArgument * args,environment * env,outputWriter * ow)103 builtInCase (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
104 {
105 dynstring string;
106 int i;
107
108 if (!(numArgs % 2 == 1))
109 {
110 issueError(ERRMAC_WRONG_NUM_ARGS, "case");
111 return;
112 }
113
114 string = bcExecuteIntoDS(args[0].bytecode, env);
115
116 for (i = 1; i < numArgs; i += 2)
117 {
118 value *theList = bcExecuteIntoValue(args[i].bytecode, env, 0);
119 int length,
120 j;
121
122 if (theList->type != VALUE_LIST)
123 {
124 if (theList->type == VALUE_SCALAR
125 && strcmp(theList->v.scalar.scalar.data, "else") == 0)
126 bcExecute(args[i + 1].bytecode, env, ow);
127 else
128 issueError(ERRMAC_VALUE_WRONG_TYPE, cStringForValueType(theList->type),
129 cStringForValueType(VALUE_LIST));
130
131 return;
132 }
133
134 length = valueListLength(theList);
135 for (j = 0; j < length; ++j)
136 {
137 value *scalar = valueNewScalarFromValue(valueListGetElement(theList, j));
138
139 if (strcmp(string.data, scalar->v.scalar.scalar.data) == 0)
140 {
141 bcExecute(args[i + 1].bytecode, env, ow);
142 return;
143 }
144 }
145 }
146 }
147
148 void
builtInCaseGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)149 builtInCaseGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
150 environment *globalEffects)
151 {
152 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
153 if (numArgs > 1)
154 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 1));
155 }
156
157 void
builtInFor(int numArgs,macroArgument * args,environment * env,outputWriter * ow)158 builtInFor (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
159 {
160 dynstring counter,
161 ds;
162 int start,
163 stop,
164 increment = 1,
165 i;
166
167 if (!(numArgs == 4 || numArgs == 5))
168 {
169 issueError(ERRMAC_WRONG_NUM_ARGS, "for");
170 return;
171 }
172
173 ds = bcExecuteIntoDS(args[1].bytecode, env);
174 start = atoi(ds.data);
175
176 ds = bcExecuteIntoDS(args[2].bytecode, env);
177 stop = atoi(ds.data);
178
179 if (numArgs == 5)
180 {
181 ds = bcExecuteIntoDS(args[3].bytecode, env);
182 increment = atoi(ds.data);
183
184 if (increment == 0)
185 {
186 issueError(ERRMAC_NULL_INCREMENT);
187 return;
188 }
189 }
190 else
191 if (start <= stop)
192 increment = 1;
193 else
194 increment = -1;
195
196 i = start;
197 counter = bcExecuteIntoDS(args[0].bytecode, env);
198
199 while ((increment > 0 && i <= stop) || (increment < 0 && i >= stop))
200 {
201 char numberString[64];
202 environment *newEnv = envNew(env);
203
204 sprintf(numberString, "%d", i);
205 envAddBinding(newEnv, &counter, valueNewScalarFromCString(numberString));
206
207 DO_JUMP_CODE {
208 bcExecute(args[numArgs - 1].bytecode, newEnv, ow);
209 } WITH_JUMP_HANDLER {
210 JUMP_HANDLE_RETURN;
211
212 if (JUMP_CODE == JUMP_CODE_BREAK)
213 {
214 if (JUMP_INFO == 0)
215 return;
216 else
217 JUMP(JUMP_CODE_BREAK | (JUMP_INFO - 1));
218 }
219 } END_JUMP_HANDLER;
220
221 /* i = atoi(entry->value->v.scalar.scalar.data); */
222 i += increment;
223 }
224 }
225
226 environment*
builtInForEnvironmentor(int numArgs,bcArgument * args,environment * parentEnv)227 builtInForEnvironmentor (int numArgs, bcArgument *args, environment *parentEnv)
228 {
229 if (numArgs < 3)
230 return parentEnv;
231
232 if (bcIsString(args->bc))
233 {
234 dynstring name = bcExecuteIntoDS(args->bc, 0);
235 environment *env = envNew(parentEnv);
236
237 envModifyOrAddBinding(env, &name, 0, env);
238
239 return env;
240 }
241
242 return parentEnv;
243 }
244
245 void
builtInForeach(int numArgs,macroArgument * args,environment * env,outputWriter * ow)246 builtInForeach (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
247 {
248 value *list;
249
250 if (!(numArgs == 3))
251 {
252 issueError(ERRMAC_WRONG_NUM_ARGS, "foreach");
253 return;
254 }
255
256 list = bcExecuteIntoCopiedValue(args[1].bytecode, env);
257 if (valueListLength(list) > 0)
258 {
259 int i;
260 dynstring counter = bcExecuteIntoDS(args[0].bytecode, env);
261
262 for (i = 0; i < valueListLength(list); ++i)
263 {
264 environment *newEnv = envNew(env);
265
266 envAddBinding(newEnv, &counter, valueListGetElement(list, i));
267
268 bcExecute(args[2].bytecode, newEnv, ow);
269 }
270 }
271 }
272
273 environment*
builtInForeachEnvironmentor(int numArgs,bcArgument * args,environment * parentEnv)274 builtInForeachEnvironmentor (int numArgs, bcArgument *args, environment *parentEnv)
275 {
276 if (numArgs < 2)
277 return parentEnv;
278
279 if (bcIsString(args->bc))
280 {
281 dynstring name = bcExecuteIntoDS(args->bc, 0);
282 environment *env = envNew(parentEnv);
283
284 envModifyOrAddBinding(env, &name, 0, env);
285
286 return env;
287 }
288
289 return parentEnv;
290 }
291
292 void
builtInForeachkey(int numArgs,macroArgument * args,environment * env,outputWriter * ow)293 builtInForeachkey (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
294 {
295 value *hash;
296 dynstring counter;
297 int count,
298 i;
299 hstate state;
300
301 if (!(numArgs == 3))
302 {
303 issueError(ERRMAC_WRONG_NUM_ARGS, "foreachkey");
304 return;
305 }
306
307 hash = bcExecuteIntoCopiedValue(args[1].bytecode, env);
308 if (hash->type != VALUE_HASH)
309 {
310 issueError(ERRMAC_VALUE_WRONG_TYPE,
311 cStringForValueType(hash->type),
312 cStringForValueType(VALUE_HASH));
313 return;
314 }
315 counter = bcExecuteIntoDS(args[0].bytecode, env);
316
317 count = hash_count(hash->v.hash.hash);
318 state = hash_state(hash->v.hash.hash);
319 for (i = 0; i < count; ++i)
320 {
321 environment *newEnv = envNew(env);
322 char *key;
323
324 hash_next(&state, &key);
325 envAddBinding(newEnv, &counter, valueNewScalarFromCString(key));
326
327 bcExecute(args[2].bytecode, newEnv, ow);
328 }
329 }
330
331 environment*
builtInForeachkeyEnvironmentor(int numArgs,bcArgument * args,environment * parentEnv)332 builtInForeachkeyEnvironmentor (int numArgs, bcArgument *args, environment *parentEnv)
333 {
334 if (numArgs < 2)
335 return parentEnv;
336
337 if (bcIsString(args->bc))
338 {
339 dynstring name = bcExecuteIntoDS(args->bc, 0);
340 environment *env = envNew(parentEnv);
341
342 envModifyOrAddBinding(env, &name, 0, env);
343
344 return env;
345 }
346
347 return parentEnv;
348 }
349
350 void
builtInWhile(int numArgs,macroArgument * args,environment * env,outputWriter * ow)351 builtInWhile (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
352 {
353 if (!(numArgs == 2))
354 {
355 issueError(ERRMAC_WRONG_NUM_ARGS, "while");
356 return;
357 }
358
359 while (valueBoolValue(bcExecuteIntoValue(args[0].bytecode, env, 0)))
360 bcExecute(args[1].bytecode, env, ow);
361 }
362
363 void
builtInWhileGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)364 builtInWhileGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
365 environment *globalEffects)
366 {
367 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
368 }
369
370 void
builtInUntil(int numArgs,macroArgument * args,environment * env,outputWriter * ow)371 builtInUntil (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
372 {
373 if (!(numArgs == 2))
374 {
375 issueError(ERRMAC_WRONG_NUM_ARGS, "until");
376 return;
377 }
378
379 while (!valueBoolValue(bcExecuteIntoValue(args[0].bytecode, env, 0)))
380 bcExecute(args[1].bytecode, env, ow);
381 }
382
383 void
builtInUntilGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)384 builtInUntilGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
385 environment *globalEffects)
386 {
387 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
388 }
389
390 void
builtInDowhile(int numArgs,macroArgument * args,environment * env,outputWriter * ow)391 builtInDowhile (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
392 {
393 if (!(numArgs == 2))
394 {
395 issueError(ERRMAC_WRONG_NUM_ARGS, "dowhile");
396 return;
397 }
398
399 do
400 {
401 bcExecute(args[0].bytecode, env, ow);
402 } while (valueBoolValue(bcExecuteIntoValue(args[1].bytecode, env, 0)));
403 }
404
405 void
builtInDowhileGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)406 builtInDowhileGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
407 environment *globalEffects)
408 {
409 environment *unionEnv =
410 envNewUnion((environment*)listNThElementData(globalEffectList, 0),
411 (environment*)listNThElementData(globalEffectList, 1));
412
413 envAddBindings(globalEffects, unionEnv);
414 }
415
416 void
builtInDountil(int numArgs,macroArgument * args,environment * env,outputWriter * ow)417 builtInDountil (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
418 {
419 if (!(numArgs == 2))
420 {
421 issueError(ERRMAC_WRONG_NUM_ARGS, "dountil");
422 return;
423 }
424
425 do
426 {
427 bcExecute(args[0].bytecode, env, ow);
428 } while (!valueBoolValue(bcExecuteIntoValue(args[1].bytecode, env, 0)));
429 }
430
431 void
builtInDountilGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)432 builtInDountilGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
433 environment *globalEffects)
434 {
435 environment *unionEnv =
436 envNewUnion((environment*)listNThElementData(globalEffectList, 0),
437 (environment*)listNThElementData(globalEffectList, 1));
438
439 envAddBindings(globalEffects, unionEnv);
440 }
441
442 void
builtInAnd(int numArgs,macroArgument * args,environment * env,outputWriter * ow)443 builtInAnd (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
444 {
445 int i;
446
447 for (i = 0; i < numArgs; ++i)
448 if (!valueBoolValue(bcExecuteIntoValue(args[i].bytecode, env, 0)))
449 {
450 OUT_CHAR(ow, '0');
451 return;
452 }
453
454 OUT_CHAR(ow, '1');
455 }
456
457 void
builtInAndGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)458 builtInAndGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
459 environment *globalEffects)
460 {
461 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
462 }
463
464 void
builtInOr(int numArgs,macroArgument * args,environment * env,outputWriter * ow)465 builtInOr (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
466 {
467 int i;
468
469 for (i = 0; i < numArgs; ++i)
470 if (valueBoolValue(bcExecuteIntoValue(args[i].bytecode, env, 0)))
471 {
472 OUT_CHAR(ow, '1');
473 return;
474 }
475
476 OUT_CHAR(ow, '0');
477 }
478
479 void
builtInOrGlobalEffector(int numArgs,bcArgument * args,list * globalEffectList,environment * globalEffects)480 builtInOrGlobalEffector (int numArgs, bcArgument *args, list *globalEffectList,
481 environment *globalEffects)
482 {
483 envAddBindings(globalEffects, (environment*)listNThElementData(globalEffectList, 0));
484 }
485
486 void
builtInNot(int numArgs,macroArgument * args,environment * env,outputWriter * ow)487 builtInNot (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
488 {
489 if (!numArgs == 1)
490 {
491 issueError(ERRMAC_WRONG_NUM_ARGS, "not");
492 return;
493 }
494
495 if (valueBoolValue(args[0].value.value))
496 {
497 OUT_CHAR(ow, '0');
498 }
499 else
500 {
501 OUT_CHAR(ow, '1');
502 }
503 }
504
505 void
builtInReturn(int numArgs,macroArgument * args,environment * env,outputWriter * ow)506 builtInReturn (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
507 {
508 if (!(numArgs == 0))
509 {
510 issueError(ERRMAC_WRONG_NUM_ARGS, "return");
511 return;
512 }
513
514 JUMP(JUMP_CODE_RETURN);
515 }
516
517 void
builtInBreak(int numArgs,macroArgument * args,environment * env,outputWriter * ow)518 builtInBreak (int numArgs, macroArgument *args, environment *env, outputWriter *ow)
519 {
520 int depth;
521
522 if (!(numArgs == 0 || numArgs == 1))
523 {
524 issueError(ERRMAC_WRONG_NUM_ARGS, "break");
525 return;
526 }
527
528 if (numArgs == 1)
529 {
530 transformArgumentToScalar(&args[0]);
531 depth = atoi(args[0].value.value->v.scalar.scalar.data);
532 if (depth < 0)
533 {
534 issueError(ERRMAC_INVALID_MACRO_ARG, "break",
535 args[0].value.value->v.scalar.scalar.data);
536 return;
537 }
538 }
539 else
540 depth = 0;
541
542 JUMP(JUMP_CODE_BREAK | depth);
543 }
544
545 void
registerFlowCtl(void)546 registerFlowCtl (void)
547 {
548 registerBuiltIn("if", builtInIf, 0, builtInIfGlobalEffector, 0);
549 registerBuiltIn("cond", builtInCond, 0, builtInCondGlobalEffector, 0);
550 registerBuiltIn("case", builtInCase, 0, 0, 0);
551 registerBuiltIn("for", builtInFor, 0, 0, builtInForEnvironmentor);
552 registerBuiltIn("foreach", builtInForeach, 0, 0, builtInForeachEnvironmentor);
553 registerBuiltIn("foreachkey", builtInForeachkey, 0, 0, builtInForeachkeyEnvironmentor);
554 registerBuiltIn("while", builtInWhile, 0, builtInWhileGlobalEffector, 0);
555 registerBuiltIn("until", builtInUntil, 0, builtInUntilGlobalEffector, 0);
556 registerBuiltIn("dowhile", builtInDowhile, 0, builtInDowhileGlobalEffector, 0);
557 registerBuiltIn("dountil", builtInDountil, 0, builtInDountilGlobalEffector, 0);
558 registerBuiltIn("and", builtInAnd, 0, builtInAndGlobalEffector, 0);
559 registerBuiltIn("or", builtInOr, 0, builtInOrGlobalEffector, 0);
560 registerBuiltIn("not", builtInNot, 1, 0, 0);
561 registerBuiltIn("return", builtInReturn, 1, 0, 0);
562 registerBuiltIn("break", builtInBreak, 1, 0, 0);
563 }
564