From f19adcd1b3e7f15ce83d1d43c5acee312c9f56ca Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Fri, 8 Mar 2013 09:17:54 +0000 Subject: [PATCH] Implemented __builtin_mod, and % operator. Added floor builtin to the standalone executor. Mod works so long as the compiler can find a suitable definition of "float floor(float)", otherwise it prints a diagnostic and gives up (original id1 Quake had floor so we can depend on it). --- exec.c | 13 +++++++- lexer.c | 6 ++++ parser.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/exec.c b/exec.c index f44bd62..d9ced65 100644 --- a/exec.c +++ b/exec.c @@ -825,6 +825,16 @@ static int qc_strcmp(qc_program *prog) return 0; } +static int qc_floor(qc_program *prog) +{ + qcany *num, out; + CheckArgs(1); + num = GetArg(0); + out._float = floor(num->_float); + Return(out); + return 0; +} + static prog_builtin qc_builtins[] = { NULL, &qc_print, /* 1 */ @@ -839,7 +849,8 @@ static prog_builtin qc_builtins[] = { &qc_strcat, /* 10 */ &qc_strcmp, /* 11 */ &qc_normalize, /* 12 */ - &qc_sqrt /* 13 */ + &qc_sqrt, /* 13 */ + &qc_floor /* 14 */ }; static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]); diff --git a/lexer.c b/lexer.c index 4235613..97fcbf3 100644 --- a/lexer.c +++ b/lexer.c @@ -1361,6 +1361,12 @@ int lex_do(lex_file *lex) return (lex->tok.ttype = TOKEN_OPERATOR); } + if (ch == '%') { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + if (isident_start(ch)) { const char *v; diff --git a/parser.c b/parser.c index bd4f1c1..5340f1a 100644 --- a/parser.c +++ b/parser.c @@ -120,8 +120,10 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname); static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname); static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef); + static ast_expression *parser_builtin_pow(parser_t *); static ast_expression *parser_builtin_exp(parser_t *); +static ast_expression *parser_builtin_mod(parser_t *); static void parseerror(parser_t *parser, const char *fmt, ...) { @@ -964,10 +966,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } break; + case opid1('%'): + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, + (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1)))); + } else { + /* generate a call to __builtin_mod */ + ast_expression *mod = parser_builtin_mod(parser); + ast_call *call = NULL; + if (!mod) return false; /* can return null for missing floor */ + + call = ast_call_new(parser_ctx(parser), mod); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + + out = (ast_expression*)call; + } + break; + case opid2('%','='): - compile_error(ctx, "qc does not have a modulo operator"); + compile_error(ctx, "%= is unimplemented"); return false; + case opid1('|'): case opid1('&'): if (NotSameType(TYPE_FLOAT)) { @@ -1858,6 +1885,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) var = parser_builtin_pow(parser); if (!strcmp(parser_tokval(parser), "__builtin_exp")) var = parser_builtin_exp(parser); + if (!strcmp(parser_tokval(parser), "__builtin_mod")) + var = parser_builtin_mod(parser); if (!var) { char *correct = NULL; @@ -3501,6 +3530,72 @@ static ast_expression *parser_builtin_exp(parser_t *parser) { return (ast_expression*)exp_func_val; } +static ast_expression *parser_builtin_mod(parser_t *parser) { + /* + * float __builtin_mod(float x, float y) { + * return x - y * floor(x / y); + * } + */ + static ast_value *mod_func_val = NULL; + static ast_expression *mod_floor = NULL; + + if (!mod_floor) { + if (!(mod_floor = parser_find_global(parser, "floor"))) { + parseerror(parser, "internal error: no suitable definition found for `floor` (required for % and __builtin_mod)"); + return NULL; + } + } + + if (!mod_func_val) { + ast_value *mod_args[2]; + ast_function *mod_func = NULL; + ast_block *mod_body = ast_block_new (parser_ctx(parser)); + ast_call *mod_call = ast_call_new (parser_ctx(parser), mod_floor); + mod_func_val = ast_value_new (parser_ctx(parser), "__builtin_mod", TYPE_FUNCTION); + mod_func_val->expression.next = (ast_expression*)ast_value_new(parser_ctx(parser), "", TYPE_FLOAT); + mod_func = ast_function_new(parser_ctx(parser), "__builtin_mod", mod_func_val); + mod_args[0] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + mod_args[1] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + + /* floor(x/y) */ + vec_push(mod_call->params, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_DIV_F, + (ast_expression*)mod_args[0], + (ast_expression*)mod_args[1] + ) + ); + + vec_push(mod_body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_SUB_F, + (ast_expression*)mod_args[0], + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_MUL_F, + (ast_expression*)mod_args[1], + (ast_expression*)mod_call + ) + ) + ) + ); + + vec_push(mod_func_val->expression.params, mod_args[0]); + vec_push(mod_func_val->expression.params, mod_args[1]); + + vec_push(mod_func->blocks, mod_body); + + vec_push(parser->functions, mod_func); + vec_push(parser->globals, (ast_expression*)mod_func_val); + } + + return (ast_expression*)mod_func_val; +} + /* parse computed goto sides */ static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { ast_expression *on_true; -- 2.39.2