X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=5fa0d899f201d9948e2f971eb3126a8c33841582;hb=52312791130eff70f21c428b23756a1b49e67804;hp=b7831d4e1d9f65dfc05147f9107bdf38defa9d2b;hpb=36d02d010e2dbfb76247a6c09a919b4710efc4da;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index b7831d4..5fa0d89 100644 --- a/parser.c +++ b/parser.c @@ -54,6 +54,7 @@ typedef struct { ast_value *imm_float_one; ast_value *imm_vector_zero; ast_value *nil; + ast_value *reserved_version; size_t crc_globals; size_t crc_fields; @@ -75,8 +76,8 @@ typedef struct { ht *typedefs; /* same as above but for the spelling corrector */ - ht *correct_variables; - size_t ***correct_variables_score; /* vector of vector of size_t* */ + correct_trie_t **correct_variables; + size_t ***correct_variables_score; /* vector of vector of size_t* */ /* not to be used directly, we use the hash table */ ast_expression **_locals; @@ -88,18 +89,15 @@ typedef struct { /* we store the '=' operator info */ const oper_info *assign_op; - /* TYPE_FIELD -> parser_find_fields is used instead of find_var - * TODO: TYPE_VECTOR -> x, y and z are accepted in the gmqcc standard - * anything else: type error - */ - qcint memberof; - /* Keep track of our ternary vs parenthesis nesting state. * If we reach a 'comma' operator in a ternary without a paren, * we shall trigger -Wternary-precedence. */ enum parser_pot *pot; + /* magic values */ + ast_value *const_vec[3]; + /* pragma flags */ bool noref; } parser_t; @@ -109,6 +107,7 @@ static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10; static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); +static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e); static bool parse_typedef(parser_t *parser); static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring); static ast_block* parse_block(parser_t *parser); @@ -555,7 +554,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) asvalue[i] = (ast_value*)exprs[i]; if (exprs[i]->expression.vtype == TYPE_NOEXPR && - !(i != 0 && op->id == opid2('?',':'))) + !(i != 0 && op->id == opid2('?',':')) && + !(i == 1 && op->id == opid1('.'))) { if (ast_istype(exprs[i], ast_label)) compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier"); @@ -588,7 +588,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; case opid1('.'): - if (exprs[0]->expression.vtype == TYPE_ENTITY) { + if (exprs[0]->expression.vtype == TYPE_VECTOR && + exprs[1]->expression.vtype == TYPE_NOEXPR) + { + if (exprs[1] == (ast_expression*)parser->const_vec[0]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); + else if (exprs[1] == (ast_expression*)parser->const_vec[1]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); + else if (exprs[1] == (ast_expression*)parser->const_vec[2]) + out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); + else { + parseerror(parser, "access to invalid vector component"); + return false; + } + } + else if (exprs[0]->expression.vtype == TYPE_ENTITY) { if (exprs[1]->expression.vtype != TYPE_FIELD) { parseerror(parser, "type error: right hand of member-operand should be an entity-field"); return false; @@ -596,7 +610,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]); } else if (exprs[0]->expression.vtype == TYPE_VECTOR) { - parseerror(parser, "internal error: vector access is not supposed to be handled at this point"); + parseerror(parser, "vectors cannot be accessed this way"); return false; } else { @@ -928,14 +942,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) exprs[0], exprs[1]); break; case opid1('^'): - parseerror(parser, "TODO: bitxor"); + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^"); return false; case opid2('<','<'): case opid2('>','>'): + if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) { + if (op->id == opid2('<','<')) + out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1)))); + else + out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1)))); + break; + } case opid3('<','<','='): case opid3('>','>','='): - parseerror(parser, "TODO: shifts"); + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); return false; case opid2('|','|'): @@ -1511,7 +1532,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma ast_expression *expr = NULL; shunt sy; bool wantop = false; - bool gotmemberof = false; /* only warn once about an assignment in a truth value because the current code * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part */ @@ -1532,11 +1552,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma while (true) { - if (gotmemberof) - gotmemberof = false; - else - parser->memberof = 0; - if (OPTS_FLAG(TRANSLATABLE_STRINGS) && parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_")) { @@ -1572,32 +1587,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } else if (parser->tok == TOKEN_IDENT) { + const char *ctoken = parser_tokval(parser); + ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL; ast_expression *var; if (wantop) { parseerror(parser, "expected operator or end of statement"); goto onerr; } wantop = true; - /* variable */ - if (opts.standard == COMPILER_GMQCC) + /* a_vector.{x,y,z} */ + if (!vec_size(sy.ops) || + !vec_last(sy.ops).etype || + operators[vec_last(sy.ops).etype-1].id != opid1('.') || + (prev >= intrinsic_debug_typestring && + prev <= intrinsic_debug_typestring)) { - if (parser->memberof == TYPE_ENTITY) { - /* still get vars first since there could be a fieldpointer */ - var = parser_find_var(parser, parser_tokval(parser)); - if (!var) - var = parser_find_field(parser, parser_tokval(parser)); - } - else if (parser->memberof == TYPE_VECTOR) - { - parseerror(parser, "TODO: implement effective vector member access"); - goto onerr; - } - else if (parser->memberof) { - parseerror(parser, "namespace for member not found"); - goto onerr; - } - else - var = parser_find_var(parser, parser_tokval(parser)); + /* When adding more intrinsics, fix the above condition */ + prev = NULL; + } + if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1]) + { + var = (ast_expression*)parser->const_vec[ctoken[0]-'x']; } else { var = parser_find_var(parser, parser_tokval(parser)); if (!var) @@ -1642,22 +1652,28 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma * We should also consider adding correction tables for * other things as well. */ - for (i = 0; i < vec_size(parser->correct_variables); i++) { - correct = correct_str(parser->correct_variables[i], "ello"); - if (strcmp(correct, parser_tokval(parser))) { - break; - } else if (correct) { - mem_d(correct); + if (OPTS_FLAG(ENHANCED_DIAGNOSTICS)) { + correction_t corr; + correct_init(&corr); + + for (i = 0; i < vec_size(parser->correct_variables); i++) { + correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); + if (strcmp(correct, parser_tokval(parser))) { + break; + } else if (correct) { + mem_d(correct); + correct = NULL; + } } - } + correct_free(&corr); - if (correct) { - parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); - mem_d(correct); - } else { - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + if (correct) { + parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + mem_d(correct); + goto onerr; + } } - + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); goto onerr; } } @@ -1872,24 +1888,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma olast = NULL; } - if (op->id == opid1('.') && opts.standard == COMPILER_GMQCC) { - /* for gmqcc standard: open up the namespace of the previous type */ - ast_expression *prevex = vec_last(sy.out).out; - if (!prevex) { - parseerror(parser, "unexpected member operator"); - goto onerr; - } - if (prevex->expression.vtype == TYPE_ENTITY) - parser->memberof = TYPE_ENTITY; - else if (prevex->expression.vtype == TYPE_VECTOR) - parser->memberof = TYPE_VECTOR; - else { - parseerror(parser, "type error: type has no members"); - goto onerr; - } - gotmemberof = true; - } - if (op->id == opid1('(')) { if (wantop) { size_t sycount = vec_size(sy.out); @@ -1943,7 +1941,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma goto onerr; } if (parser->tok == ';' || - (!parens && parser->tok == ']')) + (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}'))) { break; } @@ -1982,8 +1980,13 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels); if (!e) return NULL; + if (parser->tok != ';') { + parseerror(parser, "semicolon expected after expression"); + ast_unref(e); + return NULL; + } if (!parser_next(parser)) { - ast_delete(e); + ast_unref(e); return NULL; } return e; @@ -1998,7 +2001,7 @@ static void parser_enterblock(parser_t *parser) vec_push(parser->_block_ctx, parser_ctx(parser)); /* corrector */ - vec_push(parser->correct_variables, util_htnew(PARSER_HT_SIZE)); + vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); } @@ -2061,6 +2064,19 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression * ); } +static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e) +{ + vec_push(parser->globals, e); + util_htset(parser->htglobals, name, e); + + /* corrector */ + correct_add ( + parser->correct_variables[0], + &parser->correct_variables_score[0], + name + ); +} + static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot) { bool ifnot = false; @@ -2164,6 +2180,8 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) ast_delete(cond); return false; } + if (!ontrue) + ontrue = (ast_expression*)ast_block_new(parser_ctx(parser)); /* check for an else */ if (!strcmp(parser_tokval(parser), "else")) { /* parse into the 'else' branch */ @@ -2641,6 +2659,13 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express return false; } + if (!vec_size(loops)) { + if (is_continue) + parseerror(parser, "`continue` can only be used inside loops"); + else + parseerror(parser, "`break` can only be used inside loops or switches"); + } + if (parser->tok == TOKEN_IDENT) { if (!OPTS_FLAG(LOOP_LABELS)) parseerror(parser, "labeled loops not activated, try using -floop-labels"); @@ -3043,28 +3068,35 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * } /* parse computed goto sides */ -static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *side) { +static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) { ast_expression *on_true; ast_expression *on_false; + ast_expression *cond; - if (!side) + if (!*side) return NULL; - if (ast_istype(side, ast_ternary)) { - on_true = parse_goto_computed(parser, ((ast_ternary*)side)->on_true); - on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false); + if (ast_istype(*side, ast_ternary)) { + ast_ternary *tern = (ast_ternary*)*side; + on_true = parse_goto_computed(parser, &tern->on_true); + on_false = parse_goto_computed(parser, &tern->on_false); if (!on_true || !on_false) { parseerror(parser, "expected label or expression in ternary"); - if (((ast_ternary*)side)->on_false) ast_unref(((ast_ternary*)side)->on_false); - if (((ast_ternary*)side)->on_true) ast_unref(((ast_ternary*)side)->on_true); + if (on_true) ast_unref(on_true); + if (on_false) ast_unref(on_false); return NULL; } - return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->cond, on_true, on_false); - } else if (ast_istype(side, ast_label)) { - ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)side)->name); - ast_goto_set_label(gt, ((ast_label*)side)); + cond = tern->cond; + tern->cond = NULL; + ast_delete(tern); + *side = NULL; + return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false); + } else if (ast_istype(*side, ast_label)) { + ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name); + ast_goto_set_label(gt, ((ast_label*)*side)); + *side = NULL; return (ast_expression*)gt; } return NULL; @@ -3089,7 +3121,7 @@ static bool parse_goto(parser_t *parser, ast_expression **out) /* failed to parse expression for goto */ if (!(expression = parse_expression(parser, false, true)) || - !(*out = parse_goto_computed(parser, expression))) { + !(*out = parse_goto_computed(parser, &expression))) { parseerror(parser, "invalid goto expression"); ast_unref(expression); return false; @@ -3392,6 +3424,106 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } } +static bool parse_enum(parser_t *parser) +{ + qcfloat num = 0; + ast_value **values = NULL; + ast_value *var = NULL; + ast_value *asvalue; + + ast_expression *old; + + if (!parser_next(parser) || parser->tok != '{') { + parseerror(parser, "expected `{` after `enum` keyword"); + return false; + } + + while (true) { + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + if (parser->tok == '}') { + /* allow an empty enum */ + break; + } + parseerror(parser, "expected identifier or `}`"); + goto onerror; + } + + old = parser_find_field(parser, parser_tokval(parser)); + if (!old) + old = parser_find_global(parser, parser_tokval(parser)); + if (old) { + parseerror(parser, "value `%s` has already been declared here: %s:%i", + parser_tokval(parser), ast_ctx(old).file, ast_ctx(old).line); + goto onerror; + } + + var = ast_value_new(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT); + vec_push(values, var); + var->cvq = CV_CONST; + var->hasvalue = true; + var->constval.vfloat = num++; + + parser_addglobal(parser, var->name, (ast_expression*)var); + + if (!parser_next(parser)) { + parseerror(parser, "expected `=`, `}` or comma after identifier"); + goto onerror; + } + + if (parser->tok == ',') + continue; + if (parser->tok == '}') + break; + if (parser->tok != '=') { + parseerror(parser, "expected `=`, `}` or comma after identifier"); + goto onerror; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected expression after `=`"); + goto onerror; + } + + /* We got a value! */ + old = parse_expression_leave(parser, true, false, false); + asvalue = (ast_value*)old; + if (!ast_istype(old, ast_value) || asvalue->cvq != CV_CONST || !asvalue->hasvalue) { + compile_error(ast_ctx(var), "constant value or expression expected"); + goto onerror; + } + num = (var->constval.vfloat = asvalue->constval.vfloat) + 1; + + if (parser->tok == '}') + break; + if (parser->tok != ',') { + parseerror(parser, "expected `}` or comma after expression"); + goto onerror; + } + } + + if (parser->tok != '}') { + parseerror(parser, "internal error: breaking without `}`"); + goto onerror; + } + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after enumeration"); + goto onerror; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error after enumeration"); + goto onerror; + } + + vec_free(values); + return true; + +onerror: + vec_free(values); + return false; +} + static bool parse_block_into(parser_t *parser, ast_block *block) { bool retval = true; @@ -3584,8 +3716,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) return false; } - vec_push(parser->globals, (ast_expression*)thinkfunc); - util_htset(parser->htglobals, thinkfunc->name, thinkfunc); + parser_addglobal(parser, thinkfunc->name, (ast_expression*)thinkfunc); nextthink = (ast_expression*)thinkfunc; @@ -4802,12 +4933,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } else { - vec_push(parser->globals, (ast_expression*)var); - util_htset(parser->htglobals, var->name, var); + parser_addglobal(parser, var->name, (ast_expression*)var); if (isvector) { for (i = 0; i < 3; ++i) { - vec_push(parser->globals, (ast_expression*)me[i]); - util_htset(parser->htglobals, me[i]->name, me[i]); + parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]); } } } @@ -5164,6 +5293,10 @@ static bool parser_global_statement(parser_t *parser) return false; return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring); } + else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum")) + { + return parse_enum(parser); + } else if (parser->tok == TOKEN_KEYWORD) { if (!strcmp(parser_tokval(parser), "typedef")) { @@ -5301,12 +5434,30 @@ bool parser_init() vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); vec_push(parser->_blocktypedefs, 0); + /* corrector */ + vec_push(parser->correct_variables, correct_trie_new()); + vec_push(parser->correct_variables_score, NULL); + empty_ctx.file = ""; empty_ctx.line = 0; parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); parser->nil->cvq = CV_CONST; if (OPTS_FLAG(UNTYPED_NIL)) util_htset(parser->htglobals, "nil", (void*)parser->nil); + + parser->const_vec[0] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); + 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) { + parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING); + parser->reserved_version->cvq = CV_CONST; + parser->reserved_version->hasvalue = true; + parser->reserved_version->expression.flags |= AST_FLAG_INCLUDE_DEF; + parser->reserved_version->constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING); + } else { + parser->reserved_version = NULL; + } return true; } @@ -5406,9 +5557,6 @@ void parser_cleanup() for (i = 0; i < vec_size(parser->correct_variables); ++i) { correct_del(parser->correct_variables[i], parser->correct_variables_score[i]); } - for (i = 0; i < vec_size(parser->correct_variables_score); ++i) { - vec_free(parser->correct_variables_score[i]); - } vec_free(parser->correct_variables); vec_free(parser->correct_variables_score); @@ -5430,6 +5578,10 @@ void parser_cleanup() ast_value_delete(parser->nil); + ast_value_delete(parser->const_vec[0]); + ast_value_delete(parser->const_vec[1]); + ast_value_delete(parser->const_vec[2]); + mem_d(parser); } @@ -5491,6 +5643,13 @@ bool parser_finish(const char *output) return false; } } + if (parser->reserved_version && + !ast_global_codegen(parser->reserved_version, ir, false)) + { + con_out("failed to generate reserved::version"); + ir_builder_delete(ir); + return false; + } for (i = 0; i < vec_size(parser->imm_float); ++i) { if (!ast_global_codegen(parser->imm_float[i], ir, false)) { con_out("failed to generate global %s\n", parser->imm_float[i]->name);