mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-01-31 12:00:38 +00:00
Variadic functions: works for builtins, warns for implemenetd functions, -Wvariadic-function, on by default
This commit is contained in:
parent
deb7ccb830
commit
f22112bcb8
7 changed files with 60 additions and 13 deletions
16
ast.c
16
ast.c
|
@ -64,11 +64,12 @@ static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype)
|
|||
static void ast_expression_init(ast_expression *self,
|
||||
ast_expression_codegen *codegen)
|
||||
{
|
||||
self->expression.codegen = codegen;
|
||||
self->expression.vtype = TYPE_VOID;
|
||||
self->expression.next = NULL;
|
||||
self->expression.outl = NULL;
|
||||
self->expression.outr = NULL;
|
||||
self->expression.codegen = codegen;
|
||||
self->expression.vtype = TYPE_VOID;
|
||||
self->expression.next = NULL;
|
||||
self->expression.outl = NULL;
|
||||
self->expression.outr = NULL;
|
||||
self->expression.variadic = false;
|
||||
MEM_VECTOR_INIT(&self->expression, params);
|
||||
}
|
||||
|
||||
|
@ -106,6 +107,7 @@ ast_value* ast_value_copy(const ast_value *self)
|
|||
}
|
||||
fromex = &self->expression;
|
||||
selfex = &cp->expression;
|
||||
selfex->variadic = fromex->variadic;
|
||||
for (i = 0; i < fromex->params_count; ++i) {
|
||||
ast_value *v = ast_value_copy(fromex->params[i]);
|
||||
if (!v || !ast_expression_common_params_add(selfex, v)) {
|
||||
|
@ -129,6 +131,7 @@ bool ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
|
|||
}
|
||||
fromex = &other->expression;
|
||||
selfex = &self->expression;
|
||||
selfex->variadic = fromex->variadic;
|
||||
for (i = 0; i < fromex->params_count; ++i) {
|
||||
ast_value *v = ast_value_copy(fromex->params[i]);
|
||||
if (!v || !ast_expression_common_params_add(selfex, v))
|
||||
|
@ -178,6 +181,7 @@ ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
|
|||
else
|
||||
selfex->next = NULL;
|
||||
|
||||
selfex->variadic = fromex->variadic;
|
||||
for (i = 0; i < fromex->params_count; ++i) {
|
||||
ast_value *v = ast_value_copy(fromex->params[i]);
|
||||
if (!v || !ast_expression_common_params_add(selfex, v)) {
|
||||
|
@ -198,6 +202,8 @@ bool ast_compare_type(ast_expression *a, ast_expression *b)
|
|||
return false;
|
||||
if (a->expression.params_count != b->expression.params_count)
|
||||
return false;
|
||||
if (a->expression.variadic != b->expression.variadic)
|
||||
return false;
|
||||
if (a->expression.params_count) {
|
||||
size_t i;
|
||||
for (i = 0; i < a->expression.params_count; ++i) {
|
||||
|
|
1
ast.h
1
ast.h
|
@ -107,6 +107,7 @@ typedef struct
|
|||
int vtype;
|
||||
ast_expression *next;
|
||||
MEM_VECTOR_MAKE(ast_value*, params);
|
||||
bool variadic;
|
||||
/* The codegen functions should store their output values
|
||||
* so we can call it multiple times without re-evaluating.
|
||||
* Store lvalue and rvalue seperately though. So that
|
||||
|
|
|
@ -23,6 +23,8 @@ void(void() callback) testcallback = {
|
|||
callback();
|
||||
};
|
||||
|
||||
void(float) has1param = {};
|
||||
|
||||
void() main = {
|
||||
local entity pawn, pawn2;
|
||||
|
||||
|
@ -36,4 +38,6 @@ void() main = {
|
|||
print("Yes\n");
|
||||
|
||||
testcallback(printworking);
|
||||
|
||||
has1param();
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,...) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
|
@ -18,6 +18,9 @@ $frame stand3
|
|||
$modelname foobar
|
||||
$modelname foobar3
|
||||
|
||||
void(string a, ...) hasvaria = {
|
||||
};
|
||||
|
||||
void() main = {
|
||||
entity pawn;
|
||||
vector vec;
|
||||
|
@ -40,8 +43,8 @@ void() main = {
|
|||
pawn = spawn();
|
||||
pawn.mema = 3;
|
||||
pawn.memb = 5;
|
||||
print2(ftos(pawn.mema), "\n");
|
||||
print2(ftos(pawn.memb), "\n");
|
||||
print(ftos(pawn.mema), "\n");
|
||||
print(ftos(pawn.memb), "\n");
|
||||
|
||||
print("SECOND TEST\n");
|
||||
for (a = 0; a < 3; a = a + 1) {
|
||||
|
|
1
main.c
1
main.c
|
@ -395,6 +395,7 @@ int main(int argc, char **argv) {
|
|||
options_set(opts_warn, WARN_LOCAL_CONSTANTS, true);
|
||||
options_set(opts_warn, WARN_VOID_VARIABLES, true);
|
||||
options_set(opts_warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
|
||||
options_set(opts_warn, WARN_VARIADIC_FUNCTION, true);
|
||||
|
||||
if (!options_parse(argc, argv)) {
|
||||
return usage();
|
||||
|
|
41
parser.c
41
parser.c
|
@ -306,6 +306,7 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc
|
|||
int vtype = basetype;
|
||||
int temptype;
|
||||
size_t i;
|
||||
bool variadic = false;
|
||||
|
||||
MEM_VECTOR_INIT(¶ms, p);
|
||||
|
||||
|
@ -333,6 +334,18 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc
|
|||
}
|
||||
}
|
||||
|
||||
if (parser->tok == TOKEN_DOTS) {
|
||||
/* variadic args */
|
||||
variadic = true;
|
||||
if (!parser_next(parser))
|
||||
goto on_error;
|
||||
if (parser->tok != ')') {
|
||||
parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
|
||||
goto on_error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
temptype = parser_token(parser)->constval.t;
|
||||
if (!parser_next(parser))
|
||||
goto on_error;
|
||||
|
@ -359,6 +372,7 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc
|
|||
}
|
||||
fval->expression.next = (ast_expression*)param;
|
||||
MEM_VECTOR_MOVE(¶m->expression, params, &fval->expression, params);
|
||||
fval->expression.variadic = param->expression.variadic;
|
||||
param = fval;
|
||||
}
|
||||
|
||||
|
@ -387,6 +401,7 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc
|
|||
var = ast_value_new(ctx, "<unnamed>", vtype);
|
||||
if (!var)
|
||||
goto on_error;
|
||||
var->expression.variadic = variadic;
|
||||
MEM_VECTOR_MOVE(¶ms, p, &var->expression, params);
|
||||
return var;
|
||||
on_error:
|
||||
|
@ -952,16 +967,21 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
|
|||
parseerror(parser, "could not determine function return type");
|
||||
return false;
|
||||
} else {
|
||||
if (fun->expression.params_count != paramcount) {
|
||||
if (fun->expression.params_count != paramcount &&
|
||||
!(fun->expression.variadic &&
|
||||
fun->expression.params_count < paramcount))
|
||||
{
|
||||
ast_value *fval;
|
||||
const char *fewmany = (fun->expression.params_count > paramcount) ? "few" : "many";
|
||||
|
||||
fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
|
||||
if (opts_standard == COMPILER_GMQCC)
|
||||
{
|
||||
if (fval)
|
||||
parseerror(parser, "too few parameters for call to %s: expected %i, got %i",
|
||||
parseerror(parser, "too %s parameters for call to %s: expected %i, got %i", fewmany,
|
||||
fval->name, (int)fun->expression.params_count, paramcount);
|
||||
else
|
||||
parseerror(parser, "too few parameters for function call: expected %i, got %i",
|
||||
parseerror(parser, "too %s parameters for function call: expected %i, got %i", fewmany,
|
||||
(int)fun->expression.params_count, paramcount);
|
||||
return false;
|
||||
}
|
||||
|
@ -969,11 +989,11 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
|
|||
{
|
||||
if (fval)
|
||||
return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
|
||||
"too few parameters for call to %s: expected %i, got %i",
|
||||
"too %s parameters for call to %s: expected %i, got %i", fewmany,
|
||||
fval->name, (int)fun->expression.params_count, paramcount);
|
||||
else
|
||||
return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
|
||||
"too few parameters for function call: expected %i, got %i",
|
||||
"too %s parameters for function call: expected %i, got %i", fewmany,
|
||||
(int)fun->expression.params_count, paramcount);
|
||||
}
|
||||
}
|
||||
|
@ -2024,6 +2044,7 @@ static bool parser_variable(parser_t *parser, ast_block *localblock)
|
|||
|
||||
fval->expression.next = (ast_expression*)var;
|
||||
MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params);
|
||||
fval->expression.variadic = var->expression.variadic;
|
||||
|
||||
/* we compare the type late here, but it's easier than
|
||||
* messing with the parameter-vector etc. earlier
|
||||
|
@ -2217,6 +2238,15 @@ nextvar:
|
|||
has_frame_think = false;
|
||||
old = parser->function;
|
||||
|
||||
if (var->expression.variadic) {
|
||||
if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
|
||||
"variadic function with implementation will not be able to access additional parameters"))
|
||||
{
|
||||
ast_value_delete(typevar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (localblock) {
|
||||
parseerror(parser, "cannot declare functions within functions");
|
||||
ast_value_delete(typevar);
|
||||
|
@ -2579,6 +2609,7 @@ static bool parser_do(parser_t *parser)
|
|||
}
|
||||
fval->expression.next = (ast_expression*)var;
|
||||
MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params);
|
||||
fval->expression.variadic = var->expression.variadic;
|
||||
var = fval;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,3 +14,4 @@ GMQCC_DEFINE_FLAG(LOCAL_SHADOWS)
|
|||
GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS)
|
||||
GMQCC_DEFINE_FLAG(VOID_VARIABLES)
|
||||
GMQCC_DEFINE_FLAG(IMPLICIT_FUNCTION_POINTER)
|
||||
GMQCC_DEFINE_FLAG(VARIADIC_FUNCTION)
|
||||
|
|
Loading…
Reference in a new issue