pathsys: cosmetix
[k8jam.git] / src / compile.c
blobaf0151042d7a59747d535e92ceee14336af1db3e
1 /*
2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
3 * This file is part of Jam - see jam.c for Copyright information.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * compile.c - compile parsed jam statements
21 * External routines:
23 * compile_append() - append list results of two statements
24 * compile_break() - compile 'break/continue/return' rule
25 * compile_eval() - evaluate if to determine which leg to compile
26 * compile_foreach() - compile the "for x in y" statement
27 * compile_if() - compile 'if' rule
28 * compile_include() - support for 'include' - call include() on file
29 * compile_list() - expand and return a list
30 * compile_local() - declare (and set) local variables
31 * compile_null() - do nothing -- a stub for parsing
32 * compile_on() - run rule under influence of on-target variables
33 * compile_rule() - compile a single user defined rule
34 * compile_rules() - compile a chain of rules
35 * compile_set() - compile the "set variable" statement
36 * compile_setcomp() - support for `rule` - save parse tree
37 * compile_setexec() - support for `actions` - save execution string
38 * compile_settings() - compile the "on =" (set variable on exec) statement
39 * compile_switch() - compile 'switch' rule
41 * Internal routines:
43 * debug_compile() - printf with indent to show rule expansion.
44 * evaluate_rule() - execute a rule invocation
46 #include "jam.h"
48 #include "lists.h"
49 #include "parse.h"
50 #include "compile.h"
51 #include "variable.h"
52 #include "expand.h"
53 #include "rules.h"
54 #include "newstr.h"
55 #include "search.h"
56 #include "matchglob.h"
57 #include "filesys.h"
60 static const char *set_names[] = { "=", "+=", "-=", "?=" };
61 static void debug_compile (int which, const char *s);
65 * compile_append() - append list results of two statements
67 * parse->left more compile_append() by left-recursion
68 * parse->right single rule
70 LIST *compile_append (PARSE *parse, LOL *args, int *jmp) {
71 /* Append right to left. */
72 return list_append(
73 (*parse->left->func)(parse->left, args, jmp),
74 (*parse->right->func)(parse->right, args, jmp)
80 * compile_break() - compile 'break/continue/return' rule
82 * parse->left results
83 * parse->num JMP_BREAK/CONTINUE/RETURN
85 LIST *compile_break (PARSE *parse, LOL *args, int *jmp) {
86 LIST *lv = (*parse->left->func)(parse->left, args, jmp);
87 *jmp = parse->num;
88 return lv;
93 * compile_eval() - evaluate if to determine which leg to compile
95 * Returns:
96 * list if expression true - compile 'then' clause
97 * L0 if expression false - compile 'else' clause
99 static JAMFA_PURE int lcmp (LIST *t, LIST *s) {
100 int status = 0;
101 while (!status && (t || s)) {
102 const char *st = (t ? t->string : "");
103 const char *ss = (s ? s->string : "");
104 status = strcmp(st, ss);
105 t = (t ? list_next(t) : t);
106 s = (s ? list_next(s) : s);
108 return status;
112 static int recmp (LIST *s, LIST *restr) {
113 int res;
115 printf("restr: <%s>\n", (restr ? restr->string : "(null)"));
116 printf("s : <%s>\n", (s ? s->string : "(null)"));
118 regexp_t *re = regexp_compile((restr ? restr->string : ""), 0);
119 res = regexp_execute(re, (s ? s->string : ""), NULL, 0);
120 regexp_free(re);
121 return (res > 0);
125 LIST *compile_eval (PARSE *parse, LOL *args, int *jmp) {
126 LIST *ll, *lr, *s, *t;
127 int status = 0;
128 /* short circuit lr eval for &&, ||, 'in' and 'any-in' */
129 ll = (*parse->left->func)(parse->left, args, jmp);
130 lr = 0;
131 switch (parse->num) {
132 case EXPR_AND:
133 case EXPR_IN:
134 case EXPR_ANYIN:
135 if (ll) goto eval;
136 break;
137 case EXPR_OR:
138 if (!ll) goto eval;
139 break;
140 default:
141 eval: lr = (*parse->right->func)(parse->right, args, jmp);
142 break;
144 /* now eval */
145 switch (parse->num) {
146 case EXPR_NOT: if (!ll) status = 1; break;
147 case EXPR_AND: if (ll && lr) status = 1; break;
148 case EXPR_OR: if (ll || lr) status = 1; break;
149 case EXPR_IN:
150 /* "a in b": make sure each of ll is equal to something in lr */
151 for (t = ll; t; t = list_next(t)) {
152 for (s = lr; s; s = list_next(s)) if (strcmp(t->string, s->string) == 0) break;
153 if (!s) break;
155 /* No more ll? success */
156 if (!t) status = 1;
157 break;
158 case EXPR_ANYIN:
159 /* "a any-in b": does b contains at least one item from a? */
160 for (t = ll; t && !status; t = list_next(t)) {
161 for (s = lr; s; s = list_next(s)) if (strcmp(t->string, s->string) == 0) { status = 1; break; } /* success */
163 break;
164 case EXPR_EXISTS: if (lcmp(ll, L0) != 0) status = 1; break;
165 case EXPR_EQUALS: if (lcmp(ll, lr) == 0) status = 1; break;
166 case EXPR_NOTEQ: if (lcmp(ll, lr) != 0) status = 1; break;
167 case EXPR_LESS: if (lcmp(ll, lr) < 0) status = 1; break;
168 case EXPR_LESSEQ: if (lcmp(ll, lr) <= 0) status = 1; break;
169 case EXPR_MORE: if (lcmp(ll, lr) > 0) status = 1; break;
170 case EXPR_MOREEQ: if (lcmp(ll, lr) >= 0) status = 1; break;
171 case EXPR_REXPEQ: if (recmp(ll, lr)) status = 1; break;
173 if (DEBUG_IF) {
174 debug_compile(0, "if");
175 list_print(ll);
176 printf("(%d) ", status);
177 list_print_ex(stdout, lr, LPFLAG_NO_TRSPACE);
178 printf("\n");
180 /* find something to return */
181 /* in odd circumstances (like "" = "") we'll have to return a new string */
182 if (!status) t = NULL;
183 else if (ll) { t = ll; ll = NULL; }
184 else if (lr) { t = lr; lr = NULL; }
185 else t = list_new(L0, "1", 0);
186 if (ll) list_free(ll);
187 if (lr) list_free(lr);
188 return t;
193 * compile_foreach() - compile the "for x in y" statement
195 * Compile_foreach() resets the given variable name to each specified
196 * value, executing the commands enclosed in braces for each iteration.
198 * parse->string index variable
199 * parse->left variable values
200 * parse->right rule to compile
202 LIST *compile_foreach (PARSE *p, LOL *args, int *jmp) {
203 LIST *nv = (*p->left->func)(p->left, args, jmp);
204 LIST *result = NULL;
205 SETTINGS *s = NULL;
206 if (p->num) {
207 /* have 'local' */
208 s = addsettings(s, VAR_SET, p->string, L0);
209 pushsettings(s);
211 /* for each value for var */
212 for (LIST *l = nv; l && *jmp == JMP_NONE; l = list_next(l)) {
213 /* reset $(p->string) for each val */
214 var_set(p->string, list_new(L0, l->string, 1), VAR_SET);
215 /* keep only last result */
216 list_free(result);
217 result = (*p->right->func)(p->right, args, jmp);
218 /* continue loop? */
219 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
221 /* here by break/continue? */
222 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
223 if (p->num) {
224 /* restore locals */
225 popsettings(s);
226 freesettings(s);
228 list_free(nv);
229 /* returns result of last loop */
230 return result;
235 * compile_if() - compile 'if' rule
237 * parse->left condition tree
238 * parse->right then tree
239 * parse->third else tree
241 LIST *compile_if (PARSE *p, LOL *args, int *jmp) {
242 LIST *l = (*p->left->func)(p->left, args, jmp);
243 p = (l ? p->right : p->third);
244 list_free(l);
245 return (*p->func)(p, args, jmp);
250 * compile_include() - support for 'include' - call include() on file
252 * parse->left list of files to include (can only do 1)
253 * parse->num !0: softinclude
255 LIST *compile_include (PARSE *parse, LOL *args, int *jmp) {
256 int softinc = parse->num;
257 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
258 if (DEBUG_COMPILE) {
259 debug_compile(0, (softinc ? "softinclude" : "include"));
260 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
261 printf("\n");
263 if (nt) {
264 TARGET *t = bindtarget(nt->string);
265 /* bind the include file under the influence of "on-target" variables */
266 /* though they are targets, include files are not built with make() */
267 /* needn't copysettings(), as search sets no vars */
268 pushsettings(t->settings);
269 t->boundname = search(t->name, &t->time);
270 popsettings(t->settings);
271 /* don't parse missing file if NOCARE set */
272 if (t->time || !(t->flags&T_FLAG_NOCARE)) {
273 int doit = 1;
274 if (file_type(t->boundname) != 0 || access(t->boundname, R_OK) != 0) {
275 if (!softinc) {
276 printf("Failed to include file '%s'\n", t->boundname);
277 exit(EXITBAD);
279 doit = 0;
281 if (doit) parse_file(t->boundname);
284 list_free(nt);
285 return L0;
290 * compile_list() - expand and return a list
292 * parse->string - character string to expand
293 * parse->num - "don't expand" flag
295 LIST *compile_list (PARSE *parse, LOL *args, int *jmp) {
296 /* voodoo 1 means: parse->string is a copyable string */
297 return (parse->num ? list_new(L0, parse->string, 1) : var_expand(parse->string, NULL, args, 1));
302 * compile_local() - declare (and set) local variables
304 * parse->left list of variables
305 * parse->right list of values
306 * parse->third rules to execute
308 LIST *compile_local (PARSE *parse, LOL *args, int *jmp) {
309 LIST *l;
310 SETTINGS *s = NULL;
311 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
312 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
313 LIST *result;
314 if (DEBUG_COMPILE) {
315 debug_compile(0, "local");
316 list_print(nt);
317 printf("= ");
318 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
319 printf("\n");
321 /* initial value is ns */
322 for (l = nt; l; l = list_next(l)) s = addsettings(s, 0, l->string, list_copy((LIST *)0, ns));
323 list_free(ns);
324 list_free(nt);
325 /* note that callees of the current context get this "local" */
326 /* variable, making it not so much local as layered */
327 pushsettings(s);
328 result = (*parse->third->func)(parse->third, args, jmp);
329 popsettings(s);
330 freesettings(s);
331 return result;
336 * compile_null() - do nothing -- a stub for parsing
338 JAMFA_CONST LIST *compile_null (PARSE *parse, LOL *args, int *jmp) {
339 return L0;
344 * compile_on() - run rule under influence of on-target variables
346 * parse->left target list; only first used
347 * parse->right rule to run
349 LIST *compile_on (PARSE *parse, LOL *args, int *jmp) {
350 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
351 LIST *result = NULL;
352 if (DEBUG_COMPILE) {
353 debug_compile(0, "on");
354 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
355 printf("\n");
357 /* copy settings, so that 'on target var on target = val' doesn't set var globally */
358 if (nt) {
359 TARGET *t = bindtarget(nt->string);
360 SETTINGS *s = copysettings(t->settings);
361 pushsettings(s);
362 result = (*parse->right->func)(parse->right, args, jmp);
363 popsettings(s);
364 freesettings(s);
366 list_free(nt);
367 return result;
372 * compile_rule() - compile a single user defined rule
374 * parse->left list of rules to run
375 * parse->right parameters (list of lists) to rule, recursing left
377 * Wrapped around evaluate_rule() so that headers() can share it.
379 LIST *compile_rule (PARSE *parse, LOL *args, int *jmp) {
380 LOL nargs[1];
381 LIST *result = NULL;
382 LIST *ll, *l;
383 PARSE *p;
384 /* list of rules to run -- normally 1! */
385 ll = (*parse->left->func)(parse->left, args, jmp);
386 /* build up the list of arg lists */
387 lol_init(nargs);
388 for (p = parse->right; p; p = p->left) lol_add(nargs, (*p->right->func)(p->right, args, jmp));
389 /* run rules, appending results from each */
390 for (l = ll; l; l = list_next(l)) result = evaluate_rule(l->string, nargs, result);
391 list_free(ll);
392 lol_free(nargs);
393 return result;
398 * evaluate_rule() - execute a rule invocation
400 LIST *evaluate_rule (const char *rulename, LOL *args, LIST *result) {
401 //#infdef OPT_EXPAND_RULE_NAMES_EXT
402 /*RULE *rule = bindrule(rulename);*/
403 //#else
404 RULE *rule;
405 char *expanded, *c;
406 dstring_t buf;
407 dstr_init(&buf);
408 if (var_string(rulename, &buf, args, ' ') < 0) {
409 dstr_done(&buf);
410 printf("Failed to expand rule %s -- expansion too long\n", rulename);
411 exit(EXITBAD);
413 expanded = dstr_cstr(&buf);
414 while (expanded[0] == ' ') ++expanded;
415 while ((c = strrchr(expanded, ' '))) *c = '\0';
416 if (DEBUG_COMPILE) {
417 debug_compile(1, rulename);
418 if (strcmp(rulename, expanded)) printf("-> %s ", expanded);
419 lol_print(args);
420 printf("\n");
422 rule = bindrule(expanded);
423 dstr_done(&buf);
424 //#endif
425 /* check traditional targets $(<) and sources $(>) */
426 if (!rule->actions && !rule->procedure) printf("warning: unknown rule %s\n", rule->name);
427 /* if this rule will be executed for updating the targets then construct the action for make() */
428 if (rule->actions) {
429 TARGETS *t;
430 ACTION *action;
431 /* the action is associated with this instance of this rule */
432 action = (ACTION *)malloc(sizeof(ACTION));
433 memset((char *)action, '\0', sizeof(*action));
434 action->rule = rule;
435 action->targets = targetlist((TARGETS *)0, lol_get(args, 0));
436 action->sources = targetlist((TARGETS *)0, lol_get(args, 1));
437 /* append this action to the actions of each target */
438 for (t = action->targets; t; t = t->next) t->target->actions = actionlist(t->target->actions, action);
440 /* now recursively compile any parse tree associated with this rule */
441 if (rule->procedure) {
442 PARSE *parse = rule->procedure;
443 SETTINGS *s = 0;
444 int jmp = JMP_NONE;
445 LIST *l;
446 int i;
447 /* build parameters as local vars */
448 for (l = rule->params, i = 0; l; l = l->next, i++) s = addsettings(s, 0, l->string, list_copy(L0, lol_get(args, i)));
449 /* run rule */
450 /* bring in local params */
451 /* refer/free to ensure rule not freed during use */
452 parse_refer(parse);
453 pushsettings(s);
454 result = list_append(result, (*parse->func)(parse, args, &jmp));
455 popsettings(s);
456 freesettings(s);
457 parse_free(parse);
459 if (DEBUG_COMPILE) debug_compile(-1, 0);
460 return result;
465 * compile_rules() - compile a chain of rules
467 * parse->left single rule
468 * parse->right more compile_rules() by right-recursion
470 LIST *compile_rules (PARSE *parse, LOL *args, int *jmp) {
471 /* ignore result from first statement; return the 2nd */
472 /* optimize recursion on the right by looping */
473 LIST *result = NULL;
474 while (*jmp == JMP_NONE && parse->func == compile_rules) {
475 list_free(result);
476 result = (*parse->left->func)(parse->left, args, jmp);
477 parse = parse->right;
479 if (*jmp == JMP_NONE) {
480 list_free(result);
481 result = (*parse->func)(parse, args, jmp);
483 return result;
488 * compile_set() - compile the "set variable" statement
490 * parse->left variable names
491 * parse->right variable values
492 * parse->num VAR_SET/APPEND/DEFAULT
494 LIST *compile_set (PARSE *parse, LOL *args, int *jmp) {
495 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
496 LIST *ns = (*parse->right->func)(parse->right, args, jmp);
497 LIST *l;
498 if (DEBUG_COMPILE) {
499 debug_compile(0, "set");
500 list_print(nt);
501 printf("%s ", set_names[parse->num]);
502 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
503 printf("\n");
505 /* call var_set to set variable */
506 /* var_set keeps ns, so need to copy it */
507 for (l = nt; l; l = list_next(l)) var_set(l->string, list_copy(L0, ns), parse->num);
508 list_free(nt);
509 return ns;
514 * compile_setcomp() - support for `rule` - save parse tree
516 * parse->string rule name
517 * parse->left list of argument names
518 * parse->right rules for rule
520 LIST *compile_setcomp (PARSE *parse, LOL *args, int *jmp) {
521 RULE *rule = bindrule(parse->string);
522 LIST *params = 0;
523 PARSE *p;
524 /* build param list */
525 for (p = parse->left; p; p = p->left) params = list_new(params, p->string, 1);
526 if (DEBUG_COMPILE) {
527 debug_compile(0, "rule");
528 printf("%s ", parse->string);
529 list_print_ex(stdout, params, LPFLAG_NO_TRSPACE);
530 printf("\n");
532 /* free old one, if present */
533 if (rule->procedure) parse_free(rule->procedure);
534 if (rule->params) list_free(rule->params);
535 rule->procedure = parse->right;
536 rule->params = params;
537 /* we now own this parse tree */
538 /* don't let parse_free() release it */
539 parse_refer(parse->right);
540 return L0;
545 * compile_setexec() - support for `actions` - save execution string
547 * parse->string rule name
548 * parse->string1 OS command string
549 * parse->num flags
550 * parse->left `bind` variables
552 * Note that the parse flags (as defined in compile.h) are transfered
553 * directly to the rule flags (as defined in rules.h).
555 LIST *compile_setexec (PARSE *parse, LOL *args, int *jmp) {
556 RULE *rule = bindrule(parse->string);
557 LIST *bindlist = (*parse->left->func)(parse->left, args, jmp);
558 /* free old one, if present */
559 if (rule->actions) {
560 freestr(rule->actions);
561 list_free(rule->bindlist);
563 rule->actions = copystr(parse->string1);
564 rule->bindlist = bindlist;
565 rule->flags = parse->num;
566 return L0;
571 * compile_settings() - compile the "on =" (set variable on exec) statement
573 * parse->left variable names
574 * parse->right target name
575 * parse->third variable value
576 * parse->num VAR_SET/APPEND/DEFAULT
578 LIST *compile_settings (PARSE *parse, LOL *args, int *jmp) {
579 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
580 LIST *ns = (*parse->third->func)(parse->third, args, jmp);
581 LIST *targets = (*parse->right->func)(parse->right, args, jmp);
582 LIST *ts;
583 if (DEBUG_COMPILE) {
584 debug_compile(0, "set");
585 list_print(nt);
586 printf("on ");
587 list_print(targets);
588 printf("%s ", set_names[parse->num]);
589 list_print_ex(stdout, ns, LPFLAG_NO_TRSPACE);
590 printf("\n");
592 /* call addsettings to save variable setting addsettings keeps ns, so need to copy it */
593 /* pass append flag to addsettings() */
594 for (ts = targets; ts; ts = list_next(ts)) {
595 TARGET *t = bindtarget(ts->string);
596 for (LIST *l = nt; l; l = list_next(l)) {
597 t->settings = addsettings(t->settings, parse->num, l->string, list_copy((LIST *)0, ns));
600 list_free(nt);
601 list_free(targets);
602 return ns;
607 * compile_switch() - compile 'switch' rule
609 * parse->left switch value (only 1st used)
610 * parse->right cases
612 * cases->left 1st case
613 * cases->right next cases
615 * case->string argument to match
616 * case->left parse tree to execute
618 LIST *compile_switch (PARSE *parse, LOL *args, int *jmp) {
619 LIST *nt = (*parse->left->func)(parse->left, args, jmp);
620 LIST *result = NULL;
621 if (DEBUG_COMPILE) {
622 debug_compile(0, "switch");
623 list_print_ex(stdout, nt, LPFLAG_NO_TRSPACE);
624 printf("\n");
626 /* step through cases */
627 for (parse = parse->right; parse; parse = parse->right) {
628 if (!matchglob(parse->left->string, nt?nt->string:"")) {
629 /* get and exec parse tree for this case */
630 parse = parse->left->left;
631 result = (*parse->func)(parse, args, jmp);
632 break;
635 list_free(nt);
636 return result;
641 * compile_while() - compile 'while' rule
643 * parse->left condition tree
644 * parse->right execution tree
646 LIST *compile_while (PARSE *p, LOL *args, int *jmp) {
647 LIST *result = NULL;
648 LIST *l;
649 /* returns the value from the last execution of the block */
650 while ((*jmp == JMP_NONE ) && (l = (*p->left->func)(p->left, args, jmp))) {
651 /* always toss while's expression */
652 list_free(l);
653 /* keep only last result */
654 list_free(result);
655 result = (*p->right->func)(p->right, args, jmp);
656 /* continue loop? */
657 if (*jmp == JMP_CONTINUE) *jmp = JMP_NONE;
659 /* here by break/continue? */
660 if (*jmp == JMP_BREAK || *jmp == JMP_CONTINUE) *jmp = JMP_NONE;
661 /* returns result of last loop */
662 return result;
667 * debug_compile() - printf with indent to show rule expansion.
669 static void debug_compile (int which, const char *s) {
670 static int level = 0;
671 static const char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
672 int i = ((1+level)*2)%35;
673 if (which >= 0) printf("%*.*s ", i, i, indent);
674 if (s) printf("%s ", s);
675 level += which;