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