From a0fe6ff6e09cee50c10ee6ab51fab39742def378 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Wed, 30 Nov 2011 00:00:35 +0000 Subject: [PATCH] Reimplement the interpreter. [SVN r75736] --- v2/engine/build.bat | 2 +- v2/engine/build.jam | 2 +- v2/engine/build.sh | 2 +- v2/engine/builtins.c | 123 +- v2/engine/builtins.h | 82 +- v2/engine/compile.c | 702 +------ v2/engine/compile.h | 20 - v2/engine/constants.c | 3 + v2/engine/constants.h | 1 + v2/engine/frames.c | 3 +- v2/engine/frames.h | 3 +- v2/engine/function.c | 3019 ++++++++++++++++++++++++++++++ v2/engine/function.h | 33 + v2/engine/jam.c | 2 + v2/engine/jamgram.c | 40 +- v2/engine/jamgram.y | 40 +- v2/engine/jamgram.yy | 40 +- v2/engine/lists.c | 37 + v2/engine/lists.h | 3 + v2/engine/modules.c | 2 +- v2/engine/modules/order.c | 4 +- v2/engine/modules/path.c | 2 +- v2/engine/modules/property-set.c | 2 +- v2/engine/modules/regex.c | 2 +- v2/engine/modules/sequence.c | 2 +- v2/engine/modules/set.c | 2 +- v2/engine/native.c | 8 +- v2/engine/native.h | 4 +- v2/engine/parse.c | 17 +- v2/engine/parse.h | 25 +- v2/engine/rules.c | 18 +- v2/engine/rules.h | 6 +- v2/engine/subst.c | 7 +- v2/engine/w32_getreg.c | 10 +- 34 files changed, 3340 insertions(+), 928 deletions(-) create mode 100644 v2/engine/function.c create mode 100644 v2/engine/function.h diff --git a/v2/engine/build.bat b/v2/engine/build.bat index ee1ff8db9..a9770e57c 100644 --- a/v2/engine/build.bat +++ b/v2/engine/build.bat @@ -438,7 +438,7 @@ echo ### set YYACC_SOURCES=yyacc.c set MKJAMBASE_SOURCES=mkjambase.c set BJAM_SOURCES= -set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c constants.c debug.c execnt.c expand.c filent.c glob.c hash.c +set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c constants.c debug.c execnt.c expand.c filent.c function.c glob.c hash.c set BJAM_SOURCES=%BJAM_SOURCES% hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c set BJAM_SOURCES=%BJAM_SOURCES% object.c option.c output.c parse.c pathunix.c regexp.c set BJAM_SOURCES=%BJAM_SOURCES% rules.c scan.c search.c subst.c timestamp.c variable.c modules.c diff --git a/v2/engine/build.jam b/v2/engine/build.jam index f5ab656be..cb5fd1638 100644 --- a/v2/engine/build.jam +++ b/v2/engine/build.jam @@ -471,7 +471,7 @@ if --show-locate-target in $(ARGV) # We have some different files for UNIX, and NT. jam.source = - command.c compile.c constants.c debug.c expand.c glob.c + command.c compile.c constants.c debug.c expand.c function.c glob.c hash.c hcache.c headers.c hdrmacro.c jam.c jambase.c jamgram.c lists.c make.c make1.c mem.c object.c diff --git a/v2/engine/build.sh b/v2/engine/build.sh index dd914c636..8adf216e6 100755 --- a/v2/engine/build.sh +++ b/v2/engine/build.sh @@ -245,7 +245,7 @@ echo "###" YYACC_SOURCES="yyacc.c" MKJAMBASE_SOURCES="mkjambase.c" BJAM_SOURCES="\ - command.c compile.c constants.c debug.c expand.c glob.c hash.c\ + command.c compile.c constants.c debug.c expand.c function.c glob.c hash.c\ hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c\ object.c option.c output.c parse.c pathunix.c regexp.c\ rules.c scan.c search.c subst.c timestamp.c variable.c modules.c\ diff --git a/v2/engine/builtins.c b/v2/engine/builtins.c index 85d91feaf..073b0a4ef 100644 --- a/v2/engine/builtins.c +++ b/v2/engine/builtins.c @@ -69,20 +69,20 @@ #define C0 (OBJECT *)0 #if defined( OS_NT ) || defined( OS_CYGWIN ) - LIST * builtin_system_registry ( PARSE *, FRAME * ); - LIST * builtin_system_registry_names( PARSE *, FRAME * ); + LIST * builtin_system_registry ( FRAME *, int ); + LIST * builtin_system_registry_names( FRAME *, int ); #endif int glob( const char * s, const char * c ); void backtrace ( FRAME * ); void backtrace_line ( FRAME * ); -void print_source_line( PARSE * ); +void print_source_line( FRAME * ); -RULE * bind_builtin( const char * name_, LIST * (* f)( PARSE *, FRAME * ), int flags, const char * * args ) +RULE * bind_builtin( const char * name_, LIST * (* f)( FRAME *, int flags ), int flags, const char * * args ) { - PARSE * p; + FUNCTION * func; RULE * result; argument_list* arg_list = 0; OBJECT * name = object_new( name_ ); @@ -93,11 +93,11 @@ RULE * bind_builtin( const char * name_, LIST * (* f)( PARSE *, FRAME * ), int f lol_build( arg_list->data, args ); } - p = parse_make( f, P0, P0, P0, C0, C0, flags ); + func = function_builtin( f, flags ); - result = new_rule_body( root_module(), name, arg_list, p, 1 ); + result = new_rule_body( root_module(), name, arg_list, func, 1 ); - parse_free( p ); + function_free( func ); object_free( name ); @@ -434,7 +434,7 @@ void load_builtins() * The CALC rule performs simple mathematical operations on two arguments. */ -LIST * builtin_calc( PARSE * parse, FRAME * frame ) +LIST * builtin_calc( FRAME * frame, int flags ) { LIST * arg = lol_get( frame->args, 0 ); @@ -488,7 +488,7 @@ LIST * builtin_calc( PARSE * parse, FRAME * frame ) * targets and sources as TARGETs. */ -LIST * builtin_depends( PARSE * parse, FRAME * frame ) +LIST * builtin_depends( FRAME * frame, int flags ) { LIST * targets = lol_get( frame->args, 0 ); LIST * sources = lol_get( frame->args, 1 ); @@ -502,7 +502,7 @@ LIST * builtin_depends( PARSE * parse, FRAME * frame ) /* TARGET, creating it if needed. The internal include */ /* TARGET shares the name of its parent. */ - if ( parse->num ) + if ( flags ) { if ( !t->includes ) { @@ -534,7 +534,7 @@ LIST * builtin_depends( PARSE * parse, FRAME * frame ) * argument. */ -LIST * builtin_rebuilds( PARSE * parse, FRAME * frame ) +LIST * builtin_rebuilds( FRAME * frame, int flags ) { LIST * targets = lol_get( frame->args, 0 ); LIST * rebuilds = lol_get( frame->args, 1 ); @@ -557,7 +557,7 @@ LIST * builtin_rebuilds( PARSE * parse, FRAME * frame ) * taken. */ -LIST * builtin_echo( PARSE * parse, FRAME * frame ) +LIST * builtin_echo( FRAME * frame, int flags ) { list_print( lol_get( frame->args, 0 ) ); printf( "\n" ); @@ -573,7 +573,7 @@ LIST * builtin_echo( PARSE * parse, FRAME * frame ) * with a failure status. */ -LIST * builtin_exit( PARSE * parse, FRAME * frame ) +LIST * builtin_exit( FRAME * frame, int flags ) { list_print( lol_get( frame->args, 0 ) ); printf( "\n" ); @@ -596,11 +596,11 @@ LIST * builtin_exit( PARSE * parse, FRAME * frame ) * It binds each target as a TARGET. */ -LIST * builtin_flags( PARSE * parse, FRAME * frame ) +LIST * builtin_flags( FRAME * frame, int flags ) { LIST * l = lol_get( frame->args, 0 ); for ( ; l; l = list_next( l ) ) - bindtarget( l->value )->flags |= parse->num; + bindtarget( l->value )->flags |= flags; return L0; } @@ -697,7 +697,7 @@ static LIST * downcase_list( LIST * in ) } -LIST * builtin_glob( PARSE * parse, FRAME * frame ) +LIST * builtin_glob( FRAME * frame, int flags ) { LIST * l = lol_get( frame->args, 0 ); LIST * r = lol_get( frame->args, 1 ); @@ -870,7 +870,7 @@ LIST * glob_recursive( const char * pattern ) } -LIST * builtin_glob_recursive( PARSE * parse, FRAME * frame ) +LIST * builtin_glob_recursive( FRAME * frame, int flags ) { LIST * result = L0; LIST * l = lol_get( frame->args, 0 ); @@ -884,7 +884,7 @@ LIST * builtin_glob_recursive( PARSE * parse, FRAME * frame ) * builtin_match() - MATCH rule, regexp matching. */ -LIST * builtin_match( PARSE * parse, FRAME * frame ) +LIST * builtin_match( FRAME * frame, int flags ) { LIST * l; LIST * r; @@ -930,7 +930,7 @@ LIST * builtin_match( PARSE * parse, FRAME * frame ) return result; } -LIST * builtin_split_by_characters( PARSE * parse, FRAME * frame ) +LIST * builtin_split_by_characters( FRAME * frame, int flags ) { LIST * l1 = lol_get( frame->args, 0 ); LIST * l2 = lol_get( frame->args, 1 ); @@ -956,7 +956,7 @@ LIST * builtin_split_by_characters( PARSE * parse, FRAME * frame ) return result; } -LIST * builtin_hdrmacro( PARSE * parse, FRAME * frame ) +LIST * builtin_hdrmacro( FRAME * frame, int flags ) { LIST * l = lol_get( frame->args, 0 ); @@ -992,7 +992,7 @@ static void add_rule_name( void * r_, void * result_ ) } -LIST * builtin_rulenames( PARSE * parse, FRAME * frame ) +LIST * builtin_rulenames( FRAME * frame, int flags ) { LIST * arg0 = lol_get( frame->args, 0 ); LIST * result = L0; @@ -1035,7 +1035,7 @@ static struct hash * get_running_module_vars() } -LIST * builtin_varnames( PARSE * parse, FRAME * frame ) +LIST * builtin_varnames( FRAME * frame, int flags ) { LIST * arg0 = lol_get( frame->args, 0 ); LIST * result = L0; @@ -1060,7 +1060,7 @@ LIST * builtin_varnames( PARSE * parse, FRAME * frame ) * Clears all rules and variables from the given module. */ -LIST * builtin_delete_module( PARSE * parse, FRAME * frame ) +LIST * builtin_delete_module( FRAME * frame, int flags ) { LIST * arg0 = lol_get( frame->args, 0 ); LIST * result = L0; @@ -1100,7 +1100,7 @@ static void unknown_rule( FRAME * frame, const char * key, OBJECT * module_name, * variables. */ -LIST * builtin_import( PARSE * parse, FRAME * frame ) +LIST * builtin_import( FRAME * frame, int flags ) { LIST * source_module_list = lol_get( frame->args, 0 ); LIST * source_rules = lol_get( frame->args, 1 ); @@ -1164,7 +1164,7 @@ LIST * builtin_import( PARSE * parse, FRAME * frame ) * is issued. */ -LIST * builtin_export( PARSE * parse, FRAME * frame ) +LIST * builtin_export( FRAME * frame, int flags ) { LIST * module_list = lol_get( frame->args, 0 ); LIST * rules = lol_get( frame->args, 1 ); @@ -1190,12 +1190,12 @@ LIST * builtin_export( PARSE * parse, FRAME * frame ) * indicated for a given procedure in debug output or an error backtrace. */ -static void get_source_line( PARSE * procedure, const char * * file, int * line ) +static void get_source_line( FRAME * frame, const char * * file, int * line ) { - if ( procedure ) + if ( frame->file ) { - const char * f = object_str( procedure->file ); - int l = procedure->line; + const char * f = object_str( frame->file ); + int l = frame->line; if ( !strcmp( f, "+" ) ) { f = "jambase.c"; @@ -1212,12 +1212,12 @@ static void get_source_line( PARSE * procedure, const char * * file, int * line } -void print_source_line( PARSE * p ) +void print_source_line( FRAME * frame ) { const char * file; int line; - get_source_line( p, &file, &line ); + get_source_line( frame, &file, &line ); if ( line < 0 ) printf( "(builtin):" ); else @@ -1238,7 +1238,7 @@ void backtrace_line( FRAME * frame ) } else { - print_source_line( frame->procedure ); + print_source_line( frame ); printf( " in %s\n", frame->rulename ); } } @@ -1264,7 +1264,7 @@ void backtrace( FRAME * frame ) * period. */ -LIST * builtin_backtrace( PARSE * parse, FRAME * frame ) +LIST * builtin_backtrace( FRAME * frame, int flags ) { LIST * levels_arg = lol_get( frame->args, 0 ); int levels = levels_arg ? atoi( object_str( levels_arg->value ) ) : (int)( (unsigned int)(-1) >> 1 ) ; @@ -1275,7 +1275,7 @@ LIST * builtin_backtrace( PARSE * parse, FRAME * frame ) const char * file; int line; char buf[32]; - get_source_line( frame->procedure, &file, &line ); + get_source_line( frame, &file, &line ); sprintf( buf, "%d", line ); result = list_new( result, object_new( file ) ); result = list_new( result, object_new( buf ) ); @@ -1298,7 +1298,7 @@ LIST * builtin_backtrace( PARSE * parse, FRAME * frame ) * behavior. */ -LIST * builtin_caller_module( PARSE * parse, FRAME * frame ) +LIST * builtin_caller_module( FRAME * frame, int flags ) { LIST * levels_arg = lol_get( frame->args, 0 ); int levels = levels_arg ? atoi( object_str( levels_arg->value ) ) : 0 ; @@ -1328,7 +1328,7 @@ LIST * builtin_caller_module( PARSE * parse, FRAME * frame ) * Usage: pwd = [ PWD ] ; */ -LIST * builtin_pwd( PARSE * parse, FRAME * frame ) +LIST * builtin_pwd( FRAME * frame, int flags ) { return pwd(); } @@ -1338,7 +1338,7 @@ LIST * builtin_pwd( PARSE * parse, FRAME * frame ) * Adds targets to the list of target that jam will attempt to update. */ -LIST * builtin_update( PARSE * parse, FRAME * frame ) +LIST * builtin_update( FRAME * frame, int flags ) { LIST * result = list_copy( L0, targets_to_update() ); LIST * arg1 = lol_get( frame->args, 0 ); @@ -1358,7 +1358,7 @@ int last_update_now_status; Third parameter, if non-empty, specifies that the -n option should have no effect -- that is, all out-of-date targets should be rebuild. */ -LIST * builtin_update_now( PARSE * parse, FRAME * frame ) +LIST * builtin_update_now( FRAME * frame, int flags ) { LIST * targets = lol_get( frame->args, 0 ); LIST * log = lol_get( frame->args, 1 ); @@ -1437,7 +1437,7 @@ LIST * builtin_update_now( PARSE * parse, FRAME * frame ) return L0; } -LIST * builtin_search_for_target( PARSE * parse, FRAME * frame ) +LIST * builtin_search_for_target( FRAME * frame, int flags ) { LIST * arg1 = lol_get( frame->args, 0 ); LIST * arg2 = lol_get( frame->args, 1 ); @@ -1446,7 +1446,7 @@ LIST * builtin_search_for_target( PARSE * parse, FRAME * frame ) } -LIST * builtin_import_module( PARSE * parse, FRAME * frame ) +LIST * builtin_import_module( FRAME * frame, int flags ) { LIST * arg1 = lol_get( frame->args, 0 ); LIST * arg2 = lol_get( frame->args, 1 ); @@ -1456,14 +1456,14 @@ LIST * builtin_import_module( PARSE * parse, FRAME * frame ) } -LIST * builtin_imported_modules( PARSE * parse, FRAME * frame ) +LIST * builtin_imported_modules( FRAME * frame, int flags ) { LIST * arg0 = lol_get( frame->args, 0 ); return imported_modules( bindmodule( arg0 ? arg0->value : 0 ) ); } -LIST * builtin_instance( PARSE * parse, FRAME * frame ) +LIST * builtin_instance( FRAME * frame, int flags ) { LIST * arg1 = lol_get( frame->args, 0 ); LIST * arg2 = lol_get( frame->args, 1 ); @@ -1474,14 +1474,14 @@ LIST * builtin_instance( PARSE * parse, FRAME * frame ) } -LIST * builtin_sort( PARSE * parse, FRAME * frame ) +LIST * builtin_sort( FRAME * frame, int flags ) { LIST * arg1 = lol_get( frame->args, 0 ); return list_sort( arg1 ); } -LIST * builtin_normalize_path( PARSE * parse, FRAME * frame ) +LIST * builtin_normalize_path( FRAME * frame, int flags ) { LIST * arg = lol_get( frame->args, 0 ); @@ -1603,7 +1603,7 @@ LIST * builtin_normalize_path( PARSE * parse, FRAME * frame ) } -LIST * builtin_native_rule( PARSE * parse, FRAME * frame ) +LIST * builtin_native_rule( FRAME * frame, int flags ) { LIST * module_name = lol_get( frame->args, 0 ); LIST * rule_name = lol_get( frame->args, 1 ); @@ -1630,7 +1630,7 @@ LIST * builtin_native_rule( PARSE * parse, FRAME * frame ) } -LIST * builtin_has_native_rule( PARSE * parse, FRAME * frame ) +LIST * builtin_has_native_rule( FRAME * frame, int flags ) { LIST * module_name = lol_get( frame->args, 0 ); LIST * rule_name = lol_get( frame->args, 1 ); @@ -1651,7 +1651,7 @@ LIST * builtin_has_native_rule( PARSE * parse, FRAME * frame ) } -LIST * builtin_user_module( PARSE * parse, FRAME * frame ) +LIST * builtin_user_module( FRAME * frame, int flags ) { LIST * module_name = lol_get( frame->args, 0 ); for ( ; module_name; module_name = module_name->next ) @@ -1663,7 +1663,7 @@ LIST * builtin_user_module( PARSE * parse, FRAME * frame ) } -LIST * builtin_nearest_user_location( PARSE * parse, FRAME * frame ) +LIST * builtin_nearest_user_location( FRAME * frame, int flags ) { FRAME * nearest_user_frame = frame->module->user_module ? frame : frame->prev_user; @@ -1676,7 +1676,7 @@ LIST * builtin_nearest_user_location( PARSE * parse, FRAME * frame ) int line; char buf[32]; - get_source_line( nearest_user_frame->procedure, &file, &line ); + get_source_line( nearest_user_frame, &file, &line ); sprintf( buf, "%d", line ); result = list_new( result, object_new( file ) ); result = list_new( result, object_new( buf ) ); @@ -1685,7 +1685,7 @@ LIST * builtin_nearest_user_location( PARSE * parse, FRAME * frame ) } -LIST * builtin_check_if_file( PARSE * parse, FRAME * frame ) +LIST * builtin_check_if_file( FRAME * frame, int flags ) { LIST * name = lol_get( frame->args, 0 ); return file_is_file( name->value ) == 1 @@ -1694,7 +1694,7 @@ LIST * builtin_check_if_file( PARSE * parse, FRAME * frame ) } -LIST * builtin_md5( PARSE * parse, FRAME * frame ) +LIST * builtin_md5( FRAME * frame, int flags ) { LIST * l = lol_get( frame->args, 0 ); const char* s = object_str( l->value ); @@ -1715,7 +1715,7 @@ LIST * builtin_md5( PARSE * parse, FRAME * frame ) return list_new( L0, object_new( hex_output ) ); } -LIST *builtin_file_open( PARSE * parse, FRAME * frame ) +LIST *builtin_file_open( FRAME * frame, int flags ) { const char * name = object_str( lol_get( frame->args, 0 )->value ); const char * mode = object_str( lol_get( frame->args, 1 )->value ); @@ -1742,7 +1742,7 @@ LIST *builtin_file_open( PARSE * parse, FRAME * frame ) } } -LIST *builtin_pad( PARSE *parse, FRAME *frame ) +LIST *builtin_pad( FRAME * frame, int flags ) { OBJECT * string = lol_get( frame->args, 0 )->value; const char * width_s = object_str( lol_get( frame->args, 1 )->value ); @@ -1767,7 +1767,7 @@ LIST *builtin_pad( PARSE *parse, FRAME *frame ) } } -LIST *builtin_precious( PARSE * parse, FRAME * frame ) +LIST *builtin_precious( FRAME * frame, int flags ) { LIST * targets = lol_get(frame->args, 0); @@ -1780,7 +1780,7 @@ LIST *builtin_precious( PARSE * parse, FRAME * frame ) return L0; } -LIST *builtin_self_path( PARSE * parse, FRAME * frame ) +LIST *builtin_self_path( FRAME * frame, int flags ) { extern const char * saved_argv0; char * p = executable_path( saved_argv0 ); @@ -1796,7 +1796,7 @@ LIST *builtin_self_path( PARSE * parse, FRAME * frame ) } } -LIST *builtin_makedir( PARSE * parse, FRAME * frame ) +LIST *builtin_makedir( FRAME * frame, int flags ) { LIST * path = lol_get( frame->args, 0 ); @@ -1813,7 +1813,7 @@ LIST *builtin_makedir( PARSE * parse, FRAME * frame ) #ifdef HAVE_PYTHON -LIST * builtin_python_import_rule( PARSE * parse, FRAME * frame ) +LIST * builtin_python_import_rule( FRAME * frame, int flags ) { static int first_time = 1; const char * python_module = object_str( lol_get( frame->args, 0 )->value ); @@ -1944,7 +1944,6 @@ PyObject* bjam_call( PyObject * self, PyObject * args ) inner->prev = 0; inner->prev_user = 0; inner->module = bindmodule( constant_python_interface ); - inner->procedure = 0; /* Extract the rule name and arguments from 'args'. */ @@ -2164,7 +2163,7 @@ PyObject * bjam_backtrace( PyObject * self, PyObject * args ) int line; char buf[ 32 ]; - get_source_line( f->procedure, &file, &line ); + get_source_line( f, &file, &line ); sprintf( buf, "%d", line ); /* PyTuple_SetItem steals reference. */ @@ -2277,7 +2276,7 @@ static char * rtrim( char * s ) return s; } -LIST * builtin_shell( PARSE * parse, FRAME * frame ) +LIST * builtin_shell( FRAME * frame, int flags ) { LIST * command = lol_get( frame->args, 0 ); LIST * result = 0; @@ -2357,7 +2356,7 @@ LIST * builtin_shell( PARSE * parse, FRAME * frame ) #else /* #ifdef HAVE_POPEN */ -LIST * builtin_shell( PARSE * parse, FRAME * frame ) +LIST * builtin_shell( FRAME * frame, int flags ) { return L0; } diff --git a/v2/engine/builtins.h b/v2/engine/builtins.h index 5fed07c96..0c65b118d 100644 --- a/v2/engine/builtins.h +++ b/v2/engine/builtins.h @@ -21,47 +21,47 @@ void init_property_set(); void init_sequence(); void init_order(); -LIST *builtin_calc( PARSE *parse, FRAME *args ); -LIST *builtin_depends( PARSE *parse, FRAME *args ); -LIST *builtin_rebuilds( PARSE *parse, FRAME *args ); -LIST *builtin_echo( PARSE *parse, FRAME *args ); -LIST *builtin_exit( PARSE *parse, FRAME *args ); -LIST *builtin_flags( PARSE *parse, FRAME *args ); -LIST *builtin_glob( PARSE *parse, FRAME *args ); -LIST *builtin_glob_recursive( PARSE *parse, FRAME *frame ); -LIST *builtin_subst( PARSE *parse, FRAME *args ); -LIST *builtin_match( PARSE *parse, FRAME *args ); -LIST *builtin_split_by_characters( PARSE *parse, FRAME *args ); -LIST *builtin_hdrmacro( PARSE *parse, FRAME *args ); -LIST *builtin_rulenames( PARSE *parse, FRAME *args ); -LIST *builtin_varnames( PARSE *parse, FRAME *args ); -LIST *builtin_delete_module( PARSE *parse, FRAME *args ); -LIST *builtin_import( PARSE *parse, FRAME *args ); -LIST *builtin_export( PARSE *parse, FRAME *args ); -LIST *builtin_caller_module( PARSE *parse, FRAME *args ); -LIST *builtin_backtrace( PARSE *parse, FRAME *args ); -LIST *builtin_pwd( PARSE *parse, FRAME *args ); -LIST *builtin_update( PARSE *parse, FRAME *args ); -LIST *builtin_update_now( PARSE *parse, FRAME *args ); -LIST *builtin_search_for_target( PARSE *parse, FRAME *args ); -LIST *builtin_import_module( PARSE *parse, FRAME *args ); -LIST *builtin_imported_modules( PARSE *parse, FRAME *frame ); -LIST *builtin_instance( PARSE *parse, FRAME *frame ); -LIST *builtin_sort( PARSE *parse, FRAME *frame ); -LIST *builtin_normalize_path( PARSE *parse, FRAME *frame ); -LIST *builtin_native_rule( PARSE *parse, FRAME *frame ); -LIST *builtin_has_native_rule( PARSE *parse, FRAME *frame ); -LIST *builtin_user_module( PARSE *parse, FRAME *frame ); -LIST *builtin_nearest_user_location( PARSE *parse, FRAME *frame ); -LIST *builtin_check_if_file( PARSE *parse, FRAME *frame ); -LIST *builtin_python_import_rule( PARSE *parse, FRAME *frame ); -LIST *builtin_shell( PARSE *parse, FRAME *frame ); -LIST *builtin_md5( PARSE *parse, FRAME *frame ); -LIST *builtin_file_open( PARSE *parse, FRAME *frame ); -LIST *builtin_pad( PARSE *parse, FRAME *frame ); -LIST *builtin_precious( PARSE *parse, FRAME *frame ); -LIST *builtin_self_path( PARSE *parse, FRAME *frame ); -LIST *builtin_makedir( PARSE *parse, FRAME *frame ); +LIST *builtin_calc( FRAME * frame, int flags ); +LIST *builtin_depends( FRAME * frame, int flags ); +LIST *builtin_rebuilds( FRAME * frame, int flags ); +LIST *builtin_echo( FRAME * frame, int flags ); +LIST *builtin_exit( FRAME * frame, int flags ); +LIST *builtin_flags( FRAME * frame, int flags ); +LIST *builtin_glob( FRAME * frame, int flags ); +LIST *builtin_glob_recursive( FRAME * frame, int flags ); +LIST *builtin_subst( FRAME * frame, int flags ); +LIST *builtin_match( FRAME * frame, int flags ); +LIST *builtin_split_by_characters( FRAME * frame, int flags ); +LIST *builtin_hdrmacro( FRAME * frame, int flags ); +LIST *builtin_rulenames( FRAME * frame, int flags ); +LIST *builtin_varnames( FRAME * frame, int flags ); +LIST *builtin_delete_module( FRAME * frame, int flags ); +LIST *builtin_import( FRAME * frame, int flags ); +LIST *builtin_export( FRAME * frame, int flags ); +LIST *builtin_caller_module( FRAME * frame, int flags ); +LIST *builtin_backtrace( FRAME * frame, int flags ); +LIST *builtin_pwd( FRAME * frame, int flags ); +LIST *builtin_update( FRAME * frame, int flags ); +LIST *builtin_update_now( FRAME * frame, int flags ); +LIST *builtin_search_for_target( FRAME * frame, int flags ); +LIST *builtin_import_module( FRAME * frame, int flags ); +LIST *builtin_imported_modules( FRAME * frame, int flags ); +LIST *builtin_instance( FRAME * frame, int flags ); +LIST *builtin_sort( FRAME * frame, int flags ); +LIST *builtin_normalize_path( FRAME * frame, int flags ); +LIST *builtin_native_rule( FRAME * frame, int flags ); +LIST *builtin_has_native_rule( FRAME * frame, int flags ); +LIST *builtin_user_module( FRAME * frame, int flags ); +LIST *builtin_nearest_user_location( FRAME * frame, int flags ); +LIST *builtin_check_if_file( FRAME * frame, int flags ); +LIST *builtin_python_import_rule( FRAME * frame, int flags ); +LIST *builtin_shell( FRAME * frame, int flags ); +LIST *builtin_md5( FRAME * frame, int flags ); +LIST *builtin_file_open( FRAME * frame, int flags ); +LIST *builtin_pad( FRAME * frame, int flags ); +LIST *builtin_precious( FRAME * frame, int flags ); +LIST *builtin_self_path( FRAME * frame, int flags ); +LIST *builtin_makedir( FRAME * frame, int flags ); void backtrace( FRAME *frame ); extern int last_update_now_status; diff --git a/v2/engine/compile.c b/v2/engine/compile.c index 0b3b3c3bd..20eeedb83 100644 --- a/v2/engine/compile.c +++ b/v2/engine/compile.c @@ -92,7 +92,7 @@ int glob( const char * s, const char * c ); /* Internal functions from builtins.c */ void backtrace( FRAME * frame ); void backtrace_line( FRAME * frame ); -void print_source_line( PARSE * p ); +void print_source_line( FRAME * frame ); struct frame * frame_before_python_call; @@ -105,7 +105,8 @@ void frame_init( FRAME* frame ) lol_init(frame->args); frame->module = root_module(); frame->rulename = "module scope"; - frame->procedure = 0; + frame->file = 0; + frame->line = -1; } @@ -115,459 +116,18 @@ void frame_free( FRAME* frame ) } -/* - * 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, FRAME * frame ) -{ - /* Append right to left. */ - return list_append( - parse_evaluate( parse->left, frame ), - parse_evaluate( parse->right, frame ) ); -} - - -/* - * compile_eval() - evaluate if to determine which leg to compile - * - * Returns: - * list if expression true - compile 'then' clause - * L0 if expression false - compile 'else' clause - */ - -static int lcmp( LIST * t, LIST * s ) -{ - int status = 0; - - while ( !status && ( t || s ) ) - { - const char *st = t ? object_str( t->value ) : ""; - const char *ss = s ? object_str( s->value ) : ""; - - status = strcmp( st, ss ); - - t = t ? list_next( t ) : t; - s = s ? list_next( s ) : s; - } - - return status; -} - -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 ( object_equal( t->value, s->value ) ) - 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, object_new( "1" ) ); - - if ( ll ) list_free( ll ); - if ( lr ) list_free( lr ); - return t; -} - - -/* - * 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, 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, object_copy( l->value ) ); - 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; -} - -/* - * compile_if() - compile 'if' rule - * - * parse->left condition tree - * parse->right then tree - * parse->third else tree - */ - -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; -} - - -/* - * 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, 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->value ); - - /* DWA 2001/10/22 - Perforce Jam cleared the arguments here, which - * prevents an included file from being treated as part of the body of a - * rule. I did not 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. - If the name resolves to directory, let it error out. */ - object_free( t->boundname ); - t->boundname = search( t->name, &t->time, 0, 0 ); - popsettings( t->settings ); - - parse_file( t->boundname, frame ); - } - - list_free( nt ); - - return L0; -} - -static LIST* evaluate_in_module ( OBJECT * 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 * 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->value : 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. */ - - OBJECT * 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 ); - object_free( class_module ); - - return L0; -} - - -/* - * compile_list() - expand and return a list. - * - * parse->string - character string to expand. - */ - -LIST * compile_list( PARSE * parse, FRAME * frame ) -{ - /* s is a copyable string */ - OBJECT * o = parse->string; - const char * s = object_str( o ); - return var_expand( L0, s, s + strlen( s ), frame->args, o ); -} - - -/* - * 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, 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, VAR_SET, l->value, list_copy( L0, 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; -} - - -/* - * compile_null() - do nothing -- a stub for parsing. - */ - -LIST * compile_null( PARSE * parse, FRAME * frame ) -{ - 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, 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->value ); - pushsettings( t->settings ); - result = parse_evaluate( parse->right, frame ); - 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, 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; -} - - static void argument_error( const char * message, RULE * rule, FRAME * frame, LIST * arg ) { LOL * actual = frame->args; - assert( frame->procedure != 0 ); + assert( rule->procedure != 0 ); backtrace_line( frame->prev ); printf( "*** argument error\n* rule %s ( ", frame->rulename ); lol_print( rule->arguments->data ); printf( " )\n* called with: ( " ); lol_print( actual ); printf( " )\n* %s %s\n", message, arg ? object_str ( arg->value ) : "" ); - print_source_line( rule->procedure ); + function_location( rule->procedure, &frame->file, &frame->line ); + print_source_line( frame ); printf( "see definition of rule '%s' being called\n", object_str( rule->name ) ); backtrace( frame->prev ); exit( 1 ); @@ -949,25 +509,7 @@ evaluate_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, object_str( rulename ), object_str( rulename )+strlen(object_str( rulename )), arg_context, 0 ); - } - - if ( !l ) - { - backtrace_line( frame->prev ); - printf( "warning: rulename %s expands to empty string\n", object_str( rulename ) ); - backtrace( frame->prev ); - return result; - } - - rule = bindrule( l->value, frame->module ); + rule = bindrule( rulename, frame->module ); rulename = rule->name; #ifdef HAVE_PYTHON @@ -1005,17 +547,11 @@ evaluate_rule( } #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 && !object_equal( rulename, rule->procedure->rulename ) ) + && rule->procedure != 0 && !object_equal( rulename, function_rulename( rule->procedure ) ) ) { char buf[256] = ""; strncat( buf, object_str( rule->module->name ), sizeof( buf ) - 1 ); @@ -1047,7 +583,7 @@ evaluate_rule( frame->rulename = object_str( rulename ); /* And enter record profile info. */ if ( DEBUG_PROFILE ) - profile_enter( rule->procedure->rulename, prof ); + profile_enter( function_rulename( rule->procedure ), prof ); } /* Check traditional targets $(<) and sources $(>). */ @@ -1126,21 +662,22 @@ evaluate_rule( } /* Now recursively compile any parse tree associated with this rule. - * parse_refer()/parse_free() call pair added to ensure rule not freed + * function_refer()/function_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 ); + FUNCTION * function = rule->procedure; + + function_refer( function ); pushsettings( local_args ); - result = parse_evaluate( parse, frame ); + result = function_run( function, frame, stack_global() ); popsettings( local_args ); freesettings( local_args ); - parse_free( parse ); + function_free( function ); } if ( frame->module != prev_module ) @@ -1179,7 +716,6 @@ LIST * call_rule( OBJECT * rulename, FRAME * caller_frame, ... ) inner->prev_user = caller_frame->module->user_module ? caller_frame : caller_frame->prev_user; inner->module = caller_frame->module; - inner->procedure = 0; va_start( va, caller_frame ); for ( ; ; ) @@ -1199,212 +735,6 @@ LIST * call_rule( OBJECT * rulename, FRAME * caller_frame, ... ) } -/* - * compile_rules() - compile a chain of rules - * - * parse->left single rule - * parse->right more compile_rules() by right-recursion - */ - -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 ); -} - - -/* - * assign_var_mode() - convert ASSIGN_XXX compilation flag into corresponding - * VAR_XXX variable set flag. - */ - -static int assign_var_mode( int parsenum, char const * * tracetext ) -{ - char const * trace; - int setflag; - switch ( parsenum ) - { - 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 ( tracetext ) - *tracetext = trace ; - return setflag; -} - -/* - * 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, 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->value, 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 - * parse->right optional list-of-lists describing arguments - */ - -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; -} - - -/* - * 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 transferred directly - * to the rule flags (as defined in rules.h). - */ - -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; -} - - -/* - * 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, 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->value ); - LIST * l; - - for ( l = nt; l; l = list_next( l ) ) - t->settings = addsettings( t->settings, setflag, l->value, - 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, 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( object_str( parse->left->string ), nt ? object_str( nt->value ) : "" ) ) - { - /* Get & exec parse tree for this case. */ - parse = parse->left->left; - result = parse_evaluate( parse, frame ); - break; - } - } - - list_free( nt ); - return result; -} - /* * debug_compile() - printf with indent to show rule expansion. @@ -1419,7 +749,7 @@ static void debug_compile( int which, const char * s, FRAME * frame ) { int i; - print_source_line( frame->procedure ); + print_source_line( frame ); i = ( level + 1 ) * 2; while ( i > 35 ) diff --git a/v2/engine/compile.h b/v2/engine/compile.h index 40823f610..1c002d90f 100644 --- a/v2/engine/compile.h +++ b/v2/engine/compile.h @@ -24,26 +24,6 @@ void compile_builtins(); -LIST *compile_append( PARSE *parse, FRAME *frame ); -LIST *compile_foreach( PARSE *parse, FRAME *frame ); -LIST *compile_if( PARSE *parse, FRAME *frame ); -LIST *compile_eval( PARSE *parse, FRAME *args ); -LIST *compile_include( PARSE *parse, FRAME *frame ); -LIST *compile_list( PARSE *parse, FRAME *frame ); -LIST *compile_local( PARSE *parse, FRAME *frame ); -LIST *compile_module( PARSE *parse, FRAME *frame ); -LIST *compile_class( PARSE *parse, FRAME *frame ); -LIST *compile_null( PARSE *parse, FRAME *frame ); -LIST *compile_on( PARSE *parse, FRAME *frame ); -LIST *compile_rule( PARSE *parse, FRAME *frame ); -LIST *compile_rules( PARSE *parse, FRAME *frame ); -LIST *compile_set( PARSE *parse, FRAME *frame ); -LIST *compile_setcomp( PARSE *parse, FRAME *frame ); -LIST *compile_setexec( PARSE *parse, FRAME *frame ); -LIST *compile_settings( PARSE *parse, FRAME *frame ); -LIST *compile_switch( PARSE *parse, FRAME *frame ); -LIST *compile_while( PARSE *parse, FRAME *frame ); - LIST *evaluate_rule( OBJECT * rulename, FRAME * frame ); LIST *call_rule( OBJECT * rulename, FRAME * caller_frame, ...); diff --git a/v2/engine/constants.c b/v2/engine/constants.c index 08c1bc521..618824433 100644 --- a/v2/engine/constants.c +++ b/v2/engine/constants.c @@ -19,6 +19,7 @@ void constants_init( void ) { + constant_empty = object_new( "" ); constant_builtin = object_new( "(builtin)" ); constant_other = object_new( "[OTHER]" ); constant_total = object_new( "[TOTAL]" ); @@ -40,6 +41,7 @@ void constants_init( void ) void constants_done( void ) { + object_free( constant_empty ); object_free( constant_builtin ); object_free( constant_other ); object_free( constant_total ); @@ -59,6 +61,7 @@ void constants_done( void ) object_free( constant_MAIN_PYTHON ); } +OBJECT * constant_empty; OBJECT * constant_builtin; OBJECT * constant_other; OBJECT * constant_total; diff --git a/v2/engine/constants.h b/v2/engine/constants.h index 01e8ee247..e2f237a89 100644 --- a/v2/engine/constants.h +++ b/v2/engine/constants.h @@ -16,6 +16,7 @@ void constants_init( void ); void constants_done( void ); +extern OBJECT * constant_empty; /* "" */ extern OBJECT * constant_builtin; /* "(builtin)" */ extern OBJECT * constant_other; /* "[OTHER]" */ extern OBJECT * constant_total; /* "[TOTAL]" */ diff --git a/v2/engine/frames.c b/v2/engine/frames.c index 84889f09e..29f7f03cd 100644 --- a/v2/engine/frames.c +++ b/v2/engine/frames.c @@ -13,7 +13,8 @@ void frame_init( FRAME* frame ) lol_init(frame->args); frame->module = root_module(); frame->rulename = "module scope"; - frame->procedure = 0; + frame->file = 0; + frame->line = -1; } void frame_free( FRAME* frame ) diff --git a/v2/engine/frames.h b/v2/engine/frames.h index 5fcfe3062..1e2040d14 100644 --- a/v2/engine/frames.h +++ b/v2/engine/frames.h @@ -20,7 +20,8 @@ struct frame FRAME * prev_user; LOL args[ 1 ]; module_t * module; - PARSE * procedure; + OBJECT * file; + int line; const char * rulename; }; diff --git a/v2/engine/function.c b/v2/engine/function.c new file mode 100644 index 000000000..31575c866 --- /dev/null +++ b/v2/engine/function.c @@ -0,0 +1,3019 @@ +/* + * Copyright 2011 Steven Watanabe + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#include "lists.h" +#include "pathsys.h" +#include "mem.h" +#include "constants.h" +#include "jam.h" +#include "frames.h" +#include "function.h" +#include "rules.h" +#include "variable.h" +#include "compile.h" +#include "search.h" +#include "class.h" +#include +#include +#include + +# ifdef OS_CYGWIN +# include +# include +# endif + +#define INSTR_PUSH_EMPTY 0 +#define INSTR_PUSH_CONSTANT 1 +#define INSTR_PUSH_ARG 2 +#define INSTR_PUSH_VAR 3 +#define INSTR_PUSH_GROUP 4 +#define INSTR_PUSH_RESULT 5 +#define INSTR_PUSH_APPEND 6 +#define INSTR_SWAP 7 + +#define INSTR_JUMP_EMPTY 8 +#define INSTR_JUMP_NOT_EMPTY 9 + +#define INSTR_JUMP 10 +#define INSTR_JUMP_LT 11 +#define INSTR_JUMP_LE 12 +#define INSTR_JUMP_GT 13 +#define INSTR_JUMP_GE 14 +#define INSTR_JUMP_EQ 15 +#define INSTR_JUMP_NE 16 +#define INSTR_JUMP_IN 17 +#define INSTR_JUMP_NOT_IN 18 + +#define INSTR_JUMP_NOT_GLOB 19 + +#define INSTR_TRY_POP_FRONT 20 + +#define INSTR_SET_RESULT 21 +#define INSTR_RETURN 22 +#define INSTR_POP 23 + +#define INSTR_PUSH_LOCAL 24 +#define INSTR_POP_LOCAL 25 +#define INSTR_SET 26 +#define INSTR_APPEND 27 +#define INSTR_DEFAULT 28 + +#define INSTR_PUSH_LOCAL_GROUP 29 +#define INSTR_POP_LOCAL_GROUP 30 +#define INSTR_SET_GROUP 31 +#define INSTR_APPEND_GROUP 32 +#define INSTR_DEFAULT_GROUP 33 + +#define INSTR_PUSH_ON 34 +#define INSTR_POP_ON 35 +#define INSTR_SET_ON 36 +#define INSTR_APPEND_ON 37 +#define INSTR_DEFAULT_ON 38 + +#define INSTR_CALL_RULE 39 + +#define INSTR_APPLY_MODIFIERS 40 +#define INSTR_APPLY_INDEX 41 +#define INSTR_APPLY_INDEX_MODIFIERS 42 +#define INSTR_APPLY_MODIFIERS_GROUP 43 +#define INSTR_APPLY_INDEX_GROUP 44 +#define INSTR_APPLY_INDEX_MODIFIERS_GROUP 45 +#define INSTR_COMBINE_STRINGS 46 + +#define INSTR_INCLUDE 47 +#define INSTR_RULE 48 +#define INSTR_ACTIONS 49 +#define INSTR_PUSH_MODULE 50 +#define INSTR_POP_MODULE 51 +#define INSTR_CLASS 52 + +typedef struct instruction +{ + unsigned int op_code; + int arg; +} instruction; + +typedef struct _subfunction +{ + OBJECT * name; + FUNCTION * code; + int arguments; + int local; +} SUBFUNCTION; + +typedef struct _subaction +{ + OBJECT * name; + OBJECT * command; + int flags; +} SUBACTION; + +#define FUNCTION_BUILTIN 0 +#define FUNCTION_JAM 1 + +struct _function +{ + int type; + int reference_count; + OBJECT * rulename; +}; + +typedef struct _builtin_function +{ + int type; + int reference_count; + OBJECT * rulename; + LIST * ( * func )( FRAME *, int flags ); + int flags; +} BUILTIN_FUNCTION; + +typedef struct _jam_function +{ + int type; + int reference_count; + OBJECT * rulename; + instruction * code; + int num_constants; + OBJECT * * constants; + int num_subfunctions; + SUBFUNCTION * functions; + int num_subactions; + SUBACTION * actions; + OBJECT * file; + int line; +} JAM_FUNCTION; + + +struct _stack +{ + void * data; +}; + +static void * stack; + +STACK * stack_global() +{ + static STACK result; + if ( !stack ) + { + int size = 1 << 21; + stack = BJAM_MALLOC( size ); + result.data = (char *)stack + size; + } + return &result; +} + +void stack_push( STACK * s, LIST * l ) +{ + *--(*(LIST * * *)&s->data) = l; +} + +LIST * stack_pop( STACK * s ) +{ + return *(*(LIST * * *)&s->data)++; +} + +LIST * stack_top(STACK * s) +{ + return *(LIST * *)s->data; +} + +LIST * stack_at( STACK * s, int n ) +{ + return *((LIST * *)s->data + n); +} + +void stack_set( STACK * s, int n, LIST * value ) +{ + *((LIST * *)s->data + n) = value; +} + +void * stack_get( STACK * s ) +{ + return (LIST * *)s->data; +} + +void * stack_allocate( STACK * s, int size ) +{ + *(char * *)&s->data -= size; + return s->data; +} + +void stack_deallocate( STACK * s, int size ) +{ + *(char * *)&s->data += size; +} + +LIST * frame_get_local( FRAME * frame, int idx ) +{ + /* The only local variables are the arguments */ + return list_copy( L0, lol_get( frame->args, idx ) ); +} + +static OBJECT * function_get_constant( JAM_FUNCTION * function, int idx ) +{ + return function->constants[ idx ]; +} + +static LIST * function_get_variable( JAM_FUNCTION * function, FRAME * frame, int idx ) +{ + return list_copy( L0, var_get( function->constants[idx] ) ); +} + +static void function_set_variable( JAM_FUNCTION * function, FRAME * frame, int idx, LIST * value ) +{ + var_set( function->constants[idx], value, VAR_SET ); +} + +static LIST * function_swap_variable( JAM_FUNCTION * function, FRAME * frame, int idx, LIST * value ) +{ + return var_swap( function->constants[idx], value ); +} + +static void function_append_variable( JAM_FUNCTION * function, FRAME * frame, int idx, LIST * value ) +{ + var_set( function->constants[idx], value, VAR_APPEND ); +} + +static void function_default_variable( JAM_FUNCTION * function, FRAME * frame, int idx, LIST * value ) +{ + var_set( function->constants[idx], value, VAR_DEFAULT ); +} + +static void function_set_rule( JAM_FUNCTION * function, FRAME * frame, STACK * s, int idx ) +{ + SUBFUNCTION * sub = function->functions + idx; + argument_list * args = 0; + + if ( sub->arguments ) + { + int i; + args = args_new(); + for ( i = sub->arguments; i > 0; --i ) + { + lol_add( args->data, stack_at( s, i - 1 ) ); + } + + for ( i = 0; i < sub->arguments; ++i ) + { + stack_pop( s ); + } + } + + new_rule_body( frame->module, sub->name, args, sub->code, !sub->local ); +} + +static void function_set_actions( JAM_FUNCTION * function, FRAME * frame, STACK * s, int idx ) +{ + SUBACTION * sub = function->actions + idx; + LIST * bindlist = stack_pop( s ); + + new_rule_actions( frame->module, sub->name, sub->command, bindlist, sub->flags ); +} + +/* + * returns the index if name is "<", ">", "1", "2", ... or "19" + * otherwise returns -1. + */ + +static int get_argument_index( const char * s ) +{ + if( s[ 0 ] != '\0') + { + if( s[ 1 ] == '\0' ) + { + switch ( s[ 0 ] ) + { + case '<': return 0; + case '>': return 1; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return s[ 0 ] - '1'; + } + } + else if ( s[ 0 ] == '1' && s[ 2 ] == '\0' ) + { + switch( s[ 1 ] ) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return s[ 1 ] - '0' + 10 - 1; + } + } + } + return -1; +} + +static LIST * function_get_named_variable( JAM_FUNCTION * function, FRAME * frame, OBJECT * name ) +{ + int idx = get_argument_index( object_str( name ) ); + if( idx != -1 ) + { + return list_copy( L0, lol_get( frame->args, idx ) ); + } + else + { + return list_copy( L0, var_get( name ) ); + } +} + +static void function_set_named_variable( JAM_FUNCTION * function, FRAME * frame, OBJECT * name, LIST * value) +{ + var_set( name, value, VAR_SET ); +} + +static LIST * function_swap_named_variable( JAM_FUNCTION * function, FRAME * frame, OBJECT * name, LIST * value ) +{ + return var_swap( name, value ); +} + +static void function_append_named_variable( JAM_FUNCTION * function, FRAME * frame, OBJECT * name, LIST * value) +{ + var_set( name, value, VAR_APPEND ); +} + +static void function_default_named_variable( JAM_FUNCTION * function, FRAME * frame, OBJECT * name, LIST * value ) +{ + var_set( name, value, VAR_DEFAULT ); +} + +static LIST * function_call_rule( JAM_FUNCTION * function, FRAME * frame, STACK * s, int n_args, const char * unexpanded, OBJECT * file, int line ) +{ + FRAME inner[ 1 ]; + int i; + LIST * first = stack_pop( s ); + LIST * result = L0; + OBJECT * rulename; + + frame->file = file; + frame->line = line; + + if ( !first ) + { + backtrace_line( frame ); + printf( "warning: rulename %s expands to empty string\n", unexpanded ); + backtrace( frame ); + return result; + } + + rulename = object_copy( first->value ); + + 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. */ + + for( i = 0; i < n_args; ++i ) + { + lol_add( inner->args, stack_at( s, n_args - i - 1 ) ); + } + + for( i = 0; i < n_args; ++i ) + { + stack_pop( s ); + } + + if ( inner->args->count == 0 ) + { + lol_add( inner->args, list_pop_front( first ) ); + } + else + { + LIST * * l = &inner->args->list[0]; + *l = list_append( list_pop_front( first ), *l ); + } + + result = evaluate_rule( rulename, inner ); + frame_free( inner ); + object_free( rulename ); + return result; +} + +/* Variable expansion */ + +typedef struct +{ + int sub1; + int sub2; +} subscript_t; + +typedef struct +{ + PATHNAME f; /* :GDBSMR -- pieces */ + char parent; /* :P -- go to parent directory */ + char filemods; /* one of the above applied */ + char downshift; /* :L -- downshift result */ + char upshift; /* :U -- upshift result */ + char to_slashes; /* :T -- convert "\" to "/" */ + char to_windows; /* :W -- convert cygwin to native paths */ + PATHPART empty; /* :E -- default for empties */ + PATHPART join; /* :J -- join list with char */ +} VAR_EDITS; + +static LIST * apply_modifiers_impl( LIST * result, string * buf, VAR_EDITS * edits, int n, LIST * iter, LIST * end ); +static void get_iters( subscript_t subscript, LIST * * first, LIST * * last, int length ); +static void var_edit_file( const char * in, string * out, VAR_EDITS * edits ); +static void var_edit_shift( string * out, size_t pos, VAR_EDITS * edits ); +static int var_edit_parse( const char * mods, VAR_EDITS * edits, int havezeroed ); + + +/* + * var_edit_parse() - parse : modifiers into PATHNAME structure + * + * The : modifiers in a $(varname:modifier) currently support replacing or + * omitting elements of a filename, and so they are parsed into a PATHNAME + * structure (which contains pointers into the original string). + * + * Modifiers of the form "X=value" replace the component X with the given value. + * Modifiers without the "=value" cause everything but the component X to be + * omitted. X is one of: + * + * G + * D directory name + * B base name + * S .suffix + * M (member) + * R root directory - prepended to whole path + * + * This routine sets: + * + * f->f_xxx.ptr = 0 + * f->f_xxx.len = 0 + * -> leave the original component xxx + * + * f->f_xxx.ptr = string + * f->f_xxx.len = strlen( string ) + * -> replace component xxx with string + * + * f->f_xxx.ptr = "" + * f->f_xxx.len = 0 + * -> omit component xxx + * + * var_edit_file() below and path_build() obligingly follow this convention. + */ + +static int var_edit_parse( const char * mods, VAR_EDITS * edits, int havezeroed ) +{ + while ( *mods ) + { + PATHPART * fp; + + switch ( *mods++ ) + { + case 'L': edits->downshift = 1; continue; + case 'U': edits->upshift = 1; continue; + case 'P': edits->parent = edits->filemods = 1; continue; + case 'E': fp = &edits->empty; goto strval; + case 'J': fp = &edits->join; goto strval; + case 'G': fp = &edits->f.f_grist; goto fileval; + case 'R': fp = &edits->f.f_root; goto fileval; + case 'D': fp = &edits->f.f_dir; goto fileval; + case 'B': fp = &edits->f.f_base; goto fileval; + case 'S': fp = &edits->f.f_suffix; goto fileval; + case 'M': fp = &edits->f.f_member; goto fileval; + case 'T': edits->to_slashes = 1; continue; + case 'W': edits->to_windows = 1; continue; + default: + break; /* Should complain, but so what... */ + } + + fileval: + /* Handle :CHARS, where each char (without a following =) selects a + * particular file path element. On the first such char, we deselect all + * others (by setting ptr = "", len = 0) and for each char we select + * that element (by setting ptr = 0). + */ + edits->filemods = 1; + + if ( *mods != '=' ) + { + if ( !havezeroed++ ) + { + int i; + for ( i = 0; i < 6; ++i ) + { + edits->f.part[ i ].len = 0; + edits->f.part[ i ].ptr = ""; + } + } + + fp->ptr = 0; + continue; + } + + strval: + /* Handle :X=value, or :X */ + if ( *mods != '=' ) + { + fp->ptr = ""; + fp->len = 0; + } + else + { + fp->ptr = ++mods; + fp->len = strlen( mods ); + mods += fp->len; + } + } + + return havezeroed; +} + +/* + * var_edit_file() - copy input target name to output, modifying filename. + */ + +static void var_edit_file( const char * in, string * out, VAR_EDITS * edits ) +{ + if ( edits->filemods ) + { + PATHNAME pathname; + + /* Parse apart original filename, putting parts into "pathname". */ + path_parse( in, &pathname ); + + /* Replace any pathname with edits->f */ + if ( edits->f.f_grist .ptr ) pathname.f_grist = edits->f.f_grist; + if ( edits->f.f_root .ptr ) pathname.f_root = edits->f.f_root; + if ( edits->f.f_dir .ptr ) pathname.f_dir = edits->f.f_dir; + if ( edits->f.f_base .ptr ) pathname.f_base = edits->f.f_base; + if ( edits->f.f_suffix.ptr ) pathname.f_suffix = edits->f.f_suffix; + if ( edits->f.f_member.ptr ) pathname.f_member = edits->f.f_member; + + /* If requested, modify pathname to point to parent. */ + if ( edits->parent ) + path_parent( &pathname ); + + /* Put filename back together. */ + path_build( &pathname, out, 0 ); + } + else + { + string_append( out, in ); + } +} + +/* + * var_edit_shift() - do upshift/downshift mods. + */ + +static void var_edit_shift( string * out, size_t pos, VAR_EDITS * edits ) +{ + if ( edits->upshift || edits->downshift || edits->to_windows || edits->to_slashes ) + { + /* Handle upshifting, downshifting and slash translation now. */ + char * p; +# ifdef OS_CYGWIN + if ( edits->to_windows ) + { + /* FIXME: skip grist */ + char result[ MAX_PATH + 1 ]; + cygwin_conv_to_win32_path( out->value + pos, result ); + assert( strlen( result ) <= MAX_PATH ); + string_truncate( out, pos ); + string_append( out, result ); + edits->to_slashes = 0; + } +# endif + for ( p = out->value + pos; *p; ++p) + { + if ( edits->upshift ) + *p = toupper( *p ); + else if ( edits->downshift ) + *p = tolower( *p ); + if ( edits->to_slashes && ( *p == '\\' ) ) + *p = '/'; + } + } +} + +/* + * Reads n LISTs from the top of the STACK and + * combines them to form VAR_EDITS. + * + * returns the number of VAR_EDITS pushed onto + * the STACK. + */ + +static int expand_modifiers( STACK * s, int n ) +{ + int i; + int total = 1; + LIST * * args = stack_get( s ); + for( i = 0; i < n; ++i) + total *= list_length( args[i] ); + + if ( total != 0 ) + { + VAR_EDITS * out = stack_allocate( s, total * sizeof(VAR_EDITS) ); + LIST * * iter = stack_allocate( s, n * sizeof(LIST *) ); + for (i = 0; i < n; ++i ) + { + iter[i] = args[i]; + } + i = 0; + { + int havezeroed; + loop: + memset( out, 0, sizeof( *out ) ); + havezeroed = 0; + for (i = 0; i < n; ++i ) + { + havezeroed = var_edit_parse( object_str( iter[i]->value ), out, havezeroed ); + } + ++out; + while ( --i >= 0 ) + { + if ( iter[i]->next ) + { + iter[i] = iter[i]->next; + goto loop; + } + else + { + iter[i] = args[i]; + } + } + } + stack_deallocate( s, n * sizeof( LIST * ) ); + } + return total; +} + +static LIST * apply_modifiers( STACK * s, int n ) +{ + LIST * value = stack_top( s ); + LIST * result = L0; + VAR_EDITS * edits = (VAR_EDITS *)( (LIST * *)stack_get( s ) + 1 ); + string buf[1]; + string_new( buf ); + result = apply_modifiers_impl( result, buf, edits, n, value, L0 ); + string_free( buf ); + return result; +} + +/* + * Parse a string of the form "1-2", "-2--1", "2-" + * and return the two subscripts. + */ + +subscript_t parse_subscript( const char * s ) +{ + subscript_t result; + result.sub1 = 0; + result.sub2 = 0; + do /* so we can use "break" */ + { + /* Allow negative subscripts. */ + if ( !isdigit( *s ) && ( *s != '-' ) ) + { + result.sub2 = 0; + break; + } + result.sub1 = atoi( s ); + + /* Skip over the first symbol, which is either a digit or dash. */ + ++s; + while ( isdigit( *s ) ) ++s; + + if ( *s == '\0' ) + { + result.sub2 = result.sub1; + break; + } + + if ( *s != '-' ) + { + result.sub2 = 0; + break; + } + + ++s; + + if ( *s == '\0' ) + { + result.sub2 = -1; + break; + } + + if ( !isdigit( *s ) && ( *s != '-' ) ) + { + result.sub2 = 0; + break; + } + + /* First, compute the index of the last element. */ + result.sub2 = atoi( s ); + while ( isdigit( *++s ) ); + + if ( *s != '\0' ) + result.sub2 = 0; + + } while ( 0 ); + return result; +} + +static LIST * apply_subscript( STACK * s ) +{ + LIST * value = stack_top( s ); + LIST * indices = stack_at( s, 1 ); + LIST * result = L0; + int length = list_length( value ); + string buf[1]; + string_new( buf ); + for ( ; indices; indices = list_next( indices ) ) + { + LIST * iter = value; + LIST * end; + subscript_t subscript = parse_subscript( object_str( indices->value ) ); + get_iters( subscript, &iter, &end, length ); + for ( ; iter != end; iter = list_next( iter ) ) + { + result = list_new( result, object_copy( iter->value ) ); + } + } + string_free( buf ); + return result; +} + +/* + * Reads the LIST from first and applies subscript to it. + * The results are written to *first and *last. + */ + +static void get_iters( subscript_t subscript, LIST * * first, LIST * * last, int length ) +{ + int start; + int size; + LIST * iter; + LIST * end; + { + + if ( subscript.sub1 < 0 ) + start = length + subscript.sub1; + else if( subscript.sub1 > length ) + start = length; + else + start = subscript.sub1 - 1; + + if ( subscript.sub2 < 0 ) + size = length + 1 + subscript.sub2 - start; + else + size = subscript.sub2 - start; + + /* + * HACK: When the first subscript is before the start of the + * list, it magically becomes the beginning of the list. + * This is inconsistent, but needed for backwards + * compatibility. + */ + if ( start < 0 ) + start = 0; + + /* The "sub2 < 0" test handles the semantic error of sub2 < + * sub1. + */ + if ( size < 0 ) + size = 0; + + if ( start + size > length ) + size = length - start; + } + + iter = *first; + while ( start-- > 0 ) + iter = list_next( iter ); + + end = iter; + while ( size-- > 0 ) + end = list_next( end ); + + *first = iter; + *last = end; +} + +static LIST * apply_modifiers_empty( LIST * result, string * buf, VAR_EDITS * edits, int n) +{ + int i; + for ( i = 0; i < n; ++i ) + { + if ( edits[i].empty.ptr ) + { + /** FIXME: is empty.ptr always null-terminated? */ + var_edit_file( edits[i].empty.ptr, buf, edits + i ); + var_edit_shift( buf, 0, edits + i ); + result = list_new( result, object_new( buf->value ) ); + string_truncate( buf, 0 ); + } + } + return result; +} + +static LIST * apply_modifiers_non_empty( LIST * result, string * buf, VAR_EDITS * edits, int n, LIST * begin, LIST * end ) +{ + int i; + LIST * iter; + for ( i = 0; i < n; ++i ) + { + if ( edits[i].join.ptr ) + { + var_edit_file( object_str( begin->value ), buf, edits + i ); + var_edit_shift( buf, 0, edits + i ); + for ( iter = list_next( begin ); iter != end; iter = list_next( iter ) ) + { + size_t size; + string_append( buf, edits[i].join.ptr ); + size = buf->size; + var_edit_file( object_str( iter->value ), buf, edits + i ); + var_edit_shift( buf, size, edits + i ); + } + result = list_new( result, object_new( buf->value ) ); + string_truncate( buf, 0 ); + } + else + { + for ( iter = begin; iter != end; iter = iter->next ) + { + var_edit_file( object_str( iter->value ), buf, edits + i ); + var_edit_shift( buf, 0, edits + i ); + result = list_new( result, object_new( buf->value ) ); + string_truncate( buf, 0 ); + } + } + } + return result; +} + +static LIST * apply_modifiers_impl( LIST * result, string * buf, VAR_EDITS * edits, int n, LIST * iter, LIST * end ) +{ + if ( iter != end ) + { + return apply_modifiers_non_empty( result, buf, edits, n, iter, end ); + } + else + { + return apply_modifiers_empty( result, buf, edits, n ); + } +} + +static LIST * apply_subscript_and_modifiers( STACK * s, int n ) +{ + LIST * value = stack_top( s ); + LIST * indices = stack_at( s, 1 ); + LIST * result = L0; + VAR_EDITS * edits = (VAR_EDITS *)((LIST * *)stack_get( s ) + 2); + int length = list_length( value ); + string buf[1]; + string_new( buf ); + for ( ; indices; indices = list_next( indices ) ) + { + LIST * iter = value; + LIST * end; + subscript_t sub = parse_subscript( object_str( indices->value ) ); + get_iters( sub, &iter, &end, length ); + result = apply_modifiers_impl( result, buf, edits, n, iter, end ); + } + string_free( buf ); + return result; +} + +typedef struct expansion_item +{ + LIST * elem; + LIST * saved; + int size; +} expansion_item; + +static LIST * expand( expansion_item * elem, int length ) +{ + LIST * result = L0; + string buf[1]; + int size = 0; + int i; + assert( length > 0 ); + for ( i = 0; i < length; ++i ) + { + int max = 0; + LIST * l; + if ( !elem[i].elem ) return result; + for ( l = elem[i].elem; l; l = l->next ) + { + int len = strlen( object_str( l->value ) ); + if ( len > max ) max = len; + } + size += max; + } + string_new( buf ); + string_reserve( buf, size ); + i = 0; + { + loop: + for ( ; i < length; ++i ) + { + elem[i].size = buf->size; + string_append( buf, object_str( elem[i].elem->value ) ); + } + result = list_new( result, object_new( buf->value ) ); + while ( --i >= 0 ) + { + if(elem[i].elem->next) + { + elem[i].elem = elem[i].elem->next; + string_truncate( buf, elem[i].size ); + goto loop; + } + else + { + elem[i].elem = elem[i].saved; + } + } + } + string_free( buf ); + return result; +} + +struct dynamic_array +{ + int size; + int capacity; + void * data; +}; + +static void dynamic_array_init( struct dynamic_array * array ) +{ + array->size = 0; + array->capacity = 0; + array->data = 0; +} + +static void dynamic_array_free( struct dynamic_array * array ) +{ + BJAM_FREE( array->data ); +} + +static void dynamic_array_push_impl( struct dynamic_array * array, void * value, int unit_size ) +{ + if ( array->capacity == 0 ) + { + array->capacity = 2; + array->data = BJAM_MALLOC( array->capacity * unit_size ); + } + else if ( array->capacity == array->size ) + { + void * new_data; + array->capacity *= 2; + new_data = BJAM_MALLOC( array->capacity * unit_size ); + memcpy( new_data, array->data, array->size * unit_size ); + BJAM_FREE( array->data ); + array->data = new_data; + } + memcpy( (char *)array->data + array->size * unit_size, value, unit_size ); + ++array->size; +} + +#define dynamic_array_push( array, value ) ( dynamic_array_push_impl( array, &value, sizeof(value) ) ) +#define dynamic_array_at( type, array, idx ) (((type *)(array)->data)[idx]) + +/* + * struct compiler + */ + +struct label_info +{ + int absolute_position; + struct dynamic_array uses[1]; +}; + +struct stored_rule +{ + OBJECT * name; + PARSE * parse; + int arguments; + int local; +}; + +typedef struct compiler +{ + struct dynamic_array code[1]; + struct dynamic_array constants[1]; + struct dynamic_array labels[1]; + struct dynamic_array rules[1]; + struct dynamic_array actions[1]; +} compiler; + +static void compiler_init( compiler * c ) +{ + dynamic_array_init( c->code ); + dynamic_array_init( c->constants ); + dynamic_array_init( c->labels ); + dynamic_array_init( c->rules ); + dynamic_array_init( c->actions ); +} + +static void compiler_free( compiler * c ) +{ + int i; + dynamic_array_free( c->actions ); + dynamic_array_free( c->rules ); + for ( i = 0; i < c->labels->size; ++i ) + { + dynamic_array_free( dynamic_array_at( struct label_info, c->labels, i ).uses ); + } + dynamic_array_free( c->labels ); + dynamic_array_free( c->constants ); + dynamic_array_free( c->code ); +} + +static void compile_emit_instruction( compiler * c, instruction instr ) +{ + dynamic_array_push( c->code, instr ); +} + +static int compile_new_label( compiler * c ) +{ + int result = c->labels->size; + struct label_info info; + info.absolute_position = -1; + dynamic_array_init( info.uses ); + dynamic_array_push( c->labels, info ); + return result; +} + +static void compile_set_label( compiler * c, int label ) +{ + struct label_info * l = &dynamic_array_at( struct label_info, c->labels, label ); + int pos = c->code->size; + int i; + assert( l->absolute_position == -1 ); + l->absolute_position = pos; + for ( i = 0; i < l->uses->size; ++i ) + { + int id = dynamic_array_at( int, l->uses, i ); + int offset = (int)(pos - id - 1); + dynamic_array_at( instruction, c->code, id ).arg = offset; + } +} + +static void compile_emit( compiler * c, unsigned int op_code, int arg ) +{ + instruction instr; + instr.op_code = op_code; + instr.arg = arg; + compile_emit_instruction( c, instr ); +} + +static void compile_emit_branch( compiler * c, unsigned int op_code, int label ) +{ + struct label_info * l = &dynamic_array_at( struct label_info, c->labels, label ); + int pos = c->code->size; + instruction instr; + instr.op_code = op_code; + if ( l->absolute_position == -1 ) + { + instr.arg = 0; + dynamic_array_push( l->uses, pos ); + } + else + { + instr.arg = (int)( l->absolute_position - pos - 1 ); + } + compile_emit_instruction( c, instr ); +} + +static int compile_emit_constant( compiler * c, OBJECT * value ) +{ + OBJECT * copy = object_copy( value ); + dynamic_array_push( c->constants, copy ); + return c->constants->size - 1; +} + +static int compile_emit_rule( compiler * c, OBJECT * name, PARSE * parse, int arguments, int local ) +{ + struct stored_rule rule; + rule.name = object_copy( name ); + rule.parse = parse; + rule.arguments = arguments; + rule.local = local; + dynamic_array_push( c->rules, rule ); + return (int)( c->rules->size - 1 ); +} + +static int compile_emit_actions( compiler * c, OBJECT * name, OBJECT * command, int flags ) +{ + SUBACTION a; + a.name = object_copy( name ); + a.command = object_copy( command ); + a.flags = flags; + dynamic_array_push( c->actions, a ); + return (int)( c->actions->size - 1 ); +} + +static JAM_FUNCTION * compile_to_function( compiler * c ) +{ + JAM_FUNCTION * result = BJAM_MALLOC( sizeof(JAM_FUNCTION) ); + int i; + result->type = FUNCTION_JAM; + result->reference_count = 1; + + result->rulename = 0; + + result->code = BJAM_MALLOC( c->code->size * sizeof(instruction) ); + memcpy( result->code, c->code->data, c->code->size * sizeof(instruction) ); + + result->constants = BJAM_MALLOC( c->constants->size * sizeof(OBJECT *) ); + memcpy( result->constants, c->constants->data, c->constants->size * sizeof(OBJECT *) ); + result->num_constants = c->constants->size; + + result->num_subfunctions = c->rules->size; + result->functions = BJAM_MALLOC( c->rules->size * sizeof(SUBFUNCTION) ); + for ( i = 0; i < c->rules->size; ++i ) + { + struct stored_rule * rule = &dynamic_array_at( struct stored_rule, c->rules, i ); + result->functions[i].name = rule->name; + result->functions[i].code = function_compile( rule->parse ); + result->functions[i].arguments = rule->arguments; + result->functions[i].local = rule->local; + } + + result->actions = BJAM_MALLOC( c->actions->size * sizeof(SUBACTION) ); + memcpy( result->actions, c->actions->data, c->actions->size * sizeof(SUBACTION) ); + result->num_subactions = c->actions->size; + + result->file = 0; + result->line = -1; + + return result; +} + +/* + * Parsing of variable expansions + */ + +typedef struct VAR_PARSE_GROUP +{ + struct dynamic_array elems[1]; +} VAR_PARSE_GROUP; + +#define VAR_PARSE_TYPE_VAR 0 +#define VAR_PARSE_TYPE_STRING 1 + +typedef struct _var_parse +{ + int type; /* string or variable */ +} VAR_PARSE; + +typedef struct +{ + int type; + VAR_PARSE_GROUP * name; + VAR_PARSE_GROUP * subscript; + struct dynamic_array modifiers[1]; +} VAR_PARSE_VAR; + +typedef struct +{ + int type; + OBJECT * s; +} VAR_PARSE_STRING; + +static void var_parse_free( VAR_PARSE * ); + +/* + * VAR_PARSE_GROUP + */ + +static VAR_PARSE_GROUP * var_parse_group_new() +{ + VAR_PARSE_GROUP * result = BJAM_MALLOC( sizeof( VAR_PARSE_GROUP ) ); + dynamic_array_init( result->elems ); + return result; +} + +static void var_parse_group_free( VAR_PARSE_GROUP * group ) +{ + int i; + for ( i = 0; i < group->elems->size; ++i ) + { + var_parse_free( dynamic_array_at( VAR_PARSE *, group->elems, i ) ); + } + dynamic_array_free( group->elems ); + BJAM_FREE( group ); +} + +static void var_parse_group_add( VAR_PARSE_GROUP * group, VAR_PARSE * elem ) +{ + dynamic_array_push( group->elems, elem ); +} + +static void var_parse_group_maybe_add_constant( VAR_PARSE_GROUP * group, const char * start, const char * end ) +{ + if ( start != end ) + { + string buf[1]; + VAR_PARSE_STRING * value = (VAR_PARSE_STRING *)BJAM_MALLOC( sizeof(VAR_PARSE_STRING) ); + value->type = VAR_PARSE_TYPE_STRING; + string_new( buf ); + string_append_range( buf, start, end ); + value->s = object_new( buf->value ); + string_free( buf ); + var_parse_group_add( group, (VAR_PARSE *)value ); + } +} + +/* + * VAR_PARSE_VAR + */ + +static VAR_PARSE_VAR * var_parse_var_new() +{ + VAR_PARSE_VAR * result = BJAM_MALLOC( sizeof( VAR_PARSE_VAR ) ); + result->type = VAR_PARSE_TYPE_VAR; + result->name = var_parse_group_new(); + result->subscript = 0; + dynamic_array_init( result->modifiers ); + return result; +} + +static void var_parse_var_free( VAR_PARSE_VAR * var ) +{ + int i; + var_parse_group_free( var->name ); + if ( var->subscript ) + var_parse_group_free( var->subscript ); + for( i = 0; i < var->modifiers->size; ++i ) + var_parse_group_free( dynamic_array_at( VAR_PARSE_GROUP *, var->modifiers, i ) ); + dynamic_array_free( var->modifiers ); + BJAM_FREE( var ); +} + +static VAR_PARSE_GROUP * var_parse_var_new_modifier( VAR_PARSE_VAR * var ) +{ + VAR_PARSE_GROUP * result = var_parse_group_new(); + dynamic_array_push( var->modifiers, result ); + return result; +} + +/* + * VAR_PARSE_STRING + */ + +static void var_parse_string_free( VAR_PARSE_STRING * string ) +{ + object_free( string->s ); + BJAM_FREE( string ); +} + +/* + * VAR_PARSE + */ + +static void var_parse_free( VAR_PARSE * parse ) +{ + if ( parse->type == VAR_PARSE_TYPE_VAR ) + { + var_parse_var_free( (VAR_PARSE_VAR *)parse ); + } + else if ( parse->type == VAR_PARSE_TYPE_STRING ) + { + var_parse_string_free( (VAR_PARSE_STRING *)parse ); + } + else + { + assert(!"Invalid type"); + } +} + +/* + * Compile VAR_PARSE + */ + +static void var_parse_group_compile( const VAR_PARSE_GROUP * parse, compiler * c ); + +static void var_parse_var_compile( const VAR_PARSE_VAR * parse, compiler * c ) +{ + int expand_name = 0; + /* If there are modifiers, emit them in reverse order. */ + if ( parse->modifiers->size > 0 ) + { + int i; + for ( i = 0; i < parse->modifiers->size; ++i ) + { + var_parse_group_compile( dynamic_array_at( VAR_PARSE_GROUP *, parse->modifiers, parse->modifiers->size - i - 1 ), c ); + } + } + + /* If there's a subscript, emit it. */ + if ( parse->subscript ) + { + var_parse_group_compile( parse->subscript, c ); + } + + /* If the variable name is empty, look it up. */ + if ( parse->name->elems->size == 0 ) + { + compile_emit( c, INSTR_PUSH_VAR, compile_emit_constant( c, constant_empty ) ); + } + /* If the variable name doesn't need to be expanded, look it up. */ + else if ( parse->name->elems->size == 1 && + dynamic_array_at( VAR_PARSE *, parse->name->elems, 0 )->type == VAR_PARSE_TYPE_STRING ) + { + OBJECT * name = ( (VAR_PARSE_STRING *)dynamic_array_at( VAR_PARSE *, parse->name->elems, 0 ) )->s; + int idx = get_argument_index( object_str( name ) ); + if ( idx != -1 ) + { + compile_emit( c, INSTR_PUSH_ARG, idx ); + } + else + { + compile_emit( c, INSTR_PUSH_VAR, compile_emit_constant( c, name ) ); + } + } + /* Otherwise, push the var names and use the group instruction. */ + else + { + var_parse_group_compile( parse->name, c ); + expand_name = 1; + } + + /** Select the instruction for expanding the variable. */ + if ( !parse->modifiers->size && !parse->subscript && !expand_name ) + { + /* Nothing to do */ + } + else if ( !parse->modifiers->size && !parse->subscript && expand_name ) + { + compile_emit( c, INSTR_PUSH_GROUP, 0 ); + } + else if ( !parse->modifiers->size && parse->subscript && !expand_name ) + { + compile_emit( c, INSTR_APPLY_INDEX, 0 ); + } + else if ( !parse->modifiers->size && parse->subscript && expand_name ) + { + compile_emit( c, INSTR_APPLY_INDEX_GROUP, 0 ); + } + if ( parse->modifiers->size && !parse->subscript && !expand_name ) + { + compile_emit( c, INSTR_APPLY_MODIFIERS, parse->modifiers->size ); + } + else if ( parse->modifiers->size && !parse->subscript && expand_name ) + { + compile_emit( c, INSTR_APPLY_MODIFIERS_GROUP, parse->modifiers->size ); + } + else if ( parse->modifiers->size && parse->subscript && !expand_name ) + { + compile_emit( c, INSTR_APPLY_INDEX_MODIFIERS, parse->modifiers->size ); + } + else if ( parse->modifiers->size && parse->subscript && expand_name ) + { + compile_emit( c, INSTR_APPLY_INDEX_MODIFIERS_GROUP, parse->modifiers->size ); + } +} + +static void var_parse_string_compile( const VAR_PARSE_STRING * parse, compiler * c ) +{ + compile_emit( c, INSTR_PUSH_CONSTANT, compile_emit_constant( c, parse->s ) ); +} + +static void var_parse_compile( const VAR_PARSE * parse, compiler * c ) +{ + if( parse->type == VAR_PARSE_TYPE_VAR ) + { + var_parse_var_compile( (const VAR_PARSE_VAR *)parse, c ); + } + else if( parse->type == VAR_PARSE_TYPE_STRING ) + { + var_parse_string_compile( (const VAR_PARSE_STRING *)parse, c ); + } +} + +static void var_parse_group_compile( const VAR_PARSE_GROUP * parse, compiler * c ) +{ + /* Emit the elements in reverse order. */ + int i; + for( i = 0; i < parse->elems->size; ++i) + { + var_parse_compile( dynamic_array_at( VAR_PARSE *, parse->elems, parse->elems->size - i - 1 ), c ); + } + /* If there're no elements, emit an empty string. */ + if ( parse->elems->size == 0 ) + { + compile_emit( c, INSTR_PUSH_CONSTANT, compile_emit_constant( c, constant_empty ) ); + } + /* If there's more than one element, combine them. */ + if ( parse->elems->size > 1 ) + { + compile_emit( c, INSTR_COMBINE_STRINGS, parse->elems->size ); + } +} + +/* + * Parse VAR_PARSE_VAR + */ + +static VAR_PARSE * parse_variable( const char * * string ); +static int try_parse_variable( const char * * s_, const char * * string, VAR_PARSE_GROUP * out); +static void balance_parentheses( const char * * s_, const char * * string, VAR_PARSE_GROUP * out); + +/* + * Parses a string that can contain variables to expand. + */ + +static VAR_PARSE_GROUP * parse_expansion( const char * * string ) +{ + VAR_PARSE_GROUP * result = var_parse_group_new(); + const char * s = *string; + for (;;) + { + if(try_parse_variable( &s, string, result )) {} + else if(s[0] == '\0') + { + var_parse_group_maybe_add_constant( result, *string, s ); + return result; + } + else + { + ++s; + } + } +} + +/* + * Checks whether the string a *s_ starts with + * a variable expansion "$(". *string should point + * to the first unemitted character before *s. + * If *s_ starts with variable expansion, appends + * elements to out up to the closing ")", and + * adjusts *s_ and *string to point to next character. + * Returns 1 if s_ starts with a variable, 0 otherwise. + */ + +static int try_parse_variable( const char * * s_, const char * * string, VAR_PARSE_GROUP * out) +{ + const char * s = *s_; + if(s[0] == '$' && s[1] == '(') + { + var_parse_group_maybe_add_constant( out, *string, s ); + s += 2; + var_parse_group_add( out, parse_variable( &s ) ); + *string = s; + *s_ = s; + return 1; + } + else + { + return 0; + } +} + +static const char * current_file = ""; +static int current_line; + +static void parse_error( const char * message ) +{ + printf( "%s:%d: %s\n", current_file, current_line, message ); + exit(1); +} + +/* + * Parses a single variable up to the closing ")" and + * adjusts *string to point to the next character. *string + * should point to the character immediately after + * the initial "$(" + */ + +static VAR_PARSE * parse_variable( const char * * string ) +{ + VAR_PARSE_VAR * result = var_parse_var_new(); + VAR_PARSE_GROUP * name = result->name; + const char * s = *string; + for ( ; ; ) + { + if ( try_parse_variable( &s, string, name ) ) {} + else if ( s[0] == ':' ) + { + VAR_PARSE_GROUP * mod; + var_parse_group_maybe_add_constant( name, *string, s ); + ++s; + *string = s; + mod = var_parse_var_new_modifier( result ); + for ( ; ; ) + { + if ( try_parse_variable( &s, string, mod ) ) {} + else if ( s[0] == ')' ) + { + var_parse_group_maybe_add_constant( mod, *string, s ); + ++s; + *string = s; + return (VAR_PARSE *)result; + } + else if ( s[0] == '(' ) + { + ++s; + balance_parentheses( &s, string, mod ); + } + else if ( s[0] == ':' ) + { + var_parse_group_maybe_add_constant( mod, *string, s ); + ++s; + *string = s; + mod = var_parse_var_new_modifier( result ); + } + else if ( s[0] == '[' ) + { + parse_error("unexpected subscript"); + } + else if ( s[0] == '\0' ) + { + parse_error( "unbalanced parentheses" ); + } + else + { + ++s; + } + } + } + else if ( s[0] == '[' ) + { + VAR_PARSE_GROUP * subscript = var_parse_group_new(); + result->subscript = subscript; + var_parse_group_maybe_add_constant( name, *string, s ); + ++s; + *string = s; + for ( ; ; ) + { + if ( try_parse_variable( &s, string, subscript ) ) {} + else if ( s[0] == ']' ) + { + var_parse_group_maybe_add_constant( subscript, *string, s ); + ++s; + *string = s; + if ( s[0] == '\0' ) + { + parse_error( "unbalanced parentheses" ); + } + else if ( s[0] == ')' || s[0] == ':' ) + { + break; + } + else + { + parse_error( "unexpected text following []" ); + } + } + else if ( isdigit( s[0] ) || s[0] == '-' ) + { + ++s; + } + else + { + parse_error( "malformed subscript" ); + } + } + } + else if ( s[0] == ')' ) + { + var_parse_group_maybe_add_constant( name, *string, s ); + ++s; + *string = s; + return (VAR_PARSE *)result; + } + else if ( s[0] == '(' ) + { + ++s; + balance_parentheses( &s, string, name ); + } + else if ( s[0] == '\0' ) + { + parse_error( "unbalanced parentheses" ); + } + else + { + ++s; + } + } +} + +/* + * Given that *s_ points to the character after a "(", + * parses up to the matching ")". *string should + * point to the first unemitted character before *s_. + * + * When the function returns, *s_ will point to the character + * after the ")", and *string will point to the first + * unemitted character before *s_. The range from *string + * to *s_ does not contain any variables that need to be + * expanded. + */ + +void balance_parentheses( const char * * s_, const char * * string, VAR_PARSE_GROUP * out) +{ + int depth = 1; + const char * s = *s_; + for ( ; ; ) + { + if ( try_parse_variable( &s, string, out ) ) { } + else if(s[0] == ':' || s[0] == '[' || s[0] == '\0') + { + parse_error( "unbalanced parentheses" ); + } + else if(s[0] == ')') + { + ++s; + if(--depth == 0) break; + } + else if(s[0] == '(') + { + ++depth; + ++s; + } + else + { + ++s; + } + } + *s_ = s; +} + +/* + * Main compile + */ + +#define RESULT_STACK 0 +#define RESULT_RETURN 1 +#define RESULT_NONE 2 + +static void compile_parse( PARSE * parse, compiler * c, int result_location ); + +static void compile_condition( PARSE * parse, compiler * c, int branch_true, int label ) +{ + assert( parse->type == PARSE_EVAL ); + switch ( parse->num ) + { + case EXPR_EXISTS: + { + compile_parse( parse->left, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_NOT_EMPTY, label ); + else + compile_emit_branch( c, INSTR_JUMP_EMPTY, label ); + break; + } + case EXPR_EQUALS: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_EQ, label ); + else + compile_emit_branch( c, INSTR_JUMP_NE, label ); + break; + } + case EXPR_NOTEQ: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_NE, label ); + else + compile_emit_branch( c, INSTR_JUMP_EQ, label ); + break; + } + case EXPR_LESS: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_LT, label ); + else + compile_emit_branch( c, INSTR_JUMP_GE, label ); + break; + } + case EXPR_LESSEQ: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_LE, label ); + else + compile_emit_branch( c, INSTR_JUMP_GT, label ); + break; + } + case EXPR_MORE: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_GT, label ); + else + compile_emit_branch( c, INSTR_JUMP_LE, label ); + break; + } + case EXPR_MOREEQ: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_GE, label ); + else + compile_emit_branch( c, INSTR_JUMP_LT, label ); + break; + } + case EXPR_IN: + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + if ( branch_true ) + compile_emit_branch( c, INSTR_JUMP_IN, label ); + else + compile_emit_branch( c, INSTR_JUMP_NOT_IN, label ); + break; + } + case EXPR_AND: + { + if ( branch_true ) + { + int f = compile_new_label( c ); + compile_condition( parse->left, c, 0, f ); + compile_condition( parse->right, c, 1, label ); + compile_set_label( c, f ); + } + else + { + compile_condition( parse->left, c, 0, label ); + compile_condition( parse->right, c, 0, label ); + } + break; + } + case EXPR_OR: + { + if ( branch_true ) + { + compile_condition( parse->left, c, 1, label ); + compile_condition( parse->right, c, 1, label ); + } + else + { + int t = compile_new_label( c ); + compile_condition( parse->left, c, 1, t ); + compile_condition( parse->right, c, 0, label ); + compile_set_label( c, t ); + } + break; + } + case EXPR_NOT: + { + compile_condition( parse->left, c, !branch_true, label ); + break; + } + } +} + +static void adjust_result( compiler * c, int actual_location, int desired_location ) +{ + if ( actual_location == desired_location ) + ; + else if ( actual_location == RESULT_STACK && desired_location == RESULT_RETURN ) + compile_emit( c, INSTR_SET_RESULT, 0 ); + else if( actual_location == RESULT_STACK && desired_location == RESULT_NONE ) + compile_emit( c, INSTR_POP, 0 ); + else if( actual_location == RESULT_RETURN && desired_location == RESULT_STACK ) + compile_emit( c, INSTR_PUSH_RESULT, 0 ); + else if ( actual_location == RESULT_RETURN && desired_location == RESULT_NONE ) + ; + else if ( actual_location == RESULT_NONE && desired_location == RESULT_STACK ) + compile_emit( c, INSTR_PUSH_EMPTY, 0 ); + else if ( actual_location == RESULT_NONE && desired_location == RESULT_RETURN ) + { + compile_emit( c, INSTR_PUSH_EMPTY, 0 ); + compile_emit( c, INSTR_SET_RESULT, 0 ); + } + else + { + assert( !"invalid result location" ); + } +} + +static const char * parse_type( PARSE * parse ) +{ + switch ( parse->type ) + { + case PARSE_APPEND: return "append"; + case PARSE_EVAL: return "eval"; + case PARSE_RULES: return "rules"; + default: return "unknown"; + } +} + +static void compile_parse( PARSE * parse, compiler * c, int result_location ) +{ + if ( parse->type == PARSE_APPEND ) + { + /* + * append is associative, so flip the parse tree of chained + * appends around to keep the stack from getting too deep. + */ + compile_parse( parse->right, c, RESULT_STACK ); + while ( parse->left->type == PARSE_APPEND ) + { + compile_parse( parse->left->right, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_APPEND, 0 ); + parse = parse->left; + } + compile_parse( parse->left, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_APPEND, 0 ); + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_EVAL ) + { + /* FIXME: This is only needed because of the bizarre parsing of conditions. */ + if ( parse->num == EXPR_EXISTS ) + { + compile_parse( parse->left, c, result_location ); + } + else + { + printf( "%s:%d: Conditional used as list (check operator precedence).\n", object_str(parse->file), parse->line, parse->num ); + exit( 1 ); + } + } + else if ( parse->type == PARSE_FOREACH ) + { + int var = compile_emit_constant( c, parse->string ); + int top = compile_new_label( c ); + int end = compile_new_label( c ); + + /* + * Evaluate the list. + */ + compile_parse( parse->left, c, RESULT_STACK ); + + /* Localize the loop variable */ + if ( parse->num ) + { + compile_emit( c, INSTR_PUSH_EMPTY, 0 ); + compile_emit( c, INSTR_PUSH_LOCAL, var ); + compile_emit( c, INSTR_SWAP, 1 ); + } + + compile_set_label( c, top ); + compile_emit_branch( c, INSTR_TRY_POP_FRONT, end ); + compile_emit( c, INSTR_SET, var ); + compile_emit( c, INSTR_POP, 0 ); + + /* Run the loop body */ + compile_parse( parse->right, c, RESULT_NONE ); + + compile_emit_branch( c, INSTR_JUMP, top ); + compile_set_label( c, end ); + + if ( parse->num ) + { + compile_emit( c, INSTR_POP_LOCAL, var ); + } + + adjust_result( c, RESULT_NONE, result_location); + } + else if( parse->type == PARSE_IF ) + { + int nested_result = result_location == RESULT_NONE? RESULT_NONE : RESULT_RETURN; + int f = compile_new_label( c ); + /* Emit the condition */ + compile_condition( parse->left, c, 0, f ); + /* Emit the if block */ + compile_parse( parse->right, c, nested_result ); + if ( parse->third->type != PARSE_NULL ) + { + /* Emit the else block */ + int end = compile_new_label( c ); + compile_emit_branch( c, INSTR_JUMP, end ); + compile_set_label( c, f ); + compile_parse( parse->third, c, nested_result ); + compile_set_label( c, end ); + } + else + { + compile_set_label( c, f ); + } + + adjust_result( c, nested_result, result_location); + } + else if( parse->type == PARSE_WHILE ) + { + int nested_result = result_location == RESULT_NONE? RESULT_NONE : RESULT_RETURN; + int test = compile_new_label( c ); + int top = compile_new_label( c ); + /* Jump to the loop test */ + compile_emit_branch( c, INSTR_JUMP, test ); + compile_set_label( c, top ); + /* Emit the loop body */ + compile_parse( parse->right, c, nested_result ); + /* Emit the condition */ + compile_set_label( c, test ); + compile_condition( parse->left, c, 1, top ); + + adjust_result( c, nested_result, result_location ); + } + else if ( parse->type == PARSE_INCLUDE ) + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_emit( c, INSTR_INCLUDE, 0 ); + adjust_result( c, RESULT_NONE, result_location ); + } + else if ( parse->type == PARSE_MODULE ) + { + int nested_result = result_location == RESULT_NONE? RESULT_NONE : RESULT_RETURN; + compile_parse( parse->left, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_MODULE, 0 ); + compile_parse( parse->right, c, nested_result ); + compile_emit( c, INSTR_POP_MODULE, 0 ); + adjust_result( c, nested_result, result_location ); + } + else if ( parse->type == PARSE_CLASS ) + { + /* Evaluate the class name. */ + compile_parse( parse->left->right, c, RESULT_STACK ); + /* Evaluate the base classes. */ + if ( parse->left->left ) + { + compile_parse( parse->left->left->right, c, RESULT_STACK ); + } + else + { + compile_emit( c, INSTR_PUSH_EMPTY, 0 ); + } + compile_emit( c, INSTR_CLASS, 0 ); + compile_parse( parse->right, c, RESULT_NONE ); + compile_emit( c, INSTR_POP_MODULE, 0 ); + + adjust_result( c, RESULT_NONE, result_location ); + } + else if ( parse->type == PARSE_LIST ) + { + OBJECT * o = parse->string; + const char * s = object_str( o ); + VAR_PARSE_GROUP * group; + current_file = object_str( parse->file ); + current_line = parse->line; + group = parse_expansion( &s ); + var_parse_group_compile( group, c ); + var_parse_group_free( group ); + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_LOCAL ) + { + int nested_result = result_location == RESULT_NONE? RESULT_NONE : RESULT_RETURN; + /* + * This should be left recursive group of compile_appends + */ + PARSE * vars = parse->left; + + /* Special case an empty list of vars */ + if ( vars->type == PARSE_NULL ) + { + compile_parse( parse->right, c, RESULT_NONE ); + compile_parse( parse->third, c, result_location ); + nested_result = result_location; + } + /* + * Check whether there is exactly one variable + * with a constant name + */ + else if ( vars->left->type == PARSE_NULL && + vars->right->type == PARSE_LIST ) + { + const char * s = object_str( vars->right->string ); + VAR_PARSE_GROUP * group; + current_file = object_str( parse->file ); + current_line = parse->line; + group = parse_expansion( &s ); + if ( group->elems->size == 1 && + dynamic_array_at( VAR_PARSE *, group->elems, 0 )->type == VAR_PARSE_TYPE_STRING ) + { + int name = compile_emit_constant( c, ( (VAR_PARSE_STRING *)dynamic_array_at( VAR_PARSE *, group->elems, 0 ) )->s ); + var_parse_group_free( group ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_LOCAL, name ); + compile_parse( parse->third, c, nested_result ); + compile_emit( c, INSTR_POP_LOCAL, name ); + } + else + { + var_parse_group_compile( group, c ); + var_parse_group_free( group ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_LOCAL_GROUP, 0 ); + compile_parse( parse->third, c, nested_result ); + compile_emit( c, INSTR_POP_LOCAL_GROUP, 0 ); + } + } + else + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, INSTR_PUSH_LOCAL_GROUP, 0 ); + compile_parse( parse->third, c, nested_result ); + compile_emit( c, INSTR_POP_LOCAL_GROUP, 0 ); + } + adjust_result( c, nested_result, result_location ); + } + else if ( parse->type == PARSE_ON ) + { + int end = compile_new_label( c ); + compile_parse( parse->left, c, RESULT_STACK ); + compile_emit_branch( c, INSTR_PUSH_ON, end ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, INSTR_POP_ON, 0 ); + compile_set_label( c, end ); + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_RULE ) + { + PARSE * p; + int n = 0; + VAR_PARSE_GROUP * group; + const char * s = object_str( parse->string ); + + if ( parse->left->left == NULL && parse->left->right->type == PARSE_NULL ) + ; + else + for ( p = parse->left; p; p = p->left ) + { + compile_parse( p->right, c, RESULT_STACK ); + ++n; + } + + current_file = object_str( parse->file ); + current_line = parse->line; + group = parse_expansion( &s ); + var_parse_group_compile( group, c ); + var_parse_group_free( group ); + compile_emit( c, INSTR_CALL_RULE, n ); + compile_emit( c, compile_emit_constant( c, parse->string ), parse->line ); + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_RULES ) + { + do compile_parse( parse->left, c, RESULT_NONE ); + while ( ( parse = parse->right )->type == PARSE_RULES ); + compile_parse( parse, c, result_location ); + } + else if ( parse->type == PARSE_SET ) + { + PARSE * vars = parse->left; + unsigned int op_code; + unsigned int op_code_group; + + switch ( parse->num ) + { + case ASSIGN_SET: default: op_code = INSTR_SET; op_code_group = INSTR_SET_GROUP; break; + case ASSIGN_APPEND: op_code = INSTR_APPEND; op_code_group = INSTR_APPEND_GROUP; break; + case ASSIGN_DEFAULT: op_code = INSTR_DEFAULT; op_code_group = INSTR_DEFAULT_GROUP; break; + } + + /* + * Check whether there is exactly one variable + * with a constant name + */ + if ( vars->type == PARSE_LIST ) + { + const char * s = object_str( vars->string ); + VAR_PARSE_GROUP * group; + current_file = object_str( parse->file ); + current_line = parse->line; + group = parse_expansion( &s ); + if ( group->elems->size == 1 && + dynamic_array_at( VAR_PARSE *, group->elems, 0 )->type == VAR_PARSE_TYPE_STRING ) + { + int name = compile_emit_constant( c, ( (VAR_PARSE_STRING *)dynamic_array_at( VAR_PARSE *, group->elems, 0 ) )->s ); + var_parse_group_free( group ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, op_code, name ); + } + else + { + var_parse_group_compile( group, c ); + var_parse_group_free( group ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, op_code_group, 0 ); + } + } + else + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + compile_emit( c, op_code_group, 0 ); + } + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_SETCOMP ) + { + int n_args = 0; + int rule_id; + if ( parse->right ) + { + PARSE * p; + for ( p = parse->right; p; p = p->left ) + { + compile_parse( p->right, c, RESULT_STACK ); + ++n_args; + } + } + + rule_id = compile_emit_rule( c, parse->string, parse->left, n_args, parse->num ); + + compile_emit( c, INSTR_RULE, rule_id ); + adjust_result( c, RESULT_NONE, result_location ); + } + else if ( parse->type == PARSE_SETEXEC ) + { + int actions_id = compile_emit_actions( c, parse->string, parse->string1, parse->num ); + + compile_parse( parse->left, c, RESULT_STACK ); + + compile_emit( c, INSTR_ACTIONS, actions_id ); + adjust_result( c, RESULT_NONE, result_location ); + } + else if ( parse->type == PARSE_SETTINGS ) + { + compile_parse( parse->left, c, RESULT_STACK ); + compile_parse( parse->third, c, RESULT_STACK ); + compile_parse( parse->right, c, RESULT_STACK ); + + switch ( parse->num ) + { + case ASSIGN_SET: default: compile_emit( c, INSTR_SET_ON, 0 ); break; + case ASSIGN_APPEND: compile_emit( c, INSTR_APPEND_ON, 0 ); break; + case ASSIGN_DEFAULT: compile_emit( c, INSTR_DEFAULT_ON, 0 ); break; + } + + adjust_result( c, RESULT_STACK, result_location ); + } + else if ( parse->type == PARSE_SWITCH ) + { + int switch_end = compile_new_label( c ); + compile_parse( parse->left, c, RESULT_STACK ); + + for ( parse = parse->right; parse; parse = parse->right ) + { + int id = compile_emit_constant( c, parse->left->string ); + int next_case = compile_new_label( c ); + compile_emit( c, INSTR_PUSH_CONSTANT, id ); + compile_emit_branch( c, INSTR_JUMP_NOT_GLOB, next_case ); + compile_parse( parse->left->left, c, result_location ); + compile_emit_branch( c, INSTR_JUMP, switch_end ); + compile_set_label( c, next_case ); + } + compile_emit( c, INSTR_POP, 0 ); + adjust_result( c, RESULT_NONE, result_location ); + compile_set_label( c, switch_end ); + } + else if ( parse->type == PARSE_NULL ) + { + adjust_result( c, RESULT_NONE, result_location ); + } + else + { + assert( !"unknown PARSE type." ); + } +} + +OBJECT * function_rulename( FUNCTION * function ) +{ + return function->rulename; +} + +void function_set_rulename( FUNCTION * function, OBJECT * rulename ) +{ + function->rulename = rulename; +} + +void function_location( FUNCTION * function_, OBJECT * * file, int * line ) +{ + if ( function_->type == FUNCTION_BUILTIN ) + { + *file = constant_builtin; + *line = -1; + } + else + { + JAM_FUNCTION * function = (JAM_FUNCTION *)function_; + *file = function->file; + *line = function->line; + } +} + +FUNCTION * function_builtin( LIST * ( * func )( FRAME * frame, int flags ), int flags ) +{ + BUILTIN_FUNCTION * result = BJAM_MALLOC( sizeof( BUILTIN_FUNCTION ) ); + result->type = FUNCTION_BUILTIN; + result->reference_count = 1; + result->rulename = 0; + result->func = func; + result->flags = flags; + return (FUNCTION *)result; +} + +FUNCTION * function_compile( PARSE * parse ) +{ + compiler c[1]; + JAM_FUNCTION * result; + compiler_init( c ); + compile_parse( parse, c, RESULT_RETURN ); + compile_emit( c, INSTR_RETURN, 0 ); + result = compile_to_function( c ); + compiler_free( c ); + result->file = object_copy( parse->file ); + result->line = parse->line; + return (FUNCTION *)result; +} + +void function_refer( FUNCTION * func ) +{ + ++func->reference_count; +} + +void function_free( FUNCTION * function_ ) +{ + int i; + + if ( --function_->reference_count != 0 ) return; + + if ( function_->rulename ) object_free( function_->rulename ); + + if ( function_->type == FUNCTION_JAM ) + { + JAM_FUNCTION * func = (JAM_FUNCTION *)function_; + + BJAM_FREE( func->code ); + for ( i = 0; i < func->num_constants; ++i ) + { + object_free( func->constants[i] ); + } + BJAM_FREE( func->constants ); + + for ( i = 0; i < func->num_subfunctions; ++i ) + { + object_free( func->functions[i].name ); + function_free( func->functions[i].code ); + } + BJAM_FREE( func->functions ); + + for ( i = 0; i < func->num_subactions; ++i ) + { + object_free( func->actions[i].name ); + object_free( func->actions[i].command ); + } + BJAM_FREE( func->actions ); + + object_free( func->file ); + } + + BJAM_FREE( function_ ); +} + +/* + * WARNING: The instruction set is tuned for Jam and + * is not really generic. Be especially careful about + * stack push/pop. + */ + +LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s ) +{ + JAM_FUNCTION * function; + instruction * code; + LIST * l; + LIST * r; + LIST * result = L0; + void * saved_stack = s->data; + + if ( function_->type == FUNCTION_BUILTIN ) + { + BUILTIN_FUNCTION * f = (BUILTIN_FUNCTION *)function_; + return f->func( frame, f->flags ); + } + + function = (JAM_FUNCTION *)function_; + code = function->code; + for ( ; ; ) + { + switch ( code->op_code ) + { + + /* + * Basic stack manipulation + */ + + case INSTR_PUSH_EMPTY: + { + stack_push( s, L0 ); + break; + } + + case INSTR_PUSH_CONSTANT: + { + OBJECT * value = function_get_constant( function, code->arg ); + stack_push( s, list_new( L0, object_copy( value ) ) ); + break; + } + + case INSTR_PUSH_ARG: + { + stack_push( s, frame_get_local( frame, code->arg ) ); + break; + } + + case INSTR_PUSH_VAR: + { + stack_push( s, function_get_variable( function, frame, code->arg ) ); + break; + } + + case INSTR_PUSH_GROUP: + { + LIST * value = L0; + l = stack_pop( s ); + for ( r = l; r; r = list_next( r ) ) + { + LIST * one = function_get_named_variable( function, frame, r->value ); + value = list_append( value, one ); + } + list_free( l ); + stack_push( s, value ); + break; + } + + case INSTR_PUSH_APPEND: + { + r = stack_pop( s ); + l = stack_pop( s ); + stack_push( s, list_append( r, l ) ); + break; + } + + case INSTR_SWAP: + { + l = stack_top( s ); + stack_set( s, 0, stack_at( s, code->arg ) ); + stack_set( s, code->arg, l ); + break; + } + + case INSTR_POP: + { + list_free( stack_pop( s ) ); + break; + } + + /* + * Branch instructions + */ + + case INSTR_JUMP: + { + code += code->arg; + break; + } + + case INSTR_JUMP_EMPTY: + { + l = stack_pop( s ); + if ( !list_cmp( l, L0 ) ) { code += code->arg; } + list_free( l ); + break; + } + + case INSTR_JUMP_NOT_EMPTY: + { + l = stack_pop( s ); + if( list_cmp( l, L0 ) ) { code += code->arg; } + list_free( l ); + break; + } + + case INSTR_JUMP_LT: + { + r = stack_pop( s ); + l = stack_pop( s ); + if ( list_cmp( l, r ) < 0 ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + case INSTR_JUMP_LE: + { + r = stack_pop( s ); + l = stack_pop( s ); + if ( list_cmp( l, r ) <= 0 ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + case INSTR_JUMP_GT: + { + r = stack_pop( s ); + l = stack_pop( s ); + if ( list_cmp( l, r ) > 0 ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + case INSTR_JUMP_GE: + { + r = stack_pop( s ); + l = stack_pop( s ); + if ( list_cmp( l, r ) >= 0 ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + case INSTR_JUMP_EQ: + { + r = stack_pop( s ); + l = stack_pop( s ); + if( list_cmp( l, r ) == 0 ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + case INSTR_JUMP_NE: + { + r = stack_pop(s); + l = stack_pop(s); + if( list_cmp(l, r) != 0 ) { code += code->arg; } + list_free(l); + list_free(r); + break; + } + + case INSTR_JUMP_IN: + { + r = stack_pop(s); + l = stack_pop(s); + if ( list_is_sublist( l, r ) ) { code += code->arg; } + list_free(l); + list_free(r); + break; + } + + case INSTR_JUMP_NOT_IN: + { + r = stack_pop( s ); + l = stack_pop( s ); + if( !list_is_sublist( l, r ) ) { code += code->arg; } + list_free( l ); + list_free( r ); + break; + } + + /* + * For + */ + + case INSTR_TRY_POP_FRONT: + { + l = stack_pop( s ); + if( !l ) + { + code += code->arg; + } + else + { + r = list_new( L0, object_copy( l->value ) ); + l = list_pop_front( l ); + stack_push( s, l ); + stack_push( s, r ); + } + break; + } + + /* + * Switch + */ + + case INSTR_JUMP_NOT_GLOB: + { + const char * pattern; + const char * match; + l = stack_pop( s ); + r = stack_top( s ); + pattern = l ? object_str( l->value ) : ""; + match = r ? object_str( r->value ) : ""; + if( glob( pattern, match ) ) + { + code += code->arg; + } + else + { + list_free( stack_pop( s ) ); + } + list_free( l ); + break; + } + + /* + * Return + */ + + case INSTR_SET_RESULT: + { + list_free( result ); + result = stack_pop( s ); + break; + } + + case INSTR_PUSH_RESULT: + { + stack_push( s, result ); + result = L0; + break; + } + + case INSTR_RETURN: + { + assert( saved_stack == s->data ); + return result; + } + + /* + * Local variables + */ + + case INSTR_PUSH_LOCAL: + { + LIST * value = stack_pop( s ); + stack_push( s, function_swap_variable( function, frame, code->arg, value ) ); + break; + } + + case INSTR_POP_LOCAL: + { + function_set_variable( function, frame, code->arg, stack_pop( s ) ); + break; + } + + case INSTR_PUSH_LOCAL_GROUP: + { + LIST * value = stack_pop( s ); + l = stack_pop( s ); + for( r = l; r; r = list_next( r ) ) + { + LIST * saved = function_swap_named_variable( function, frame, r->value, list_copy( L0, value ) ); + stack_push( s, saved ); + } + list_free( value ); + stack_push( s, l ); + break; + } + + case INSTR_POP_LOCAL_GROUP: + { + r = stack_pop( s ); + l = list_reverse( r ); + list_free( r ); + for( r = l; r; r = list_next( r ) ) + { + function_set_named_variable( function, frame, r->value, stack_pop( s ) ); + } + list_free( l ); + break; + } + + /* + * on $(TARGET) variables + */ + + case INSTR_PUSH_ON: + { + LIST * targets = stack_top( s ); + if ( targets ) + { + /* + * FIXME: push the state onto the stack instead of + * using pushsettings. + */ + TARGET * t = bindtarget( targets->value ); + pushsettings( t->settings ); + } + else + { + /* + * [ on $(TARGET) ... ] is ignored if $(TARGET) is empty. + */ + list_free( stack_pop( s ) ); + stack_push( s, L0 ); + code += code->arg; + } + break; + } + + case INSTR_POP_ON: + { + LIST * result = stack_pop( s ); + LIST * targets = stack_pop( s ); + if ( targets ) + { + TARGET * t = bindtarget( targets->value ); + popsettings( t->settings ); + } + list_free( targets ); + stack_push( s, result ); + break; + } + + case INSTR_SET_ON: + { + LIST * targets = stack_pop( s ); + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + LIST * ts; + for ( ts = targets; ts; ts = list_next( ts ) ) + { + TARGET * t = bindtarget( ts->value ); + LIST * l; + + for ( l = vars; l; l = list_next( l ) ) + t->settings = addsettings( t->settings, VAR_SET, l->value, + list_copy( L0, value ) ); + } + list_free( vars ); + list_free( targets ); + stack_push( s, value ); + break; + } + + case INSTR_APPEND_ON: + { + LIST * targets = stack_pop( s ); + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + LIST * ts; + for ( ts = targets; ts; ts = list_next( ts ) ) + { + TARGET * t = bindtarget( ts->value ); + LIST * l; + + for ( l = vars; l; l = list_next( l ) ) + t->settings = addsettings( t->settings, VAR_APPEND, l->value, + list_copy( L0, value ) ); + } + list_free( vars ); + list_free( targets ); + stack_push( s, value ); + break; + } + + case INSTR_DEFAULT_ON: + { + LIST * targets = stack_pop( s ); + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + LIST * ts; + for ( ts = targets; ts; ts = list_next( ts ) ) + { + TARGET * t = bindtarget( ts->value ); + LIST * l; + + for ( l = vars; l; l = list_next( l ) ) + t->settings = addsettings( t->settings, VAR_DEFAULT, l->value, + list_copy( L0, value ) ); + } + list_free( vars ); + list_free( targets ); + stack_push( s, value ); + break; + } + + /* + * Variable setting + */ + + case INSTR_SET: + { + function_set_variable( function, frame, code->arg, list_copy( L0, stack_top( s ) ) ); + break; + } + + case INSTR_APPEND: + { + function_append_variable( function, frame, code->arg, list_copy( L0, stack_top( s ) ) ); + break; + } + case INSTR_DEFAULT: + { + function_default_variable( function, frame, code->arg, list_copy( L0, stack_top( s ) ) ); + break; + } + + case INSTR_SET_GROUP: + { + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + for( r = vars; r; r = list_next( r ) ) + function_set_named_variable( function, frame, r->value, list_copy( L0, value ) ); + list_free( vars ); + stack_push( s, value ); + break; + } + + case INSTR_APPEND_GROUP: + { + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + for( r = vars; r; r = list_next( r ) ) + function_append_named_variable( function, frame, r->value, list_copy( L0, value ) ); + list_free( vars ); + stack_push( s, value ); + break; + } + + case INSTR_DEFAULT_GROUP: + { + LIST * value = stack_pop( s ); + LIST * vars = stack_pop( s ); + for( r = vars; r; r = list_next( r ) ) + function_default_named_variable( function, frame, r->value, list_copy( L0, value ) ); + list_free( vars ); + stack_push( s, value ); + break; + } + + /* + * Rules + */ + + case INSTR_CALL_RULE: + { + const char * unexpanded = + object_str( function_get_constant( function, code[1].op_code ) ); + LIST * result = function_call_rule( function, frame, s, code->arg, unexpanded, function->file, code[1].arg ); + stack_push( s, result ); + ++code; + break; + } + + case INSTR_RULE: + { + function_set_rule( function, frame, s, code->arg ); + break; + } + + case INSTR_ACTIONS: + { + function_set_actions( function, frame, s, code->arg ); + break; + } + + /* + * Variable expansion + */ + + case INSTR_APPLY_MODIFIERS: + { + int n; + int i; + l = stack_pop( s ); + n = expand_modifiers( s, code->arg ); + stack_push( s, l ); + l = apply_modifiers( s, n ); + list_free( stack_pop( s ) ); + stack_deallocate( s, n * sizeof( VAR_EDITS ) ); + for ( i = 0; i < code->arg; ++i ) + list_free( stack_pop( s ) ); /* pop modifiers */ + stack_push( s, l ); + break; + } + + case INSTR_APPLY_INDEX: + { + l = apply_subscript( s ); + list_free( stack_pop( s ) ); + list_free( stack_pop( s ) ); + stack_push( s, l ); + break; + } + + case INSTR_APPLY_INDEX_MODIFIERS: + { + int i; + int n; + l = stack_pop( s ); + r = stack_pop( s ); + n = expand_modifiers( s, code->arg ); + stack_push( s, r ); + stack_push( s, l ); + l = apply_subscript_and_modifiers( s, n ); + list_free( stack_pop( s ) ); + list_free( stack_pop( s ) ); + stack_deallocate( s, n * sizeof( VAR_EDITS ) ); + for ( i = 0; i < code->arg; ++i ) + list_free( stack_pop( s ) ); /* pop modifiers */ + stack_push( s, l ); + break; + } + + case INSTR_APPLY_MODIFIERS_GROUP: + { + int i; + LIST * vars = stack_pop( s ); + int n = expand_modifiers( s, code->arg ); + LIST * result = L0; + for( l = vars; l; l = list_next( l ) ) + { + stack_push( s, function_get_named_variable( function, frame, l->value ) ); + result = list_append( result, apply_modifiers( s, n ) ); + list_free( stack_pop( s ) ); + } + list_free( vars ); + stack_deallocate( s, n * sizeof( VAR_EDITS ) ); + for ( i = 0; i < code->arg; ++i ) + list_free( stack_pop( s ) ); /* pop modifiers */ + stack_push( s, result ); + break; + } + + case INSTR_APPLY_INDEX_GROUP: + { + LIST * vars = stack_pop( s ); + LIST * result = L0; + for( l = vars; l; l = list_next( l ) ) + { + stack_push( s, function_get_named_variable( function, frame, l->value ) ); + result = list_append( result, apply_subscript( s ) ); + list_free( stack_pop( s ) ); + } + list_free( vars ); + list_free( stack_pop( s ) ); + stack_push( s, result ); + break; + } + + case INSTR_APPLY_INDEX_MODIFIERS_GROUP: + { + int i; + LIST * vars = stack_pop( s ); + LIST * r = stack_pop( s ); + int n = expand_modifiers( s, code->arg ); + LIST * result = L0; + stack_push( s, r ); + for( l = vars; l; l = list_next( l ) ) + { + stack_push( s, function_get_named_variable( function, frame, l->value ) ); + result = list_append( result, apply_subscript_and_modifiers( s, n ) ); + list_free( stack_pop( s ) ); + } + list_free( stack_pop( s ) ); + list_free( vars ); + stack_deallocate( s, n * sizeof( VAR_EDITS ) ); + for ( i = 0; i < code->arg; ++i ) + list_free( stack_pop( s ) ); /* pop modifiers */ + stack_push( s, result ); + break; + } + + case INSTR_COMBINE_STRINGS: + { + LIST * result; + size_t buffer_size = code->arg * sizeof( expansion_item ); + LIST * * stack_pos = stack_get( s ); + expansion_item * items = stack_allocate( s, buffer_size ); + int i; + for( i = 0; i < code->arg; ++i ) + { + items[i].elem = items[i].saved = stack_pos[i]; + } + result = expand( items, code->arg ); + stack_deallocate( s, buffer_size ); + for( i = 0; i < code->arg; ++i ) + { + list_free( stack_pop( s ) ); + } + stack_push( s, result ); + break; + } + + case INSTR_INCLUDE: + { + LIST * nt = stack_pop( s ); + + if ( nt ) + { + TARGET * t = bindtarget( nt->value ); + list_free( nt ); + + /* DWA 2001/10/22 - Perforce Jam cleared the arguments here, which + * prevents an included file from being treated as part of the body of a + * rule. I did not 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. + If the name resolves to directory, let it error out. */ + object_free( t->boundname ); + t->boundname = search( t->name, &t->time, 0, 0 ); + popsettings( t->settings ); + + parse_file( t->boundname, frame ); + } + + break; + } + + /* + * Classes and modules + */ + + case INSTR_PUSH_MODULE: + { + LIST * module_name = stack_pop( s ); + + module_t * outer_module = frame->module; + frame->module = module_name ? bindmodule( module_name->value ) : root_module(); + + list_free( module_name ); + + if ( outer_module != frame->module ) + { + exit_module( outer_module ); + enter_module( frame->module ); + } + + *(module_t * *)stack_allocate( s, sizeof( module_t * ) ) = outer_module; + + break; + } + + case INSTR_POP_MODULE: + { + module_t * outer_module = *(module_t * *)stack_get( s ); + stack_deallocate( s, sizeof( module_t * ) ); + if ( outer_module != frame->module ) + { + exit_module( frame->module ); + enter_module( outer_module ); + frame->module = outer_module; + } + break; + } + + case INSTR_CLASS: + { + LIST * bases = stack_pop( s ); + LIST * name = stack_pop( s ); + OBJECT * class_module = make_class_module( name, bases, frame ); + + module_t * outer_module = frame->module; + frame->module = bindmodule( class_module ); + object_free( class_module ); + + if ( outer_module != frame->module ) + { + exit_module( outer_module ); + enter_module( frame->module ); + } + + *(module_t * *)stack_allocate( s, sizeof( module_t * ) ) = outer_module; + + break; + } + + } + ++code; + } +} + +void function_done( void ) +{ + BJAM_FREE( stack ); +} diff --git a/v2/engine/function.h b/v2/engine/function.h new file mode 100644 index 000000000..4c52a969f --- /dev/null +++ b/v2/engine/function.h @@ -0,0 +1,33 @@ +/* + * Copyright 2011 Steven Watanabe + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + */ + +#ifndef FUNCTION_SW20111123_H +#define FUNCTION_SW20111123_H + +#include "object.h" +#include "frames.h" +#include "lists.h" +#include "parse.h" + +typedef struct _function FUNCTION; +typedef struct _stack STACK; + +STACK * stack_global( void ); +void stack_push( STACK * s, LIST * l ); +LIST * stack_pop( STACK * s ); + +FUNCTION * function_compile( PARSE * parse ); +FUNCTION * function_builtin( LIST * ( * func )( FRAME * frame, int flags ), int flags ); +void function_refer( FUNCTION * ); +void function_free( FUNCTION * ); +OBJECT * function_rulename( FUNCTION * ); +void function_set_rulename( FUNCTION *, OBJECT * ); +void function_location( FUNCTION *, OBJECT * *, int * ); +LIST * function_run( FUNCTION * function, FRAME * frame, STACK * s ); + +void function_done( void ); + +#endif diff --git a/v2/engine/jam.c b/v2/engine/jam.c index 252923ef8..aaadf727c 100644 --- a/v2/engine/jam.c +++ b/v2/engine/jam.c @@ -126,6 +126,7 @@ #include "class.h" #include "execcmd.h" #include "constants.h" +#include "function.h" /* Macintosh is "special" */ #ifdef OS_MAC @@ -619,6 +620,7 @@ int main( int argc, char * * argv, char * * arg_environ ) regex_done(); exec_done(); pwd_done(); + function_done(); list_done(); constants_done(); object_done(); diff --git a/v2/engine/jamgram.c b/v2/engine/jamgram.c index b7c44c5ba..48c85228e 100644 --- a/v2/engine/jamgram.c +++ b/v2/engine/jamgram.c @@ -80,29 +80,29 @@ # define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ -# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define F0 -1 # define P0 (PARSE *)0 # define S0 (OBJECT *)0 -# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) -# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) -# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) -# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) -# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) -# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) -# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) -# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) -# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 ) -# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) -# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) -# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) -# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) -# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) -# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) -# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) -# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) -# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) -# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) +# define pappend( l,r ) parse_make( PARSE_APPEND,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( PARSE_EVAL,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( PARSE_FOREACH,l,r,P0,s,S0,x ) +# define pif( l,r,t ) parse_make( PARSE_IF,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( PARSE_INCLUDE,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( PARSE_LIST,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( PARSE_LOCAL,l,r,t,S0,S0,0 ) +# define pmodule( l,r ) parse_make( PARSE_MODULE,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( PARSE_CLASS,l,r,P0,S0,S0,0 ) +# define pnull() parse_make( PARSE_NULL,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( PARSE_ON,l,r,P0,S0,S0,0 ) +# define prule( s,p ) parse_make( PARSE_RULE,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( PARSE_RULES,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( PARSE_SET,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( PARSE_SETTINGS,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( PARSE_SETCOMP,p,a,P0,s,S0,l ) +# define psete( s,l,s1,f ) parse_make( PARSE_SETEXEC,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( PARSE_SWITCH,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( PARSE_WHILE,l,r,P0,S0,S0,0 ) # define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) # define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) diff --git a/v2/engine/jamgram.y b/v2/engine/jamgram.y index f44ddd533..543f1561a 100644 --- a/v2/engine/jamgram.y +++ b/v2/engine/jamgram.y @@ -105,29 +105,29 @@ # define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ -# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define F0 -1 # define P0 (PARSE *)0 # define S0 (OBJECT *)0 -# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) -# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) -# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) -# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) -# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) -# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) -# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) -# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) -# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 ) -# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) -# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) -# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) -# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) -# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) -# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) -# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) -# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) -# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) -# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) +# define pappend( l,r ) parse_make( PARSE_APPEND,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( PARSE_EVAL,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( PARSE_FOREACH,l,r,P0,s,S0,x ) +# define pif( l,r,t ) parse_make( PARSE_IF,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( PARSE_INCLUDE,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( PARSE_LIST,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( PARSE_LOCAL,l,r,t,S0,S0,0 ) +# define pmodule( l,r ) parse_make( PARSE_MODULE,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( PARSE_CLASS,l,r,P0,S0,S0,0 ) +# define pnull() parse_make( PARSE_NULL,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( PARSE_ON,l,r,P0,S0,S0,0 ) +# define prule( s,p ) parse_make( PARSE_RULE,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( PARSE_RULES,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( PARSE_SET,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( PARSE_SETTINGS,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( PARSE_SETCOMP,p,a,P0,s,S0,l ) +# define psete( s,l,s1,f ) parse_make( PARSE_SETEXEC,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( PARSE_SWITCH,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( PARSE_WHILE,l,r,P0,S0,S0,0 ) # define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) # define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) diff --git a/v2/engine/jamgram.yy b/v2/engine/jamgram.yy index 37b5f0ab0..8d20e3896 100644 --- a/v2/engine/jamgram.yy +++ b/v2/engine/jamgram.yy @@ -61,29 +61,29 @@ # define YYMAXDEPTH 10000 /* for OSF and other less endowed yaccs */ -# define F0 (LIST *(*)(PARSE *, FRAME *))0 +# define F0 -1 # define P0 (PARSE *)0 # define S0 (OBJECT *)0 -# define pappend( l,r ) parse_make( compile_append,l,r,P0,S0,S0,0 ) -# define peval( c,l,r ) parse_make( compile_eval,l,r,P0,S0,S0,c ) -# define pfor( s,l,r,x ) parse_make( compile_foreach,l,r,P0,s,S0,x ) -# define pif( l,r,t ) parse_make( compile_if,l,r,t,S0,S0,0 ) -# define pincl( l ) parse_make( compile_include,l,P0,P0,S0,S0,0 ) -# define plist( s ) parse_make( compile_list,P0,P0,P0,s,S0,0 ) -# define plocal( l,r,t ) parse_make( compile_local,l,r,t,S0,S0,0 ) -# define pmodule( l,r ) parse_make( compile_module,l,r,P0,S0,S0,0 ) -# define pclass( l,r ) parse_make( compile_class,l,r,P0,S0,S0,0 ) -# define pnull() parse_make( compile_null,P0,P0,P0,S0,S0,0 ) -# define pon( l,r ) parse_make( compile_on,l,r,P0,S0,S0,0 ) -# define prule( s,p ) parse_make( compile_rule,p,P0,P0,s,S0,0 ) -# define prules( l,r ) parse_make( compile_rules,l,r,P0,S0,S0,0 ) -# define pset( l,r,a ) parse_make( compile_set,l,r,P0,S0,S0,a ) -# define pset1( l,r,t,a ) parse_make( compile_settings,l,r,t,S0,S0,a ) -# define psetc( s,p,a,l ) parse_make( compile_setcomp,p,a,P0,s,S0,l ) -# define psete( s,l,s1,f ) parse_make( compile_setexec,l,P0,P0,s,s1,f ) -# define pswitch( l,r ) parse_make( compile_switch,l,r,P0,S0,S0,0 ) -# define pwhile( l,r ) parse_make( compile_while,l,r,P0,S0,S0,0 ) +# define pappend( l,r ) parse_make( PARSE_APPEND,l,r,P0,S0,S0,0 ) +# define peval( c,l,r ) parse_make( PARSE_EVAL,l,r,P0,S0,S0,c ) +# define pfor( s,l,r,x ) parse_make( PARSE_FOREACH,l,r,P0,s,S0,x ) +# define pif( l,r,t ) parse_make( PARSE_IF,l,r,t,S0,S0,0 ) +# define pincl( l ) parse_make( PARSE_INCLUDE,l,P0,P0,S0,S0,0 ) +# define plist( s ) parse_make( PARSE_LIST,P0,P0,P0,s,S0,0 ) +# define plocal( l,r,t ) parse_make( PARSE_LOCAL,l,r,t,S0,S0,0 ) +# define pmodule( l,r ) parse_make( PARSE_MODULE,l,r,P0,S0,S0,0 ) +# define pclass( l,r ) parse_make( PARSE_CLASS,l,r,P0,S0,S0,0 ) +# define pnull() parse_make( PARSE_NULL,P0,P0,P0,S0,S0,0 ) +# define pon( l,r ) parse_make( PARSE_ON,l,r,P0,S0,S0,0 ) +# define prule( s,p ) parse_make( PARSE_RULE,p,P0,P0,s,S0,0 ) +# define prules( l,r ) parse_make( PARSE_RULES,l,r,P0,S0,S0,0 ) +# define pset( l,r,a ) parse_make( PARSE_SET,l,r,P0,S0,S0,a ) +# define pset1( l,r,t,a ) parse_make( PARSE_SETTINGS,l,r,t,S0,S0,a ) +# define psetc( s,p,a,l ) parse_make( PARSE_SETCOMP,p,a,P0,s,S0,l ) +# define psete( s,l,s1,f ) parse_make( PARSE_SETEXEC,l,P0,P0,s,s1,f ) +# define pswitch( l,r ) parse_make( PARSE_SWITCH,l,r,P0,S0,S0,0 ) +# define pwhile( l,r ) parse_make( PARSE_WHILE,l,r,P0,S0,S0,0 ) # define pnode( l,r ) parse_make( F0,l,r,P0,S0,S0,0 ) # define psnode( s,l ) parse_make( F0,l,P0,P0,s,S0,0 ) diff --git a/v2/engine/lists.c b/v2/engine/lists.c index 41930d1fe..c291ed082 100644 --- a/v2/engine/lists.c +++ b/v2/engine/lists.c @@ -202,6 +202,43 @@ LIST * list_pop_front( LIST * l ) return result; } +LIST * list_reverse( LIST * l ) +{ + LIST * result = L0; + for ( ; l; l = l->next ) + { + result = list_append( list_new(L0, object_copy( l->value ) ), result ); + } + return result; +} + +int list_cmp( LIST * t, LIST * s ) +{ + int status = 0; + + while ( !status && ( t || s ) ) + { + const char *st = t ? object_str( t->value ) : ""; + const char *ss = s ? object_str( s->value ) : ""; + + status = strcmp( st, ss ); + + t = t ? list_next( t ) : t; + s = s ? list_next( s ) : s; + } + + return status; +} + +int list_is_sublist( LIST * sub, LIST * l ) +{ + for ( ; sub; sub = sub->next ) + { + if ( !list_in( l, sub->value ) ) + return 0; + } + return 1; +} /* * list_print() - print a list of strings to stdout diff --git a/v2/engine/lists.h b/v2/engine/lists.h index b73f99408..0a1595af7 100644 --- a/v2/engine/lists.h +++ b/v2/engine/lists.h @@ -87,6 +87,9 @@ LIST * list_pop_front( LIST *l ); LIST * list_sort( LIST *l); LIST * list_unique( LIST *sorted_list); int list_in(LIST* l, OBJECT* value); +LIST * list_reverse( LIST * ); +int list_cmp( LIST * lhs, LIST * rhs ); +int list_is_sublist( LIST * sub, LIST * l ); void list_done(); # define list_next( l ) ((l)->next) diff --git a/v2/engine/modules.c b/v2/engine/modules.c index 6e61b2793..044e9a111 100644 --- a/v2/engine/modules.c +++ b/v2/engine/modules.c @@ -89,7 +89,7 @@ static void delete_native_rule( void * xrule, void * data ) args_free( rule->arguments ); object_free( rule->name ); if ( rule->procedure ) - parse_free( rule->procedure ); + function_free( rule->procedure ); } diff --git a/v2/engine/modules/order.c b/v2/engine/modules/order.c index c9acc5eb3..4b442cd93 100644 --- a/v2/engine/modules/order.c +++ b/v2/engine/modules/order.c @@ -12,7 +12,7 @@ /* Use quite klugy approach: when we add order dependency from 'a' to 'b', just append 'b' to of value of variable 'a'. */ -LIST *add_pair( PARSE *parse, FRAME *frame ) +LIST *add_pair( FRAME *frame, int flags ) { LIST* arg = lol_get( frame->args, 0 ); @@ -74,7 +74,7 @@ void topological_sort(int** graph, int num_vertices, int* result) BJAM_FREE(colors); } -LIST *order( PARSE *parse, FRAME *frame ) +LIST *order( FRAME *frame, int flags ) { LIST* arg = lol_get( frame->args, 0 ); LIST* tmp; diff --git a/v2/engine/modules/path.c b/v2/engine/modules/path.c index e1605080c..cfa9b0b8c 100644 --- a/v2/engine/modules/path.c +++ b/v2/engine/modules/path.c @@ -6,7 +6,7 @@ #include "../timestamp.h" #include "../object.h" -LIST *path_exists( PARSE *parse, FRAME *frame ) +LIST *path_exists( FRAME *frame, int flags ) { LIST* l = lol_get( frame->args, 0 ); diff --git a/v2/engine/modules/property-set.c b/v2/engine/modules/property-set.c index d8dca3435..05817fa7f 100644 --- a/v2/engine/modules/property-set.c +++ b/v2/engine/modules/property-set.c @@ -41,7 +41,7 @@ rule create ( raw-properties * ) } */ -LIST *property_set_create( PARSE *parse, FRAME *frame ) +LIST *property_set_create( FRAME *frame, int flags ) { LIST* properties = lol_get( frame->args, 0 ); LIST* sorted = 0; diff --git a/v2/engine/modules/regex.c b/v2/engine/modules/regex.c index 234caf313..257bf0f47 100644 --- a/v2/engine/modules/regex.c +++ b/v2/engine/modules/regex.c @@ -25,7 +25,7 @@ rule transform ( list * : pattern : indices * ) return $(result) ; } */ -LIST *regex_transform( PARSE *parse, FRAME *frame ) +LIST *regex_transform( FRAME *frame, int flags ) { LIST* l = lol_get( frame->args, 0 ); LIST* pattern = lol_get( frame->args, 1 ); diff --git a/v2/engine/modules/sequence.c b/v2/engine/modules/sequence.c index 0d57fa5b4..714432ac5 100644 --- a/v2/engine/modules/sequence.c +++ b/v2/engine/modules/sequence.c @@ -10,7 +10,7 @@ # endif -LIST *sequence_select_highest_ranked( PARSE *parse, FRAME *frame ) +LIST *sequence_select_highest_ranked( FRAME *frame, int flags ) { /* Returns all of 'elements' for which corresponding element in parallel */ /* list 'rank' is equal to the maximum value in 'rank'. */ diff --git a/v2/engine/modules/set.c b/v2/engine/modules/set.c index 78c4cc5d1..3c4125874 100644 --- a/v2/engine/modules/set.c +++ b/v2/engine/modules/set.c @@ -17,7 +17,7 @@ } return $(result) ; */ -LIST *set_difference( PARSE *parse, FRAME *frame ) +LIST *set_difference( FRAME *frame, int flags ) { LIST* b = lol_get( frame->args, 0 ); diff --git a/v2/engine/native.c b/v2/engine/native.c index 1c901d686..29626fe98 100644 --- a/v2/engine/native.c +++ b/v2/engine/native.c @@ -6,12 +6,8 @@ #include "hash.h" #include "object.h" -# define P0 (PARSE *)0 -# define C0 (OBJECT *)0 - - void declare_native_rule( const char * module, const char * rule, const char * * args, - LIST * (*f)( PARSE *, FRAME * ), int version ) + LIST * (*f)( FRAME *, int ), int version ) { OBJECT * module_obj = 0; module_t * m; @@ -40,7 +36,7 @@ void declare_native_rule( const char * module, const char * rule, const char * * { n.arguments = 0; } - n.procedure = parse_make( f, P0, P0, P0, C0, C0, 0 ); + n.procedure = function_builtin( f, 0 ); n.version = version; hashenter(m->native_rules, (HASHDATA**)&np); } diff --git a/v2/engine/native.h b/v2/engine/native.h index ddb7637a5..ffa102ba7 100644 --- a/v2/engine/native.h +++ b/v2/engine/native.h @@ -11,7 +11,7 @@ struct native_rule_t { OBJECT * name; argument_list * arguments; - PARSE * procedure; + FUNCTION * procedure; /* Version of the interface that the native rule provides. It's possible that we want to change the set parameter for existing native rule. In that case, version number @@ -27,7 +27,7 @@ struct native_rule_t typedef struct native_rule_t native_rule_t ; void declare_native_rule( const char * module, const char * rule, const char * * args, - LIST * (*f)( PARSE *, FRAME * ), int version ); + LIST * (*f)( FRAME *, int ), int version ); diff --git a/v2/engine/parse.c b/v2/engine/parse.c index a3eaf282e..167ad1745 100644 --- a/v2/engine/parse.c +++ b/v2/engine/parse.c @@ -17,6 +17,7 @@ #include "object.h" #include "modules.h" #include "frames.h" +#include "function.h" /* * parse.c - make and destroy parse trees as driven by the parser @@ -41,6 +42,7 @@ void parse_file( OBJECT * f, FRAME * frame ) for ( ; ; ) { PARSE * p; + FUNCTION * func; /* Filled by yyparse() calling parse_save(). */ yypsave = 0; @@ -50,8 +52,10 @@ void parse_file( OBJECT * f, FRAME * frame ) break; /* Run the parse tree. */ - list_free( parse_evaluate( p, frame ) ); + func = function_compile( p ); parse_free( p ); + list_free( function_run( func, frame, stack_global() ) ); + function_free( func ); } } @@ -63,7 +67,7 @@ void parse_save( PARSE * p ) PARSE * parse_make( - LIST * (* func)( PARSE *, FRAME * ), + int type, PARSE * left, PARSE * right, PARSE * third, @@ -73,7 +77,7 @@ PARSE * parse_make( { PARSE * p = (PARSE *)BJAM_MALLOC( sizeof( PARSE ) ); - p->func = func; + p->type = type; p->left = left; p->right = right; p->third = third; @@ -126,10 +130,3 @@ void parse_free( PARSE * p ) BJAM_FREE( (char *)p ); } - - -LIST * parse_evaluate( PARSE * p, FRAME * frame ) -{ - frame->procedure = p; - return (*p->func)( p, frame ); -} diff --git a/v2/engine/parse.h b/v2/engine/parse.h index db6e1c649..882a029f4 100644 --- a/v2/engine/parse.h +++ b/v2/engine/parse.h @@ -21,12 +21,32 @@ * parse.h - make and destroy parse trees as driven by the parser. */ +#define PARSE_APPEND 0 +#define PARSE_FOREACH 1 +#define PARSE_IF 2 +#define PARSE_EVAL 3 +#define PARSE_INCLUDE 4 +#define PARSE_LIST 5 +#define PARSE_LOCAL 6 +#define PARSE_MODULE 7 +#define PARSE_CLASS 8 +#define PARSE_NULL 9 +#define PARSE_ON 10 +#define PARSE_RULE 11 +#define PARSE_RULES 12 +#define PARSE_SET 13 +#define PARSE_SETCOMP 14 +#define PARSE_SETEXEC 15 +#define PARSE_SETTINGS 16 +#define PARSE_SWITCH 17 +#define PARSE_WHILE 18 + /* * Parse tree node. */ struct _PARSE { - LIST * (* func)( PARSE *, FRAME * ); + int type; PARSE * left; PARSE * right; PARSE * third; @@ -34,7 +54,6 @@ struct _PARSE { OBJECT * string1; int num; int refs; -/* module * module; */ OBJECT * rulename; OBJECT * file; int line; @@ -44,7 +63,7 @@ void parse_file( OBJECT *, FRAME * ); void parse_save( PARSE * ); PARSE * parse_make( - LIST * (* func)( PARSE *, FRAME * ), + int type, PARSE * left, PARSE * right, PARSE * third, diff --git a/v2/engine/rules.c b/v2/engine/rules.c index de049dfe9..c28169453 100644 --- a/v2/engine/rules.c +++ b/v2/engine/rules.c @@ -45,7 +45,7 @@ */ static void set_rule_actions( RULE *, rule_actions * ); -static void set_rule_body ( RULE *, argument_list *, PARSE * procedure ); +static void set_rule_body ( RULE *, argument_list *, FUNCTION * procedure ); static struct hash * targethash = 0; @@ -93,7 +93,7 @@ static RULE * enter_rule( OBJECT * rulename, module_t * target_module ) if ( hashenter( demand_rules( target_module ), (HASHDATA * *)&r ) ) { r->name = object_copy( rulename ); - r->procedure = (PARSE *)0; + r->procedure = 0; r->module = 0; r->actions = 0; r->arguments = 0; @@ -136,7 +136,7 @@ void rule_free( RULE * r ) object_free( r->name ); r->name = 0; if ( r->procedure ) - parse_free( r->procedure ); + function_free( r->procedure ); r->procedure = 0; if ( r->arguments ) args_free( r->arguments ); @@ -659,7 +659,7 @@ void actions_free( rule_actions * a ) * set_rule_body() - set the argument list and procedure of the given rule. */ -static void set_rule_body( RULE * rule, argument_list * args, PARSE * procedure ) +static void set_rule_body( RULE * rule, argument_list * args, FUNCTION * procedure ) { if ( args ) args_refer( args ); @@ -668,9 +668,9 @@ static void set_rule_body( RULE * rule, argument_list * args, PARSE * procedure rule->arguments = args; if ( procedure ) - parse_refer( procedure ); + function_refer( procedure ); if ( rule->procedure ) - parse_free( rule->procedure ); + function_free( rule->procedure ); rule->procedure = procedure; } @@ -719,7 +719,7 @@ static RULE * global_rule( RULE * r ) * exported to the global module as modulename.rulename. */ -RULE * new_rule_body( module_t * m, OBJECT * rulename, argument_list * args, PARSE * procedure, int exported ) +RULE * new_rule_body( module_t * m, OBJECT * rulename, argument_list * args, FUNCTION * procedure, int exported ) { RULE * local = define_rule( m, rulename, m ); local->exported = exported; @@ -730,8 +730,8 @@ RULE * new_rule_body( module_t * m, OBJECT * rulename, argument_list * args, PAR * can use, e.g. in profiling output. Only do this once, since this could be * called multiple times with the same procedure. */ - if ( procedure->rulename == 0 ) - procedure->rulename = global_rule_name( local ); + if ( function_rulename( procedure ) == 0 ) + function_set_rulename( procedure, global_rule_name( local ) ); return local; } diff --git a/v2/engine/rules.h b/v2/engine/rules.h index df7219aac..b67dc40a9 100644 --- a/v2/engine/rules.h +++ b/v2/engine/rules.h @@ -15,7 +15,7 @@ #include "modules.h" #include "jam.h" -#include "parse.h" +#include "function.h" /* @@ -81,7 +81,7 @@ typedef struct argument_list argument_list; struct _rule { OBJECT * name; - PARSE * procedure; /* parse tree from RULE */ + FUNCTION * procedure; argument_list * arguments; /* argument checking info, or NULL for unchecked */ rule_actions * actions; /* build actions, or NULL for no actions */ @@ -260,7 +260,7 @@ void args_refer( argument_list * ); /* Rule related functions. */ RULE * bindrule ( OBJECT * rulename, module_t * ); RULE * import_rule ( RULE * source, module_t *, OBJECT * name ); -RULE * new_rule_body ( module_t *, OBJECT * rulename, argument_list *, PARSE * procedure, int exprt ); +RULE * new_rule_body ( module_t *, OBJECT * rulename, argument_list *, FUNCTION * func, int exprt ); RULE * new_rule_actions( module_t *, OBJECT * rulename, OBJECT * command, LIST * bindlist, int flags ); void rule_free ( RULE * ); diff --git a/v2/engine/subst.c b/v2/engine/subst.c index c70165327..42199112f 100644 --- a/v2/engine/subst.c +++ b/v2/engine/subst.c @@ -5,9 +5,9 @@ #include "object.h" #include "lists.h" -#include "parse.h" #include "compile.h" #include "frames.h" +#include "builtins.h" struct regex_entry { @@ -35,10 +35,7 @@ regexp* regex_compile( OBJECT* pattern ) return e->regex; } -LIST* -builtin_subst( - PARSE *parse, - FRAME *frame ) +LIST * builtin_subst( FRAME * frame, int flags ) { LIST* result = L0; LIST* arg1 = lol_get( frame->args, 0 ); diff --git a/v2/engine/w32_getreg.c b/v2/engine/w32_getreg.c index d9152696c..8f67e058c 100644 --- a/v2/engine/w32_getreg.c +++ b/v2/engine/w32_getreg.c @@ -57,10 +57,7 @@ static HKEY get_key(char const** path) return p->value; } -LIST* -builtin_system_registry( - PARSE *parse, - FRAME *frame ) +LIST * builtin_system_registry( FRAME * frame, int flags ) { char const* path = object_str( lol_get(frame->args, 0)->value ); LIST* result = L0; @@ -187,10 +184,7 @@ static LIST* get_value_names(HKEY key, char const* path) return result; } -LIST* -builtin_system_registry_names( - PARSE *parse, - FRAME *frame ) +LIST * builtin_system_registry_names( FRAME * frame, int flags ) { char const* path = object_str( lol_get(frame->args, 0)->value ); char const* result_type = object_str( lol_get(frame->args, 1)->value );