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
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
43 * debug_compile() - printf with indent to show rule expansion.
44 * evaluate_rule() - execute a rule invocation
56 #include "matchglob.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. */
73 (*parse
->left
->func
)(parse
->left
, args
, jmp
),
74 (*parse
->right
->func
)(parse
->right
, args
, jmp
)
80 * compile_break() - compile 'break/continue/return' rule
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
);
93 * compile_eval() - evaluate if to determine which leg to compile
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
) {
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
);
112 static int recmp (LIST
*s
, LIST
*restr
) {
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);
125 LIST
*compile_eval (PARSE
*parse
, LOL
*args
, int *jmp
) {
126 LIST
*ll
, *lr
, *s
, *t
;
128 /* short circuit lr eval for &&, ||, 'in' and 'any-in' */
129 ll
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
131 switch (parse
->num
) {
141 eval
: lr
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
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;
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;
155 /* No more ll? success */
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 */
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;
174 debug_compile(0, "if");
176 printf("(%d) ", status
);
177 list_print_ex(stdout
, lr
, LPFLAG_NO_TRSPACE
);
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
);
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
);
208 s
= addsettings(s
, VAR_SET
, p
->string
, L0
);
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 */
217 result
= (*p
->right
->func
)(p
->right
, args
, jmp
);
219 if (*jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
221 /* here by break/continue? */
222 if (*jmp
== JMP_BREAK
|| *jmp
== JMP_CONTINUE
) *jmp
= JMP_NONE
;
229 /* returns result of last loop */
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
);
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
);
259 debug_compile(0, (softinc
? "softinclude" : "include"));
260 list_print_ex(stdout
, nt
, LPFLAG_NO_TRSPACE
);
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
)) {
274 if (file_type(t
->boundname
) != 0 || access(t
->boundname
, R_OK
) != 0) {
276 printf("Failed to include file '%s'\n", t
->boundname
);
281 if (doit
) parse_file(t
->boundname
);
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
) {
311 LIST
*nt
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
312 LIST
*ns
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
315 debug_compile(0, "local");
318 list_print_ex(stdout
, ns
, LPFLAG_NO_TRSPACE
);
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
));
325 /* note that callees of the current context get this "local" */
326 /* variable, making it not so much local as layered */
328 result
= (*parse
->third
->func
)(parse
->third
, args
, jmp
);
336 * compile_null() - do nothing -- a stub for parsing
338 JAMFA_CONST LIST
*compile_null (PARSE
*parse
, LOL
*args
, int *jmp
) {
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
);
353 debug_compile(0, "on");
354 list_print_ex(stdout
, nt
, LPFLAG_NO_TRSPACE
);
357 /* copy settings, so that 'on target var on target = val' doesn't set var globally */
359 TARGET
*t
= bindtarget(nt
->string
);
360 SETTINGS
*s
= copysettings(t
->settings
);
362 result
= (*parse
->right
->func
)(parse
->right
, args
, jmp
);
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
) {
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 */
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
);
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);*/
408 if (var_string(rulename
, &buf
, args
, ' ') < 0) {
410 printf("Failed to expand rule %s -- expansion too long\n", rulename
);
413 expanded
= dstr_cstr(&buf
);
414 while (expanded
[0] == ' ') ++expanded
;
415 while ((c
= strrchr(expanded
, ' '))) *c
= '\0';
417 debug_compile(1, rulename
);
418 if (strcmp(rulename
, expanded
)) printf("-> %s ", expanded
);
422 rule
= bindrule(expanded
);
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() */
431 /* the action is associated with this instance of this rule */
432 action
= (ACTION
*)malloc(sizeof(ACTION
));
433 memset((char *)action
, '\0', sizeof(*action
));
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
;
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
)));
450 /* bring in local params */
451 /* refer/free to ensure rule not freed during use */
454 result
= list_append(result
, (*parse
->func
)(parse
, args
, &jmp
));
459 if (DEBUG_COMPILE
) debug_compile(-1, 0);
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 */
474 while (*jmp
== JMP_NONE
&& parse
->func
== compile_rules
) {
476 result
= (*parse
->left
->func
)(parse
->left
, args
, jmp
);
477 parse
= parse
->right
;
479 if (*jmp
== JMP_NONE
) {
481 result
= (*parse
->func
)(parse
, args
, jmp
);
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
);
499 debug_compile(0, "set");
501 printf("%s ", set_names
[parse
->num
]);
502 list_print_ex(stdout
, ns
, LPFLAG_NO_TRSPACE
);
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
);
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
);
524 /* build param list */
525 for (p
= parse
->left
; p
; p
= p
->left
) params
= list_new(params
, p
->string
, 1);
527 debug_compile(0, "rule");
528 printf("%s ", parse
->string
);
529 list_print_ex(stdout
, params
, LPFLAG_NO_TRSPACE
);
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
);
545 * compile_setexec() - support for `actions` - save execution string
547 * parse->string rule name
548 * parse->string1 OS command string
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 */
560 freestr(rule
->actions
);
561 list_free(rule
->bindlist
);
563 rule
->actions
= copystr(parse
->string1
);
564 rule
->bindlist
= bindlist
;
565 rule
->flags
= parse
->num
;
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
);
584 debug_compile(0, "set");
588 printf("%s ", set_names
[parse
->num
]);
589 list_print_ex(stdout
, ns
, LPFLAG_NO_TRSPACE
);
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
));
607 * compile_switch() - compile 'switch' rule
609 * parse->left switch value (only 1st used)
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
);
622 debug_compile(0, "switch");
623 list_print_ex(stdout
, nt
, LPFLAG_NO_TRSPACE
);
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
);
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
) {
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 */
653 /* keep only last result */
655 result
= (*p
->right
->func
)(p
->right
, args
, jmp
);
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 */
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
);