added WINDOZE=1 understanding to stop myself from copypasting mingw cross-build lines
[k8jam.git] / compile.c
blobec7a2031f97f4b08a92f600d480e7db8bed6b8c0
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
7 /*
8 * compile.c - compile parsed jam statements
10 * External routines:
12 * compile_append() - append list results of two statements
13 * compile_break() - compile 'break/continue/return' rule
14 * compile_eval() - evaluate if to determine which leg to compile
15 * compile_foreach() - compile the "for x in y" statement
16 * compile_if() - compile 'if' rule
17 * compile_include() - support for 'include' - call include() on file
18 * compile_list() - expand and return a list
19 * compile_local() - declare (and set) local variables
20 * compile_null() - do nothing -- a stub for parsing
21 * compile_on() - run rule under influence of on-target variables
22 * compile_rule() - compile a single user defined rule
23 * compile_rules() - compile a chain of rules
24 * compile_set() - compile the "set variable" statement
25 * compile_setcomp() - support for `rule` - save parse tree
26 * compile_setexec() - support for `actions` - save execution string
27 * compile_settings() - compile the "on =" (set variable on exec) statement
28 * compile_switch() - compile 'switch' rule
30 * Internal routines:
32 * debug_compile() - printf with indent to show rule expansion.
33 * evaluate_rule() - execute a rule invocation
35 * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of
36 * the awkward sounding "settings".
37 * 04/12/94 (seiwald) - Combined build_depends() with build_includes().
38 * 04/12/94 (seiwald) - actionlist() now just appends a single action.
39 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
40 * 05/13/94 (seiwald) - include files are now bound as targets, and thus
41 * can make use of $(SEARCH)
42 * 06/01/94 (seiwald) - new 'actions existing' does existing sources
43 * 08/23/94 (seiwald) - Support for '+=' (append to variable)
44 * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
45 * 01/22/95 (seiwald) - Exit rule.
46 * 02/02/95 (seiwald) - Always rule; LEAVES rule.
47 * 02/14/95 (seiwald) - NoUpdate rule.
48 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
49 * 09/07/00 (seiwald) - stop crashing when a rule redefines itself
50 * 09/11/00 (seiwald) - new evaluate_rule() for headers().
51 * 09/11/00 (seiwald) - rules now return values, accessed via [ rule arg ... ]
52 * 09/12/00 (seiwald) - don't complain about rules invoked without targets
53 * 01/13/01 (seiwald) - fix case where rule is defined within another
54 * 01/10/01 (seiwald) - built-ins split out to builtin.c.
55 * 01/11/01 (seiwald) - optimize compile_rules() for tail recursion
56 * 01/21/01 (seiwald) - replace evaluate_if() with compile_eval()
57 * 01/24/01 (seiwald) - 'while' statement
58 * 03/23/01 (seiwald) - "[ on target rule ]" support
59 * 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
60 * 03/02/02 (seiwald) - rules can be invoked via variable names
61 * 03/12/02 (seiwald) - &&,&,||,|,in now short-circuit again
62 * 03/25/02 (seiwald) - if ( "" a b ) one again returns true
63 * 06/21/02 (seiwald) - support for named parameters
64 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
65 * 10/22/02 (seiwald) - working return/break/continue statements
66 * 11/04/02 (seiwald) - const-ing for string literals
67 * 11/18/02 (seiwald) - remove bogus search() in 'on' statement.
68 * 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
71 #include "jam.h"
73 #include "lists.h"
74 #include "parse.h"
75 #include "compile.h"
76 #include "variable.h"
77 #include "expand.h"
78 #include "rules.h"
79 #include "newstr.h"
80 #include "search.h"
83 static const char *set_names[] = { "=", "+=", "?=" };
84 static void debug_compile (int which, const char *s);
85 int glob (const char *s, const char *c);
89 * compile_append() - append list results of two statements
91 * parse->left more compile_append() by left-recursion
92 * parse->right single rule
94 LIST *compile_append (PARSE *parse, LOL *args, int *jmp) {
95 /* Append right to left. */
96 return list_append(
97 (*parse->left->func)(parse->left, args, jmp),
98 (*parse->right->func)(parse->right, args, jmp));
103 * compile_break() - compile 'break/continue/return' rule
105 * parse->left results
106 * parse->num JMP_BREAK/CONTINUE/RETURN
108 LIST *compile_break (PARSE *parse, LOL *args, int *jmp) {
109 LIST *lv = (*parse->left->func)(parse->left, args, jmp);
110 *jmp = parse->num;
111 return lv;
116 * compile_eval() - evaluate if to determine which leg to compile
118 * Returns:
119 * list if expression true - compile 'then' clause
120 * L0 if expression false - compile 'else' clause
122 static int lcmp (LIST *t, LIST *s) {
123 int status = 0;
125 while (!status && (t || s)) {
126 const char *st = t?t->string:"";
127 const char *ss = s?s->string:"";
129 status = strcmp(st, ss);
130 t = t?list_next(t):t;
131 s = s?list_next(s):s;
134 return status;
138 LIST *compile_eval (PARSE *parse, LOL *args, int *jmp) {
139 LIST *ll, *lr, *s, *t;
140 int status = 0;
142 /* Short circuit lr eval for &&, ||, and 'in' */
143 ll = (*parse->left->func)(parse->left, args, jmp);
144 lr = 0;
146 switch (parse->num) {
147 case EXPR_AND: case EXPR_IN:
148 if (ll) goto eval;
149 break;
150 case EXPR_OR:
151 if (!ll) goto eval;
152 break;
153 default:
154 eval:
155 lr = (*parse->right->func)(parse->right, args, jmp);
158 /* Now eval */
159 switch ( parse->num) {
160 case EXPR_NOT:
161 if (!ll) status = 1;
162 break;
163 case EXPR_AND:
164 if (ll && lr) status = 1;
165 break;
166 case EXPR_OR:
167 if (ll || lr) status = 1;
168 break;
169 case EXPR_IN:
170 /* "a in b": make sure each of */
171 /* ll is equal to something in lr. */
172 for (t = ll; t; t = list_next(t)) {
173 for (s = lr; s; s = list_next(s)) if (!strcmp(t->string, s->string)) break;
174 if (!s) break;
176 /* No more ll? Success */
177 if (!t) status = 1;
178 break;
179 case EXPR_EXISTS:
180 if (lcmp(ll, L0) != 0) status = 1;
181 break;
182 case EXPR_EQUALS:
183 if (lcmp(ll, lr) == 0) status = 1;
184 break;
185 case EXPR_NOTEQ:
186 if (lcmp(ll, lr) != 0) status = 1;
187 break;
188 case EXPR_LESS:
189 if (lcmp(ll, lr) < 0) status = 1;
190 break;
191 case EXPR_LESSEQ:
192 if (lcmp(ll, lr) <= 0) status = 1;
193 break;
194 case EXPR_MORE:
195 if (lcmp(ll, lr) > 0) status = 1;
196 break;
197 case EXPR_MOREEQ:
198 if (lcmp(ll, lr) >= 0) status = 1;
199 break;
202 if (DEBUG_IF) {
203 debug_compile(0, "if");
204 list_print(ll);
205 printf("(%d) ", status);
206 list_print(lr);
207 printf("\n");
210 /* Find something to return. */
211 /* In odd circumstances (like "" = "") */
212 /* we'll have to return a new string. */
213 if (!status) t = 0;
214 else if (ll) t = ll, ll = 0;
215 else if (lr) t = lr, lr = 0;
216 else t = list_new(L0, "1", 0);
218 if (ll) list_free(ll);
219 if (lr) list_free(lr);
221 return t;
226 * compile_foreach() - compile the "for x in y" statement
228 * Compile_foreach() resets the given variable name to each specified
229 * value, executing the commands enclosed in braces for each iteration.
231 * parse->string index variable
232 * parse->left variable values
233 * parse->right rule to compile
235 LIST *compile_foreach (PARSE *p, LOL *args, int *jmp) {
236 LIST *nv = (*p->left->func)(p->left, args, jmp);
237 LIST *result = 0;
238 LIST *l;
240 /* for each value for var */
241 for (l = nv; l && *jmp == JMP_NONE; l = list_next(l)) {
242 /* Reset $(p->string) for each val. */
243 var_set(p->string, list_new(L0, l->string, 1), VAR_SET);
244 /* Keep only last result. */
245 list_free(result);
246 result = (*p->right->func)(p->right, args, jmp);
247 /* continue loop? */
248 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
250 /* Here by break/continue? */
251 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
252 list_free(nv);
253 /* Returns result of last loop */
254 return result;
259 * compile_if() - compile 'if' rule
261 * parse->left condition tree
262 * parse->right then tree
263 * parse->third else tree
265 LIST *compile_if (PARSE *p, LOL *args, int *jmp) {
266 LIST *l = (*p->left->func)(p->left, args, jmp);
267 p = l?p->right:p->third;
268 list_free(l);
269 return (*p->func)(p, args, jmp);
274 * compile_include() - support for 'include' - call include() on file
276 * parse->left list of files to include (can only do 1)
278 LIST *compile_include (PARSE *parse, LOL *args, int *jmp) {
279 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
280 if (DEBUG_COMPILE) {
281 debug_compile(0, "include");
282 list_print(nt);
283 printf("\n");
285 if (nt) {
286 TARGET *t = bindtarget(nt->string);
287 /* Bind the include file under the influence of */
288 /* "on-target" variables. Though they are targets, */
289 /* include files are not built with make(). */
290 /* Needn't copysettings(), as search sets no vars. */
291 pushsettings(t->settings);
292 t->boundname = search(t->name, &t->time);
293 popsettings(t->settings);
294 /* Don't parse missing file if NOCARE set */
295 if (t->time || !(t->flags & T_FLAG_NOCARE)) parse_file(t->boundname);
297 list_free(nt);
298 return L0;
303 * compile_list() - expand and return a list
305 * parse->string - character string to expand
307 LIST *compile_list (PARSE *parse, LOL *args, int *jmp) {
308 /* voodoo 1 means: s is a copyable string */
309 const char *s = parse->string;
310 return var_expand(L0, s, s+strlen(s), args, 1);
315 * compile_local() - declare (and set) local variables
317 * parse->left list of variables
318 * parse->right list of values
319 * parse->third rules to execute
321 LIST *compile_local (PARSE *parse, LOL *args, int *jmp) {
322 LIST *l;
323 SETTINGS *s = 0;
324 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
325 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
326 LIST *result;
328 if (DEBUG_COMPILE) {
329 debug_compile(0, "local");
330 list_print(nt);
331 printf(" = ");
332 list_print(ns);
333 printf("\n");
336 /* Initial value is ns */
337 for (l = nt; l; l = list_next(l)) s = addsettings(s, 0, l->string, list_copy((LIST *)0, ns));
338 list_free(ns);
339 list_free(nt);
340 /* Note that callees of the current context get this "local" */
341 /* variable, making it not so much local as layered. */
342 pushsettings(s);
343 result = (*parse->third->func)(parse->third, args, jmp);
344 popsettings(s);
345 freesettings(s);
347 return result;
352 * compile_null() - do nothing -- a stub for parsing
354 LIST *compile_null (PARSE *parse, LOL *args, int *jmp) {
355 return L0;
360 * compile_on() - run rule under influence of on-target variables
362 * parse->left target list; only first used
363 * parse->right rule to run
365 LIST *compile_on (PARSE *parse, LOL *args, int *jmp) {
366 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
367 LIST *result = 0;
369 if (DEBUG_COMPILE) {
370 debug_compile(0, "on");
371 list_print(nt);
372 printf("\n");
376 * Copy settings, so that 'on target var on target = val'
377 * doesn't set var globally.
379 if (nt) {
380 TARGET *t = bindtarget(nt->string);
381 SETTINGS *s = copysettings(t->settings);
383 pushsettings(s);
384 result = (*parse->right->func)(parse->right, args, jmp);
385 popsettings(s);
386 freesettings(s);
388 list_free(nt);
390 return result;
395 * compile_rule() - compile a single user defined rule
397 * parse->left list of rules to run
398 * parse->right parameters (list of lists) to rule, recursing left
400 * Wrapped around evaluate_rule() so that headers() can share it.
402 LIST *compile_rule (PARSE *parse, LOL *args, int *jmp) {
403 LOL nargs[1];
404 LIST *result = 0;
405 LIST *ll, *l;
406 PARSE *p;
408 /* list of rules to run -- normally 1! */
409 ll = (*parse->left->func)(parse->left, args, jmp);
410 /* Build up the list of arg lists */
411 lol_init(nargs);
412 for (p = parse->right; p; p = p->left) lol_add(nargs, (*p->right->func)(p->right, args, jmp));
413 /* Run rules, appending results from each */
414 for (l = ll; l; l = list_next(l)) result = evaluate_rule(l->string, nargs, result);
415 list_free(ll);
416 lol_free(nargs);
418 return result;
423 * evaluate_rule() - execute a rule invocation
425 LIST *evaluate_rule (const char *rulename, LOL *args, LIST *result) {
426 RULE *rule = bindrule(rulename);
428 if (DEBUG_COMPILE) {
429 debug_compile(1, rulename);
430 lol_print(args);
431 printf("\n");
434 /* Check traditional targets $(<) and sources $(>) */
435 if (!rule->actions && !rule->procedure) printf("warning: unknown rule %s\n", rule->name);
436 /* If this rule will be executed for updating the targets */
437 /* then construct the action for make(). */
438 if (rule->actions) {
439 TARGETS *t;
440 ACTION *action;
442 /* The action is associated with this instance of this rule */
443 action = (ACTION *)malloc(sizeof(ACTION));
444 memset((char *)action, '\0', sizeof(*action));
445 action->rule = rule;
446 action->targets = targetlist((TARGETS *)0, lol_get(args, 0));
447 action->sources = targetlist((TARGETS *)0, lol_get(args, 1));
449 /* Append this action to the actions of each target */
450 for (t = action->targets; t; t = t->next) t->target->actions = actionlist(t->target->actions, action);
452 /* Now recursively compile any parse tree associated with this rule */
453 if (rule->procedure) {
454 PARSE *parse = rule->procedure;
455 SETTINGS *s = 0;
456 int jmp = JMP_NONE;
457 LIST *l;
458 int i;
460 /* build parameters as local vars */
461 for (l = rule->params, i = 0; l; l = l->next, i++) s = addsettings(s, 0, l->string, list_copy(L0, lol_get(args, i)));
462 /* Run rule. */
463 /* Bring in local params. */
464 /* refer/free to ensure rule not freed during use. */
465 parse_refer(parse);
466 pushsettings(s);
467 result = list_append(result, (*parse->func)(parse, args, &jmp));
468 popsettings(s);
469 freesettings(s);
470 parse_free(parse);
473 if (DEBUG_COMPILE) debug_compile(-1, 0);
475 return result;
480 * compile_rules() - compile a chain of rules
482 * parse->left single rule
483 * parse->right more compile_rules() by right-recursion
485 LIST *compile_rules (PARSE *parse, LOL *args, int *jmp) {
486 /* Ignore result from first statement; return the 2nd. */
487 /* Optimize recursion on the right by looping. */
488 LIST *result = 0;
490 while (*jmp == JMP_NONE && parse->func == compile_rules) {
491 list_free(result);
492 result = (*parse->left->func)(parse->left, args, jmp);
493 parse = parse->right;
495 if (*jmp == JMP_NONE) {
496 list_free(result);
497 result = (*parse->func)(parse, args, jmp);
500 return result;
505 * compile_set() - compile the "set variable" statement
507 * parse->left variable names
508 * parse->right variable values
509 * parse->num VAR_SET/APPEND/DEFAULT
511 LIST *compile_set (PARSE *parse, LOL *args, int *jmp) {
512 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
513 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
514 LIST *l;
516 if (DEBUG_COMPILE) {
517 debug_compile(0, "set");
518 list_print(nt);
519 printf(" %s ", set_names[parse->num]);
520 list_print(ns);
521 printf("\n");
523 /* Call var_set to set variable */
524 /* var_set keeps ns, so need to copy it */
525 for (l = nt; l; l = list_next(l)) var_set(l->string, list_copy(L0, ns), parse->num);
526 list_free(nt);
528 return ns;
533 * compile_setcomp() - support for `rule` - save parse tree
535 * parse->string rule name
536 * parse->left list of argument names
537 * parse->right rules for rule
539 LIST *compile_setcomp (PARSE *parse, LOL *args, int *jmp) {
540 RULE *rule = bindrule(parse->string);
541 LIST *params = 0;
542 PARSE *p;
544 /* Build param list */
545 for (p = parse->left; p; p = p->left) params = list_new(params, p->string, 1);
547 if (DEBUG_COMPILE) {
548 debug_compile(0, "rule");
549 printf("%s ", parse->string);
550 list_print(params);
551 printf("\n");
554 /* Free old one, if present */
555 if (rule->procedure) parse_free(rule->procedure);
556 if (rule->params) list_free(rule->params);
558 rule->procedure = parse->right;
559 rule->params = params;
561 /* we now own this parse tree */
562 /* don't let parse_free() release it */
563 parse_refer(parse->right);
565 return L0;
570 * compile_setexec() - support for `actions` - save execution string
572 * parse->string rule name
573 * parse->string1 OS command string
574 * parse->num flags
575 * parse->left `bind` variables
577 * Note that the parse flags (as defined in compile.h) are transfered
578 * directly to the rule flags (as defined in rules.h).
580 LIST *compile_setexec (PARSE *parse, LOL *args, int *jmp) {
581 RULE *rule = bindrule(parse->string);
582 LIST *bindlist = (*parse->left->func)(parse->left, args, jmp);
584 /* Free old one, if present */
585 if (rule->actions) {
586 freestr(rule->actions);
587 list_free(rule->bindlist);
590 rule->actions = copystr(parse->string1);
591 rule->bindlist = bindlist;
592 rule->flags = parse->num;
594 return L0;
599 * compile_settings() - compile the "on =" (set variable on exec) statement
601 * parse->left variable names
602 * parse->right target name
603 * parse->third variable value
604 * parse->num VAR_SET/APPEND/DEFAULT
606 LIST *compile_settings (PARSE *parse, LOL *args, int *jmp) {
607 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
608 LIST *ns = (*parse->third->func)(parse->third, args, jmp);
609 LIST *targets = (*parse->right->func)(parse->right, args, jmp);
610 LIST *ts;
612 if (DEBUG_COMPILE) {
613 debug_compile(0, "set");
614 list_print(nt);
615 printf("on ");
616 list_print(targets);
617 printf(" %s ", set_names[parse->num]);
618 list_print(ns);
619 printf("\n");
622 /* Call addsettings to save variable setting */
623 /* addsettings keeps ns, so need to copy it */
624 /* Pass append flag to addsettings() */
625 for (ts = targets; ts; ts = list_next(ts)) {
626 TARGET *t = bindtarget(ts->string);
627 LIST *l;
629 for (l = nt; l; l = list_next(l))
630 t->settings = addsettings(t->settings, parse->num, l->string, list_copy((LIST *)0, ns));
633 list_free(nt);
634 list_free(targets);
636 return ns;
641 * compile_switch() - compile 'switch' rule
643 * parse->left switch value (only 1st used)
644 * parse->right cases
646 * cases->left 1st case
647 * cases->right next cases
649 * case->string argument to match
650 * case->left parse tree to execute
652 LIST *compile_switch (PARSE *parse, LOL *args, int *jmp) {
653 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
654 LIST *result = 0;
656 if (DEBUG_COMPILE) {
657 debug_compile(0, "switch");
658 list_print(nt);
659 printf("\n");
661 /* Step through cases */
662 for (parse = parse->right; parse; parse = parse->right) {
663 if (!glob( parse->left->string, nt?nt->string:"")) {
664 /* Get & exec parse tree for this case */
665 parse = parse->left->left;
666 result = (*parse->func)(parse, args, jmp);
667 break;
670 list_free(nt);
672 return result;
677 * compile_while() - compile 'while' rule
679 * parse->left condition tree
680 * parse->right execution tree
682 LIST *compile_while (PARSE *p, LOL *args, int *jmp) {
683 LIST *result = 0;
684 LIST *l;
686 /* Returns the value from the last execution of the block */
687 while ((*jmp == JMP_NONE ) && (l = (*p->left->func)(p->left, args, jmp))) {
688 /* Always toss while's expression */
689 list_free(l);
690 /* Keep only last result. */
691 list_free(result);
692 result = (*p->right->func)(p->right, args, jmp);
693 /* continue loop? */
694 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
696 /* Here by break/continue? */
697 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
698 /* Returns result of last loop */
700 return result;
705 * debug_compile() - printf with indent to show rule expansion.
708 static void debug_compile (int which, const char *s) {
709 static int level = 0;
710 static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
711 int i = ((1+level)*2)%35;
713 if (which >= 0) printf("%*.*s ", i, i, indent);
714 if (s) printf("%s ", s);
715 level += which;