2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
4 * This file is part of Jam - see jam.c for Copyright information.
7 * compile.c - compile parsed jam statements
11 * compile_append() - append list results of two statements
12 * compile_break() - compile 'break/continue/return' rule
13 * compile_eval() - evaluate if to determine which leg to compile
14 * compile_foreach() - compile the "for x in y" statement
15 * compile_if() - compile 'if' rule
16 * compile_include() - support for 'include' - call include() on file
17 * compile_list() - expand and return a list
18 * compile_local() - declare (and set) local variables
19 * compile_null() - do nothing -- a stub for parsing
20 * compile_on() - run rule under influence of on-target variables
21 * compile_rule() - compile a single user defined rule
22 * compile_rules() - compile a chain of rules
23 * compile_set() - compile the "set variable" statement
24 * compile_setcomp() - support for `rule` - save parse tree
25 * compile_setexec() - support for `actions` - save execution string
26 * compile_settings() - compile the "on =" (set variable on exec) statement
27 * compile_switch() - compile 'switch' rule
31 * debug_compile() - printf with indent to show rule expansion.
32 * evaluate_rule() - execute a rule invocation
34 * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of
35 * the awkward sounding "settings".
36 * 04/12/94 (seiwald) - Combined build_depends() with build_includes().
37 * 04/12/94 (seiwald) - actionlist() now just appends a single action.
38 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
39 * 05/13/94 (seiwald) - include files are now bound as targets, and thus
40 * can make use of $(SEARCH)
41 * 06/01/94 (seiwald) - new 'actions existing' does existing sources
42 * 08/23/94 (seiwald) - Support for '+=' (append to variable)
43 * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
44 * 01/22/95 (seiwald) - Exit rule.
45 * 02/02/95 (seiwald) - Always rule; LEAVES rule.
46 * 02/14/95 (seiwald) - NoUpdate rule.
47 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
48 * 09/07/00 (seiwald) - stop crashing when a rule redefines itself
49 * 09/11/00 (seiwald) - new evaluate_rule() for headers().
50 * 09/11/00 (seiwald) - rules now return values, accessed via [ rule arg ... ]
51 * 09/12/00 (seiwald) - don't complain about rules invoked without targets
52 * 01/13/01 (seiwald) - fix case where rule is defined within another
53 * 01/10/01 (seiwald) - built-ins split out to builtin.c.
54 * 01/11/01 (seiwald) - optimize compile_rules() for tail recursion
55 * 01/21/01 (seiwald) - replace evaluate_if() with compile_eval()
56 * 01/24/01 (seiwald) - 'while' statement
57 * 03/23/01 (seiwald) - "[ on target rule ]" support
58 * 02/28/02 (seiwald) - merge EXEC_xxx flags in with RULE_xxx
59 * 03/02/02 (seiwald) - rules can be invoked via variable names
60 * 03/12/02 (seiwald) - &&,&,||,|,in now short-circuit again
61 * 03/25/02 (seiwald) - if ( "" a b ) one again returns true
62 * 06/21/02 (seiwald) - support for named parameters
63 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
64 * 10/22/02 (seiwald) - working return/break/continue statements
65 * 11/04/02 (seiwald) - const-ing for string literals
66 * 11/18/02 (seiwald) - remove bogus search() in 'on' statement.
67 * 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
79 #include "matchglob.h"
83 static const char *set_names
[] = { "=", "+=", "-=", "?=" };
84 static void debug_compile (int which
, const char *s
);
88 * compile_append() - append list results of two statements
90 * parse->left more compile_append() by left-recursion
91 * parse->right single rule
93 LIST
*compile_append (PARSE
*parse
, LOL
*args
, int *jmp
) {
94 /* Append right to left. */
96 (*parse
->left
->func
)(parse
->left
, args
, jmp
),
97 (*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
);
116 * compile_eval() - evaluate if to determine which leg to compile
119 * list if expression true - compile 'then' clause
120 * L0 if expression false - compile 'else' clause
122 static JAMFA_PURE
int lcmp (LIST
*t
, LIST
*s
) {
124 while (!status
&& (t
|| s
)) {
125 const char *st
= (t
? t
->string
: "");
126 const char *ss
= (s
? s
->string
: "");
127 status
= strcmp(st
, ss
);
128 t
= (t
? list_next(t
) : t
);
129 s
= (s
? list_next(s
) : s
);
135 LIST
*compile_eval (PARSE
*parse
, LOL
*args
, int *jmp
) {
136 LIST
*ll
, *lr
, *s
, *t
;
138 /* short circuit lr eval for &&, ||, 'in' and 'any-in' */
139 ll
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
141 switch (parse
->num
) {
151 eval
: lr
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
155 switch (parse
->num
) {
156 case EXPR_NOT
: if (!ll
) status
= 1; break;
157 case EXPR_AND
: if (ll
&& lr
) status
= 1; break;
158 case EXPR_OR
: if (ll
|| lr
) status
= 1; break;
160 /* "a in b": make sure each of ll is equal to something in lr */
161 for (t
= ll
; t
; t
= list_next(t
)) {
162 for (s
= lr
; s
; s
= list_next(s
)) if (strcmp(t
->string
, s
->string
) == 0) break;
165 /* No more ll? success */
169 /* "a any-in b": does b contains at least one item from a? */
170 for (t
= ll
; t
&& !status
; t
= list_next(t
)) {
171 for (s
= lr
; s
; s
= list_next(s
)) if (strcmp(t
->string
, s
->string
) == 0) { status
= 1; break; } /* success */
174 case EXPR_EXISTS
: if (lcmp(ll
, L0
) != 0) status
= 1; break;
175 case EXPR_EQUALS
: if (lcmp(ll
, lr
) == 0) status
= 1; break;
176 case EXPR_NOTEQ
: if (lcmp(ll
, lr
) != 0) status
= 1; break;
177 case EXPR_LESS
: if (lcmp(ll
, lr
) < 0) status
= 1; break;
178 case EXPR_LESSEQ
: if (lcmp(ll
, lr
) <= 0) status
= 1; break;
179 case EXPR_MORE
: if (lcmp(ll
, lr
) > 0) status
= 1; break;
180 case EXPR_MOREEQ
: if (lcmp(ll
, lr
) >= 0) status
= 1; break;
183 debug_compile(0, "if");
185 printf("(%d) ", status
);
186 list_print_ex(lr
, LPFLAG_NO_TRSPACE
);
189 /* find something to return */
190 /* in odd circumstances (like "" = "") we'll have to return a new string */
191 if (!status
) t
= NULL
;
192 else if (ll
) { t
= ll
; ll
= NULL
; }
193 else if (lr
) { t
= lr
; lr
= NULL
; }
194 else t
= list_new(L0
, "1", 0);
195 if (ll
) list_free(ll
);
196 if (lr
) list_free(lr
);
202 * compile_foreach() - compile the "for x in y" statement
204 * Compile_foreach() resets the given variable name to each specified
205 * value, executing the commands enclosed in braces for each iteration.
207 * parse->string index variable
208 * parse->left variable values
209 * parse->right rule to compile
211 LIST
*compile_foreach (PARSE
*p
, LOL
*args
, int *jmp
) {
212 LIST
*nv
= (*p
->left
->func
)(p
->left
, args
, jmp
);
217 s
= addsettings(s
, VAR_SET
, p
->string
, L0
);
220 /* for each value for var */
221 for (LIST
*l
= nv
; l
&& *jmp
== JMP_NONE
; l
= list_next(l
)) {
222 /* reset $(p->string) for each val */
223 var_set(p
->string
, list_new(L0
, l
->string
, 1), VAR_SET
);
224 /* keep only last result */
226 result
= (*p
->right
->func
)(p
->right
, args
, jmp
);
228 if (*jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
230 /* here by break/continue? */
231 if (*jmp
== JMP_BREAK
|| *jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
238 /* returns result of last loop */
244 * compile_if() - compile 'if' rule
246 * parse->left condition tree
247 * parse->right then tree
248 * parse->third else tree
250 LIST
*compile_if (PARSE
*p
, LOL
*args
, int *jmp
) {
251 LIST
*l
= (*p
->left
->func
)(p
->left
, args
, jmp
);
252 p
= (l
? p
->right
: p
->third
);
254 return (*p
->func
)(p
, args
, jmp
);
259 * compile_include() - support for 'include' - call include() on file
261 * parse->left list of files to include (can only do 1)
263 static LIST
*compile_include_internal (PARSE
*parse
, LOL
*args
, int *jmp
, int assoft
) {
264 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
266 debug_compile(0, (assoft
? "softinclude" : "include"));
267 list_print_ex(nt
, LPFLAG_NO_TRSPACE
);
271 TARGET
*t
= bindtarget(nt
->string
);
272 /* bind the include file under the influence of "on-target" variables */
273 /* though they are targets, include files are not built with make() */
274 /* needn't copysettings(), as search sets no vars */
275 pushsettings(t
->settings
);
276 t
->boundname
= search(t
->name
, &t
->time
);
277 popsettings(t
->settings
);
278 /* don't parse missing file if NOCARE set */
279 if (t
->time
|| !(t
->flags
&T_FLAG_NOCARE
)) {
281 if (file_type(t
->boundname
) != 0 || access(t
->boundname
, R_OK
) != 0) {
283 printf("Failed to include file '%s'\n", t
->boundname
);
288 if (doit
) parse_file(t
->boundname
);
297 * compile_include() - support for 'include' - call include() on file
299 * parse->left list of files to include (can only do 1)
301 LIST
*compile_include (PARSE
*parse
, LOL
*args
, int *jmp
) {
302 return compile_include_internal(parse
, args
, jmp
, 0);
307 * compile_softinclude() - support for 'softinclude' - call include() on file if file exists
309 * parse->left list of files to include (can only do 1)
311 LIST
*compile_softinclude (PARSE
*parse
, LOL
*args
, int *jmp
) {
312 return compile_include_internal(parse
, args
, jmp
, 1);
317 * compile_list() - expand and return a list
319 * parse->string - character string to expand
321 LIST
*compile_list (PARSE
*parse
, LOL
*args
, int *jmp
) {
322 /* voodoo 1 means: s is a copyable string */
323 const char *s
= parse
->string
;
324 return var_expand(L0
, s
, s
+strlen(s
), args
, 1);
329 * compile_local() - declare (and set) local variables
331 * parse->left list of variables
332 * parse->right list of values
333 * parse->third rules to execute
335 LIST
*compile_local (PARSE
*parse
, LOL
*args
, int *jmp
) {
338 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
339 LIST
*ns
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
342 debug_compile(0, "local");
345 list_print_ex(ns
, LPFLAG_NO_TRSPACE
);
348 /* initial value is ns */
349 for (l
= nt
; l
; l
= list_next(l
)) s
= addsettings(s
, 0, l
->string
, list_copy((LIST
*)0, ns
));
352 /* note that callees of the current context get this "local" */
353 /* variable, making it not so much local as layered */
355 result
= (*parse
->third
->func
)(parse
->third
, args
, jmp
);
363 * compile_null() - do nothing -- a stub for parsing
365 LIST
*compile_null (PARSE
*parse
, LOL
*args
, int *jmp
) {
371 * compile_on() - run rule under influence of on-target variables
373 * parse->left target list; only first used
374 * parse->right rule to run
376 LIST
*compile_on (PARSE
*parse
, LOL
*args
, int *jmp
) {
377 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
380 debug_compile(0, "on");
381 list_print_ex(nt
, LPFLAG_NO_TRSPACE
);
384 /* copy settings, so that 'on target var on target = val' doesn't set var globally */
386 TARGET
*t
= bindtarget(nt
->string
);
387 SETTINGS
*s
= copysettings(t
->settings
);
389 result
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
399 * compile_rule() - compile a single user defined rule
401 * parse->left list of rules to run
402 * parse->right parameters (list of lists) to rule, recursing left
404 * Wrapped around evaluate_rule() so that headers() can share it.
406 LIST
*compile_rule (PARSE
*parse
, LOL
*args
, int *jmp
) {
411 /* list of rules to run -- normally 1! */
412 ll
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
413 /* build up the list of arg lists */
415 for (p
= parse
->right
; p
; p
= p
->left
) lol_add(nargs
, (*p
->right
->func
)(p
->right
, args
, jmp
));
416 /* run rules, appending results from each */
417 for (l
= ll
; l
; l
= list_next(l
)) result
= evaluate_rule(l
->string
, nargs
, result
);
425 * evaluate_rule() - execute a rule invocation
427 LIST
*evaluate_rule (const char *rulename
, LOL
*args
, LIST
*result
) {
428 //#infdef OPT_EXPAND_RULE_NAMES_EXT
429 /*RULE *rule = bindrule(rulename);*/
435 if (var_string(rulename
, &buf
, args
, ' ') < 0) {
437 printf("Failed to expand rule %s -- expansion too long\n", rulename
);
440 expanded
= dstr_cstr(&buf
);
441 while (expanded
[0] == ' ') ++expanded
;
442 while ((c
= strrchr(expanded
, ' '))) *c
= '\0';
444 debug_compile(1, rulename
);
445 if (strcmp(rulename
, expanded
)) printf("-> %s ", expanded
);
449 rule
= bindrule(expanded
);
452 /* check traditional targets $(<) and sources $(>) */
453 if (!rule
->actions
&& !rule
->procedure
) printf("warning: unknown rule %s\n", rule
->name
);
454 /* if this rule will be executed for updating the targets then construct the action for make() */
458 /* the action is associated with this instance of this rule */
459 action
= (ACTION
*)malloc(sizeof(ACTION
));
460 memset((char *)action
, '\0', sizeof(*action
));
462 action
->targets
= targetlist((TARGETS
*)0, lol_get(args
, 0));
463 action
->sources
= targetlist((TARGETS
*)0, lol_get(args
, 1));
464 /* append this action to the actions of each target */
465 for (t
= action
->targets
; t
; t
= t
->next
) t
->target
->actions
= actionlist(t
->target
->actions
, action
);
467 /* now recursively compile any parse tree associated with this rule */
468 if (rule
->procedure
) {
469 PARSE
*parse
= rule
->procedure
;
474 /* build parameters as local vars */
475 for (l
= rule
->params
, i
= 0; l
; l
= l
->next
, i
++) s
= addsettings(s
, 0, l
->string
, list_copy(L0
, lol_get(args
, i
)));
477 /* bring in local params */
478 /* refer/free to ensure rule not freed during use */
481 result
= list_append(result
, (*parse
->func
)(parse
, args
, &jmp
));
486 if (DEBUG_COMPILE
) debug_compile(-1, 0);
492 * compile_rules() - compile a chain of rules
494 * parse->left single rule
495 * parse->right more compile_rules() by right-recursion
497 LIST
*compile_rules (PARSE
*parse
, LOL
*args
, int *jmp
) {
498 /* ignore result from first statement; return the 2nd */
499 /* optimize recursion on the right by looping */
501 while (*jmp
== JMP_NONE
&& parse
->func
== compile_rules
) {
503 result
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
504 parse
= parse
->right
;
506 if (*jmp
== JMP_NONE
) {
508 result
= (*parse
->func
)(parse
, args
, jmp
);
515 * compile_set() - compile the "set variable" statement
517 * parse->left variable names
518 * parse->right variable values
519 * parse->num VAR_SET/APPEND/DEFAULT
521 LIST
*compile_set (PARSE
*parse
, LOL
*args
, int *jmp
) {
522 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
523 LIST
*ns
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
526 debug_compile(0, "set");
528 printf("%s ", set_names
[parse
->num
]);
529 list_print_ex(ns
, LPFLAG_NO_TRSPACE
);
532 /* call var_set to set variable */
533 /* var_set keeps ns, so need to copy it */
534 for (l
= nt
; l
; l
= list_next(l
)) var_set(l
->string
, list_copy(L0
, ns
), parse
->num
);
541 * compile_setcomp() - support for `rule` - save parse tree
543 * parse->string rule name
544 * parse->left list of argument names
545 * parse->right rules for rule
547 LIST
*compile_setcomp (PARSE
*parse
, LOL
*args
, int *jmp
) {
548 RULE
*rule
= bindrule(parse
->string
);
551 /* build param list */
552 for (p
= parse
->left
; p
; p
= p
->left
) params
= list_new(params
, p
->string
, 1);
554 debug_compile(0, "rule");
555 printf("%s ", parse
->string
);
556 list_print_ex(params
, LPFLAG_NO_TRSPACE
);
559 /* free old one, if present */
560 if (rule
->procedure
) parse_free(rule
->procedure
);
561 if (rule
->params
) list_free(rule
->params
);
562 rule
->procedure
= parse
->right
;
563 rule
->params
= params
;
564 /* we now own this parse tree */
565 /* don't let parse_free() release it */
566 parse_refer(parse
->right
);
572 * compile_setexec() - support for `actions` - save execution string
574 * parse->string rule name
575 * parse->string1 OS command string
577 * parse->left `bind` variables
579 * Note that the parse flags (as defined in compile.h) are transfered
580 * directly to the rule flags (as defined in rules.h).
582 LIST
*compile_setexec (PARSE
*parse
, LOL
*args
, int *jmp
) {
583 RULE
*rule
= bindrule(parse
->string
);
584 LIST
*bindlist
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
585 /* free old one, if present */
587 freestr(rule
->actions
);
588 list_free(rule
->bindlist
);
590 rule
->actions
= copystr(parse
->string1
);
591 rule
->bindlist
= bindlist
;
592 rule
->flags
= parse
->num
;
598 * compile_settings() - compile the "on =" (set variable on exec) statement
600 * parse->left variable names
601 * parse->right target name
602 * parse->third variable value
603 * parse->num VAR_SET/APPEND/DEFAULT
605 LIST
*compile_settings (PARSE
*parse
, LOL
*args
, int *jmp
) {
606 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
607 LIST
*ns
= (*parse
->third
->func
)(parse
->third
, args
, jmp
);
608 LIST
*targets
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
611 debug_compile(0, "set");
615 printf("%s ", set_names
[parse
->num
]);
616 list_print_ex(ns
, LPFLAG_NO_TRSPACE
);
619 /* call addsettings to save variable setting addsettings keeps ns, so need to copy it */
620 /* pass append flag to addsettings() */
621 for (ts
= targets
; ts
; ts
= list_next(ts
)) {
622 TARGET
*t
= bindtarget(ts
->string
);
623 for (LIST
*l
= nt
; l
; l
= list_next(l
)) {
624 t
->settings
= addsettings(t
->settings
, parse
->num
, l
->string
, list_copy((LIST
*)0, ns
));
634 * compile_switch() - compile 'switch' rule
636 * parse->left switch value (only 1st used)
639 * cases->left 1st case
640 * cases->right next cases
642 * case->string argument to match
643 * case->left parse tree to execute
645 LIST
*compile_switch (PARSE
*parse
, LOL
*args
, int *jmp
) {
646 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
649 debug_compile(0, "switch");
650 list_print_ex(nt
, LPFLAG_NO_TRSPACE
);
653 /* step through cases */
654 for (parse
= parse
->right
; parse
; parse
= parse
->right
) {
655 if (!matchglob(parse
->left
->string
, nt
?nt
->string
:"")) {
656 /* get and exec parse tree for this case */
657 parse
= parse
->left
->left
;
658 result
= (*parse
->func
)(parse
, args
, jmp
);
668 * compile_while() - compile 'while' rule
670 * parse->left condition tree
671 * parse->right execution tree
673 LIST
*compile_while (PARSE
*p
, LOL
*args
, int *jmp
) {
676 /* returns the value from the last execution of the block */
677 while ((*jmp
== JMP_NONE
) && (l
= (*p
->left
->func
)(p
->left
, args
, jmp
))) {
678 /* always toss while's expression */
680 /* keep only last result */
682 result
= (*p
->right
->func
)(p
->right
, args
, jmp
);
684 if (*jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
686 /* here by break/continue? */
687 if (*jmp
== JMP_BREAK
|| *jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
688 /* returns result of last loop */
694 * debug_compile() - printf with indent to show rule expansion.
696 static void debug_compile (int which
, const char *s
) {
697 static int level
= 0;
698 static const char indent
[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|";
699 int i
= ((1+level
)*2)%35;
700 if (which
>= 0) printf("%*.*s ", i
, i
, indent
);
701 if (s
) printf("%s ", s
);