MEM_VEC_FUNCTIONS(parser_t, varentry_t, locals)
MEM_VEC_FUNCTIONS(parser_t, ast_function*, functions)
-static void parser_pop_local(parser_t *parser);
+static bool GMQCC_WARN parser_pop_local(parser_t *parser);
static bool parser_variable(parser_t *parser, ast_block *localblock);
static ast_block* parser_parse_block(parser_t *parser, bool warnreturn);
static bool parser_parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
int vtype = basetype;
int temptype;
size_t i;
+ bool variadic = false;
MEM_VECTOR_INIT(¶ms, p);
}
}
+ 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;
+ }
+ if (opts_standard == COMPILER_QCC) {
+ if (parsewarning(parser, WARN_EXTENSIONS, "variadic functions are not available in this standard"))
+ goto on_error;
+ }
+ break;
+ }
+
temptype = parser_token(parser)->constval.t;
if (!parser_next(parser))
goto on_error;
}
fval->expression.next = (ast_expression*)param;
MEM_VECTOR_MOVE(¶m->expression, params, &fval->expression, params);
+ fval->expression.variadic = param->expression.variadic;
param = fval;
}
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:
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;
}
{
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);
}
}
parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
goto onerr;
}
+ if (ast_istype(var, ast_value))
+ ((ast_value*)var)->uses++;
if (!shunt_out_add(&sy, syexp(parser_ctx(parser), var))) {
parseerror(parser, "out of memory");
goto onerr;
ast_loop *aloop;
ast_expression *initexpr, *cond, *increment, *ontrue;
size_t oldblocklocal;
+ bool retval = true;
lex_ctx ctx = parser_ctx(parser);
*out = (ast_expression*)aloop;
while (parser->locals_count > parser->blocklocal)
- parser_pop_local(parser);
+ retval = retval && parser_pop_local(parser);
parser->blocklocal = oldblocklocal;
- return true;
+ return retval;
onerr:
if (initexpr) ast_delete(initexpr);
if (cond) ast_delete(cond);
if (increment) ast_delete(increment);
while (parser->locals_count > parser->blocklocal)
- parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
parser->blocklocal = oldblocklocal;
return false;
}
}
}
-static void parser_pop_local(parser_t *parser)
+static bool GMQCC_WARN parser_pop_local(parser_t *parser)
{
+ varentry_t *ve;
parser->locals_count--;
+
+ ve = &parser->locals[parser->locals_count];
+ if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) {
+ if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name))
+ return false;
+ }
mem_d(parser->locals[parser->locals_count].name);
+ return true;
}
static bool parser_parse_block_into(parser_t *parser, ast_block *block, bool warnreturn)
{
size_t oldblocklocal;
+ bool retval = true;
oldblocklocal = parser->blocklocal;
parser->blocklocal = parser->locals_count;
cleanup:
while (parser->locals_count > parser->blocklocal)
- parser_pop_local(parser);
+ retval = retval && parser_pop_local(parser);
parser->blocklocal = oldblocklocal;
return !!block;
}
bool hadproto;
bool isparam;
+ bool retval = true;
+
int basetype = parser_token(parser)->constval.t;
if (!parser_next(parser)) {
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
!ast_block_collect(localblock, vy.var) ||
!ast_block_collect(localblock, vz.var))
{
- parser_pop_local(parser);
- parser_pop_local(parser);
- parser_pop_local(parser);
- parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
ast_value_delete(var);
ast_value_delete(typevar);
return false;
}
if (localblock && !ast_block_locals_add(localblock, var))
{
- parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
ast_value_delete(var);
ast_value_delete(typevar);
return false;
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);
!ast_block_collect(block, vy.var) ||
!ast_block_collect(block, vz.var) )
{
- parser_pop_local(parser);
- parser_pop_local(parser);
- parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
+ (void)!parser_pop_local(parser);
ast_block_delete(block);
ast_value_delete(typevar);
return false;
}
parser->function = old;
while (parser->locals_count)
- parser_pop_local(parser);
+ retval = retval && parser_pop_local(parser);
if (!block) {
ast_value_delete(typevar);
else if (opts_standard == COMPILER_QCC)
parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)");
ast_value_delete(typevar);
- return true;
+ return retval;
} else {
ast_expression *cexp;
ast_value *cval;
cexp = parser_expression_leave(parser, true);
+ if (!cexp) {
+ ast_value_delete(typevar);
+ return false;
+ }
+
cval = (ast_value*)cexp;
if (!ast_istype(cval, ast_value) || !cval->isconst)
parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
}
fval->expression.next = (ast_expression*)var;
MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params);
+ fval->expression.variadic = var->expression.variadic;
var = fval;
}