mirror of
https://github.com/boostorg/build.git
synced 2026-02-12 12:02:24 +00:00
Merge Python function support into function.c
[SVN r77518]
This commit is contained in:
@@ -1841,12 +1841,7 @@ LIST * builtin_python_import_rule( FRAME * frame, int flags )
|
||||
if ( pFunc && PyCallable_Check( pFunc ) )
|
||||
{
|
||||
module_t * m = bindmodule( jam_module );
|
||||
RULE * r = bindrule( jam_rule, m );
|
||||
|
||||
/* Make pFunc owned. */
|
||||
Py_INCREF( pFunc );
|
||||
|
||||
r->python_function = pFunc;
|
||||
new_rule_body( m, jam_rule, 0, function_python( pFunc, 0 ), 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2014,25 +2009,7 @@ PyObject * bjam_import_rule( PyObject * self, PyObject * args )
|
||||
object_free( rule_name );
|
||||
|
||||
/* Make pFunc owned. */
|
||||
Py_INCREF( func );
|
||||
|
||||
r->python_function = func;
|
||||
r->arguments = 0;
|
||||
|
||||
if (bjam_signature)
|
||||
{
|
||||
argument_list * arg_list = args_new();
|
||||
Py_ssize_t i;
|
||||
|
||||
Py_ssize_t s = PySequence_Size (bjam_signature);
|
||||
for (i = 0; i < s; ++i)
|
||||
{
|
||||
PyObject* v = PySequence_GetItem (bjam_signature, i);
|
||||
lol_add(arg_list->data, list_from_python (v));
|
||||
Py_DECREF(v);
|
||||
}
|
||||
r->arguments = arg_list;
|
||||
}
|
||||
r->procedure = function_python( func, bjam_signature );
|
||||
|
||||
Py_INCREF( Py_None );
|
||||
return Py_None;
|
||||
|
||||
@@ -115,391 +115,6 @@ void frame_free( FRAME* frame )
|
||||
}
|
||||
|
||||
|
||||
static void argument_error( const char * message, RULE * rule, FRAME * frame, OBJECT * arg )
|
||||
{
|
||||
LOL * actual = frame->args;
|
||||
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 ) : "" );
|
||||
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 );
|
||||
}
|
||||
|
||||
|
||||
/* Define delimiters for type check elements in argument lists (and return type
|
||||
* specifications, eventually).
|
||||
*/
|
||||
# define TYPE_OPEN_DELIM '['
|
||||
# define TYPE_CLOSE_DELIM ']'
|
||||
|
||||
/*
|
||||
* is_type_name() - true iff the given string represents a type check
|
||||
* specification.
|
||||
*/
|
||||
|
||||
int is_type_name( const char * s )
|
||||
{
|
||||
return ( s[ 0 ] == TYPE_OPEN_DELIM ) &&
|
||||
( s[ strlen( s ) - 1 ] == TYPE_CLOSE_DELIM );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* arg_modifier - if the next element of formal is a single character, return
|
||||
* that; return 0 otherwise. Used to extract "*+?" modifiers * from argument
|
||||
* lists.
|
||||
*/
|
||||
|
||||
static char arg_modifier( LISTITER iter, LISTITER end )
|
||||
{
|
||||
iter = list_next( iter );
|
||||
if ( iter != end )
|
||||
{
|
||||
const char * next = object_str( list_item( iter ) );
|
||||
if ( next && ( next[ 0 ] != 0 ) && ( next[ 1 ] == 0 ) )
|
||||
return next[ 0 ];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* type_check() - checks that each element of values satisfies the requirements
|
||||
* of type_name.
|
||||
*
|
||||
* caller - the frame of the rule calling the rule whose arguments are
|
||||
* being checked
|
||||
*
|
||||
* called - the rule being called
|
||||
*
|
||||
* arg_name - a list element containing the name of the argument being
|
||||
* checked
|
||||
*/
|
||||
|
||||
static void type_check_range
|
||||
(
|
||||
OBJECT * type_name,
|
||||
LISTITER iter,
|
||||
LISTITER end,
|
||||
FRAME * caller,
|
||||
RULE * called,
|
||||
OBJECT * arg_name
|
||||
)
|
||||
{
|
||||
static module_t * typecheck = 0;
|
||||
|
||||
/* If nothing to check, bail now. */
|
||||
if ( iter == end || !type_name )
|
||||
return;
|
||||
|
||||
if ( !typecheck )
|
||||
{
|
||||
typecheck = bindmodule( constant_typecheck );
|
||||
}
|
||||
|
||||
/* If the checking rule can not be found, also bail. */
|
||||
if ( !typecheck->rules || !hash_find( typecheck->rules, type_name ) )
|
||||
return;
|
||||
|
||||
for ( ; iter != end; iter = list_next( iter ) )
|
||||
{
|
||||
LIST *error;
|
||||
FRAME frame[1];
|
||||
frame_init( frame );
|
||||
frame->module = typecheck;
|
||||
frame->prev = caller;
|
||||
frame->prev_user = caller->module->user_module ? caller : caller->prev_user;
|
||||
|
||||
/* Prepare the argument list */
|
||||
lol_add( frame->args, list_new( L0, object_copy( list_item( iter ) ) ) );
|
||||
error = evaluate_rule( type_name, frame );
|
||||
|
||||
if ( !list_empty( error ) )
|
||||
argument_error( object_str( list_front( error ) ), called, caller, arg_name );
|
||||
|
||||
frame_free( frame );
|
||||
}
|
||||
}
|
||||
|
||||
static void type_check
|
||||
(
|
||||
OBJECT * type_name,
|
||||
LIST * values,
|
||||
FRAME * caller,
|
||||
RULE * called,
|
||||
OBJECT * arg_name
|
||||
)
|
||||
{
|
||||
type_check_range( type_name, list_begin( values ), list_end( values ), caller, called, arg_name );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* collect_arguments() - local argument checking and collection
|
||||
*/
|
||||
static SETTINGS *
|
||||
collect_arguments( RULE* rule, FRAME* frame )
|
||||
{
|
||||
SETTINGS *locals = 0;
|
||||
|
||||
LOL * all_actual = frame->args;
|
||||
LOL * all_formal = rule->arguments ? rule->arguments->data : 0;
|
||||
if ( all_formal ) /* Nothing to set; nothing to check */
|
||||
{
|
||||
int max = all_formal->count > all_actual->count
|
||||
? all_formal->count
|
||||
: all_actual->count;
|
||||
|
||||
int n;
|
||||
for ( n = 0; n < max ; ++n )
|
||||
{
|
||||
LIST *actual = lol_get( all_actual, n );
|
||||
OBJECT * type_name = 0;
|
||||
|
||||
LIST *formal;
|
||||
LISTITER formal_iter, formal_end;
|
||||
LISTITER actual_iter = list_begin( actual ), actual_end = list_end( actual );
|
||||
for ( formal = lol_get( all_formal, n ),
|
||||
formal_iter = list_begin( formal ), formal_end = list_end( formal );
|
||||
formal_iter != formal_end; formal_iter = list_next( formal_iter ) )
|
||||
{
|
||||
OBJECT * name = list_item( formal_iter );
|
||||
|
||||
if ( is_type_name( object_str( name ) ) )
|
||||
{
|
||||
if ( type_name )
|
||||
argument_error( "missing argument name before type name:", rule, frame, name );
|
||||
|
||||
if ( list_next( formal_iter ) == formal_end )
|
||||
argument_error( "missing argument name after type name:", rule, frame, name );
|
||||
|
||||
type_name = name;
|
||||
}
|
||||
else
|
||||
{
|
||||
LIST* value = L0;
|
||||
char modifier;
|
||||
OBJECT* arg_name = list_item( formal_iter ); /* hold the argument name for type checking */
|
||||
int multiple = 0;
|
||||
|
||||
/* Stop now if a variable number of arguments are specified */
|
||||
if ( object_str( name )[0] == '*' && object_str( name )[1] == 0 )
|
||||
return locals;
|
||||
|
||||
modifier = arg_modifier( formal_iter, formal_end );
|
||||
|
||||
if ( actual_iter == actual_end && modifier != '?' && modifier != '*' )
|
||||
argument_error( "missing argument", rule, frame, name );
|
||||
|
||||
switch ( modifier )
|
||||
{
|
||||
case '+':
|
||||
case '*':
|
||||
value = list_copy_range( actual, actual_iter, actual_end );
|
||||
multiple = 1;
|
||||
actual_iter = actual_end;
|
||||
/* skip an extra element for the modifier */
|
||||
formal_iter = list_next( formal_iter );
|
||||
break;
|
||||
case '?':
|
||||
/* skip an extra element for the modifier */
|
||||
formal_iter = list_next( formal_iter );
|
||||
/* fall through */
|
||||
default:
|
||||
if ( actual_iter != actual_end ) /* in case actual is missing */
|
||||
{
|
||||
value = list_new( L0, object_copy( list_item( actual_iter ) ) );
|
||||
actual_iter = list_next( actual_iter );
|
||||
}
|
||||
}
|
||||
|
||||
locals = addsettings(locals, VAR_SET, name, value);
|
||||
locals->multiple = multiple;
|
||||
type_check( type_name, value, frame, rule, arg_name );
|
||||
type_name = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( actual_iter != actual_end )
|
||||
{
|
||||
argument_error( "extra argument", rule, frame, list_item( actual_iter ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
return locals;
|
||||
}
|
||||
|
||||
RULE *
|
||||
enter_rule( char *rulename, module_t *target_module );
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
static int python_instance_number = 0;
|
||||
|
||||
|
||||
/* Given a Python object, return a string to use in Jam
|
||||
code instead of said object.
|
||||
If the object is string, use the string value
|
||||
If the object implemenets __jam_repr__ method, use that.
|
||||
Otherwise return 0. */
|
||||
OBJECT *python_to_string(PyObject* value)
|
||||
{
|
||||
if (PyString_Check(value))
|
||||
{
|
||||
return object_new(PyString_AsString(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* See if this is an instance that defines special __jam_repr__
|
||||
method. */
|
||||
if (PyInstance_Check(value)
|
||||
&& PyObject_HasAttrString(value, "__jam_repr__"))
|
||||
{
|
||||
PyObject* repr = PyObject_GetAttrString(value, "__jam_repr__");
|
||||
if (repr)
|
||||
{
|
||||
PyObject* arguments2 = PyTuple_New(0);
|
||||
PyObject* value2 = PyObject_Call(repr, arguments2, 0);
|
||||
Py_DECREF(repr);
|
||||
Py_DECREF(arguments2);
|
||||
if (PyString_Check(value2))
|
||||
{
|
||||
return object_new(PyString_AsString(value2));
|
||||
}
|
||||
Py_DECREF(value2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static LIST*
|
||||
call_python_function(RULE* r, FRAME* frame)
|
||||
{
|
||||
LIST * result = 0;
|
||||
PyObject * arguments = 0;
|
||||
PyObject * kw = NULL;
|
||||
int i ;
|
||||
PyObject * py_result;
|
||||
FRAME * prev_frame_before_python_call;
|
||||
|
||||
if (r->arguments)
|
||||
{
|
||||
SETTINGS * args;
|
||||
|
||||
arguments = PyTuple_New(0);
|
||||
kw = PyDict_New();
|
||||
|
||||
for (args = collect_arguments(r, frame); args; args = args->next)
|
||||
{
|
||||
PyObject *key = PyString_FromString(object_str(args->symbol));
|
||||
PyObject *value = 0;
|
||||
if (args->multiple)
|
||||
value = list_to_python(args->value);
|
||||
else {
|
||||
if (!list_empty(args->value))
|
||||
value = PyString_FromString(object_str(list_front(args->value)));
|
||||
}
|
||||
|
||||
if (value)
|
||||
PyDict_SetItem(kw, key, value);
|
||||
Py_DECREF(key);
|
||||
Py_XDECREF(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments = PyTuple_New( frame->args->count );
|
||||
for ( i = 0; i < frame->args->count; ++i )
|
||||
{
|
||||
PyObject * arg = PyList_New(0);
|
||||
LIST* l = lol_get( frame->args, i);
|
||||
LISTITER iter = list_begin( l ), end = list_end( l );
|
||||
|
||||
for ( ; iter != end; iter = list_next( iter ) )
|
||||
{
|
||||
PyObject * v = PyString_FromString(object_str(list_item(iter)));
|
||||
PyList_Append( arg, v );
|
||||
Py_DECREF(v);
|
||||
}
|
||||
/* Steals reference to 'arg' */
|
||||
PyTuple_SetItem( arguments, i, arg );
|
||||
}
|
||||
}
|
||||
|
||||
prev_frame_before_python_call = frame_before_python_call;
|
||||
frame_before_python_call = frame;
|
||||
py_result = PyObject_Call( r->python_function, arguments, kw );
|
||||
frame_before_python_call = prev_frame_before_python_call;
|
||||
Py_DECREF(arguments);
|
||||
Py_XDECREF(kw);
|
||||
if ( py_result != NULL )
|
||||
{
|
||||
if ( PyList_Check( py_result ) )
|
||||
{
|
||||
int size = PyList_Size( py_result );
|
||||
int i;
|
||||
for ( i = 0; i < size; ++i )
|
||||
{
|
||||
PyObject * item = PyList_GetItem( py_result, i );
|
||||
OBJECT *s = python_to_string (item);
|
||||
if (!s) {
|
||||
fprintf( stderr, "Non-string object returned by Python call.\n" );
|
||||
} else {
|
||||
result = list_new (result, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( py_result == Py_None )
|
||||
{
|
||||
result = L0;
|
||||
}
|
||||
else
|
||||
{
|
||||
OBJECT *s = python_to_string(py_result);
|
||||
if (s)
|
||||
result = list_new(L0, s);
|
||||
else
|
||||
/* We have tried all we could. Return empty list. There are
|
||||
cases, e.g. feature.feature function that should return
|
||||
value for the benefit of Python code and which also can be
|
||||
called by Jam code, where no sensible value can be
|
||||
returned. We cannot even emit a warning, since there will
|
||||
be a pile of them. */
|
||||
result = L0;
|
||||
}
|
||||
|
||||
Py_DECREF( py_result );
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_Print();
|
||||
fprintf(stderr,"Call failed\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
module_t * python_module()
|
||||
{
|
||||
static module_t * python = 0;
|
||||
if ( !python )
|
||||
python = bindmodule(constant_python);
|
||||
return python;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
LIST * function_run_with_args( FUNCTION * function_, FRAME * frame, STACK * s );
|
||||
|
||||
/*
|
||||
* evaluate_rule() - execute a rule invocation.
|
||||
*/
|
||||
@@ -516,14 +131,6 @@ evaluate_rule(
|
||||
|
||||
rule = bindrule( rulename, frame->module );
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
if ( rule->python_function )
|
||||
{
|
||||
frame->module = python_module();
|
||||
return call_python_function( rule, frame );
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( DEBUG_COMPILE )
|
||||
{
|
||||
/* Try hard to indicate in which module the rule is going to execute. */
|
||||
@@ -654,7 +261,7 @@ evaluate_rule(
|
||||
FUNCTION * function = rule->procedure;
|
||||
|
||||
function_refer( function );
|
||||
result = function_run_with_args( function, frame, stack_global() );
|
||||
result = function_run( function, frame, stack_global() );
|
||||
function_free( function );
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +166,21 @@ typedef struct _jam_function
|
||||
} JAM_FUNCTION;
|
||||
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
#define FUNCTION_PYTHON 2
|
||||
|
||||
typedef struct _python_function
|
||||
{
|
||||
FUNCTION base;
|
||||
PyObject * python_function;
|
||||
} PYTHON_FUNCTION;
|
||||
|
||||
static LIST * call_python_function( PYTHON_FUNCTION * function, FRAME * frame );
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
struct _stack
|
||||
{
|
||||
void * data;
|
||||
@@ -2516,9 +2531,17 @@ void function_location( FUNCTION * function_, OBJECT * * file, int * line )
|
||||
*file = constant_builtin;
|
||||
*line = -1;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
if ( function_->type == FUNCTION_PYTHON )
|
||||
{
|
||||
*file = constant_builtin;
|
||||
*line = -1;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
JAM_FUNCTION * function = (JAM_FUNCTION *)function_;
|
||||
assert( function_->type == FUNCTION_JAM );
|
||||
*file = function->file;
|
||||
*line = function->line;
|
||||
}
|
||||
@@ -2570,9 +2593,26 @@ FUNCTION * function_compile_actions( const char * actions, OBJECT * file, int li
|
||||
return (FUNCTION *)result;
|
||||
}
|
||||
|
||||
int is_type_name( const char * s );
|
||||
static void argument_list_print( struct arg_list * args, int num_args );
|
||||
|
||||
|
||||
/* Define delimiters for type check elements in argument lists (and return type
|
||||
* specifications, eventually).
|
||||
*/
|
||||
# define TYPE_OPEN_DELIM '['
|
||||
# define TYPE_CLOSE_DELIM ']'
|
||||
|
||||
/*
|
||||
* is_type_name() - true iff the given string represents a type check
|
||||
* specification.
|
||||
*/
|
||||
|
||||
int is_type_name( const char * s )
|
||||
{
|
||||
return ( s[ 0 ] == TYPE_OPEN_DELIM ) &&
|
||||
( s[ strlen( s ) - 1 ] == TYPE_CLOSE_DELIM );
|
||||
}
|
||||
|
||||
static void argument_error( const char * message, FUNCTION * procedure, FRAME * frame, OBJECT * arg )
|
||||
{
|
||||
LOL * actual = frame->args;
|
||||
@@ -3122,8 +3162,15 @@ FUNCTION * function_unbind_variables( FUNCTION * f )
|
||||
else
|
||||
return (FUNCTION *)func;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
else if ( f->type == FUNCTION_PYTHON )
|
||||
{
|
||||
return f;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
assert( f->type == FUNCTION_BUILTIN );
|
||||
return f;
|
||||
}
|
||||
}
|
||||
@@ -3134,12 +3181,19 @@ FUNCTION * function_bind_variables( FUNCTION * f, module_t * module, int * count
|
||||
{
|
||||
return f;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
else if ( f->type == FUNCTION_PYTHON )
|
||||
{
|
||||
return f;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
JAM_FUNCTION * func = (JAM_FUNCTION *)f;
|
||||
JAM_FUNCTION * new_func = BJAM_MALLOC( sizeof( JAM_FUNCTION ) );
|
||||
instruction * code;
|
||||
int i;
|
||||
assert( f->type == FUNCTION_JAM );
|
||||
memcpy( new_func, func, sizeof( JAM_FUNCTION ) );
|
||||
new_func->base.reference_count = 1;
|
||||
new_func->base.formal_arguments = argument_list_bind_variables( f->formal_arguments, f->num_formal_arguments, module, counter );
|
||||
@@ -3250,8 +3304,17 @@ void function_free( FUNCTION * function_ )
|
||||
object_free( func->file );
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
else if ( function_->type == FUNCTION_PYTHON )
|
||||
{
|
||||
PYTHON_FUNCTION * func = (PYTHON_FUNCTION *)function_;
|
||||
Py_DECREF( func->python_function );
|
||||
if ( function_->rulename ) object_free( function_->rulename );
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
assert( function_->type == FUNCTION_BUILTIN );
|
||||
if ( function_->rulename ) object_free( function_->rulename );
|
||||
}
|
||||
|
||||
@@ -3286,28 +3349,6 @@ void function_run_actions( FUNCTION * function, FRAME * frame, STACK * s, string
|
||||
stack_deallocate( s, sizeof( string * ) );
|
||||
}
|
||||
|
||||
LIST * function_run_with_args( FUNCTION * function_, FRAME * frame, STACK * s )
|
||||
{
|
||||
LIST * result;
|
||||
if ( function_->type == FUNCTION_BUILTIN )
|
||||
{
|
||||
BUILTIN_FUNCTION * f = (BUILTIN_FUNCTION *)function_;
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_check( function_->formal_arguments, function_->num_formal_arguments, function_, frame );
|
||||
return f->func( frame, f->flags );
|
||||
}
|
||||
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_push( function_->formal_arguments, function_->num_formal_arguments, function_, frame, s );
|
||||
|
||||
result = function_run( function_, frame, s );
|
||||
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_pop( function_->formal_arguments, function_->num_formal_arguments, frame, s );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* WARNING: The instruction set is tuned for Jam and
|
||||
* is not really generic. Be especially careful about
|
||||
@@ -3326,9 +3367,26 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s )
|
||||
if ( function_->type == FUNCTION_BUILTIN )
|
||||
{
|
||||
BUILTIN_FUNCTION * f = (BUILTIN_FUNCTION *)function_;
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_check( function_->formal_arguments, function_->num_formal_arguments, function_, frame );
|
||||
return f->func( frame, f->flags );
|
||||
}
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
else if ( function_->type == FUNCTION_PYTHON )
|
||||
{
|
||||
PYTHON_FUNCTION * f = (PYTHON_FUNCTION *)function_;
|
||||
return call_python_function( f, frame );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
assert( function_->type == FUNCTION_JAM );
|
||||
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_push( function_->formal_arguments, function_->num_formal_arguments, function_, frame, s );
|
||||
|
||||
function = (JAM_FUNCTION *)function_;
|
||||
code = function->code;
|
||||
for ( ; ; )
|
||||
@@ -3590,6 +3648,8 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s )
|
||||
|
||||
case INSTR_RETURN:
|
||||
{
|
||||
if ( function_->formal_arguments )
|
||||
argument_list_pop( function_->formal_arguments, function_->num_formal_arguments, frame, s );
|
||||
#ifndef NDEBUG
|
||||
|
||||
if ( !( saved_stack == s->data ) )
|
||||
@@ -4208,6 +4268,267 @@ LIST * function_run( FUNCTION * function_, FRAME * frame, STACK * s )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
static struct arg_list * arg_list_compile_python( PyObject * bjam_signature, int * num_arguments )
|
||||
{
|
||||
if ( bjam_signature )
|
||||
{
|
||||
struct argument_list_compiler c[ 1 ];
|
||||
struct arg_list * result;
|
||||
Py_ssize_t s, i, j, inner;
|
||||
argument_list_compiler_init( c );
|
||||
|
||||
s = PySequence_Size( bjam_signature );
|
||||
for ( i = 0; i < s; ++i )
|
||||
{
|
||||
struct argument_compiler arg_comp[ 1 ];
|
||||
struct arg_list arg;
|
||||
PyObject * v = PySequence_GetItem( bjam_signature, i );
|
||||
argument_compiler_init( arg_comp );
|
||||
|
||||
inner = PySequence_Size( v );
|
||||
for ( j = 0; j < inner; ++j )
|
||||
{
|
||||
PyObject * x = PySequence_GetItem( v, j );
|
||||
argument_compiler_add( arg_comp, object_new( PyString_AsString( x ) ), constant_builtin, -1 );
|
||||
}
|
||||
|
||||
arg = arg_compile_impl( arg_comp, constant_builtin, -1 );
|
||||
dynamic_array_push( c->args, arg );
|
||||
argument_compiler_free( arg_comp );
|
||||
Py_DECREF( v );
|
||||
}
|
||||
|
||||
*num_arguments = c->args->size;
|
||||
result = BJAM_MALLOC( c->args->size * sizeof( struct arg_list ) );
|
||||
memcpy( result, c->args->data, c->args->size * sizeof( struct arg_list ) );
|
||||
argument_list_compiler_free( c );
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
*num_arguments = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION * function_python( PyObject * function, PyObject * bjam_signature )
|
||||
{
|
||||
PYTHON_FUNCTION * result = BJAM_MALLOC( sizeof( PYTHON_FUNCTION ) );
|
||||
|
||||
result->base.type = FUNCTION_PYTHON;
|
||||
result->base.reference_count = 1;
|
||||
result->base.rulename = 0;
|
||||
result->base.formal_arguments = arg_list_compile_python( bjam_signature, &result->base.num_formal_arguments );
|
||||
Py_INCREF( function );
|
||||
result->python_function = function;
|
||||
|
||||
return (FUNCTION *)result;
|
||||
}
|
||||
|
||||
static void argument_list_to_python( struct arg_list * formal, int formal_count, FUNCTION * function, FRAME * frame, PyObject * kw )
|
||||
{
|
||||
LOL * all_actual = frame->args;
|
||||
int i, j;
|
||||
|
||||
for ( i = 0; i < formal_count; ++i )
|
||||
{
|
||||
LIST *actual = lol_get( all_actual, i );
|
||||
LISTITER actual_iter = list_begin( actual ), actual_end = list_end( actual );
|
||||
for ( j = 0; j < formal[i].size; ++j )
|
||||
{
|
||||
struct argument * formal_arg = &formal[i].args[j];
|
||||
PyObject * value;
|
||||
LIST * l;
|
||||
|
||||
switch ( formal_arg->flags )
|
||||
{
|
||||
case ARG_ONE:
|
||||
if ( actual_iter == actual_end )
|
||||
argument_error( "missing argument", function, frame, formal_arg->arg_name );
|
||||
type_check_range( formal_arg->type_name, actual_iter, list_next( actual_iter ), frame, function, formal_arg->arg_name );
|
||||
value = PyString_FromString( object_str( list_item( actual_iter) ) );
|
||||
actual_iter = list_next( actual_iter );
|
||||
break;
|
||||
case ARG_OPTIONAL:
|
||||
if ( actual_iter == actual_end )
|
||||
value = 0;
|
||||
else
|
||||
{
|
||||
type_check_range( formal_arg->type_name, actual_iter, list_next( actual_iter ), frame, function, formal_arg->arg_name );
|
||||
value = PyString_FromString( object_str( list_item( actual_iter) ) );
|
||||
actual_iter = list_next( actual_iter );
|
||||
}
|
||||
break;
|
||||
case ARG_PLUS:
|
||||
if ( actual_iter == actual_end )
|
||||
argument_error( "missing argument", function, frame, formal_arg->arg_name );
|
||||
/* fallthrough */
|
||||
case ARG_STAR:
|
||||
type_check_range( formal_arg->type_name, actual_iter, actual_end, frame, function, formal_arg->arg_name );
|
||||
l = list_copy_range( actual, actual_iter, actual_end );
|
||||
value = list_to_python( l );
|
||||
list_free( l );
|
||||
actual_iter = actual_end;
|
||||
break;
|
||||
case ARG_VARIADIC:
|
||||
return;
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
PyObject * key = PyString_FromString( object_str( formal_arg->arg_name ) );
|
||||
PyDict_SetItem( kw, key, value );
|
||||
Py_DECREF( key );
|
||||
Py_DECREF( value );
|
||||
}
|
||||
}
|
||||
|
||||
if ( actual_iter != actual_end )
|
||||
{
|
||||
argument_error( "extra argument", function, frame, list_item( actual_iter ) );
|
||||
}
|
||||
}
|
||||
|
||||
for ( ; i < all_actual->count; ++i )
|
||||
{
|
||||
LIST * actual = lol_get( all_actual, i );
|
||||
if ( !list_empty( actual ) )
|
||||
{
|
||||
argument_error( "extra argument", function, frame, list_front( actual ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Given a Python object, return a string to use in Jam
|
||||
code instead of said object.
|
||||
If the object is string, use the string value
|
||||
If the object implemenets __jam_repr__ method, use that.
|
||||
Otherwise return 0. */
|
||||
OBJECT * python_to_string( PyObject * value )
|
||||
{
|
||||
if ( PyString_Check( value ) )
|
||||
{
|
||||
return object_new( PyString_AsString( value ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* See if this is an instance that defines special __jam_repr__
|
||||
method. */
|
||||
if ( PyInstance_Check( value )
|
||||
&& PyObject_HasAttrString( value, "__jam_repr__" ) )
|
||||
{
|
||||
PyObject* repr = PyObject_GetAttrString( value, "__jam_repr__" );
|
||||
if ( repr )
|
||||
{
|
||||
PyObject * arguments2 = PyTuple_New( 0 );
|
||||
PyObject * value2 = PyObject_Call( repr, arguments2, 0 );
|
||||
Py_DECREF( repr );
|
||||
Py_DECREF( arguments2 );
|
||||
if ( PyString_Check( value2 ) )
|
||||
{
|
||||
return object_new( PyString_AsString( value2 ) );
|
||||
}
|
||||
Py_DECREF( value2 );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static module_t * python_module()
|
||||
{
|
||||
static module_t * python = 0;
|
||||
if ( !python )
|
||||
python = bindmodule(constant_python);
|
||||
return python;
|
||||
}
|
||||
|
||||
static LIST * call_python_function( PYTHON_FUNCTION * function, FRAME * frame )
|
||||
{
|
||||
LIST * result = 0;
|
||||
PyObject * arguments = 0;
|
||||
PyObject * kw = NULL;
|
||||
int i ;
|
||||
PyObject * py_result;
|
||||
FRAME * prev_frame_before_python_call;
|
||||
|
||||
if ( function->base.formal_arguments )
|
||||
{
|
||||
arguments = PyTuple_New(0);
|
||||
kw = PyDict_New();
|
||||
|
||||
argument_list_to_python( function->base.formal_arguments, function->base.num_formal_arguments, &function->base, frame, kw );
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments = PyTuple_New( frame->args->count );
|
||||
for ( i = 0; i < frame->args->count; ++i )
|
||||
{
|
||||
PyTuple_SetItem( arguments, i, list_to_python( lol_get( frame->args, i ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
frame->module = python_module();
|
||||
|
||||
prev_frame_before_python_call = frame_before_python_call;
|
||||
frame_before_python_call = frame;
|
||||
py_result = PyObject_Call( function->python_function, arguments, kw );
|
||||
frame_before_python_call = prev_frame_before_python_call;
|
||||
Py_DECREF( arguments );
|
||||
Py_XDECREF( kw );
|
||||
if ( py_result != NULL )
|
||||
{
|
||||
if ( PyList_Check( py_result ) )
|
||||
{
|
||||
int size = PyList_Size( py_result );
|
||||
int i;
|
||||
for ( i = 0; i < size; ++i )
|
||||
{
|
||||
PyObject * item = PyList_GetItem( py_result, i );
|
||||
OBJECT *s = python_to_string( item );
|
||||
if ( !s ) {
|
||||
fprintf( stderr, "Non-string object returned by Python call.\n" );
|
||||
} else {
|
||||
result = list_new( result, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( py_result == Py_None )
|
||||
{
|
||||
result = L0;
|
||||
}
|
||||
else
|
||||
{
|
||||
OBJECT *s = python_to_string( py_result );
|
||||
if (s)
|
||||
result = list_new( L0, s );
|
||||
else
|
||||
/* We have tried all we could. Return empty list. There are
|
||||
cases, e.g. feature.feature function that should return
|
||||
value for the benefit of Python code and which also can be
|
||||
called by Jam code, where no sensible value can be
|
||||
returned. We cannot even emit a warning, since there will
|
||||
be a pile of them. */
|
||||
result = L0;
|
||||
}
|
||||
|
||||
Py_DECREF( py_result );
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_Print();
|
||||
fprintf( stderr,"Call failed\n" );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void function_done( void )
|
||||
{
|
||||
BJAM_FREE( stack );
|
||||
|
||||
@@ -37,4 +37,10 @@ FUNCTION * function_unbind_variables( FUNCTION * f );
|
||||
|
||||
void function_done( void );
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
|
||||
FUNCTION * function_python( PyObject * function, PyObject * bjam_signature );
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -89,9 +89,6 @@ static RULE * enter_rule( OBJECT * rulename, module_t * target_module )
|
||||
r->arguments = 0;
|
||||
r->exported = 0;
|
||||
r->module = target_module;
|
||||
#ifdef HAVE_PYTHON
|
||||
r->python_function = 0;
|
||||
#endif
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -134,11 +131,6 @@ void rule_free( RULE * r )
|
||||
if ( r->actions )
|
||||
actions_free( r->actions );
|
||||
r->actions = 0;
|
||||
#ifdef HAVE_PYTHON
|
||||
if ( r->python_function )
|
||||
Py_DECREF( r->python_function );
|
||||
r->python_function = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -90,9 +90,6 @@ struct _rule
|
||||
* the global module and be automatically
|
||||
* imported into other modules
|
||||
*/
|
||||
#ifdef HAVE_PYTHON
|
||||
PyObject * python_function;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* ACTIONS - a chain of ACTIONs. */
|
||||
|
||||
Reference in New Issue
Block a user