X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=a5c039a4c2691a688af8b540256d64a7a651ba34;hp=681207c192c01d55b74f565856ae537d78b6fdaa;hb=d8d78f61a76bffb96ff70c410312f1434fd98e5a;hpb=3593a1b6b89ff690b8e89a31707468aad014b7b1 diff --git a/parser.c b/parser.c index 681207c..a5c039a 100644 --- a/parser.c +++ b/parser.c @@ -23,6 +23,7 @@ */ #include #include +#include #include "gmqcc.h" #include "lexer.h" @@ -120,6 +121,10 @@ static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value * 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, ...) { va_list ap; @@ -961,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)) { @@ -1071,6 +1101,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); break; + case opid2('*', '*'): + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in exponentiation: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1))); + } else { + ast_call *gencall = ast_call_new(parser_ctx(parser), parser_builtin_pow(parser)); + vec_push(gencall->params, exprs[0]); + vec_push(gencall->params, exprs[1]); + out = (ast_expression*)gencall; + } + break; + case opid3('<','=','>'): /* -1, 0, or 1 */ if (NotSameType(TYPE_FLOAT)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1416,7 +1466,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) if(CanConstFold1(exprs[0])) out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0)); else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); + out = (ast_expression*) + ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); break; } #undef NotSameType @@ -1830,6 +1881,12 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { var = (ast_expression*)intrinsic_debug_typestring; } + if (!strcmp(parser_tokval(parser), "__builtin_pow")) + 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; @@ -3294,6 +3351,254 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return true; } +ast_expression *parser_builtin_pow(parser_t *parser) { + /* + * float __builtin_pow(float x, float y) { + * float value = 1.0f; + * while (y > 0) { + * while (!(y&1)) { + * y *= 0.25f; + * x *= x; + * } + * y--; + * value *= x; + * } + * return value; + * } + */ + static ast_function *pow_func = NULL; + static ast_value *pow_func_val = NULL; + if (!pow_func) { + ast_value *pow_arguments[2]; + ast_value *pow_value = ast_value_new (parser_ctx(parser), "value", TYPE_FLOAT); + ast_block *pow_body = ast_block_new (parser_ctx(parser)); + ast_block *pow_loop_body = ast_block_new (parser_ctx(parser)); + ast_block *pow_loop_nest_body = ast_block_new (parser_ctx(parser)); + ast_loop *pow_loop = NULL; + ast_loop *pow_loop_nest = NULL; + + pow_arguments[0] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + pow_arguments[1] = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + pow_func_val = ast_value_new (parser_ctx(parser), "__builtin_pow", TYPE_FUNCTION); + pow_func_val->expression.next = (ast_expression*)ast_value_new(parser_ctx(parser), "", TYPE_FLOAT); + + vec_push(pow_func_val->expression.params, pow_arguments[0]); + vec_push(pow_func_val->expression.params, pow_arguments[1]); + + pow_func = ast_function_new(parser_ctx(parser), "__builtin_pow", pow_func_val); + + /* float value; */ + vec_push(pow_body->locals, pow_value); + /* value = 1.0f; */ + vec_push(pow_body->exprs, + (ast_expression*)ast_store_new( + parser_ctx(parser), + INSTR_STORE_F, + (ast_expression*)pow_value, + (ast_expression*)parser_const_float_1(parser) + ) + ); + + /* y >>= 2 */ + vec_push(pow_loop_nest_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float(parser, 0.25f) + ) + ); + vec_push(pow_loop_nest_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_arguments[0], + (ast_expression*)pow_arguments[0] + ) + ); + + /* while (!(y&1)) */ + pow_loop_nest = ast_loop_new ( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_AND, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_1(parser) + ), + true, + NULL, + false, + NULL, + (ast_expression*)pow_loop_nest_body + ); + + vec_push(pow_loop_body->exprs, (ast_expression*)pow_loop_nest); + vec_push(pow_loop_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_SUB_F, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_1(parser) + ) + ); + vec_push(pow_loop_body->exprs, + (ast_expression*)ast_binstore_new( + parser_ctx(parser), + INSTR_STORE_F, + INSTR_MUL_F, + (ast_expression*)pow_value, + (ast_expression*)pow_arguments[0] + ) + ); + + /* while (y > 0) { */ + pow_loop = ast_loop_new( + parser_ctx(parser), + NULL, + (ast_expression*)ast_binary_new( + parser_ctx(parser), + INSTR_GT, + (ast_expression*)pow_arguments[1], + (ast_expression*)parser_const_float_0(parser) + ), + false, + NULL, + false, + NULL, + (ast_expression*)pow_loop_body + ); + /* } */ + vec_push(pow_body->exprs, (ast_expression*)pow_loop); + /* return value; */ + vec_push(pow_body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)pow_value + ) + ); + + vec_push(pow_func->blocks, pow_body); + vec_push(parser->globals, (ast_expression*)pow_func_val); + vec_push(parser->functions, pow_func); + } + + return (ast_expression*)pow_func_val; +} + +#ifndef M_E +#define M_E 2.71828182845905 +#endif +static ast_expression *parser_builtin_exp(parser_t *parser) { + /* + * float __builtin_exp(float x) { + * return __builtin_exp(E, x); + * } + */ + static ast_value *exp_func_val = NULL; + + if (!exp_func_val) { + ast_function *exp_func = NULL; + ast_value *arg = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT); + ast_block *exp_body = ast_block_new (parser_ctx(parser)); + ast_call *exp_call = ast_call_new (parser_ctx(parser), parser_builtin_pow(parser)); + exp_func_val = ast_value_new (parser_ctx(parser), "__builtin_exp", TYPE_FUNCTION); + exp_func_val->expression.next = (ast_expression*)ast_value_new(parser_ctx(parser), "", TYPE_FLOAT); + exp_func = ast_function_new(parser_ctx(parser), "__builtin_exp", exp_func_val); + + vec_push(exp_call->params, (ast_expression*)parser_const_float(parser, M_E)); + vec_push(exp_call->params, (ast_expression*)arg); + + vec_push(exp_body->exprs, + (ast_expression*)ast_return_new( + parser_ctx(parser), + (ast_expression*)exp_call + ) + ); + + vec_push(exp_func_val->expression.params, arg); + vec_push(exp_func->blocks, exp_body); + + vec_push(parser->functions, exp_func); + vec_push(parser->globals, (ast_expression*)exp_func_val); + } + + 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 `%%` operator and `__builtin_mod` intrinsic)" + ); + 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; @@ -3654,6 +3959,8 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * static bool parse_enum(parser_t *parser) { + bool flag = false; + bool reverse = false; qcfloat num = 0; ast_value **values = NULL; ast_value *var = NULL; @@ -3661,11 +3968,37 @@ static bool parse_enum(parser_t *parser) ast_expression *old; - if (!parser_next(parser) || parser->tok != '{') { - parseerror(parser, "expected `{` after `enum` keyword"); + if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) { + parseerror(parser, "expected `{` or `:` after `enum` keyword"); return false; } + /* enumeration attributes (can add more later) */ + if (parser->tok == ':') { + if (!parser_next(parser) || parser->tok != TOKEN_IDENT){ + parseerror(parser, "expected `flag` or `reverse` for enumeration attribute"); + return false; + } + + /* attributes? */ + if (!strcmp(parser_tokval(parser), "flag")) { + num = 1; + flag = true; + } + else if (!strcmp(parser_tokval(parser), "reverse")) { + reverse = true; + } + else { + parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser)); + return false; + } + + if (!parser_next(parser) || parser->tok != '{') { + parseerror(parser, "expected `{` after enum attribute "); + return false; + } + } + while (true) { if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { if (parser->tok == '}') { @@ -3689,8 +4022,9 @@ static bool parse_enum(parser_t *parser) vec_push(values, var); var->cvq = CV_CONST; var->hasvalue = true; - var->constval.vfloat = num++; + /* for flagged enumerations increment in POTs of TWO */ + var->constval.vfloat = (flag) ? (num *= 2) : (num ++); parser_addglobal(parser, var->name, (ast_expression*)var); if (!parser_next(parser)) { @@ -3729,6 +4063,13 @@ static bool parse_enum(parser_t *parser) } } + /* patch them all (for reversed attribute) */ + if (reverse) { + size_t i; + for (i = 0; i < vec_size(values); i++) + values[i]->constval.vfloat = vec_size(values) - i - 1; + } + if (parser->tok != '}') { parseerror(parser, "internal error: breaking without `}`"); goto onerror;