X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=e38cedae42fe4624d009ab404a42c2bc13d77cb2;hb=88cfa53dc27c1f6bd4d0d102b8010ad401b69904;hp=6ba90cd50a4ec0280dfe83bf005e29930d7d81a7;hpb=6b0a522bb9ca83bc1a809a06f78f5bb296d7d47f;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index 6ba90cd..e38ceda 100644 --- a/parser.c +++ b/parser.c @@ -88,18 +88,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; @@ -556,7 +553,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"); @@ -589,7 +587,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; @@ -597,7 +609,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 { @@ -929,14 +941,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('|','|'): @@ -1512,7 +1531,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 */ @@ -1533,11 +1551,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), "_")) { @@ -1573,32 +1586,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) @@ -1644,8 +1652,11 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma * other things as well. */ if (OPTS_FLAG(ENHANCED_DIAGNOSTICS)) { + correction_t corr; + correct_init(&corr); + for (i = 0; i < vec_size(parser->correct_variables); i++) { - correct = correct_str(parser->correct_variables[i], parser_tokval(parser)); + correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); if (strcmp(correct, parser_tokval(parser))) { break; } else if (correct) { @@ -1653,6 +1664,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma correct = NULL; } } + correct_free(&corr); if (correct) { parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); @@ -1875,24 +1887,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); @@ -1946,7 +1940,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; } @@ -1985,8 +1979,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; @@ -3424,6 +3423,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; @@ -5193,6 +5292,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")) { @@ -5340,6 +5443,10 @@ bool parser_init() 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); return true; } @@ -5460,6 +5567,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); }