X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=f53116180c5679591dd2bc7ed52d7484367e9852;hb=4d4851e17903a5e8b66a2eef027354594e391559;hp=f3bfdfb6ff23e72b1fd674aaaab8e4ef96030472;hpb=c3cc6f184e6bab86402289609ddd57ce733a7812;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index f3bfdfb..f531161 100644 --- a/parser.c +++ b/parser.c @@ -687,12 +687,44 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) case opid2('<','<'): case opid2('>','>'): + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform shift between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + + if (!(out = fold_op(parser->fold, op, exprs))) { + ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift"); + ast_call *call = ast_call_new(parser_ctx(parser), shift); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + out = (ast_expression*)call; + } + break; + case opid3('<','<','='): case opid3('>','>','='): - if(!(out = fold_op(parser->fold, op, exprs))) { - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts"); + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform shift operation between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); return false; } + + if(!(out = fold_op(parser->fold, op, exprs))) { + ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift"); + ast_call *call = ast_call_new(parser_ctx(parser), shift); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + out = (ast_expression*)ast_store_new( + parser_ctx(parser), + INSTR_STORE_F, + exprs[0], + (ast_expression*)call + ); + } + break; case opid2('|','|'): @@ -2830,6 +2862,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "final")) { + flags |= AST_FLAG_FINAL_DECL; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`final` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { flags |= AST_FLAG_ALIAS; *message = NULL; @@ -2934,6 +2974,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * had_var = true; else if (!strcmp(parser_tokval(parser), "noref")) had_noref = true; + else if (!strcmp(parser_tokval(parser), "final")) + flags |= AST_FLAG_FINAL_DECL; else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) { return false; } @@ -5257,6 +5299,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + if (old->flags & AST_FLAG_FINAL_DECL) { + parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } proto = (ast_value*)old; if (!ast_istype(old, ast_value)) { parseerror(parser, "internal error: not an ast_value"); @@ -5270,6 +5318,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto->expression.flags |= var->expression.flags; + /* copy the context for finals, + * so the error can show where it was actually made 'final' + */ + if (proto->expression.flags & AST_FLAG_FINAL_DECL) + ast_ctx(old) = ast_ctx(var); ast_delete(var); var = proto; }