LIST * compile_append( PARSE * parse, FRAME * frame ) { /* Append right to left. */ return list_append( parse_evaluate( parse->left, frame ), parse_evaluate( parse->right, frame ) ); }
LIST * compile_switch( PARSE * parse, FRAME * frame ) { LIST * nt = parse_evaluate( parse->left, frame ); LIST * result = 0; if ( DEBUG_COMPILE ) { debug_compile( 0, "switch", frame ); 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_evaluate( parse, frame ); break; } } list_free( nt ); return result; }
LIST * compile_on( PARSE *parse, FRAME *frame ) { LIST *nt = parse_evaluate( parse->left, frame ); LIST *result = 0; if( DEBUG_COMPILE ) { debug_compile( 0, "on", frame ); list_print( nt ); printf( "\n" ); } if( nt ) { TARGET *t = bindtarget( nt->string ); pushsettings( t->settings ); result = parse_evaluate( parse->right, frame ); popsettings( t->settings ); } list_free( nt ); return result; }
LIST * compile_foreach( PARSE * parse, FRAME * frame ) { LIST * nv = parse_evaluate( parse->left, frame ); LIST * l; SETTINGS * s = 0; if ( parse->num ) { s = addsettings( s, VAR_SET, parse->string, L0 ); pushsettings( s ); } /* Call var_set to reset $(parse->string) for each val. */ for ( l = nv; l; l = list_next( l ) ) { LIST * val = list_new( L0, copystr( l->string ) ); var_set( parse->string, val, VAR_SET ); list_free( parse_evaluate( parse->right, frame ) ); } if ( parse->num ) { popsettings( s ); freesettings( s ); } list_free( nv ); return L0; }
LIST * compile_rules( PARSE * parse, FRAME * frame ) { /* Ignore result from first statement; return the 2nd. */ /* Optimize recursion on the right by looping. */ do list_free( parse_evaluate( parse->left, frame ) ); while ( ( parse = parse->right )->func == compile_rules ); return parse_evaluate( parse, frame ); }
LIST * compile_if( PARSE * p, FRAME * frame ) { LIST * l = parse_evaluate( p->left, frame ); if ( l ) { list_free( l ); return parse_evaluate( p->right, frame ); } return parse_evaluate( p->third, frame ); }
LIST * compile_while( PARSE * p, FRAME * frame ) { LIST * r = 0; LIST * l; while ( ( l = parse_evaluate( p->left, frame ) ) ) { list_free( l ); if ( r ) list_free( r ); r = parse_evaluate( p->right, frame ); } return r; }
LIST * compile_rule( PARSE * parse, FRAME * frame ) { FRAME inner[ 1 ]; LIST * result; PARSE * p; /* Build up the list of arg lists. */ frame_init( inner ); inner->prev = frame; inner->prev_user = frame->module->user_module ? frame : frame->prev_user; inner->module = frame->module; /* This gets fixed up in evaluate_rule(), below. */ inner->procedure = parse; /* Special-case LOL of length 1 where the first list is totally empty. This is created when calling functions with no parameters, due to the way jam grammar is written. This is OK when one jam function calls another, but really not good when Jam function calls Python. */ if ( parse->left->left == NULL && parse->left->right->func == compile_null) ; else for ( p = parse->left; p; p = p->left ) lol_add( inner->args, parse_evaluate( p->right, frame ) ); /* And invoke the rule. */ result = evaluate_rule( parse->string, inner ); frame_free( inner ); return result; }
LIST * compile_rule( PARSE *parse, FRAME *frame ) { FRAME inner[1]; LIST *result; PARSE *p; /* Build up the list of arg lists */ frame_init( inner ); inner->prev = frame; inner->prev_user = frame->module->user_module ? frame : frame->prev_user; inner->module = frame->module; /* This gets fixed up in evaluate_rule(), below */ inner->procedure = parse; for( p = parse->left; p; p = p->left ) lol_add( inner->args, parse_evaluate( p->right, frame ) ); /* And invoke rule */ result = evaluate_rule( parse->string, inner ); frame_free( inner ); return result; }
LIST * compile_module( PARSE * p, FRAME * frame ) { /* Here we are entering a module declaration block. */ LIST * module_name = parse_evaluate( p->left, frame ); LIST * result = evaluate_in_module( module_name ? module_name->string : 0, p->right, frame ); list_free( module_name ); return result; }
LIST * compile_class( PARSE * p, FRAME * frame ) { /** Todo: check for empty class name. Check for class redeclaration. */ char * class_module = 0; LIST * name = parse_evaluate( p->left->right, frame ); LIST * bases = 0; if ( p->left->left ) bases = parse_evaluate( p->left->left->right, frame ); class_module = make_class_module( name, bases, frame ); evaluate_in_module( class_module, p->right, frame ); return L0; }
LIST * compile_setexec( PARSE *parse, FRAME *frame ) { LIST* bindlist = parse_evaluate( parse->left, frame ); new_rule_actions( frame->module, parse->string, parse->string1, bindlist, parse->num ); return L0; }
LIST * compile_settings( PARSE *parse, FRAME *frame ) { LIST *nt = parse_evaluate( parse->left, frame ); LIST *ns = parse_evaluate( parse->third, frame ); LIST *targets = parse_evaluate( parse->right, frame ); LIST *ts; int append = parse->num == ASSIGN_APPEND; if( DEBUG_COMPILE ) { debug_compile( 0, "set", frame); list_print( nt ); printf( " on " ); list_print( targets ); printf( " %s ", append ? "+=" : "=" ); 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_next( ts ) ) { TARGET *t = bindtarget( ts->string ); LIST *l; for( l = nt; l; l = list_next( l ) ) t->settings = addsettings( t->settings, append, l->string, list_copy( (LIST*)0, ns ) ); } list_free( nt ); list_free( targets ); return ns; }
LIST * compile_local( PARSE *parse, FRAME *frame ) { LIST *l; SETTINGS *s = 0; LIST *nt = parse_evaluate( parse->left, frame ); LIST *ns = parse_evaluate( parse->right, frame ); LIST *result; if( DEBUG_COMPILE ) { debug_compile( 0, "local", frame); list_print( nt ); printf( " = " ); list_print( ns ); printf( "\n" ); } /* Initial value is ns */ for( l = nt; l; l = list_next( l ) ) s = addsettings( s, 0, l->string, list_copy( (LIST*)0, ns ) ); list_free( ns ); 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_evaluate( parse->third, frame ); popsettings( s ); freesettings( s ); return result; }
LIST * compile_set( PARSE *parse, FRAME *frame ) { LIST *nt = parse_evaluate( parse->left, frame ); LIST *ns = parse_evaluate( parse->right, frame ); 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", frame); list_print( nt ); printf( " %s ", trace ); 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_next( l ) ) var_set( l->string, list_copy( L0, ns ), setflag ); list_free( nt ); return ns; }
LIST * compile_set( PARSE * parse, FRAME * frame ) { LIST * nt = parse_evaluate( parse->left, frame ); LIST * ns = parse_evaluate( parse->right, frame ); LIST * l; char const * trace; int setflag = assign_var_mode( parse->num, &trace ); if ( DEBUG_COMPILE ) { debug_compile( 0, "set", frame ); list_print( nt ); printf( " %s ", trace ); 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_next( l ) ) var_set( l->string, list_copy( L0, ns ), setflag ); list_free( nt ); return ns; }
LIST * compile_settings( PARSE * parse, FRAME * frame ) { LIST * nt = parse_evaluate( parse->left, frame ); LIST * ns = parse_evaluate( parse->third, frame ); LIST * targets = parse_evaluate( parse->right, frame ); LIST * ts; char const * trace; int setflag = assign_var_mode( parse->num, &trace ); if ( DEBUG_COMPILE ) { debug_compile( 0, "set", frame ); list_print( nt ); printf( " on " ); list_print( targets ); printf( " %s ", trace ); 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_next( ts ) ) { TARGET * t = bindtarget( ts->string ); LIST * l; for ( l = nt; l; l = list_next( l ) ) t->settings = addsettings( t->settings, setflag, l->string, list_copy( (LIST *)0, ns ) ); } list_free( nt ); list_free( targets ); return ns; }
LIST * compile_setcomp( PARSE * parse, FRAME * frame ) { argument_list * arg_list = 0; /* Create new LOL describing argument requirements if supplied. */ if ( parse->right ) { PARSE * p; arg_list = args_new(); for ( p = parse->right; p; p = p->left ) lol_add( arg_list->data, parse_evaluate( p->right, frame ) ); } new_rule_body( frame->module, parse->string, arg_list, parse->left, !parse->num ); return L0; }
LIST * compile_include( PARSE *parse, FRAME *frame ) { LIST *nt = parse_evaluate( parse->left, frame ); if( DEBUG_COMPILE ) { debug_compile( 0, "include", frame); list_print( nt ); printf( "\n" ); } if( nt ) { TARGET *t = bindtarget( nt->string ); /* DWA 2001/10/22 - Perforce Jam clears the arguments here, which * prevents an included file from being treated as part of the body * of a rule. I didn't see any reason to do that, so I lifted the * restriction. */ /* 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 ); /* We don't expect that file to be included is generated by some action. Therefore, pass 0 as third argument. */ t->boundname = search( t->name, &t->time, 0 ); popsettings( t->settings ); parse_file( t->boundname, frame ); } list_free( nt ); return L0; }
static LIST* evaluate_in_module ( char* module_name, PARSE * p, FRAME* frame) { LIST* result; module_t* outer_module = frame->module; frame->module = module_name ? bindmodule( module_name ) : root_module(); if ( outer_module != frame->module ) { exit_module( outer_module ); enter_module( frame->module ); } result = parse_evaluate( p, frame ); if ( outer_module != frame->module ) { exit_module( frame->module ); enter_module( outer_module ); frame->module = outer_module; } return result; }
LIST * evaluate_rule( char * rulename, FRAME * frame ) { LIST * result = L0; RULE * rule; profile_frame prof[1]; module_t * prev_module = frame->module; LIST * l; { LOL arg_context_, * arg_context = &arg_context_; if ( !frame->prev ) lol_init(arg_context); else arg_context = frame->prev->args; l = var_expand( L0, rulename, rulename+strlen(rulename), arg_context, 0 ); } if ( !l ) { backtrace_line( frame->prev ); printf( "warning: rulename %s expands to empty string\n", rulename ); backtrace( frame->prev ); return result; } rulename = l->string; rule = bindrule( l->string, frame->module ); #ifdef HAVE_PYTHON if ( rule->python_function ) { /* The below messing with modules is due to the way modules are * implemented in Jam. Suppose we are in module M1 now. The global * variable map actually holds 'M1' variables, and M1->variables hold * global variables. * * If we call Python right away, Python calls back Jam and then Jam * does 'module M1 { }' then Jam will try to swap the current global * variables with M1->variables. The result will be that global * variables map will hold global variables, and any variable settings * we do will go to the global module, not M1. * * By restoring basic state, where the global variable map holds global * variable, we make sure any future 'module M1' entry will work OK. */ LIST * result; module_t * m = python_module(); frame->module = m; exit_module( prev_module ); enter_module( m ); result = call_python_function( rule, frame ); exit_module( m ); enter_module ( prev_module ); return result; } #endif /* Drop the rule name. */ l = list_pop_front( l ); /* Tack the rest of the expansion onto the front of the first argument. */ frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) ); if ( DEBUG_COMPILE ) { /* Try hard to indicate in which module the rule is going to execute. */ if ( rule->module != frame->module && rule->procedure != 0 && strcmp( rulename, rule->procedure->rulename ) ) { char buf[256] = ""; strncat( buf, rule->module->name, sizeof( buf ) - 1 ); strncat( buf, rule->name, sizeof( buf ) - 1 ); debug_compile( 1, buf, frame ); } else { debug_compile( 1, rulename, frame ); } lol_print( frame->args ); printf( "\n" ); } if ( rule->procedure && rule->module != prev_module ) { /* Propagate current module to nested rule invocations. */ frame->module = rule->module; /* Swap variables. */ exit_module( prev_module ); enter_module( rule->module ); } /* Record current rule name in frame. */ if ( rule->procedure ) { frame->rulename = rulename; /* And enter record profile info. */ if ( DEBUG_PROFILE ) profile_enter( rule->procedure->rulename, prof ); } /* Check traditional targets $(<) and sources $(>). */ if ( !rule->actions && !rule->procedure ) { backtrace_line( frame->prev ); printf( "rule %s unknown in module %s\n", rule->name, frame->module->name ); backtrace( frame->prev ); exit( 1 ); } /* 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 *)BJAM_MALLOC( sizeof( ACTION ) ); memset( (char *)action, '\0', sizeof( *action ) ); action->rule = rule; action->targets = targetlist( (TARGETS *)0, lol_get( frame->args, 0 ) ); action->sources = targetlist( (TARGETS *)0, lol_get( frame->args, 1 ) ); /* If we have a group of targets all being built using the same action * then we must not allow any of them to be used as sources unless they * had all already been built in the first place or their joined action * has had a chance to finish its work and build all of them anew. * * Without this it might be possible, in case of a multi-process build, * for their action, triggered by buiding one of the targets, to still * be running when another target in the group reports as done in order * to avoid triggering the same action again and gets used prematurely. * * As a quick-fix to achieve this effect we make all the targets list * each other as 'included targets'. More precisely, we mark the first * listed target as including all the other targets in the list and vice * versa. This makes anyone depending on any of those targets implicitly * depend on all of them, thus making sure none of those targets can be * used as sources until all of them have been built. Note that direct * dependencies could not have been used due to the 'circular * dependency' issue. * * TODO: Although the current implementation solves the problem of one * of the targets getting used before its action completes its work it * also forces the action to run whenever any of the targets in the * group is not up to date even though some of them might not actually * be used by the targets being built. We should see how we can * correctly recognize such cases and use that to avoid running the * action if possible and not rebuild targets not actually depending on * targets that are not up to date. * * TODO: Using the 'include' feature might have side-effects due to * interaction with the actual 'inclusion scanning' system. This should * be checked. */ if ( action->targets ) { TARGET * t0 = action->targets->target; for ( t = action->targets->next; t; t = t->next ) { target_include( t->target, t0 ); target_include( t0, t->target ); } } /* 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. * parse_refer()/parse_free() call pair added to ensure rule not freed * during use. */ if ( rule->procedure ) { SETTINGS * local_args = collect_arguments( rule, frame ); PARSE * parse = rule->procedure; parse_refer( parse ); pushsettings( local_args ); result = parse_evaluate( parse, frame ); popsettings( local_args ); freesettings( local_args ); parse_free( parse ); } if ( frame->module != prev_module ) { exit_module( frame->module ); enter_module( prev_module ); } if ( DEBUG_PROFILE && rule->procedure ) profile_exit( prof ); if ( DEBUG_COMPILE ) debug_compile( -1, 0, frame); return result; }
LIST * evaluate_rule( char *rulename, FRAME *frame ) { LIST *result = L0; RULE *rule; profile_frame prof[1]; module_t *prev_module = frame->module; LIST *l; { LOL arg_context_, *arg_context = &arg_context_; if ( !frame->prev ) lol_init(arg_context); else arg_context = frame->prev->args; l = var_expand( L0, rulename, rulename+strlen(rulename), arg_context, 0 ); } if ( !l ) { backtrace_line( frame->prev ); printf( "warning: rulename %s expands to empty string\n", rulename ); backtrace( frame->prev ); return result; } rulename = l->string; rule = bindrule( l->string, frame->module ); #ifdef HAVE_PYTHON if (rule->python_function) { return call_python_function(rule, frame); } #endif /* drop the rule name */ l = list_pop_front( l ); /* tack the rest of the expansion onto the front of the first argument */ frame->args->list[0] = list_append( l, lol_get( frame->args, 0 ) ); if ( DEBUG_COMPILE ) { /* Try hard to indicate in which module the rule is going to execute */ if ( rule->module != frame->module && rule->procedure != 0 && strcmp(rulename, rule->procedure->rulename) ) { char buf[256] = ""; strncat( buf, rule->module->name, sizeof(buf) - 1 ); strncat( buf, rule->name, sizeof(buf) - 1 ); debug_compile( 1, buf, frame); } else { debug_compile( 1, rulename, frame); } lol_print( frame->args ); printf( "\n" ); } if ( rule->procedure && rule->module != prev_module ) { /* propagate current module to nested rule invocations */ frame->module = rule->module; /* swap variables */ exit_module( prev_module ); enter_module( rule->module ); } /* record current rule name in frame */ if ( rule->procedure ) { frame->rulename = rulename; /* and enter record profile info */ if ( DEBUG_PROFILE ) profile_enter( rule->procedure->rulename, prof ); } /* Check traditional targets $(<) and sources $(>) */ if( !rule->actions && !rule->procedure ) { backtrace_line( frame->prev ); printf( "rule %s unknown in module %s\n", rule->name, frame->module->name ); backtrace( frame->prev ); exit(1); } /* 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( frame->args, 0 ) ); action->sources = targetlist( (TARGETS *)0, lol_get( frame->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 ) { SETTINGS *local_args = collect_arguments( rule, frame ); PARSE *parse = rule->procedure; parse_refer( parse ); pushsettings( local_args ); result = parse_evaluate( parse, frame ); popsettings( local_args ); freesettings( local_args ); parse_free( parse ); } if ( frame->module != prev_module ) { exit_module( frame->module ); enter_module( prev_module ); } if ( DEBUG_PROFILE && rule->procedure ) profile_exit( prof ); if( DEBUG_COMPILE ) debug_compile( -1, 0, frame); return result; }
LIST * compile_eval( PARSE * parse, FRAME * frame ) { LIST * ll; LIST * lr; LIST * s; LIST * t; int status = 0; /* Short circuit lr eval for &&, ||, and 'in'. */ ll = parse_evaluate( parse->left, frame ); lr = 0; switch ( parse->num ) { case EXPR_AND: case EXPR_IN : if ( ll ) goto eval; break; case EXPR_OR : if ( !ll ) goto eval; break; default: eval: lr = parse_evaluate( parse->right, frame ); } /* Now eval. */ switch ( parse->num ) { case EXPR_NOT: if ( !ll ) status = 1; break; case EXPR_AND: if ( ll && lr ) status = 1; break; case EXPR_OR : if ( ll || lr ) status = 1; break; case EXPR_IN: /* "a in b": make sure each of ll is equal to something in lr. */ for ( t = ll; t; t = list_next( t ) ) { for ( s = lr; s; s = list_next( s ) ) if ( !strcmp( t->string, s->string ) ) break; if ( !s ) break; } /* No more ll? Success. */ if ( !t ) status = 1; break; case EXPR_EXISTS: if ( lcmp( ll, L0 ) != 0 ) status = 1; break; case EXPR_EQUALS: if ( lcmp( ll, lr ) == 0 ) status = 1; break; case EXPR_NOTEQ : if ( lcmp( ll, lr ) != 0 ) status = 1; break; case EXPR_LESS : if ( lcmp( ll, lr ) < 0 ) status = 1; break; case EXPR_LESSEQ: if ( lcmp( ll, lr ) <= 0 ) status = 1; break; case EXPR_MORE : if ( lcmp( ll, lr ) > 0 ) status = 1; break; case EXPR_MOREEQ: if ( lcmp( ll, lr ) >= 0 ) status = 1; break; } if ( DEBUG_IF ) { debug_compile( 0, "if", frame ); list_print( ll ); printf( "(%d) ", status ); list_print( lr ); printf( "\n" ); } /* Find something to return. */ /* In odd circumstances (like "" = "") */ /* we'll have to return a new string. */ if ( !status ) t = 0; else if ( ll ) t = ll, ll = 0; else if ( lr ) t = lr, lr = 0; else t = list_new( L0, newstr( "1" ) ); if ( ll ) list_free( ll ); if ( lr ) list_free( lr ); return t; }