X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=b97d92c3896ee0d26fb9c3a240afc7da2e02bf9b;hp=d64dd2e1de187c2f133b528b07a0a443cf1591a4;hb=bd54fe03b9d080b5b99af874cc625b35a82580a3;hpb=3d35804a7d79a99527cf785a133aaff0dd24b457 diff --git a/parser.c b/parser.c index d64dd2e..b97d92c 100644 --- a/parser.c +++ b/parser.c @@ -23,6 +23,7 @@ */ #include #include +#include #include "gmqcc.h" #include "lexer.h" @@ -51,7 +52,10 @@ typedef struct { ast_value *imm_float_zero; ast_value *imm_float_one; + ast_value *imm_float_neg_one; + ast_value *imm_vector_zero; + ast_value *nil; ast_value *reserved_version; @@ -59,6 +63,7 @@ typedef struct { size_t crc_fields; ast_function *function; + ht aliases; /* All the labels the function defined... * Should they be in ast_function instead? @@ -115,6 +120,7 @@ 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 void parseerror(parser_t *parser, const char *fmt, ...) { @@ -222,6 +228,12 @@ static ast_value* parser_const_float_0(parser_t *parser) return parser->imm_float_zero; } +static ast_value* parser_const_float_neg1(parser_t *parser) { + if (!parser->imm_float_neg_one) + parser->imm_float_neg_one = parser_const_float(parser, -1); + return parser->imm_float_neg_one; +} + static ast_value* parser_const_float_1(parser_t *parser) { if (!parser->imm_float_one) @@ -309,6 +321,9 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name) static ast_expression* parser_find_global(parser_t *parser, const char *name) { + ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser)); + if (var) + return var; return (ast_expression*)util_htget(parser->htglobals, name); } @@ -440,7 +455,7 @@ static sy_elem syparen(lex_ctx ctx, size_t off) { */ static bool rotate_entfield_array_index_nodes(ast_expression **out) { - ast_array_index *index; + ast_array_index *index, *oldindex; ast_entfield *entfield; ast_value *field; @@ -464,12 +479,16 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) sub = index->index; entity = entfield->entity; - ast_delete(index); + oldindex = index; index = ast_array_index_new(ctx, (ast_expression*)field, sub); entfield = ast_entfield_new(ctx, entity, (ast_expression*)index); *out = (ast_expression*)entfield; + oldindex->array = NULL; + oldindex->index = NULL; + ast_delete(oldindex); + return true; } @@ -630,7 +649,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { #if 0 /* This is not broken in fteqcc anymore */ - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { /* this error doesn't need to make us bail out */ (void)!parsewarning(parser, WARN_EXTENSIONS, "accessing array-field members of an entity without parenthesis\n" @@ -658,8 +677,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } } - if (!ast_block_set_type(blocks[0], exprs[1])) - return false; + ast_block_set_type(blocks[0], exprs[1]); vec_push(sy->out, syblock(ctx, blocks[0])); return true; @@ -1055,6 +1073,66 @@ 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 { + out = parser_builtin_pow(parser); + } + break; + + case opid3('<','=','>'): /* -1, 0, or 1 */ + 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 comparision: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + if (ConstF(0) < ConstF(1)) + out = (ast_expression*)parser_const_float_neg1(parser); + else if (ConstF(0) == ConstF(1)) + out = (ast_expression*)parser_const_float_0(parser); + else if (ConstF(0) > ConstF(1)) + out = (ast_expression*)parser_const_float_1(parser); + } else { + ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); + + eq->refs = (ast_binary_ref)false; /* references nothing */ + + /* if (lt) { */ + out = (ast_expression*)ast_ternary_new(ctx, + (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]), + /* out = -1 */ + (ast_expression*)parser_const_float_neg1(parser), + /* } else { */ + /* if (eq) { */ + (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq, + /* out = 0 */ + (ast_expression*)parser_const_float_0(parser), + /* } else { */ + /* out = 1 */ + (ast_expression*)parser_const_float_1(parser) + /* } */ + ) + /* } */ + ); + + } + break; + case opid1('>'): generated_op += 1; /* INSTR_GT */ case opid1('<'): @@ -1101,7 +1179,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } else assignop = type_storep_instr[exprs[0]->expression.vtype]; - if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1])) + if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1])) { ast_type_to_string(field->expression.next, ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); @@ -1128,7 +1206,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) assignop = type_store_instr[exprs[0]->expression.vtype]; } - if (assignop == AINSTR_END) { + if (assignop == VINSTR_END) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); @@ -1346,6 +1424,19 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) asbinstore->keep_dest = true; out = (ast_expression*)asbinstore; break; + + case opid2('~', 'P'): + if (exprs[0]->expression.vtype != TYPE_FLOAT) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1); + return false; + } + + 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]); + break; } #undef NotSameType @@ -1758,8 +1849,10 @@ 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; } - else - { + if (!strcmp(parser_tokval(parser), "__builtin_pow")) + var = parser_builtin_pow(parser); + + if (!var) { char *correct = NULL; size_t i; @@ -1784,7 +1877,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * We should also consider adding correction tables for * other things as well. */ - if (opts.correction) { + if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) { correction_t corr; correct_init(&corr); @@ -1871,8 +1964,8 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } } if (o == operator_count) { - /* no operator found... must be the end of the statement */ - break; + compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser)); + goto onerr; } /* found an operator */ op = &operators[o]; @@ -2635,7 +2728,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou if (typevar || parser->tok == TOKEN_TYPENAME) { #if 0 - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { if (parsewarning(parser, WARN_EXTENSIONS, "current standard does not allow variable declarations in for-loop initializers")) goto onerr; @@ -2864,8 +2957,44 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { + flags |= AST_FLAG_ALIAS; + *message = NULL; + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + + if (parser->tok == '(') { + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "`alias` attribute missing parameter"); + goto argerr; + } + + *message = util_strdup(parser_tokval(parser)); + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + if (parser->tok != ')') { + parseerror(parser, "`alias` attribute expected `)` after parameter"); + goto argerr; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error in attribute"); + goto argerr; + } + } + if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`alias` attribute expected `]]`"); + goto argerr; + } + } else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) { flags |= AST_FLAG_DEPRECATED; *message = NULL; @@ -3186,6 +3315,145 @@ 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 *= 2; + * x *= x; + * } + * y = y - 1; + * value = x * value; + * } + * 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; +} + /* parse computed goto sides */ static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { ast_expression *on_true; @@ -3292,26 +3560,15 @@ static bool parse_eol(parser_t *parser) return parser->tok == TOKEN_EOL; } -/* - * Traditionally you'd implement warning, error, and message - * directives in the preprocessor. However, like #pragma, these - * shouldn't depend on -fftepp to utilize. So they're instead - * implemented here. - */ -enum { - PARSE_DIRECTIVE_ERROR, - PARSE_DIRECTIVE_MESSAGE, - PARSE_DIRECTIVE_WARNING, - PARSE_DIRECTIVE_COUNT -}; - -static const char *parser_directives[PARSE_DIRECTIVE_COUNT] = { - "error", - "message", - "warning" -}; - -static bool parse_pragma_do(parser_t *parser) { +static bool parse_pragma_do(parser_t *parser) +{ + if (!parser_next(parser) || + parser->tok != TOKEN_IDENT || + strcmp(parser_tokval(parser), "pragma")) + { + parseerror(parser, "expected `pragma` keyword after `#`, got `%s`", parser_tokval(parser)); + return false; + } if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) { parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser)); return false; @@ -3322,102 +3579,35 @@ static bool parse_pragma_do(parser_t *parser) { parseerror(parser, "`noref` pragma requires an argument: 0 or 1"); return false; } - parser->noref = !!parser_token(parser)->constval.i; - if (!parse_eol(parser)) { parseerror(parser, "parse error after `noref` pragma"); return false; } - } else { - (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); - return false; } - return true; -} - -static bool parse_directive_or_pragma_do(parser_t *parser, bool *pragma) { - - size_t type = PARSE_DIRECTIVE_COUNT; - - if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { - parseerror(parser, "expected `pragma, error, message, warning` after `#`, got `%s`", - parser_tokval(parser)); - + else + { + (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); return false; } - if (!strcmp(parser_tokval(parser), "pragma" )) { - *pragma = true; - - return parse_pragma_do(parser); - } - - if (!strcmp(parser_tokval(parser), "error" )) type = PARSE_DIRECTIVE_ERROR; - if (!strcmp(parser_tokval(parser), "message")) type = PARSE_DIRECTIVE_MESSAGE; - if (!strcmp(parser_tokval(parser), "warning")) type = PARSE_DIRECTIVE_WARNING; - - switch (type) { - case PARSE_DIRECTIVE_ERROR: - case PARSE_DIRECTIVE_MESSAGE: - case PARSE_DIRECTIVE_WARNING: - *pragma = false; - - if (!parse_skipwhite(parser) || parser->tok != TOKEN_STRINGCONST) { - parseerror(parser, "expected %s, got `%`", parser_directives[type], parser_tokval(parser)); - return false; - } - - switch (type) { - case PARSE_DIRECTIVE_ERROR: - con_cprintmsg(&parser->lex->tok.ctx, LVL_ERROR, "error", parser_tokval(parser)); - compile_errors ++; /* hack */ - break; - /*break;*/ - - case PARSE_DIRECTIVE_MESSAGE: - con_cprintmsg(&parser->lex->tok.ctx, LVL_MSG, "message", parser_tokval(parser)); - break; - - case PARSE_DIRECTIVE_WARNING: - con_cprintmsg(&parser->lex->tok.ctx, LVL_WARNING, "warning", parser_tokval(parser)); - break; - } - - if (!parse_eol(parser)) { - parseerror(parser, "parse error after `%` directive", parser_directives[type]); - return false; - } - - return (type != PARSE_DIRECTIVE_ERROR); - - default: - parseerror(parser, "invalid directive `%s`", parser_tokval(parser)); - return false; - } - return true; } -static bool parse_directive_or_pragma(parser_t *parser) +static bool parse_pragma(parser_t *parser) { bool rv; - bool pragma; /* true when parsing pragma */ - parser->lex->flags.preprocessing = true; - parser->lex->flags.mergelines = true; - - rv = parse_directive_or_pragma_do(parser, &pragma); - + parser->lex->flags.mergelines = true; + rv = parse_pragma_do(parser); if (parser->tok != TOKEN_EOL) { - parseerror(parser, "junk after %s", (pragma) ? "pragma" : "directive"); + parseerror(parser, "junk after pragma"); rv = false; } parser->lex->flags.preprocessing = false; - parser->lex->flags.mergelines = false; - + parser->lex->flags.mergelines = false; if (!parser_next(parser)) { - parseerror(parser, "parse error after %s", (pragma) ? "pragma" : "directive"); + parseerror(parser, "parse error after pragma"); rv = false; } return rv; @@ -3443,7 +3633,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * parseerror(parser, "cannot declare a variable from here"); return false; } - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } @@ -3510,7 +3700,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else if (!strcmp(parser_tokval(parser), "for")) { - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?")) return false; } @@ -3830,6 +4020,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var) has_frame_think = false; old = parser->function; + if (var->expression.flags & AST_FLAG_ALIAS) { + parseerror(parser, "function aliases cannot have bodies"); + return false; + } + if (vec_size(parser->gotos) || vec_size(parser->labels)) { parseerror(parser, "gotos/labels leaking"); return false; @@ -3902,11 +4097,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var) ast_expression *functype = fld_think->expression.next; thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype); - if (!thinkfunc || !ast_type_adopt(thinkfunc, functype)) { + if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/ ast_unref(framenum); parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser)); return false; } + ast_type_adopt(thinkfunc, functype); if (!parser_next(parser)) { ast_unref(framenum); @@ -4032,6 +4228,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } + if (var->hasvalue) { + parseerror(parser, "function `%s` declared with multiple bodies", var->name); + ast_block_delete(block); + goto enderr; + } + func = ast_function_new(ast_ctx(var), var->name, var); if (!func) { parseerror(parser, "failed to allocate function for `%s`", var->name); @@ -4112,7 +4314,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (parser->tok == ';') return parser_next(parser); - else if (opts.standard == COMPILER_QCC) + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)"); return retval; @@ -4628,7 +4830,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) vec_free(params); /* sanity check */ - if (vec_size(params) > 8 && opts.standard == COMPILER_QCC) + if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ @@ -4842,7 +5044,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } /* now there may be function parens again */ - if (parser->tok == '(' && opts.standard == COMPILER_QCC) + if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); if (parser->tok == '(' && wasarray) parseerror(parser, "arrays as part of a return type is not supported"); @@ -4981,7 +5183,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Part 0: finish the type */ if (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -5004,7 +5206,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } /* for functions returning functions */ while (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -5015,7 +5217,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield var->cvq = qualifier; var->expression.flags |= qflags; - if (var->expression.flags & AST_FLAG_DEPRECATED) + + /* + * store the vstring back to var for alias and + * deprecation messages. + */ + if (var->expression.flags & AST_FLAG_DEPRECATED || + var->expression.flags & AST_FLAG_ALIAS) var->desc = vstring; /* Part 1: @@ -5074,7 +5282,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; */ } - if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) && + if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) && (old = parser_find_global(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5146,7 +5354,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield ast_delete(var); var = proto; } - if (opts.standard == COMPILER_QCC && + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC && (old = parser_find_field(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5177,7 +5385,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); var = NULL; goto skipvar; @@ -5220,10 +5428,85 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } else { - parser_addglobal(parser, var->name, (ast_expression*)var); - if (isvector) { - for (i = 0; i < 3; ++i) { - parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]); + if (!(var->expression.flags & AST_FLAG_ALIAS)) { + parser_addglobal(parser, var->name, (ast_expression*)var); + if (isvector) { + for (i = 0; i < 3; ++i) { + parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]); + } + } + } else { + ast_expression *find = parser_find_global(parser, var->desc); + + if (!find) { + compile_error(parser_ctx(parser), "undeclared variable `%s` for alias `%s`", var->desc, var->name); + return false; + } + + if (var->expression.vtype != find->expression.vtype) { + char ty1[1024]; + char ty2[1024]; + + ast_type_to_string(find, ty1, sizeof(ty1)); + ast_type_to_string((ast_expression*)var, ty2, sizeof(ty2)); + + compile_error(parser_ctx(parser), "incompatible types `%s` and `%s` for alias `%s`", + ty1, ty2, var->name + ); + return false; + } + + /* + * add alias to aliases table and to corrector + * so corrections can apply for aliases as well. + */ + util_htset(parser->aliases, var->name, find); + + /* + * add to corrector so corrections can work + * even for aliases too. + */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + var->name + ); + + /* generate aliases for vector components */ + if (isvector) { + char *buffer[3]; + + util_asprintf(&buffer[0], "%s_x", var->desc); + util_asprintf(&buffer[1], "%s_y", var->desc); + util_asprintf(&buffer[2], "%s_z", var->desc); + + util_htset(parser->aliases, me[0]->name, parser_find_global(parser, buffer[0])); + util_htset(parser->aliases, me[1]->name, parser_find_global(parser, buffer[1])); + util_htset(parser->aliases, me[2]->name, parser_find_global(parser, buffer[2])); + + mem_d(buffer[0]); + mem_d(buffer[1]); + mem_d(buffer[2]); + + /* + * add to corrector so corrections can work + * even for aliases too. + */ + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[0]->name + ); + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[1]->name + ); + correct_add ( + vec_last(parser->correct_variables), + &vec_last(parser->correct_variables_score), + me[2]->name + ); } } } @@ -5357,7 +5640,7 @@ skipvar: break; } - if (localblock && opts.standard == COMPILER_QCC) { + if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_LOCAL_CONSTANTS, "initializing expression turns variable `%s` into a constant in this standard", var->name) ) @@ -5377,7 +5660,7 @@ skipvar: break; } } - else if (opts.standard == COMPILER_QCC) { + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { parseerror(parser, "expected '=' before function body in this standard"); } @@ -5598,7 +5881,7 @@ static bool parser_global_statement(parser_t *parser) } else if (parser->tok == '#') { - return parse_directive_or_pragma(parser); + return parse_pragma(parser); } else if (parser->tok == '$') { @@ -5609,7 +5892,7 @@ static bool parser_global_statement(parser_t *parser) } else { - parseerror(parser, "unexpected token: %s", parser->lex->tok.value); + parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value); return false; } return true; @@ -5721,6 +6004,8 @@ bool parser_init() vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); vec_push(parser->_blocktypedefs, 0); + parser->aliases = util_htnew(PARSER_HT_SIZE); + /* corrector */ vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); @@ -5738,7 +6023,7 @@ bool parser_init() parser->const_vec[1] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); parser->const_vec[2] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); - if (opts.add_info) { + if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING); parser->reserved_version->cvq = CV_CONST; parser->reserved_version->hasvalue = true; @@ -5871,6 +6156,8 @@ void parser_cleanup() ast_value_delete(parser->const_vec[1]); ast_value_delete(parser->const_vec[2]); + util_htdel(parser->aliases); + mem_d(parser); } @@ -6027,7 +6314,7 @@ bool parser_finish(const char *output) return false; } } - if (opts.dump) + if (OPTS_OPTION_BOOL(OPTION_DUMP)) ir_builder_dump(ir, con_out); for (i = 0; i < vec_size(parser->functions); ++i) { if (!ir_function_finalize(parser->functions[i]->ir_func)) { @@ -6044,7 +6331,7 @@ bool parser_finish(const char *output) } if (retval) { - if (opts.dumpfin) + if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) ir_builder_dump(ir, con_out); generate_checksum(parser);