X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=eeec52e01ea9ce0cd345135b73f25cedf2bdc469;hb=62ac7e99668bdc60fc7d292769673c40b9160e79;hp=ef206557b0a384a1dd5b69d47e91e78adf68f3c6;hpb=ceb79f1897fabed29583f23dd54cbef0e7897217;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index ef20655..eeec52e 100644 --- a/parser.c +++ b/parser.c @@ -282,6 +282,29 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } +static bool check_write_to(lex_ctx_t ctx, ast_expression *expr) +{ + if (ast_istype(expr, ast_value)) { + ast_value *val = (ast_value*)expr; + if (val->cvq == CV_CONST) { + if (val->name[0] == '#') { + compile_error(ctx, "invalid assignment to a literal constant"); + return false; + } + /* + * To work around quakeworld we must elide the error and make it + * a warning instead. + */ + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) + compile_error(ctx, "assignment to constant `%s`", val->name); + else + (void)!compile_warning(ctx, WARN_CONST_OVERWRITE, "assignment to constant `%s`", val->name); + return false; + } + } + return true; +} + static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { const oper_info *op; @@ -289,7 +312,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ast_expression *out = NULL; ast_expression *exprs[3]; ast_block *blocks[3]; - ast_value *asvalue[3]; ast_binstore *asbinstore; size_t i, assignop, addop, subop; qcint_t generated_op = 0; @@ -311,8 +333,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ctx = vec_last(sy->ops).ctx; if (vec_size(sy->out) < op->operands) { - compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out), - op->op, (int)op->id); + if (op->flags & OP_PREFIX) + compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id); + else /* this should have errored previously already */ + compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id); return false; } @@ -326,7 +350,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) for (i = 0; i < op->operands; ++i) { exprs[i] = sy->out[vec_size(sy->out)+i].out; blocks[i] = sy->out[vec_size(sy->out)+i].block; - asvalue[i] = (ast_value*)exprs[i]; if (exprs[i]->vtype == TYPE_NOEXPR && !(i != 0 && op->id == opid2('?',':')) && @@ -442,24 +465,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = exprs[0]; break; case opid2('-','P'): - if (!(out = fold_op(parser->fold, op, exprs))) { - switch (exprs[0]->vtype) { - case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, - (ast_expression*)parser->fold->imm_float[0], - exprs[0]); - break; - case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, - (ast_expression*)parser->fold->imm_vector[0], - exprs[0]); - break; - default: - compile_error(ctx, "invalid types used in expression: cannot negate type %s", + if ((out = fold_op(parser->fold, op, exprs))) + break; + if (exprs[0]->vtype != TYPE_FLOAT && + exprs[0]->vtype != TYPE_VECTOR) { + compile_error(ctx, "invalid types used in unary expression: cannot negate type %s", type_name[exprs[0]->vtype]); - return false; - } + return false; } + out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]); break; case opid2('!','P'): @@ -747,6 +761,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } break; + case opid2('>', '<'): + if (NotSameType(TYPE_VECTOR)) { + 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 cross product: %s and %s", + ty1, ty2); + return false; + } + + if (!(out = fold_op(parser->fold, op, exprs))) { + out = (ast_expression*)ast_binary_new( + parser_ctx(parser), + VINSTR_CROSS, + exprs[0], + exprs[1] + ); + } + + break; + case opid3('<','=','>'): /* -1, 0, or 1 */ if (NotSameType(TYPE_FLOAT)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -879,9 +913,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); break; case opid3('+','+','P'): @@ -896,9 +928,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) addop = INSTR_ADD_F; else addop = INSTR_SUB_F; - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ast_ctx(exprs[0]), exprs[0]); if (ast_istype(exprs[0], ast_entfield)) { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, exprs[0], @@ -924,9 +954,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) addop = INSTR_SUB_F; subop = INSTR_ADD_F; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ast_ctx(exprs[0]), exprs[0]); if (ast_istype(exprs[0], ast_entfield)) { out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop, exprs[0], @@ -953,9 +981,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else @@ -990,9 +1016,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else @@ -1036,9 +1060,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ty1, ty2); return false; } - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (ast_istype(exprs[0], ast_entfield)) assignop = type_storep_instr[exprs[0]->vtype]; else @@ -1074,9 +1096,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]); if (!out) return false; - if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) { - compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name); - } + (void)check_write_to(ctx, exprs[0]); if (exprs[0]->vtype == TYPE_FLOAT) asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out); else @@ -1629,7 +1649,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma /* 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 */ - bool warn_truthvalue = true; + bool warn_parenthesis = true; /* count the parens because an if starts with one, so the * end of a condition is an unmatched closing paren @@ -1664,7 +1684,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } } if (o == operator_count) { - compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser)); + compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser)); goto onerr; } /* found an operator */ @@ -1692,6 +1712,17 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) olast = &operators[vec_last(sy.ops).etype-1]; + /* first only apply higher precedences, assoc_left+equal comes after we warn about precedence rules */ + while (olast && op->prec < olast->prec) + { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + if (vec_size(sy.ops) && !vec_last(sy.ops).isparen) + olast = &operators[vec_last(sy.ops).etype-1]; + else + olast = NULL; + } + #define IsAssignOp(x) (\ (x) == opid1('=') || \ (x) == opid2('+','=') || \ @@ -1703,14 +1734,29 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma (x) == opid2('|','=') || \ (x) == opid3('&','~','=') \ ) - if (warn_truthvalue) { + if (warn_parenthesis) { if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) || (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) || (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id)) ) { (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value"); - warn_truthvalue = false; + warn_parenthesis = false; + } + + if (olast && olast->id != op->id) { + if ((op->id == opid1('&') || op->id == opid1('|') || op->id == opid1('^')) && + (olast->id == opid1('&') || olast->id == opid1('|') || olast->id == opid1('^'))) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around bitwise operations"); + warn_parenthesis = false; + } + else if ((op->id == opid2('&','&') || op->id == opid2('|','|')) && + (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) + { + (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around logical operations"); + warn_parenthesis = false; + } } } @@ -1872,7 +1918,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma parser->lex->flags.noops = true; if (vec_size(sy.out) != 1) { - parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out)); + parseerror(parser, "expression expected"); expr = NULL; } else expr = sy.out[0].out; @@ -2738,7 +2784,15 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * else if (!strcmp(parser_tokval(parser), "inline")) { flags |= AST_FLAG_INLINE; if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + parseerror(parser, "`inline` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "eraseable")) { + flags |= AST_FLAG_ERASEABLE; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`"); *cvq = CV_WRONG; return false; } @@ -3236,7 +3290,13 @@ static bool parse_pragma_do(parser_t *parser) else { (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); - return false; + + /* skip to eol */ + while (!parse_eol(parser)) { + parser_next(parser); + } + + return true; } return true; @@ -3274,7 +3334,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); - if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { /* local variable */ if (!block) { @@ -4623,6 +4683,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) * The base type makes up every bit of type information which comes *before* the * variable name. * + * NOTE: The value must either be named, have a NULL name, or a name starting + * with '<'. In the first case, this will be the actual variable or type + * name, in the other cases it is assumed that the name will appear + * later, and an error is generated otherwise. + * * The following will be parsed in its entirety: * void() foo() * The 'basetype' in this case is 'void()' @@ -4643,8 +4708,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va ctx = parser_ctx(parser); /* types may start with a dot */ - if (parser->tok == '.') { + if (parser->tok == '.' || parser->tok == TOKEN_DOTS) { isfield = true; + if (parser->tok == TOKEN_DOTS) + morefields += 2; /* if we parsed a dot we need a typename now */ if (!parser_next(parser)) { parseerror(parser, "expected typename for field definition"); @@ -4654,8 +4721,13 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va /* Further dots are handled seperately because they won't be part of the * basetype */ - while (parser->tok == '.') { - ++morefields; + while (true) { + if (parser->tok == '.') + ++morefields; + else if (parser->tok == TOKEN_DOTS) + morefields += 3; + else + break; if (!parser_next(parser)) { parseerror(parser, "expected typename for field definition"); return NULL; @@ -4783,6 +4855,13 @@ static bool parse_typedef(parser_t *parser) if (!typevar) return false; + /* while parsing types, the ast_value's get named '' */ + if (!typevar->name || typevar->name[0] == '<') { + parseerror(parser, "missing name in typedef"); + ast_delete(typevar); + return false; + } + if ( (old = parser_find_var(parser, typevar->name)) ) { parseerror(parser, "cannot define a type with the same name as a variable: %s\n" " -> `%s` has been declared here: %s:%i", @@ -4948,6 +5027,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield return false; } + /* while parsing types, the ast_value's get named '' */ + if (!var->name || var->name[0] == '<') { + parseerror(parser, "declaration does not declare anything"); + if (basetype) + ast_delete(basetype); + return false; + } + while (true) { proto = NULL; wasarray = false; @@ -5146,6 +5233,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + /* doing this here as the above is just for a single scope */ old = parser_find_local(parser, var->name, 0, &isparam); if (old && isparam) { if (parsewarning(parser, WARN_LOCAL_SHADOWS, @@ -5159,7 +5247,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); if (ast_istype(old, ast_value)) - var = (ast_value*)old; + var = proto = (ast_value*)old; else { var = NULL; goto skipvar; @@ -5218,7 +5306,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield return false; } - if (var->expression.vtype != find->vtype) { + if (!ast_compare_type((ast_expression*)var, find)) { char ty1[1024]; char ty2[1024]; @@ -5685,7 +5773,7 @@ static bool parser_global_statement(parser_t *parser) if (parser->tok == TOKEN_IDENT) istype = parser_find_typedef(parser, parser_tokval(parser), 0); - if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') + if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS) { return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL); }