2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-13 12:22:17 +00:00

Merge Python function support into function.c

[SVN r77518]
This commit is contained in:
Steven Watanabe
2012-03-24 21:07:49 +00:00
parent 0b3b574817
commit 888393c652
6 changed files with 353 additions and 453 deletions

View File

@@ -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 );