/* * Copyright 1993, 2000 Christopher Seiwald. * * This file is part of Jam - see jam.c for Copyright information. */ #include "Compile.hpp" #include "Expand.hpp" #include "Glob.hpp" #include "Make.hpp" #include "Search.hpp" #include "String.hpp" #include "Variable.hpp" using Jam::List; using Jam::Parse; using Jam::String; /* * compile.c - compile parsed jam statements * * External routines: * * compile_append() - append list results of two statements * compile_foreach() - compile the "for x in y" statement * compile_if() - compile 'if' rule * compile_include() - support for 'include' - call include() on file * compile_list() - expand and return a list * compile_local() - declare (and set) local variables * compile_null() - do nothing -- a stub for parsing * compile_rule() - compile a single user defined rule * compile_rules() - compile a chain of rules * compile_set() - compile the "set variable" statement * compile_setcomp() - support for `rule` - save parse tree * compile_setexec() - support for `actions` - save execution string * compile_settings() - compile the "on =" (set variable on exec) statement * compile_switch() - compile 'switch' rule * * Internal routines: * * debug_compile() - printf with indent to show rule expansion. * * evaluate_if() - evaluate if to determine which leg to compile * evaluate_rule() - execute a rule invocation * * builtin_depends() - DEPENDS/INCLUDES rule * builtin_echo() - ECHO rule * builtin_exit() - EXIT rule * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule * * 02/03/94 (seiwald) - Changed trace output to read "setting" instead of * the awkward sounding "settings". * 04/12/94 (seiwald) - Combined build_depends() with build_includes(). * 04/12/94 (seiwald) - actionlist() now just appends a single action. * 04/13/94 (seiwald) - added shorthand L0 for null list pointer * 05/13/94 (seiwald) - include files are now bound as targets, and thus * can make use of $(SEARCH) * 06/01/94 (seiwald) - new 'actions existing' does existing sources * 08/23/94 (seiwald) - Support for '+=' (append to variable) * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. * 01/22/95 (seiwald) - Exit rule. * 02/02/95 (seiwald) - Always rule; LEAVES rule. * 02/14/95 (seiwald) - NoUpdate rule. * 09/11/00 (seiwald) - new evaluate_rule() for headers(). * 09/11/00 (seiwald) - compile_xxx() now return LIST *. * New compile_append() and compile_list() in * support of building lists here, rather than * in jamgram.yy. */ static void debug_compile( int which, const char *s ); static int evaluate_if( Parse *parse, LOL *args ); static List *builtin_depends( Parse *parse, LOL *args ); static List *builtin_echo( Parse *parse, LOL *args ); static List *builtin_exit( Parse *parse, LOL *args ); static List *builtin_flags( Parse *parse, LOL *args ); /* * compile_builtin() - define builtin rules */ void compile_builtins() { bindrule( "Always" )->procedure = bindrule( "ALWAYS" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_TOUCHED ); bindrule( "Depends" )->procedure = bindrule( "DEPENDS" )->procedure = new Parse( builtin_depends, NULL, NULL, NULL, NULL, NULL, T_DEPS_DEPENDS ); bindrule( "Echo" )->procedure = bindrule( "ECHO" )->procedure = new Parse( builtin_echo, NULL, NULL, NULL, NULL, NULL, 0 ); bindrule( "Exit" )->procedure = bindrule( "EXIT" )->procedure = new Parse( builtin_exit, NULL, NULL, NULL, NULL, NULL, 0 ); bindrule( "Includes" )->procedure = bindrule( "INCLUDES" )->procedure = new Parse( builtin_depends, NULL, NULL, NULL, NULL, NULL, T_DEPS_INCLUDES ); bindrule( "Leaves" )->procedure = bindrule( "LEAVES" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_LEAVES ); bindrule( "NoCare" )->procedure = bindrule( "NOCARE" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_NOCARE ); bindrule( "NOTIME" )->procedure = bindrule( "NotFile" )->procedure = bindrule( "NOTFILE" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_NOTFILE ); bindrule( "NoUpdate" )->procedure = bindrule( "NOUPDATE" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_NOUPDATE ); bindrule( "Temporary" )->procedure = bindrule( "TEMPORARY" )->procedure = new Parse( builtin_flags, NULL, NULL, NULL, NULL, NULL, T_FLAG_TEMP ); } /* * compile_append() - append list results of two statements * * parse->left more compile_append() by left-recursion * parse->right single rule */ List * compile_append( Parse *parse, LOL *args ) { /* Append right to left. */ return List::list_append( (*parse->left->func)( parse->left, args ), (*parse->right->func)( parse->right, args ) ); } /* * compile_foreach() - compile the "for x in y" statement * * Compile_foreach() resets the given variable name to each specified * value, executing the commands enclosed in braces for each iteration. * * parse->string index variable * parse->left variable values * parse->right rule to compile */ List * compile_foreach( Parse *parse, LOL *args ) { List *nv = (*parse->left->func)( parse->left, args ); List *l; /* Call var_set to reset $(parse->string) for each val. */ for( l = nv; l; l = List::list_next( l ) ) { List *val = List::list_new( NULL, String::copystr( l->string ) ); var_set( parse->string, val, VAR_SET ); List::list_free( (*parse->right->func)( parse->right, args ) ); } List::list_free( nv ); return NULL; } /* * compile_if() - compile 'if' rule * * parse->left condition tree * parse->right then tree * parse->third else tree */ List * compile_if( Parse *p, LOL *args ) { if( evaluate_if( p->left, args ) ) { return (*p->right->func)( p->right, args ); } else { return (*p->third->func)( p->third, args ); } } /* * evaluate_if() - evaluate if to determine which leg to compile * * Returns: * !0 if expression true - compile 'then' clause * 0 if expression false - compile 'else' clause */ static int evaluate_if( Parse *parse, LOL *args ) { int status; if( parse->num <= COND_OR ) { /* Handle one of the logical operators */ switch( parse->num ) { case COND_NOT: status = !evaluate_if( parse->left, args ); break; case COND_AND: status = evaluate_if( parse->left, args ) && evaluate_if( parse->right, args ); break; case COND_OR: status = evaluate_if( parse->left, args ) || evaluate_if( parse->right, args ); break; default: status = 0; /* can't happen */ } } else { /* Handle one of the comparison operators */ /* Expand targets and sources */ List *nt = (*parse->left->func)( parse->left, args ); List *ns = (*parse->right->func)( parse->right, args ); /* "a in b" make sure each of a is equal to something in b. */ /* Otherwise, step through pairwise comparison. */ if( parse->num == COND_IN ) { List *s, *t; /* Try each t until failure. */ for( status = 1, t = nt; status && t; t = List::list_next( t ) ) { int stat1; /* Try each s until success */ for( stat1 = 0, s = ns; !stat1 && s; s = List::list_next( s ) ) stat1 = !strcmp( t->string, s->string ); status = stat1; } } else { List *s = ns, *t = nt; status = 0; while( !status && ( t || s ) ) { const char *st = t ? t->string : ""; const char *ss = s ? s->string : ""; status = strcmp( st, ss ); t = t ? List::list_next( t ) : t; s = s ? List::list_next( s ) : s; } } switch( parse->num ) { case COND_EXISTS: status = status > 0 ; break; case COND_EQUALS: status = !status; break; case COND_NOTEQ: status = status != 0; break; case COND_LESS: status = status < 0; break; case COND_LESSEQ: status = status <= 0; break; case COND_MORE: status = status > 0; break; case COND_MOREEQ: status = status >= 0; break; case COND_IN: /* status = status */ break; } if( DEBUG_IF ) { debug_compile( 0, "if" ); List::list_print( nt ); printf( "(%d)", status ); List::list_print( ns ); printf( "\n" ); } List::list_free( nt ); List::list_free( ns ); } return status; } /* * compile_include() - support for 'include' - call include() on file * * parse->left list of files to include (can only do 1) */ List * compile_include( Parse *parse, LOL *args ) { List *nt = (*parse->left->func)( parse->left, args ); if( DEBUG_COMPILE ) { debug_compile( 0, "include" ); List::list_print( nt ); printf( "\n" ); } if( nt ) { TARGET *t = bindtarget( nt->string ); /* Bind the include file under the influence of */ /* "on-target" variables. Though they are targets, */ /* include files are not built with make(). */ pushsettings( t->settings ); t->boundname = search( t->name, &t->time ); popsettings( t->settings ); Parse::parse_file( t->boundname ); } List::list_free( nt ); return NULL; } /* * compile_list() - expand and return a list * * parse->string - character string to expand */ List * compile_list( Parse *parse, LOL *args ) { /* voodoo 1 means: s is a copyable string */ const char *s = parse->string; return var_expand( NULL, s, s + strlen( s ), args, 1 ); } /* * compile_local() - declare (and set) local variables * * parse->left list of variables * parse->right list of values * parse->third rules to execute */ List * compile_local( Parse *parse, LOL *args ) { List *l; SETTINGS *s = 0; List *nt = (*parse->left->func)( parse->left, args ); List *ns = (*parse->right->func)( parse->right, args ); List *result; if( DEBUG_COMPILE ) { debug_compile( 0, "local" ); List::list_print( nt ); printf( " = " ); List::list_print( ns ); printf( "\n" ); } /* Initial value is ns */ for( l = nt; l; l = List::list_next( l ) ) { s = addsettings( s, 0, l->string, List::list_copy( NULL, ns ) ); } List::list_free( ns ); List::list_free( nt ); /* Note that callees of the current context get this "local" */ /* variable, making it not so much local as layered. */ pushsettings( s ); result = (*parse->third->func)( parse->third, args ); popsettings( s ); freesettings( s ); return result; } /* * compile_null() - do nothing -- a stub for parsing */ List * compile_null( Parse *parse, LOL *args ) { return NULL; } /* * compile_rule() - compile a single user defined rule * * parse->string name of user defined rule * parse->left parameters (list of lists) to rule, recursing left * * Wrapped around evaluate_rule() so that headers() can share it. */ List * compile_rule( Parse *parse, LOL *args ) { LOL nargs[1]; List *result; Parse *p; /* Build up the list of arg lists */ lol_init( nargs ); for( p = parse->left; p; p = p->left ) lol_add( nargs, (*p->right->func)( p->right, args ) ); /* And invoke rule */ result = evaluate_rule( parse->string, nargs ); lol_free( nargs ); return result; } /* * evaluate_rule() - execute a rule invocation */ List * evaluate_rule( const char *rulename, LOL *args ) { List *result = NULL; RULE *rule = bindrule( rulename ); if( DEBUG_COMPILE ) { debug_compile( 1, rulename ); lol_print( args ); printf( "\n" ); } /* Check traditional targets $(<) and sources $(>) */ if( !rule->actions && !rule->procedure ) printf( "warning: unknown rule %s\n", rule->name ); /* If this rule will be executed for updating the targets */ /* then construct the action for make(). */ if( rule->actions ) { TARGETS *t; ACTION *action; /* The action is associated with this instance of this rule */ action = (ACTION *)malloc( sizeof( ACTION ) ); memset( (char *)action, '\0', sizeof( *action ) ); action->rule = rule; action->targets = targetlist( (TARGETS *)0, lol_get( args, 0 ) ); action->sources = targetlist( (TARGETS *)0, lol_get( args, 1 ) ); /* Append this action to the actions of each target */ for( t = action->targets; t; t = t->next ) t->target->actions = actionlist( t->target->actions, action ); } /* Now recursively compile any parse tree associated with this rule */ /* refer/free to ensure rule not freed during use */ if( rule->procedure ) { Parse *parse = rule->procedure; parse->AddRef(); result = (*parse->func)( parse, args ); parse->Release(); } if( DEBUG_COMPILE ) debug_compile( -1, 0 ); return result; } /* * compile_rules() - compile a chain of rules * * parse->left more compile_rules() by left-recursion * parse->right single rule */ List * compile_rules( Parse *parse, LOL *args ) { /* Ignore result from first statement; return the 2nd. */ List::list_free( (*parse->left->func)( parse->left, args ) ); return (*parse->right->func)( parse->right, args ); } /* * compile_set() - compile the "set variable" statement * * parse->left variable names * parse->right variable values * parse->num ASSIGN_SET/APPEND/DEFAULT */ List * compile_set( Parse *parse, LOL *args ) { List *nt = (*parse->left->func)( parse->left, args ); List *ns = (*parse->right->func)( parse->right, args ); List *l; int setflag; char *trace; switch( parse->num ) { case ASSIGN_SET: setflag = VAR_SET; trace = "="; break; case ASSIGN_APPEND: setflag = VAR_APPEND; trace = "+="; break; case ASSIGN_DEFAULT: setflag = VAR_DEFAULT; trace = "?="; break; default: setflag = VAR_SET; trace = ""; break; } if( DEBUG_COMPILE ) { debug_compile( 0, "set" ); List::list_print( nt ); printf( " %s ", trace ); List::list_print( ns ); printf( "\n" ); } /* Call var_set to set variable */ /* var_set keeps ns, so need to copy it */ for( l = nt; l; l = List::list_next( l ) ) { var_set( l->string, List::list_copy( NULL, ns ), setflag ); } List::list_free( nt ); return ns; } /* * compile_setcomp() - support for `rule` - save parse tree * * parse->string rule name * parse->left rules for rule */ List * compile_setcomp( Parse *parse, LOL *args ) { RULE *rule = bindrule( parse->string ); /* Free old one, if present */ if( rule->procedure ) { rule->procedure->Release(); } rule->procedure = parse->left; /* we now own this parse tree */ /* don't let parse_free() release it */ parse->left = 0; return NULL; } /* * compile_setexec() - support for `actions` - save execution string * * parse->string rule name * parse->string1 OS command string * parse->num flags * parse->left `bind` variables * * Note that the parse flags (as defined in compile.h) are transfered * directly to the rule flags (as defined in rules.h). */ List * compile_setexec( Parse *parse, LOL *args ) { RULE *rule = bindrule( parse->string ); List *bindlist = (*parse->left->func)( parse->left, args ); /* Free old one, if present */ if( rule->actions ) { String::freestr( rule->actions ); List::list_free( rule->bindlist ); } rule->actions = String::copystr( parse->string1 ); rule->bindlist = bindlist; rule->flags = parse->num; /* XXX translate this properly */ return NULL; } /* * compile_settings() - compile the "on =" (set variable on exec) statement * * parse->left variable names * parse->right target name * parse->third variable value * parse->num ASSIGN_SET/APPEND */ List * compile_settings( Parse *parse, LOL *args ) { List *nt = (*parse->left->func)( parse->left, args ); List *ns = (*parse->third->func)( parse->third, args ); List *targets = (*parse->right->func)( parse->right, args ); List *ts; int append = parse->num == ASSIGN_APPEND; if( DEBUG_COMPILE ) { debug_compile( 0, "set" ); List::list_print( nt ); printf( "on " ); List::list_print( targets ); printf( " %s ", append ? "+=" : "=" ); List::list_print( ns ); printf( "\n" ); } /* Call addsettings to save variable setting */ /* addsettings keeps ns, so need to copy it */ /* Pass append flag to addsettings() */ for( ts = targets; ts; ts = List::list_next( ts ) ) { TARGET *t = bindtarget( ts->string ); List *l; for( l = nt; l; l = List::list_next( l ) ) t->settings = addsettings( t->settings, append, l->string, List::list_copy( NULL, ns ) ); } List::list_free( nt ); List::list_free( targets ); return ns; } /* * compile_switch() - compile 'switch' rule * * parse->left switch value (only 1st used) * parse->right cases * * cases->left 1st case * cases->right next cases * * case->string argument to match * case->left parse tree to execute */ List * compile_switch( Parse *parse, LOL *args ) { List *nt = (*parse->left->func)( parse->left, args ); List *result = 0; if( DEBUG_COMPILE ) { debug_compile( 0, "switch" ); List::list_print( nt ); printf( "\n" ); } /* Step through cases */ for( parse = parse->right; parse; parse = parse->right ) { if( !glob( parse->left->string, nt ? nt->string : "" ) ) { /* Get & exec parse tree for this case */ parse = parse->left->left; result = (*parse->func)( parse, args ); break; } } List::list_free( nt ); return result; } /* * builtin_depends() - DEPENDS/INCLUDES rule * * The DEPENDS builtin rule appends each of the listed sources on the * dependency list of each of the listed targets. It binds both the * targets and sources as TARGETs. */ static List * builtin_depends( Parse *parse, LOL *args ) { List *targets = lol_get( args, 0 ); List *sources = lol_get( args, 1 ); int which = parse->num; List *l; for( l = targets; l; l = List::list_next( l ) ) { TARGET *t = bindtarget( l->string ); t->deps[ which ] = targetlist( t->deps[ which ], sources ); } return NULL; } /* * builtin_echo() - ECHO rule * * The ECHO builtin rule echoes the targets to the user. No other * actions are taken. */ static List * builtin_echo( Parse *parse, LOL *args ) { List::list_print( lol_get( args, 0 ) ); printf( "\n" ); return NULL; } /* * builtin_exit() - EXIT rule * * The EXIT builtin rule echoes the targets to the user and exits * the program with a failure status. */ static List * builtin_exit( Parse *parse, LOL *args ) { List::list_print( lol_get( args, 0 ) ); printf( "\n" ); exit( EXITBAD ); /* yeech */ return NULL; } /* * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule * * Builtin_flags() marks the target with the appropriate flag, for use * by make0(). It binds each target as a TARGET. */ static List * builtin_flags( Parse *parse, LOL *args ) { List *l = lol_get( args, 0 ); for( ; l; l = List::list_next( l ) ) { bindtarget( l->string )->flags |= parse->num; } return NULL; } /* * debug_compile() - printf with indent to show rule expansion. */ static void debug_compile( int which, const char *s ) { static int level = 0; static char indent[36] = ">>>>|>>>>|>>>>|>>>>|>>>>|>>>>|>>>>|"; int i = ((1+level) * 2) % 35; if( which >= 0 ) printf( "%*.*s ", i, i, indent ); if( s ) printf( "%s ", s ); level += which; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#13 | 1162 | bob_summerwill | Converted LIST into a List class, with all static methods. | ||
#12 | 1161 | bob_summerwill | Added a constructor and destructor to Parse. | ||
#11 | 1156 | bob_summerwill | Converted Parse into a class. | ||
#10 | 1153 | bob_summerwill | NewStr has been renamed as String. | ||
#9 | 1146 | bob_summerwill | Moved the NewStr global functions into a static class Jam::String. | ||
#8 | 1138 | bob_summerwill | Removed all extern "C" wrappers, so the program now has C++ linkage throughout, ready for refactoring. | ||
#7 | 1132 | bob_summerwill | All of the non-generated, non-platform-specific files are now mixed-case C++ source files. | ||
#6 | 1130 | bob_summerwill | More .C to .CPP conversions. | ||
#5 | 1129 | bob_summerwill | Some const-correctness fixes. | ||
#4 | 1127 | bob_summerwill | More file renaming. | ||
#3 | 1126 | bob_summerwill | #include dependencies between headers are now modelled explicitly, with #includes as required in header files, rather than it being a client-code responsibility. | ||
#2 | 1120 | bob_summerwill | A fair-size chunk of char* const-correctness modifications. | ||
#1 | 1117 | bob_summerwill |
Converted Compile and Expand files to C++. Some minor function prototype tweaks. |
||
//guest/bob_summerwill/jam/src/compile.c | |||||
#1 | 1106 | bob_summerwill | Integrated Jam from "public" to "guest/bob_summerwill". | ||
//guest/perforce_software/jam/src/compile.c | |||||
#2 | 486 | Perforce staff |
Jam 2.3. See RELNOTES for a list of changes from 2.2.x. Just about every source file was touched when jam got ANSI-fied. |
||
#1 | 2 | laura | Add Jam/MR 2.2 source |