1 | /* |
---|
2 | * Copyright 1993, 2000 Christopher Seiwald. |
---|
3 | * |
---|
4 | * This file is part of Jam - see jam.c for Copyright information. |
---|
5 | */ |
---|
6 | |
---|
7 | /* This file is ALSO: |
---|
8 | * Copyright 2001-2004 David Abrahams. |
---|
9 | * Distributed under the Boost Software License, Version 1.0. |
---|
10 | * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
---|
11 | */ |
---|
12 | |
---|
13 | # include "jam.h" |
---|
14 | |
---|
15 | # include "lists.h" |
---|
16 | # include "parse.h" |
---|
17 | # include "compile.h" |
---|
18 | # include "variable.h" |
---|
19 | # include "expand.h" |
---|
20 | # include "rules.h" |
---|
21 | # include "newstr.h" |
---|
22 | # include "make.h" |
---|
23 | # include "search.h" |
---|
24 | # include "hdrmacro.h" |
---|
25 | # include "hash.h" |
---|
26 | # include "modules.h" |
---|
27 | # include "strings.h" |
---|
28 | # include "builtins.h" |
---|
29 | # include "class.h" |
---|
30 | |
---|
31 | # include <time.h> |
---|
32 | # include <assert.h> |
---|
33 | # include <string.h> |
---|
34 | # include <stdarg.h> |
---|
35 | |
---|
36 | /* |
---|
37 | * compile.c - compile parsed jam statements |
---|
38 | * |
---|
39 | * External routines: |
---|
40 | * |
---|
41 | * compile_append() - append list results of two statements |
---|
42 | * compile_eval() - evaluate if to determine which leg to compile |
---|
43 | * compile_foreach() - compile the "for x in y" statement |
---|
44 | * compile_if() - compile 'if' rule |
---|
45 | * compile_while() - compile 'while' rule |
---|
46 | * compile_include() - support for 'include' - call include() on file |
---|
47 | * compile_list() - expand and return a list |
---|
48 | * compile_local() - declare (and set) local variables |
---|
49 | * compile_null() - do nothing -- a stub for parsing |
---|
50 | * compile_on() - run rule under influence of on-target variables |
---|
51 | * compile_rule() - compile a single user defined rule |
---|
52 | * compile_rules() - compile a chain of rules |
---|
53 | * compile_set() - compile the "set variable" statement |
---|
54 | * compile_setcomp() - support for `rule` - save parse tree |
---|
55 | * compile_setexec() - support for `actions` - save execution string |
---|
56 | * compile_settings() - compile the "on =" (set variable on exec) statement |
---|
57 | * compile_switch() - compile 'switch' rule |
---|
58 | * |
---|
59 | * Internal routines: |
---|
60 | * |
---|
61 | * debug_compile() - printf with indent to show rule expansion. |
---|
62 | * evaluate_rule() - execute a rule invocation |
---|
63 | * |
---|
64 | * builtin_depends() - DEPENDS/INCLUDES rule |
---|
65 | * builtin_echo() - ECHO rule |
---|
66 | * builtin_exit() - EXIT rule |
---|
67 | * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule |
---|
68 | * |
---|
69 | * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of |
---|
70 | * the awkward sounding "settings". |
---|
71 | * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). |
---|
72 | * 04/12/94 (seiwald) - actionlist() now just appends a single action. |
---|
73 | * 04/13/94 (seiwald) - added shorthand L0 for null list pointer |
---|
74 | * 05/13/94 (seiwald) - include files are now bound as targets, and thus |
---|
75 | * can make use of $(SEARCH) |
---|
76 | * 06/01/94 (seiwald) - new 'actions existing' does existing sources |
---|
77 | * 08/23/94 (seiwald) - Support for '+=' (append to variable) |
---|
78 | * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. |
---|
79 | * 01/22/95 (seiwald) - Exit rule. |
---|
80 | * 02/02/95 (seiwald) - Always rule; LEAVES rule. |
---|
81 | * 02/14/95 (seiwald) - NoUpdate rule. |
---|
82 | * 09/11/00 (seiwald) - new evaluate_rule() for headers(). |
---|
83 | * 09/11/00 (seiwald) - compile_xxx() now return LIST *. |
---|
84 | * New compile_append() and compile_list() in |
---|
85 | * support of building lists here, rather than |
---|
86 | * in jamgram.yy. |
---|
87 | * 01/10/00 (seiwald) - built-ins split out to builtin.c. |
---|
88 | */ |
---|
89 | |
---|
90 | static void debug_compile( int which, char *s, FRAME* frame ); |
---|
91 | int glob( char *s, char *c ); |
---|
92 | /* Internal functions from builtins.c */ |
---|
93 | void backtrace( FRAME *frame ); |
---|
94 | void backtrace_line( FRAME *frame ); |
---|
95 | void print_source_line( PARSE* p ); |
---|
96 | |
---|
97 | |
---|
98 | void frame_init( FRAME* frame ) |
---|
99 | { |
---|
100 | frame->prev = 0; |
---|
101 | frame->prev_user = 0; |
---|
102 | lol_init(frame->args); |
---|
103 | frame->module = root_module(); |
---|
104 | frame->rulename = "module scope"; |
---|
105 | frame->procedure = 0; |
---|
106 | } |
---|
107 | |
---|
108 | void frame_free( FRAME* frame ) |
---|
109 | { |
---|
110 | lol_free( frame->args ); |
---|
111 | } |
---|
112 | |
---|
113 | /* |
---|
114 | * compile_append() - append list results of two statements |
---|
115 | * |
---|
116 | * parse->left more compile_append() by left-recursion |
---|
117 | * parse->right single rule |
---|
118 | */ |
---|
119 | |
---|
120 | LIST * |
---|
121 | compile_append( |
---|
122 | PARSE *parse, |
---|
123 | FRAME *frame ) |
---|
124 | { |
---|
125 | /* Append right to left. */ |
---|
126 | |
---|
127 | return list_append( |
---|
128 | parse_evaluate( parse->left, frame ), |
---|
129 | parse_evaluate( parse->right, frame ) ); |
---|
130 | } |
---|
131 | |
---|
132 | /* |
---|
133 | * compile_eval() - evaluate if to determine which leg to compile |
---|
134 | * |
---|
135 | * Returns: |
---|
136 | * list if expression true - compile 'then' clause |
---|
137 | * L0 if expression false - compile 'else' clause |
---|
138 | */ |
---|
139 | |
---|
140 | static int |
---|
141 | lcmp( LIST *t, LIST *s ) |
---|
142 | { |
---|
143 | int status = 0; |
---|
144 | |
---|
145 | while( !status && ( t || s ) ) |
---|
146 | { |
---|
147 | char *st = t ? t->string : ""; |
---|
148 | char *ss = s ? s->string : ""; |
---|
149 | |
---|
150 | status = strcmp( st, ss ); |
---|
151 | |
---|
152 | t = t ? list_next( t ) : t; |
---|
153 | s = s ? list_next( s ) : s; |
---|
154 | } |
---|
155 | |
---|
156 | return status; |
---|
157 | } |
---|
158 | |
---|
159 | LIST * |
---|
160 | compile_eval( |
---|
161 | PARSE *parse, |
---|
162 | FRAME *frame ) |
---|
163 | { |
---|
164 | LIST *ll, *lr, *s, *t; |
---|
165 | int status = 0; |
---|
166 | |
---|
167 | /* Short circuit lr eval for &&, ||, and 'in' */ |
---|
168 | |
---|
169 | ll = parse_evaluate( parse->left, frame ); |
---|
170 | lr = 0; |
---|
171 | |
---|
172 | switch( parse->num ) |
---|
173 | { |
---|
174 | case EXPR_AND: |
---|
175 | case EXPR_IN: if( ll ) goto eval; break; |
---|
176 | case EXPR_OR: if( !ll ) goto eval; break; |
---|
177 | default: eval: lr = parse_evaluate( parse->right, frame ); |
---|
178 | } |
---|
179 | |
---|
180 | /* Now eval */ |
---|
181 | |
---|
182 | switch( parse->num ) |
---|
183 | { |
---|
184 | case EXPR_NOT: |
---|
185 | if( !ll ) status = 1; |
---|
186 | break; |
---|
187 | |
---|
188 | case EXPR_AND: |
---|
189 | if( ll && lr ) status = 1; |
---|
190 | break; |
---|
191 | |
---|
192 | case EXPR_OR: |
---|
193 | if( ll || lr ) status = 1; |
---|
194 | break; |
---|
195 | |
---|
196 | case EXPR_IN: |
---|
197 | /* "a in b": make sure each of */ |
---|
198 | /* ll is equal to something in lr. */ |
---|
199 | |
---|
200 | for( t = ll; t; t = list_next( t ) ) |
---|
201 | { |
---|
202 | for( s = lr; s; s = list_next( s ) ) |
---|
203 | if( !strcmp( t->string, s->string ) ) |
---|
204 | break; |
---|
205 | if( !s ) break; |
---|
206 | } |
---|
207 | |
---|
208 | /* No more ll? Success */ |
---|
209 | |
---|
210 | if( !t ) status = 1; |
---|
211 | |
---|
212 | break; |
---|
213 | |
---|
214 | case EXPR_EXISTS: if( lcmp( ll, L0 ) != 0 ) status = 1; break; |
---|
215 | case EXPR_EQUALS: if( lcmp( ll, lr ) == 0 ) status = 1; break; |
---|
216 | case EXPR_NOTEQ: if( lcmp( ll, lr ) != 0 ) status = 1; break; |
---|
217 | case EXPR_LESS: if( lcmp( ll, lr ) < 0 ) status = 1; break; |
---|
218 | case EXPR_LESSEQ: if( lcmp( ll, lr ) <= 0 ) status = 1; break; |
---|
219 | case EXPR_MORE: if( lcmp( ll, lr ) > 0 ) status = 1; break; |
---|
220 | case EXPR_MOREEQ: if( lcmp( ll, lr ) >= 0 ) status = 1; break; |
---|
221 | |
---|
222 | } |
---|
223 | |
---|
224 | if( DEBUG_IF ) |
---|
225 | { |
---|
226 | debug_compile( 0, "if", frame ); |
---|
227 | list_print( ll ); |
---|
228 | printf( "(%d) ", status ); |
---|
229 | list_print( lr ); |
---|
230 | printf( "\n" ); |
---|
231 | } |
---|
232 | |
---|
233 | /* Find something to return. */ |
---|
234 | /* In odd circumstances (like "" = "") */ |
---|
235 | /* we'll have to return a new string. */ |
---|
236 | |
---|
237 | if( !status ) t = 0; |
---|
238 | else if( ll ) t = ll, ll = 0; |
---|
239 | else if( lr ) t = lr, lr = 0; |
---|
240 | else t = list_new( L0, newstr( "1" ) ); |
---|
241 | |
---|
242 | if( ll ) list_free( ll ); |
---|
243 | if( lr ) list_free( lr ); |
---|
244 | return t; |
---|
245 | } |
---|
246 | |
---|
247 | |
---|
248 | /* |
---|
249 | * compile_foreach() - compile the "for x in y" statement |
---|
250 | * |
---|
251 | * Compile_foreach() resets the given variable name to each specified |
---|
252 | * value, executing the commands enclosed in braces for each iteration. |
---|
253 | * |
---|
254 | * parse->string index variable |
---|
255 | * parse->left variable values |
---|
256 | * parse->right rule to compile |
---|
257 | */ |
---|
258 | |
---|
259 | LIST * |
---|
260 | compile_foreach( |
---|
261 | PARSE *parse, |
---|
262 | FRAME *frame ) |
---|
263 | { |
---|
264 | LIST *nv = parse_evaluate( parse->left, frame ); |
---|
265 | LIST *l; |
---|
266 | SETTINGS *s = 0; |
---|
267 | |
---|
268 | if ( parse->num ) |
---|
269 | { |
---|
270 | s = addsettings( s, 0, parse->string, L0 ); |
---|
271 | pushsettings( s ); |
---|
272 | } |
---|
273 | |
---|
274 | /* Call var_set to reset $(parse->string) for each val. */ |
---|
275 | |
---|
276 | for( l = nv; l; l = list_next( l ) ) |
---|
277 | { |
---|
278 | LIST *val = list_new( L0, copystr( l->string ) ); |
---|
279 | |
---|
280 | var_set( parse->string, val, VAR_SET ); |
---|
281 | |
---|
282 | list_free( parse_evaluate( parse->right, frame ) ); |
---|
283 | } |
---|
284 | |
---|
285 | if ( parse->num ) |
---|
286 | { |
---|
287 | popsettings( s ); |
---|
288 | freesettings( s ); |
---|
289 | } |
---|
290 | |
---|
291 | list_free( nv ); |
---|
292 | |
---|
293 | return L0; |
---|
294 | } |
---|
295 | |
---|
296 | /* |
---|
297 | * compile_if() - compile 'if' rule |
---|
298 | * |
---|
299 | * parse->left condition tree |
---|
300 | * parse->right then tree |
---|
301 | * parse->third else tree |
---|
302 | */ |
---|
303 | |
---|
304 | LIST * |
---|
305 | compile_if( |
---|
306 | PARSE *p, |
---|
307 | FRAME *frame ) |
---|
308 | { |
---|
309 | LIST *l = parse_evaluate( p->left, frame ); |
---|
310 | if( l ) |
---|
311 | { |
---|
312 | list_free( l ); |
---|
313 | return parse_evaluate( p->right, frame ); |
---|
314 | } |
---|
315 | else |
---|
316 | { |
---|
317 | return parse_evaluate( p->third, frame ); |
---|
318 | } |
---|
319 | } |
---|
320 | |
---|
321 | LIST * |
---|
322 | compile_while( |
---|
323 | PARSE *p, |
---|
324 | FRAME *frame ) |
---|
325 | { |
---|
326 | LIST *r = 0; |
---|
327 | LIST *l; |
---|
328 | while ( l = parse_evaluate( p->left, frame ) ) |
---|
329 | { |
---|
330 | list_free( l ); |
---|
331 | if( r ) list_free( r ); |
---|
332 | r = parse_evaluate( p->right, frame ); |
---|
333 | } |
---|
334 | return r; |
---|
335 | } |
---|
336 | |
---|
337 | |
---|
338 | /* |
---|
339 | * compile_include() - support for 'include' - call include() on file |
---|
340 | * |
---|
341 | * parse->left list of files to include (can only do 1) |
---|
342 | */ |
---|
343 | |
---|
344 | LIST * |
---|
345 | compile_include( |
---|
346 | PARSE *parse, |
---|
347 | FRAME *frame ) |
---|
348 | { |
---|
349 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
350 | |
---|
351 | if( DEBUG_COMPILE ) |
---|
352 | { |
---|
353 | debug_compile( 0, "include", frame); |
---|
354 | list_print( nt ); |
---|
355 | printf( "\n" ); |
---|
356 | } |
---|
357 | |
---|
358 | if( nt ) |
---|
359 | { |
---|
360 | TARGET *t = bindtarget( nt->string ); |
---|
361 | |
---|
362 | /* DWA 2001/10/22 - Perforce Jam clears the arguments here, which |
---|
363 | * prevents an included file from being treated as part of the body |
---|
364 | * of a rule. I didn't see any reason to do that, so I lifted the |
---|
365 | * restriction. |
---|
366 | */ |
---|
367 | |
---|
368 | /* Bind the include file under the influence of */ |
---|
369 | /* "on-target" variables. Though they are targets, */ |
---|
370 | /* include files are not built with make(). */ |
---|
371 | |
---|
372 | pushsettings( t->settings ); |
---|
373 | /* We don't expect that file to be included is generated by some |
---|
374 | action. Therefore, pass 0 as third argument. */ |
---|
375 | t->boundname = search( t->name, &t->time, 0 ); |
---|
376 | popsettings( t->settings ); |
---|
377 | |
---|
378 | parse_file( t->boundname, frame ); |
---|
379 | } |
---|
380 | |
---|
381 | list_free( nt ); |
---|
382 | |
---|
383 | return L0; |
---|
384 | } |
---|
385 | |
---|
386 | static LIST* evaluate_in_module ( char* module_name, PARSE * p, FRAME* frame) |
---|
387 | { |
---|
388 | LIST* result; |
---|
389 | |
---|
390 | module_t* outer_module = frame->module; |
---|
391 | frame->module = module_name ? bindmodule( module_name ) : root_module(); |
---|
392 | |
---|
393 | if ( outer_module != frame->module ) |
---|
394 | { |
---|
395 | exit_module( outer_module ); |
---|
396 | enter_module( frame->module ); |
---|
397 | } |
---|
398 | |
---|
399 | result = parse_evaluate( p, frame ); |
---|
400 | |
---|
401 | if ( outer_module != frame->module ) |
---|
402 | { |
---|
403 | exit_module( frame->module ); |
---|
404 | enter_module( outer_module ); |
---|
405 | frame->module = outer_module; |
---|
406 | } |
---|
407 | |
---|
408 | return result; |
---|
409 | } |
---|
410 | |
---|
411 | LIST * |
---|
412 | compile_module( |
---|
413 | PARSE *p, |
---|
414 | FRAME *frame ) |
---|
415 | { |
---|
416 | /* Here we are entering a module declaration block. |
---|
417 | */ |
---|
418 | LIST* module_name = parse_evaluate( p->left, frame ); |
---|
419 | LIST* result = evaluate_in_module( module_name ? module_name->string : 0, |
---|
420 | p->right, frame ); |
---|
421 | |
---|
422 | list_free( module_name ); |
---|
423 | return result; |
---|
424 | } |
---|
425 | |
---|
426 | LIST * |
---|
427 | compile_class( |
---|
428 | PARSE *p, |
---|
429 | FRAME *frame ) |
---|
430 | { |
---|
431 | /** Todo: check for empty class name. |
---|
432 | Check for class redeclaration. */ |
---|
433 | |
---|
434 | char* class_module = 0; |
---|
435 | |
---|
436 | LIST* name = parse_evaluate( p->left->right, frame ); |
---|
437 | LIST* bases = 0; |
---|
438 | |
---|
439 | if (p->left->left) |
---|
440 | bases = parse_evaluate( p->left->left->right, frame ); |
---|
441 | |
---|
442 | class_module = make_class_module(name, bases, frame); |
---|
443 | evaluate_in_module( class_module, p->right, frame ); |
---|
444 | |
---|
445 | return L0; |
---|
446 | } |
---|
447 | |
---|
448 | |
---|
449 | /* |
---|
450 | * compile_list() - expand and return a list |
---|
451 | * |
---|
452 | * parse->string - character string to expand |
---|
453 | */ |
---|
454 | |
---|
455 | LIST * |
---|
456 | compile_list( |
---|
457 | PARSE *parse, |
---|
458 | FRAME *frame ) |
---|
459 | { |
---|
460 | /* voodoo 1 means: s is a copyable string */ |
---|
461 | char *s = parse->string; |
---|
462 | return var_expand( L0, s, s + strlen( s ), frame->args, 1 ); |
---|
463 | } |
---|
464 | |
---|
465 | /* |
---|
466 | * compile_local() - declare (and set) local variables |
---|
467 | * |
---|
468 | * parse->left list of variables |
---|
469 | * parse->right list of values |
---|
470 | * parse->third rules to execute |
---|
471 | */ |
---|
472 | |
---|
473 | LIST * |
---|
474 | compile_local( |
---|
475 | PARSE *parse, |
---|
476 | FRAME *frame ) |
---|
477 | { |
---|
478 | LIST *l; |
---|
479 | SETTINGS *s = 0; |
---|
480 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
481 | LIST *ns = parse_evaluate( parse->right, frame ); |
---|
482 | LIST *result; |
---|
483 | |
---|
484 | if( DEBUG_COMPILE ) |
---|
485 | { |
---|
486 | debug_compile( 0, "local", frame); |
---|
487 | list_print( nt ); |
---|
488 | printf( " = " ); |
---|
489 | list_print( ns ); |
---|
490 | printf( "\n" ); |
---|
491 | } |
---|
492 | |
---|
493 | /* Initial value is ns */ |
---|
494 | |
---|
495 | for( l = nt; l; l = list_next( l ) ) |
---|
496 | s = addsettings( s, 0, l->string, list_copy( (LIST*)0, ns ) ); |
---|
497 | |
---|
498 | list_free( ns ); |
---|
499 | list_free( nt ); |
---|
500 | |
---|
501 | /* Note that callees of the current context get this "local" */ |
---|
502 | /* variable, making it not so much local as layered. */ |
---|
503 | |
---|
504 | pushsettings( s ); |
---|
505 | result = parse_evaluate( parse->third, frame ); |
---|
506 | popsettings( s ); |
---|
507 | |
---|
508 | freesettings( s ); |
---|
509 | |
---|
510 | return result; |
---|
511 | } |
---|
512 | |
---|
513 | /* |
---|
514 | * compile_null() - do nothing -- a stub for parsing |
---|
515 | */ |
---|
516 | |
---|
517 | LIST * |
---|
518 | compile_null( |
---|
519 | PARSE *parse, |
---|
520 | FRAME *frame ) |
---|
521 | { |
---|
522 | return L0; |
---|
523 | } |
---|
524 | |
---|
525 | /* |
---|
526 | * compile_on() - run rule under influence of on-target variables |
---|
527 | * |
---|
528 | * parse->left list of files to include (can only do 1) |
---|
529 | * parse->right rule to run |
---|
530 | * |
---|
531 | * EXPERIMENTAL! |
---|
532 | */ |
---|
533 | |
---|
534 | LIST * |
---|
535 | compile_on( |
---|
536 | PARSE *parse, |
---|
537 | FRAME *frame ) |
---|
538 | { |
---|
539 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
540 | LIST *result = 0; |
---|
541 | |
---|
542 | if( DEBUG_COMPILE ) |
---|
543 | { |
---|
544 | debug_compile( 0, "on", frame ); |
---|
545 | list_print( nt ); |
---|
546 | printf( "\n" ); |
---|
547 | } |
---|
548 | |
---|
549 | if( nt ) |
---|
550 | { |
---|
551 | TARGET *t = bindtarget( nt->string ); |
---|
552 | pushsettings( t->settings ); |
---|
553 | |
---|
554 | result = parse_evaluate( parse->right, frame ); |
---|
555 | |
---|
556 | popsettings( t->settings ); |
---|
557 | } |
---|
558 | |
---|
559 | list_free( nt ); |
---|
560 | |
---|
561 | return result; |
---|
562 | } |
---|
563 | |
---|
564 | |
---|
565 | /* |
---|
566 | * compile_rule() - compile a single user defined rule |
---|
567 | * |
---|
568 | * parse->string name of user defined rule |
---|
569 | * parse->left parameters (list of lists) to rule, recursing left |
---|
570 | * |
---|
571 | * Wrapped around evaluate_rule() so that headers() can share it. |
---|
572 | */ |
---|
573 | |
---|
574 | LIST * |
---|
575 | compile_rule( |
---|
576 | PARSE *parse, |
---|
577 | FRAME *frame ) |
---|
578 | { |
---|
579 | FRAME inner[1]; |
---|
580 | LIST *result; |
---|
581 | PARSE *p; |
---|
582 | |
---|
583 | |
---|
584 | /* Build up the list of arg lists */ |
---|
585 | |
---|
586 | frame_init( inner ); |
---|
587 | inner->prev = frame; |
---|
588 | inner->prev_user = frame->module->user_module ? frame : frame->prev_user; |
---|
589 | inner->module = frame->module; /* This gets fixed up in evaluate_rule(), below */ |
---|
590 | inner->procedure = parse; |
---|
591 | |
---|
592 | for( p = parse->left; p; p = p->left ) |
---|
593 | lol_add( inner->args, parse_evaluate( p->right, frame ) ); |
---|
594 | |
---|
595 | /* And invoke rule */ |
---|
596 | |
---|
597 | result = evaluate_rule( parse->string, inner ); |
---|
598 | |
---|
599 | frame_free( inner ); |
---|
600 | |
---|
601 | return result; |
---|
602 | } |
---|
603 | |
---|
604 | static void argument_error( char* message, RULE* rule, FRAME* frame, LIST* arg ) |
---|
605 | { |
---|
606 | LOL* actual = frame->args; |
---|
607 | assert( frame->procedure != 0 ); |
---|
608 | backtrace_line( frame->prev ); |
---|
609 | printf( "*** argument error\n* rule %s ( ", frame->rulename ); |
---|
610 | lol_print( rule->arguments->data ); |
---|
611 | printf( " )\n* called with: ( " ); |
---|
612 | lol_print( actual ); |
---|
613 | printf( " )\n* %s %s\n", message, arg ? arg->string : "" ); |
---|
614 | print_source_line( rule->procedure ); |
---|
615 | printf( "see definition of rule '%s' being called\n", rule->name ); |
---|
616 | backtrace( frame->prev ); |
---|
617 | exit(1); |
---|
618 | } |
---|
619 | |
---|
620 | /* define delimiters for type check elements in argument lists (and |
---|
621 | * return type specifications, eventually) |
---|
622 | */ |
---|
623 | # define TYPE_OPEN_DELIM '[' |
---|
624 | # define TYPE_CLOSE_DELIM ']' |
---|
625 | |
---|
626 | /* is_type_name - true iff the given string represents a type check |
---|
627 | * specification |
---|
628 | */ |
---|
629 | static int |
---|
630 | is_type_name( char* s ) |
---|
631 | { |
---|
632 | return s[0] == TYPE_OPEN_DELIM |
---|
633 | && s[strlen(s) - 1] == TYPE_CLOSE_DELIM; |
---|
634 | } |
---|
635 | |
---|
636 | /* |
---|
637 | * arg_modifier - if the next element of formal is a single character, |
---|
638 | * return that; return 0 otherwise. Used to extract "*+?" modifiers |
---|
639 | * from argument lists. |
---|
640 | */ |
---|
641 | static char |
---|
642 | arg_modifier( LIST* formal ) |
---|
643 | { |
---|
644 | if ( formal->next ) |
---|
645 | { |
---|
646 | char *next = formal->next->string; |
---|
647 | if ( next && next[0] != 0 && next[1] == 0 ) |
---|
648 | return next[0]; |
---|
649 | } |
---|
650 | return 0; |
---|
651 | } |
---|
652 | |
---|
653 | /* |
---|
654 | * type_check - checks that each element of values satisfies the |
---|
655 | * requirements of type_name. |
---|
656 | * |
---|
657 | * caller - the frame of the rule calling the rule whose |
---|
658 | * arguments are being checked |
---|
659 | * |
---|
660 | * called - the rule being called |
---|
661 | * |
---|
662 | * arg_name - a list element containing the name of the argument |
---|
663 | * being checked |
---|
664 | */ |
---|
665 | static void |
---|
666 | type_check( char* type_name, LIST *values, FRAME* caller, RULE* called, LIST* arg_name ) |
---|
667 | { |
---|
668 | static module_t *typecheck = 0; |
---|
669 | |
---|
670 | /* if nothing to check, bail now */ |
---|
671 | if ( !values || !type_name ) |
---|
672 | return; |
---|
673 | |
---|
674 | if ( !typecheck ) |
---|
675 | typecheck = bindmodule(".typecheck"); |
---|
676 | |
---|
677 | /* if the checking rule can't be found, also bail */ |
---|
678 | { |
---|
679 | RULE checker_, *checker = &checker_; |
---|
680 | |
---|
681 | checker->name = type_name; |
---|
682 | if ( !typecheck->rules || !hashcheck( typecheck->rules, (HASHDATA**)&checker ) ) |
---|
683 | return; |
---|
684 | } |
---|
685 | |
---|
686 | exit_module( caller->module ); |
---|
687 | |
---|
688 | while ( values != 0 ) |
---|
689 | { |
---|
690 | LIST *error; |
---|
691 | FRAME frame[1]; |
---|
692 | frame_init( frame ); |
---|
693 | frame->module = typecheck; |
---|
694 | frame->prev = caller; |
---|
695 | frame->prev_user = caller->module->user_module ? caller : caller->prev_user; |
---|
696 | |
---|
697 | enter_module( typecheck ); |
---|
698 | /* Prepare the argument list */ |
---|
699 | lol_add( frame->args, list_new( L0, values->string ) ); |
---|
700 | error = evaluate_rule( type_name, frame ); |
---|
701 | |
---|
702 | exit_module( typecheck ); |
---|
703 | |
---|
704 | if ( error ) |
---|
705 | argument_error( error->string, called, caller, arg_name ); |
---|
706 | |
---|
707 | frame_free( frame ); |
---|
708 | values = values->next; |
---|
709 | } |
---|
710 | |
---|
711 | enter_module( caller->module ); |
---|
712 | } |
---|
713 | |
---|
714 | /* |
---|
715 | * collect_arguments() - local argument checking and collection |
---|
716 | */ |
---|
717 | static SETTINGS * |
---|
718 | collect_arguments( RULE* rule, FRAME* frame ) |
---|
719 | { |
---|
720 | SETTINGS *locals = 0; |
---|
721 | |
---|
722 | LOL* all_actual = frame->args; |
---|
723 | LOL *all_formal = rule->arguments ? rule->arguments->data : 0; |
---|
724 | if ( all_formal ) /* Nothing to set; nothing to check */ |
---|
725 | { |
---|
726 | int max = all_formal->count > all_actual->count |
---|
727 | ? all_formal->count |
---|
728 | : all_actual->count; |
---|
729 | |
---|
730 | int n; |
---|
731 | for ( n = 0; n < max ; ++n ) |
---|
732 | { |
---|
733 | LIST *actual = lol_get( all_actual, n ); |
---|
734 | char *type_name = 0; |
---|
735 | |
---|
736 | LIST *formal; |
---|
737 | for ( formal = lol_get( all_formal, n ); formal; formal = formal->next ) |
---|
738 | { |
---|
739 | char* name = formal->string; |
---|
740 | |
---|
741 | if ( is_type_name(name) ) |
---|
742 | { |
---|
743 | if ( type_name ) |
---|
744 | argument_error( "missing argument name before type name:", rule, frame, formal ); |
---|
745 | |
---|
746 | if ( !formal->next ) |
---|
747 | argument_error( "missing argument name after type name:", rule, frame, formal ); |
---|
748 | |
---|
749 | type_name = formal->string; |
---|
750 | } |
---|
751 | else |
---|
752 | { |
---|
753 | LIST* value = 0; |
---|
754 | char modifier; |
---|
755 | LIST* arg_name = formal; /* hold the argument name for type checking */ |
---|
756 | |
---|
757 | /* Stop now if a variable number of arguments are specified */ |
---|
758 | if ( name[0] == '*' && name[1] == 0 ) |
---|
759 | return locals; |
---|
760 | |
---|
761 | modifier = arg_modifier( formal ); |
---|
762 | |
---|
763 | if ( !actual && modifier != '?' && modifier != '*' ) |
---|
764 | argument_error( "missing argument", rule, frame, formal ); |
---|
765 | |
---|
766 | switch ( modifier ) |
---|
767 | { |
---|
768 | case '+': |
---|
769 | case '*': |
---|
770 | value = list_copy( 0, actual ); |
---|
771 | actual = 0; |
---|
772 | /* skip an extra element for the modifier */ |
---|
773 | formal = formal->next; |
---|
774 | break; |
---|
775 | case '?': |
---|
776 | /* skip an extra element for the modifier */ |
---|
777 | formal = formal->next; |
---|
778 | /* fall through */ |
---|
779 | default: |
---|
780 | if ( actual ) /* in case actual is missing */ |
---|
781 | { |
---|
782 | value = list_new( 0, actual->string ); |
---|
783 | actual = actual->next; |
---|
784 | } |
---|
785 | } |
---|
786 | |
---|
787 | locals = addsettings( locals, 0, name, value ); |
---|
788 | type_check( type_name, value, frame, rule, arg_name ); |
---|
789 | type_name = 0; |
---|
790 | } |
---|
791 | } |
---|
792 | |
---|
793 | if ( actual ) |
---|
794 | { |
---|
795 | argument_error( "extra argument", rule, frame, actual ); |
---|
796 | } |
---|
797 | } |
---|
798 | } |
---|
799 | return locals; |
---|
800 | } |
---|
801 | |
---|
802 | struct profile_info |
---|
803 | { |
---|
804 | char* name; /* name of rule being called */ |
---|
805 | clock_t cumulative; /* cumulative time spent in rule */ |
---|
806 | clock_t net; /* time spent in rule proper */ |
---|
807 | unsigned long num_entries; /* number of time rule was entered */ |
---|
808 | unsigned long stack_count; /* number of the times this function is present in stack */ |
---|
809 | }; |
---|
810 | typedef struct profile_info profile_info; |
---|
811 | |
---|
812 | struct profile_frame |
---|
813 | { |
---|
814 | profile_info* info; /* permanent storage where data accumulates */ |
---|
815 | clock_t overhead; /* overhead for profiling in this call */ |
---|
816 | clock_t entry_time; /* time of last entry to rule */ |
---|
817 | struct profile_frame* caller; /* stack frame of caller */ |
---|
818 | clock_t subrules; /* time spent in subrules */ |
---|
819 | }; |
---|
820 | typedef struct profile_frame profile_frame; |
---|
821 | |
---|
822 | static profile_frame* profile_stack = 0; |
---|
823 | static struct hash* profile_hash = 0; |
---|
824 | |
---|
825 | static void profile_enter( char* rulename, profile_frame* frame ) |
---|
826 | { |
---|
827 | clock_t start = clock(); |
---|
828 | profile_info info, *p = &info; |
---|
829 | |
---|
830 | if ( !profile_hash ) |
---|
831 | profile_hash = hashinit(sizeof(profile_info), "profile"); |
---|
832 | |
---|
833 | info.name = rulename; |
---|
834 | |
---|
835 | if ( hashenter( profile_hash, (HASHDATA **)&p ) ) |
---|
836 | p->cumulative = p->net = p->num_entries = p->stack_count = 0; |
---|
837 | |
---|
838 | ++(p->num_entries); |
---|
839 | ++(p->stack_count); |
---|
840 | |
---|
841 | frame->info = p; |
---|
842 | |
---|
843 | frame->caller = profile_stack; |
---|
844 | profile_stack = frame; |
---|
845 | |
---|
846 | frame->entry_time = clock(); |
---|
847 | frame->overhead = 0; |
---|
848 | frame->subrules = 0; |
---|
849 | |
---|
850 | /* caller pays for the time it takes to play with the hash table */ |
---|
851 | if ( frame->caller ) |
---|
852 | frame->caller->overhead += frame->entry_time - start; |
---|
853 | } |
---|
854 | |
---|
855 | static void profile_exit(profile_frame* frame) |
---|
856 | { |
---|
857 | /* cumulative time for this call */ |
---|
858 | clock_t t = clock() - frame->entry_time - frame->overhead; |
---|
859 | /* If this rule is already present on the stack, don't add the time for |
---|
860 | this instance. */ |
---|
861 | if (frame->info->stack_count == 1) |
---|
862 | frame->info->cumulative += t; |
---|
863 | /* Net time does not depend on presense of the same rule in call stack. */ |
---|
864 | frame->info->net += t - frame->subrules; |
---|
865 | |
---|
866 | if (frame->caller) |
---|
867 | { |
---|
868 | /* caller's cumulative time must account for this overhead */ |
---|
869 | frame->caller->overhead += frame->overhead; |
---|
870 | frame->caller->subrules += t; |
---|
871 | } |
---|
872 | /* pop this stack frame */ |
---|
873 | --frame->info->stack_count; |
---|
874 | profile_stack = frame->caller; |
---|
875 | } |
---|
876 | |
---|
877 | static void dump_profile_entry(void* p_, void* ignored) |
---|
878 | { |
---|
879 | profile_info* p = (profile_info*)p_; |
---|
880 | printf("%10d %10d %10d %s\n", p->cumulative, p->net, p->num_entries, p->name); |
---|
881 | } |
---|
882 | |
---|
883 | void profile_dump() |
---|
884 | { |
---|
885 | if ( profile_hash ) |
---|
886 | { |
---|
887 | printf("%10s %10s %10s %s\n", "gross", "net", "# entries", "name"); |
---|
888 | hashenumerate( profile_hash, dump_profile_entry, 0 ); |
---|
889 | } |
---|
890 | } |
---|
891 | |
---|
892 | static int python_instance_number = 0; |
---|
893 | |
---|
894 | RULE * |
---|
895 | enter_rule( char *rulename, module_t *target_module ); |
---|
896 | |
---|
897 | #ifdef HAVE_PYTHON |
---|
898 | static LIST* |
---|
899 | call_python_function(RULE* r, FRAME* frame) |
---|
900 | { |
---|
901 | LIST* result = 0; |
---|
902 | PyObject* arguments = PyTuple_New(frame->args->count); |
---|
903 | int i ; |
---|
904 | PyObject* py_result; |
---|
905 | |
---|
906 | for(i = 0; i < frame->args->count; ++i) |
---|
907 | { |
---|
908 | PyObject* arg = PyList_New(0); |
---|
909 | LIST* l = lol_get( frame->args, i); |
---|
910 | |
---|
911 | for(; l; l = l->next) |
---|
912 | { |
---|
913 | PyObject* v = PyString_FromString(l->string); |
---|
914 | /* Steals reference to 'v' */ |
---|
915 | PyList_Append(arg, v); |
---|
916 | } |
---|
917 | /* Steals reference to 'arg' */ |
---|
918 | PyTuple_SetItem(arguments, i, arg); |
---|
919 | } |
---|
920 | |
---|
921 | py_result = PyObject_CallObject(r->python_function, arguments); |
---|
922 | Py_DECREF(arguments); |
---|
923 | if (py_result != NULL) { |
---|
924 | |
---|
925 | if (PyList_Check(py_result)) { |
---|
926 | int size = PyList_Size(py_result); |
---|
927 | int i; |
---|
928 | for(i = 0; i < size; ++i) |
---|
929 | { |
---|
930 | PyObject* item = PyList_GetItem(py_result, i); |
---|
931 | if (PyString_Check(item)) |
---|
932 | { |
---|
933 | result = list_new(result, |
---|
934 | newstr(PyString_AsString(item))); |
---|
935 | } |
---|
936 | else |
---|
937 | { |
---|
938 | fprintf(stderr, "Non-string object returned by Python call\n"); |
---|
939 | } |
---|
940 | } |
---|
941 | } |
---|
942 | else if (PyInstance_Check(py_result)) |
---|
943 | { |
---|
944 | static char instance_name[1000]; |
---|
945 | static char imported_method_name[1000]; |
---|
946 | module_t* m; |
---|
947 | PyObject* method; |
---|
948 | PyObject* method_name = PyString_FromString("foo"); |
---|
949 | RULE* r; |
---|
950 | |
---|
951 | fprintf(stderr, "Got instance!\n"); |
---|
952 | |
---|
953 | snprintf(instance_name, 1000, |
---|
954 | "pyinstance%d", python_instance_number); |
---|
955 | snprintf(imported_method_name, 1000, |
---|
956 | "pyinstance%d.foo", python_instance_number); |
---|
957 | ++python_instance_number; |
---|
958 | |
---|
959 | m = bindmodule(instance_name); |
---|
960 | |
---|
961 | /* This is expected to get bound method. */ |
---|
962 | method = PyObject_GetAttr(py_result, method_name); |
---|
963 | |
---|
964 | r = bindrule( imported_method_name, root_module() ); |
---|
965 | |
---|
966 | r->python_function = method; |
---|
967 | |
---|
968 | result = list_new(0, newstr(instance_name)); |
---|
969 | |
---|
970 | Py_DECREF(method_name); |
---|
971 | } |
---|
972 | else if (py_result == Py_None) |
---|
973 | { |
---|
974 | result = L0; |
---|
975 | } |
---|
976 | else |
---|
977 | { |
---|
978 | fprintf(stderr, "Non-list object returned by Python call\n"); |
---|
979 | } |
---|
980 | |
---|
981 | Py_DECREF(py_result); |
---|
982 | } |
---|
983 | else { |
---|
984 | PyErr_Print(); |
---|
985 | fprintf(stderr,"Call failed\n"); |
---|
986 | } |
---|
987 | |
---|
988 | return result; |
---|
989 | } |
---|
990 | #endif |
---|
991 | |
---|
992 | /* |
---|
993 | * evaluate_rule() - execute a rule invocation |
---|
994 | */ |
---|
995 | |
---|
996 | LIST * |
---|
997 | evaluate_rule( |
---|
998 | char *rulename, |
---|
999 | FRAME *frame ) |
---|
1000 | { |
---|
1001 | LIST *result = L0; |
---|
1002 | RULE *rule; |
---|
1003 | profile_frame prof[1]; |
---|
1004 | module_t *prev_module = frame->module; |
---|
1005 | |
---|
1006 | LIST *l; |
---|
1007 | { |
---|
1008 | LOL arg_context_, *arg_context = &arg_context_; |
---|
1009 | if ( !frame->prev ) |
---|
1010 | lol_init(arg_context); |
---|
1011 | else |
---|
1012 | arg_context = frame->prev->args; |
---|
1013 | |
---|
1014 | l = var_expand( L0, rulename, rulename+strlen(rulename), arg_context, 0 ); |
---|
1015 | } |
---|
1016 | |
---|
1017 | if ( !l ) |
---|
1018 | { |
---|
1019 | backtrace_line( frame->prev ); |
---|
1020 | printf( "warning: rulename %s expands to empty string\n", rulename ); |
---|
1021 | backtrace( frame->prev ); |
---|
1022 | return result; |
---|
1023 | } |
---|
1024 | |
---|
1025 | rulename = l->string; |
---|
1026 | rule = bindrule( l->string, frame->module ); |
---|
1027 | |
---|
1028 | #ifdef HAVE_PYTHON |
---|
1029 | if (rule->python_function) |
---|
1030 | { |
---|
1031 | return call_python_function(rule, frame); |
---|
1032 | } |
---|
1033 | #endif |
---|
1034 | |
---|
1035 | /* drop the rule name */ |
---|
1036 | l = list_pop_front( l ); |
---|
1037 | |
---|
1038 | /* tack the rest of the expansion onto the front of the first argument */ |
---|
1039 | frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) ); |
---|
1040 | |
---|
1041 | if ( DEBUG_COMPILE ) |
---|
1042 | { |
---|
1043 | /* Try hard to indicate in which module the rule is going to execute */ |
---|
1044 | if ( rule->module != frame->module |
---|
1045 | && rule->procedure != 0 && strcmp(rulename, rule->procedure->rulename) ) |
---|
1046 | { |
---|
1047 | char buf[256] = ""; |
---|
1048 | strncat( buf, rule->module->name, sizeof(buf) - 1 ); |
---|
1049 | strncat( buf, rule->name, sizeof(buf) - 1 ); |
---|
1050 | debug_compile( 1, buf, frame); |
---|
1051 | } |
---|
1052 | else |
---|
1053 | { |
---|
1054 | debug_compile( 1, rulename, frame); |
---|
1055 | } |
---|
1056 | |
---|
1057 | lol_print( frame->args ); |
---|
1058 | printf( "\n" ); |
---|
1059 | } |
---|
1060 | |
---|
1061 | if ( rule->procedure && rule->module != prev_module ) |
---|
1062 | { |
---|
1063 | /* propagate current module to nested rule invocations */ |
---|
1064 | frame->module = rule->module; |
---|
1065 | |
---|
1066 | /* swap variables */ |
---|
1067 | exit_module( prev_module ); |
---|
1068 | enter_module( rule->module ); |
---|
1069 | } |
---|
1070 | |
---|
1071 | /* record current rule name in frame */ |
---|
1072 | if ( rule->procedure ) |
---|
1073 | { |
---|
1074 | frame->rulename = rulename; |
---|
1075 | /* and enter record profile info */ |
---|
1076 | if ( DEBUG_PROFILE ) |
---|
1077 | profile_enter( rule->procedure->rulename, prof ); |
---|
1078 | } |
---|
1079 | |
---|
1080 | /* Check traditional targets $(<) and sources $(>) */ |
---|
1081 | |
---|
1082 | if( !rule->actions && !rule->procedure ) |
---|
1083 | { |
---|
1084 | backtrace_line( frame->prev ); |
---|
1085 | printf( "rule %s unknown in module %s\n", rule->name, frame->module->name ); |
---|
1086 | backtrace( frame->prev ); |
---|
1087 | exit(1); |
---|
1088 | } |
---|
1089 | |
---|
1090 | /* If this rule will be executed for updating the targets */ |
---|
1091 | /* then construct the action for make(). */ |
---|
1092 | |
---|
1093 | if( rule->actions ) |
---|
1094 | { |
---|
1095 | TARGETS *t; |
---|
1096 | ACTION *action; |
---|
1097 | |
---|
1098 | /* The action is associated with this instance of this rule */ |
---|
1099 | |
---|
1100 | action = (ACTION *)malloc( sizeof( ACTION ) ); |
---|
1101 | memset( (char *)action, '\0', sizeof( *action ) ); |
---|
1102 | |
---|
1103 | action->rule = rule; |
---|
1104 | action->targets = targetlist( (TARGETS *)0, lol_get( frame->args, 0 ) ); |
---|
1105 | action->sources = targetlist( (TARGETS *)0, lol_get( frame->args, 1 ) ); |
---|
1106 | |
---|
1107 | /* Append this action to the actions of each target */ |
---|
1108 | |
---|
1109 | for( t = action->targets; t; t = t->next ) |
---|
1110 | t->target->actions = actionlist( t->target->actions, action ); |
---|
1111 | } |
---|
1112 | |
---|
1113 | /* Now recursively compile any parse tree associated with this rule */ |
---|
1114 | /* refer/free to ensure rule not freed during use */ |
---|
1115 | |
---|
1116 | if( rule->procedure ) |
---|
1117 | { |
---|
1118 | SETTINGS *local_args = collect_arguments( rule, frame ); |
---|
1119 | PARSE *parse = rule->procedure; |
---|
1120 | parse_refer( parse ); |
---|
1121 | |
---|
1122 | pushsettings( local_args ); |
---|
1123 | result = parse_evaluate( parse, frame ); |
---|
1124 | popsettings( local_args ); |
---|
1125 | freesettings( local_args ); |
---|
1126 | |
---|
1127 | parse_free( parse ); |
---|
1128 | } |
---|
1129 | |
---|
1130 | if ( frame->module != prev_module ) |
---|
1131 | { |
---|
1132 | exit_module( frame->module ); |
---|
1133 | enter_module( prev_module ); |
---|
1134 | } |
---|
1135 | |
---|
1136 | if ( DEBUG_PROFILE && rule->procedure ) |
---|
1137 | profile_exit( prof ); |
---|
1138 | |
---|
1139 | if( DEBUG_COMPILE ) |
---|
1140 | debug_compile( -1, 0, frame); |
---|
1141 | |
---|
1142 | return result; |
---|
1143 | } |
---|
1144 | |
---|
1145 | /* |
---|
1146 | * Call the given rule with the specified parameters. |
---|
1147 | * The parameters should be of LIST* and end with NULL pointer. |
---|
1148 | * This differs from the 'evaluate_rule' in that frame |
---|
1149 | * for called rule is prepared in 'call_rule'. |
---|
1150 | * |
---|
1151 | * This function is usefull when builtin rule (in C) wants to |
---|
1152 | * call another rule, which might be implemented in Jam. |
---|
1153 | */ |
---|
1154 | LIST *call_rule( char *rulename, FRAME* caller_frame, ...) |
---|
1155 | { |
---|
1156 | va_list va; |
---|
1157 | LIST *result; |
---|
1158 | |
---|
1159 | FRAME inner[1]; |
---|
1160 | frame_init( inner ); |
---|
1161 | inner->prev = caller_frame; |
---|
1162 | inner->prev_user = caller_frame->module->user_module ? |
---|
1163 | caller_frame : caller_frame->prev_user; |
---|
1164 | inner->module = caller_frame->module; |
---|
1165 | inner->procedure = 0; |
---|
1166 | |
---|
1167 | va_start(va, caller_frame); |
---|
1168 | for(;;) |
---|
1169 | { |
---|
1170 | LIST* l = va_arg(va, LIST*); |
---|
1171 | if (!l) |
---|
1172 | break; |
---|
1173 | lol_add(inner->args, l); |
---|
1174 | } |
---|
1175 | va_end(va); |
---|
1176 | |
---|
1177 | result = evaluate_rule(rulename, inner); |
---|
1178 | |
---|
1179 | frame_free(inner); |
---|
1180 | |
---|
1181 | return result; |
---|
1182 | } |
---|
1183 | |
---|
1184 | /* |
---|
1185 | * compile_rules() - compile a chain of rules |
---|
1186 | * |
---|
1187 | * parse->left single rule |
---|
1188 | * parse->right more compile_rules() by right-recursion |
---|
1189 | */ |
---|
1190 | |
---|
1191 | LIST * |
---|
1192 | compile_rules( |
---|
1193 | PARSE *parse, |
---|
1194 | FRAME *frame ) |
---|
1195 | { |
---|
1196 | /* Ignore result from first statement; return the 2nd. */ |
---|
1197 | /* Optimize recursion on the right by looping. */ |
---|
1198 | |
---|
1199 | do list_free( parse_evaluate( parse->left, frame ) ); |
---|
1200 | while( (parse = parse->right)->func == compile_rules ); |
---|
1201 | |
---|
1202 | return parse_evaluate( parse, frame ); |
---|
1203 | } |
---|
1204 | |
---|
1205 | /* |
---|
1206 | * compile_set() - compile the "set variable" statement |
---|
1207 | * |
---|
1208 | * parse->left variable names |
---|
1209 | * parse->right variable values |
---|
1210 | * parse->num ASSIGN_SET/APPEND/DEFAULT |
---|
1211 | */ |
---|
1212 | |
---|
1213 | LIST * |
---|
1214 | compile_set( |
---|
1215 | PARSE *parse, |
---|
1216 | FRAME *frame ) |
---|
1217 | { |
---|
1218 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
1219 | LIST *ns = parse_evaluate( parse->right, frame ); |
---|
1220 | LIST *l; |
---|
1221 | int setflag; |
---|
1222 | char *trace; |
---|
1223 | |
---|
1224 | switch( parse->num ) |
---|
1225 | { |
---|
1226 | case ASSIGN_SET: setflag = VAR_SET; trace = "="; break; |
---|
1227 | case ASSIGN_APPEND: setflag = VAR_APPEND; trace = "+="; break; |
---|
1228 | case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break; |
---|
1229 | default: setflag = VAR_SET; trace = ""; break; |
---|
1230 | } |
---|
1231 | |
---|
1232 | if( DEBUG_COMPILE ) |
---|
1233 | { |
---|
1234 | debug_compile( 0, "set", frame); |
---|
1235 | list_print( nt ); |
---|
1236 | printf( " %s ", trace ); |
---|
1237 | list_print( ns ); |
---|
1238 | printf( "\n" ); |
---|
1239 | } |
---|
1240 | |
---|
1241 | /* Call var_set to set variable */ |
---|
1242 | /* var_set keeps ns, so need to copy it */ |
---|
1243 | |
---|
1244 | for( l = nt; l; l = list_next( l ) ) |
---|
1245 | var_set( l->string, list_copy( L0, ns ), setflag ); |
---|
1246 | |
---|
1247 | list_free( nt ); |
---|
1248 | |
---|
1249 | return ns; |
---|
1250 | } |
---|
1251 | |
---|
1252 | /* |
---|
1253 | * compile_setcomp() - support for `rule` - save parse tree |
---|
1254 | * |
---|
1255 | * parse->string rule name |
---|
1256 | * parse->left rules for rule |
---|
1257 | * parse->right optional list-of-lists describing arguments |
---|
1258 | */ |
---|
1259 | |
---|
1260 | LIST * |
---|
1261 | compile_setcomp( |
---|
1262 | PARSE *parse, |
---|
1263 | FRAME *frame) |
---|
1264 | { |
---|
1265 | argument_list* arg_list = 0; |
---|
1266 | |
---|
1267 | /* Create new LOL describing argument requirements if supplied */ |
---|
1268 | if ( parse->right ) |
---|
1269 | { |
---|
1270 | PARSE *p; |
---|
1271 | arg_list = args_new(); |
---|
1272 | for( p = parse->right; p; p = p->left ) |
---|
1273 | lol_add( arg_list->data, parse_evaluate( p->right, frame ) ); |
---|
1274 | } |
---|
1275 | |
---|
1276 | new_rule_body( frame->module, parse->string, arg_list, parse->left, !parse->num ); |
---|
1277 | return L0; |
---|
1278 | } |
---|
1279 | |
---|
1280 | /* |
---|
1281 | * compile_setexec() - support for `actions` - save execution string |
---|
1282 | * |
---|
1283 | * parse->string rule name |
---|
1284 | * parse->string1 OS command string |
---|
1285 | * parse->num flags |
---|
1286 | * parse->left `bind` variables |
---|
1287 | * |
---|
1288 | * Note that the parse flags (as defined in compile.h) are transfered |
---|
1289 | * directly to the rule flags (as defined in rules.h). |
---|
1290 | */ |
---|
1291 | |
---|
1292 | LIST * |
---|
1293 | compile_setexec( |
---|
1294 | PARSE *parse, |
---|
1295 | FRAME *frame ) |
---|
1296 | { |
---|
1297 | LIST* bindlist = parse_evaluate( parse->left, frame ); |
---|
1298 | |
---|
1299 | new_rule_actions( frame->module, parse->string, parse->string1, bindlist, parse->num ); |
---|
1300 | |
---|
1301 | return L0; |
---|
1302 | } |
---|
1303 | |
---|
1304 | /* |
---|
1305 | * compile_settings() - compile the "on =" (set variable on exec) statement |
---|
1306 | * |
---|
1307 | * parse->left variable names |
---|
1308 | * parse->right target name |
---|
1309 | * parse->third variable value |
---|
1310 | * parse->num ASSIGN_SET/APPEND |
---|
1311 | */ |
---|
1312 | |
---|
1313 | LIST * |
---|
1314 | compile_settings( |
---|
1315 | PARSE *parse, |
---|
1316 | FRAME *frame ) |
---|
1317 | { |
---|
1318 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
1319 | LIST *ns = parse_evaluate( parse->third, frame ); |
---|
1320 | LIST *targets = parse_evaluate( parse->right, frame ); |
---|
1321 | LIST *ts; |
---|
1322 | int append = parse->num == ASSIGN_APPEND; |
---|
1323 | |
---|
1324 | if( DEBUG_COMPILE ) |
---|
1325 | { |
---|
1326 | debug_compile( 0, "set", frame); |
---|
1327 | list_print( nt ); |
---|
1328 | printf( " on " ); |
---|
1329 | list_print( targets ); |
---|
1330 | printf( " %s ", append ? "+=" : "=" ); |
---|
1331 | list_print( ns ); |
---|
1332 | printf( "\n" ); |
---|
1333 | } |
---|
1334 | |
---|
1335 | /* Call addsettings to save variable setting */ |
---|
1336 | /* addsettings keeps ns, so need to copy it */ |
---|
1337 | /* Pass append flag to addsettings() */ |
---|
1338 | |
---|
1339 | for( ts = targets; ts; ts = list_next( ts ) ) |
---|
1340 | { |
---|
1341 | TARGET *t = bindtarget( ts->string ); |
---|
1342 | LIST *l; |
---|
1343 | |
---|
1344 | for( l = nt; l; l = list_next( l ) ) |
---|
1345 | t->settings = addsettings( t->settings, append, |
---|
1346 | l->string, list_copy( (LIST*)0, ns ) ); |
---|
1347 | } |
---|
1348 | |
---|
1349 | list_free( nt ); |
---|
1350 | list_free( targets ); |
---|
1351 | |
---|
1352 | return ns; |
---|
1353 | } |
---|
1354 | |
---|
1355 | /* |
---|
1356 | * compile_switch() - compile 'switch' rule |
---|
1357 | * |
---|
1358 | * parse->left switch value (only 1st used) |
---|
1359 | * parse->right cases |
---|
1360 | * |
---|
1361 | * cases->left 1st case |
---|
1362 | * cases->right next cases |
---|
1363 | * |
---|
1364 | * case->string argument to match |
---|
1365 | * case->left parse tree to execute |
---|
1366 | */ |
---|
1367 | |
---|
1368 | LIST * |
---|
1369 | compile_switch( |
---|
1370 | PARSE *parse, |
---|
1371 | FRAME *frame ) |
---|
1372 | { |
---|
1373 | LIST *nt = parse_evaluate( parse->left, frame ); |
---|
1374 | LIST *result = 0; |
---|
1375 | |
---|
1376 | if( DEBUG_COMPILE ) |
---|
1377 | { |
---|
1378 | debug_compile( 0, "switch", frame); |
---|
1379 | list_print( nt ); |
---|
1380 | printf( "\n" ); |
---|
1381 | } |
---|
1382 | |
---|
1383 | /* Step through cases */ |
---|
1384 | |
---|
1385 | for( parse = parse->right; parse; parse = parse->right ) |
---|
1386 | { |
---|
1387 | if( !glob( parse->left->string, nt ? nt->string : "" ) ) |
---|
1388 | { |
---|
1389 | /* Get & exec parse tree for this case */ |
---|
1390 | parse = parse->left->left; |
---|
1391 | result = parse_evaluate( parse, frame ); |
---|
1392 | break; |
---|
1393 | } |
---|
1394 | } |
---|
1395 | |
---|
1396 | list_free( nt ); |
---|
1397 | |
---|
1398 | return result; |
---|
1399 | } |
---|
1400 | |
---|
1401 | /* |
---|
1402 | * debug_compile() - printf with indent to show rule expansion. |
---|
1403 | */ |
---|
1404 | |
---|
1405 | static void |
---|
1406 | debug_compile( int which, char *s, FRAME* frame ) |
---|
1407 | { |
---|
1408 | static int level = 0; |
---|
1409 | static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; |
---|
1410 | |
---|
1411 | if ( which >= 0 ) |
---|
1412 | { |
---|
1413 | int i; |
---|
1414 | |
---|
1415 | print_source_line( frame->procedure ); |
---|
1416 | |
---|
1417 | i = (level+1)*2; |
---|
1418 | while ( i > 35 ) |
---|
1419 | { |
---|
1420 | printf( indent ); |
---|
1421 | i -= 35; |
---|
1422 | } |
---|
1423 | |
---|
1424 | printf( "%*.*s ", i, i, indent ); |
---|
1425 | } |
---|
1426 | |
---|
1427 | if( s ) |
---|
1428 | printf( "%s ", s ); |
---|
1429 | |
---|
1430 | level += which; |
---|
1431 | } |
---|