compile.c #3

  • //
  • guest/
  • richard_geiger/
  • intjam/
  • src/
  • compile.c
  • View
  • Commits
  • Open Download .zip Download (16 KB)
/*
 * Copyright 1993, 2000 Christopher Seiwald.
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */

# include "jam.h"

# include "lists.h"
# include "parse.h"
# include "compile.h"
# include "variable.h"
# include "expand.h"
# include "rules.h"
# include "newstr.h"
# include "search.h"

/*
 * 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_on() - run rule under influence of on-target variables
 *	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
 *
 * 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.
 * 01/10/00 (seiwald) - built-ins split out to builtin.c.
 */

static void debug_compile( int which, char *s );
static int evaluate_if( PARSE *parse, LOL *args );
int glob( char *s, char *c );



/*
 * 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_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_next( l ) )
	{
	    LIST *val = list_new( L0, copystr( l->string ) );

	    var_set( parse->string, val, VAR_SET );

	    list_free( (*parse->right->func)( parse->right, args ) );
	}

	list_free( nv );

	return L0;
}

/*
 * 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_next( t ) )
		{
		    int stat1;

		    /* Try each s until success */

		    for( stat1 = 0, s = ns; !stat1 && s; s = list_next( s ) )
			stat1 = !strcmp( t->string, s->string );

		    status = stat1;
		}
	    }
	    else
	    {
		LIST *s = ns, *t = nt;

		status = 0;

		while( !status && ( t || s ) )
		{
		    char *st = t ? t->string : "";
		    char *ss = s ? s->string : "";

		    status = strcmp( st, ss );

		    t = t ? list_next( t ) : t;
		    s = s ? 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_print( nt );
		printf( "(%d)", status );
		list_print( ns );
		printf( "\n" );
	    }

	    list_free( nt );
	    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_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_file( t->boundname );
	}

	list_free( nt );

	return L0;
}

/*
 * 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 */
	char *s = parse->string;
	return var_expand( L0, 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_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->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 L0;
}

/*
 * compile_on() - run rule under influence of on-target variables
 *
 * 	parse->left	list of files to include (can only do 1)
 *	parse->right	rule to run
 *
 * EXPERIMENTAL!
 */

LIST *
compile_on(
	PARSE	*parse,
	LOL	*args )
{
	LIST	*nt = (*parse->left->func)( parse->left, args );
	LIST	*result = 0;

	if( DEBUG_COMPILE )
	{
	    debug_compile( 0, "on" );
	    list_print( nt );
	    printf( "\n" );
	}

	if( nt )
	{
	    TARGET *t = bindtarget( nt->string );

	    pushsettings( t->settings );
	    (*parse->right->func)( parse->right, args );
	    t->boundname = search( t->name, &t->time );
	    popsettings( t->settings );

	}

	list_free( nt );

	return result;
}


/*
 * 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(
	char	*rulename,
	LOL	*args )
{
	LIST	*result = L0;
	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_refer( parse );
	    result = (*parse->func)( parse, args );
	    parse_free( parse );
	}

	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_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_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;
}

/*
 * 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 )
	    parse_free( rule->procedure );

	rule->procedure = parse->left;

	/* we now own this parse tree */
	/* don't let parse_free() release it */

	parse->left = 0;	

	return L0;
}

/*
 * 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 )
	{
	    freestr( rule->actions );
	    list_free( rule->bindlist );
	}

	rule->actions = copystr( parse->string1 );
	rule->bindlist = bindlist;
	rule->flags = parse->num; /* XXX translate this properly */

	return L0;
}

/*
 * 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_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;
}

/*
 * 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_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_free( nt );

	return result;
}

/*
 * debug_compile() - printf with indent to show rule expansion.
 */

static void
debug_compile( int which, 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
#11 1317 Richard Geiger Update the copyright notices in all files touched in the upcoming
drop into //public/jam/
#10 1306 Richard Geiger Experimental support for '[ on target rule ]' in jam,
so that you can access target-specific variables.  It
is crunchy right now, in that the list of things you can
do inside []'s is limited to (a) user-defined rules and
(b) the "return" statement.

=== computer.perforce.com:1666: Change 21437 by seiwald@golly-seiwald on 2001/03/23 10:43:34

Tweaked my earlier version of documenting [ rule ] to explicitly
show both forms ( [ rule ... ] and [ on targ rule ... ] ) - rmg
#9 1298 Richard Geiger Fix 'if $(x) in $(y)' broken by change 20197.

=== computer.perforce.com:1666: Change 20374 by seiwald@golly-seiwald on 2001/02/01 12:01:44

Note: this was change 1287 in the Public Depot integration. - rmg
#8 1295 Richard Geiger Support for 'while' statement in jam.
 Useful?  Who knows.

=== computer.perforce.com:1666: Change 20250 by seiwald@golly-seiwald on 2001/01/24 15:54:02

Documented in Jam.html - rmg
#7 1288 Richard Geiger Fix typo (swapped params) in compile.c, and update jamgram.c/y
to reflect newest jamgram.yy.

=== computer.perforce.com:1666: Change 20207 by seiwald@golly-seiwald on 2001/01/22 13:32:01

Note: the changes to jamgram.[cy] from the patch were discarded, and
these were rebuilt on the integration platform, since the patch failed
due to differences in the yacc between the "internal" build platform and
the Public Depot integration platform. - rmg
#6 1287 Richard Geiger Replace evaluate_if() with compile_eval(), which returns a LIST
instead of an int.  No functional change.  This is made in
anticipation of supporting (arithmetic) expression evaluation.

=== computer.perforce.com:1666: Change 20197 by seiwald@spice on 2001/01/21 22:51:39
#5 1277 Richard Geiger Fix case where a rule is defined within another rule: it was
zeroing the definition.

=== computer.perforce.com:1666: Change 20035 by seiwald@golly-seiwald on 2001/01/13 23:20:11
#4 1259 Richard Geiger Execute the right-recursive compile_rules() function in a
loop, so as to keep the stack from growing one frame per
jam statement.

=== computer.perforce.com:1666: Change 20001 by seiwald@golly-seiwald on 2001/01/11 09:50:25

added note to RELNOTES (internal code changes) - rmg
#3 1241 Richard Geiger Split jam's built-in rules out to builtins.c from compile.c,
so that compile.c only deals with the language.

=== computer.perforce.com:1666: Change 19939 by seiwald@spice2 on 2001/01/08 22:55:10
#2 1237 Richard Geiger Experimental support for running a rule under the influence
of a target's specific variables.

=== computer.perforce.com:1666: Change 19932 by seiwald@spice on 2001/01/08 15:48:36

This feature is now considered part of the language, i.e., no
longer experimental. Hence, I have also updated Jam.html and
the RELNOTES. - rmg
#1 1207 Richard Geiger Establish this branch to use for import of Perforce Jam.
//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