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

Type checking for argument lists

[SVN r18378]
This commit is contained in:
Dave Abrahams
2003-05-10 15:03:54 +00:00
parent ce38ecc9fb
commit f2c14abcdd
2 changed files with 172 additions and 38 deletions

View File

@@ -581,6 +581,91 @@ static void argument_error( char* message, RULE* rule, FRAME* frame, LIST* arg )
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
*/
static int
is_type_name( 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( LIST* formal )
{
if ( formal->next )
{
char *next = formal->next->string;
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( char* type_name, LIST *values, FRAME* caller, RULE* called, LIST* arg_name )
{
static module *typecheck = 0;
/* if nothing to check, bail now */
if ( !values || !type_name )
return;
if ( !typecheck )
typecheck = bindmodule(".typecheck");
/* if the checking rule can't be found, also bail */
{
RULE checker_, *checker = &checker_;
checker->name = type_name;
if ( !hashcheck( typecheck->rules, (HASHDATA**)&checker ) )
return;
}
while ( values != 0 )
{
LIST *error;
FRAME frame[1];
frame_init( frame );
frame->module = typecheck;
frame->prev = caller;
/* Prepare the argument list */
lol_add( frame->args, list_new( L0, values->string ) );
error = evaluate_rule( type_name, frame );
if ( error )
argument_error( error->string, called, caller, arg_name );
frame_free( frame );
values = values->next;
}
}
/*
* collect_arguments() - local argument checking and collection
@@ -601,54 +686,64 @@ collect_arguments( RULE* rule, FRAME* frame )
int n;
for ( n = 0; n < max ; ++n )
{
LIST *formal = lol_get( all_formal, n );
LIST *actual = lol_get( all_actual, n );
char *type_name = 0;
while ( formal )
LIST *formal;
for ( formal = lol_get( all_formal, n ); formal; formal = formal->next )
{
char* name = formal->string;
char modifier = 0;
LIST* value = 0;
/* Stop now if a variable number of arguments are specified */
if ( name[0] == '*' && name[1] == 0 )
return locals;
if ( formal->next )
if ( is_type_name(name) )
{
char *next = formal->next->string;
if ( next && next[0] != 0 && next[1] == 0 )
modifier = next[0];
}
if ( !actual && modifier != '?' && modifier != '*' )
{
argument_error( "missing argument", rule, frame, formal );
}
if ( type_name )
argument_error( "missing argument name before type name:", rule, frame, formal );
if ( !formal->next )
argument_error( "missing argument name after type name:", rule, frame, formal );
switch ( modifier )
type_name = formal->string;
}
else
{
case '+':
case '*':
value = list_copy( 0, actual );
actual = 0;
/* skip an extra element for the modifier */
formal = formal->next;
break;
case '?':
/* skip an extra element for the modifier */
formal = formal->next;
/* fall through */
default:
if ( actual ) /* in case actual is missing */
LIST* value = 0;
char modifier;
LIST* arg_name = formal; /* hold the argument name for type checking */
/* Stop now if a variable number of arguments are specified */
if ( name[0] == '*' && name[1] == 0 )
return locals;
modifier = arg_modifier( formal );
if ( !actual && modifier != '?' && modifier != '*' )
argument_error( "missing argument", rule, frame, formal );
switch ( modifier )
{
value = list_new( 0, actual->string );
actual = actual->next;
case '+':
case '*':
value = list_copy( 0, actual );
actual = 0;
/* skip an extra element for the modifier */
formal = formal->next;
break;
case '?':
/* skip an extra element for the modifier */
formal = formal->next;
/* fall through */
default:
if ( actual ) /* in case actual is missing */
{
value = list_new( 0, actual->string );
actual = actual->next;
}
}
}
locals = addsettings( locals, 0, name, value );
formal = formal->next;
locals = addsettings( locals, 0, name, value );
type_check( type_name, value, frame, rule, arg_name );
type_name = 0;
}
}
if ( actual )

View File

@@ -137,6 +137,7 @@ rule class (
}
classes += $(name) ;
IMPORT class : typecheck : .typecheck : [$(name)] ;
# Each class is assigned a new module which acts as a namespace
# for its rules and normal instance variables.
@@ -284,12 +285,21 @@ rule is-a (
: type # The type to test for.
)
{
if [ MATCH "(object\\()[^@]+\\)@.*" : $(instance) ]
if [ is-instance $(instance) ]
{
return [ class.is-derived [ modules.peek $(instance) : __class__ ] : $(type) ] ;
}
}
local rule typecheck ( x )
{
local class-name = [ MATCH "^\\[(.*)\\]$" : [ BACKTRACE 1 ] ] ;
if ! [ is-a $(x) : $(class-name) ]
{
return "Expected an instance of "$(class-name)" but got \""$(x)"\" for argument" ;
}
}
rule __test__ ( )
{
module class.__test__
@@ -395,6 +405,14 @@ rule __test__ ( )
}
class derived2 : myclass ;
local rule derived2a ( )
{
derived2.__init__ ;
}
class derived2a : derived2 ;
local rule expect_derived2 ( [derived2] x ) { }
local rule bad_subclass ( )
{
# fails to call base class __init__ function
@@ -405,6 +423,27 @@ rule __test__ ( )
local b = [ new derived1 4 ] ;
local c = [ new derived2 ] ;
local d = [ new derived2 ] ;
local e = [ new derived2a ] ;
expect_derived2 $(d) ;
expect_derived2 $(e) ;
# argument checking is set up to call exit(1) directly on
# failure, and we can't hijack that with try, so we'd better
# not do this test by default. We could fix this by having
# errors look up and invoke the EXIT rule instead; EXIT can be
# hijacked ;-)
if --fail-typecheck in [ modules.peek : ARGV ]
{
try ;
{
expect_derived2 $(a) ;
}
catch
"Expected an instance of derived2 but got" instead
;
}
try ;
{