X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=f9793471f94f93bc4314c3be5f9d3ea88dd221a8;hp=cbae8d3cc68aaef87fa7ea14c42d202b379a0c56;hb=063c50fce450693f0098cbc4a28efd109cbed01b;hpb=f8db5a7c6d0ba2e5e9624e1de46bb2da9572bb32 diff --git a/parser.c b/parser.c index cbae8d3..f979347 100644 --- a/parser.c +++ b/parser.c @@ -104,9 +104,6 @@ typedef struct parser_s { /* collected information */ size_t max_param_count; - - /* code generator */ - code_t *code; } parser_t; static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; @@ -1034,8 +1031,128 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) exprs[0], exprs[1]); break; case opid1('^'): - compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^"); - return false; + /* + * Okay lets designate what the hell is an acceptable use + * of the ^ operator. In many vector processing units, XOR + * is allowed to be used on vectors, but only if the first + * operand is a vector, the second operand can be a float + * or vector. It's never legal for the first operand to be + * a float, and then the following operand to be a vector. + * Further more, the only time it is legal to do XOR otherwise + * is when both operand are floats. This nicely crafted if + * statement catches them all. + * + * In the event that the first operand is a vector, two + * possible situations can arise, thus, each element of + * vector A (operand A) is exclusive-ORed with the corresponding + * element of vector B (operand B), If B is scalar, the + * scalar value is first replicated for each element. + * + * The QCVM itself lacks a BITXOR instruction. Thus emulating + * the mathematics of it is required. The following equation + * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the + * QCVM also lacking a BITNEG instruction, we need to emulate + * ~FOO with -1 - FOO, the whole process becoming this nicely + * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)). + * + * When A is not scalar, this process is repeated for all + * components of vector A with the value in operand B, + * only if operand B is scalar. When A is not scalar, and B + * is also not scalar, this process is repeated for all + * components of the vector A with the components of vector B. + * Finally when A is scalar and B is scalar, this process is + * simply used once for A and B being LHS and RHS respectfully. + * + * Yes the semantics are a bit strange (no pun intended). + * But then again BITXOR is strange itself, consdering it's + * commutative, assocative, and elements of the BITXOR operation + * are their own inverse. + */ + if ( !(exprs[0]->vtype == TYPE_FLOAT && exprs[1]->vtype == TYPE_FLOAT) && + !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) && + !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR)) + { + compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s", + type_name[exprs[0]->vtype], + type_name[exprs[1]->vtype]); + return false; + } + + /* + * IF the first expression is float, the following will be too + * since scalar ^ vector is not allowed. + */ + if (exprs[0]->vtype == TYPE_FLOAT) { + if(CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1))))); + } else { + ast_binary *expr = ast_binary_new( + ctx, + INSTR_SUB_F, + (ast_expression*)parser_const_float_neg1(parser), + (ast_expression*)ast_binary_new( + ctx, + INSTR_BITAND, + exprs[0], + exprs[1] + ) + ); + expr->refs = AST_REF_NONE; + + out = (ast_expression*) + ast_binary_new( + ctx, + INSTR_BITAND, + (ast_expression*)ast_binary_new( + ctx, + INSTR_BITOR, + exprs[0], + exprs[1] + ), + (ast_expression*)expr + ); + } + } else { + /* + * The first is a vector: vector is allowed to xor with vector and + * with scalar, branch here for the second operand. + */ + if (exprs[1]->vtype == TYPE_VECTOR) { + /* + * Xor all the values of the vector components against the + * vectors components in question. + */ + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_vector_f( + parser, + (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))), + (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))), + (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z))) + ); + } else { + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector"); + return false; + } + } else { + /* + * Xor all the values of the vector components against the + * scalar in question. + */ + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_vector_f( + parser, + (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))), + (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))), + (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1)))) + ); + } else { + compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float"); + return false; + } + } + } + + break; case opid2('<','<'): case opid2('>','>'): @@ -1162,7 +1279,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) } else { ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]); - eq->refs = (ast_binary_ref)false; /* references nothing */ + eq->refs = AST_REF_NONE; /* if (lt) { */ out = (ast_expression*)ast_ternary_new(ctx, @@ -1568,7 +1685,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) for (i = 0; i < paramcount; ++i) vec_push(call->params, sy->out[fid+1 + i].out); vec_shrinkby(sy->out, paramcount); - (void)!ast_call_check_types(call); + (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam); if (parser->max_param_count < paramcount) parser->max_param_count = paramcount; @@ -1705,8 +1822,12 @@ static ast_expression* parse_vararg_do(parser_t *parser) ast_expression *idx, *out; ast_value *typevar; ast_value *funtype = parser->function->vtype; + lex_ctx ctx = parser_ctx(parser); - lex_ctx ctx = parser_ctx(parser); + if (!parser->function->varargs) { + parseerror(parser, "function has no variable argument list"); + return NULL; + } if (!parser_next(parser) || parser->tok != '(') { parseerror(parser, "expected parameter index and type in parenthesis"); @@ -1722,9 +1843,14 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; if (parser->tok != ',') { - ast_unref(idx); - parseerror(parser, "expected comma after parameter index"); - return NULL; + if (parser->tok != ')') { + ast_unref(idx); + parseerror(parser, "expected comma after parameter index"); + return NULL; + } + /* vararg piping: ...(start) */ + out = (ast_expression*)ast_argpipe_new(ctx, idx); + return out; } if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) { @@ -1746,22 +1872,6 @@ static ast_expression* parse_vararg_do(parser_t *parser) return NULL; } -#if 0 - if (!parser_next(parser)) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "parse error after vararg"); - return NULL; - } -#endif - - if (!parser->function->varargs) { - ast_unref(idx); - ast_delete(typevar); - parseerror(parser, "function has no variable argument list"); - return NULL; - } - if (funtype->expression.varparam && !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam)) { @@ -1927,7 +2037,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * it in the predef table. And diagnose it better :) */ if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) { - parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); + parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); return false; } @@ -1946,7 +2056,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser)); if (strcmp(correct, parser_tokval(parser))) { break; - } else if (correct) { + } else { mem_d(correct); correct = NULL; } @@ -1954,12 +2064,12 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) correct_free(&corr); if (correct) { - parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct); + parseerror(parser, "unexpected identifier: %s (did you mean %s?)", parser_tokval(parser), correct); mem_d(correct); return false; } } - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + parseerror(parser, "unexpected identifier: %s", parser_tokval(parser)); return false; } } @@ -2006,7 +2116,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma while (true) { if (parser->tok == TOKEN_TYPENAME) { - parseerror(parser, "unexpected typename"); + parseerror(parser, "unexpected typename `%s`", parser_tokval(parser)); goto onerr; } @@ -2404,7 +2514,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, } unary = (ast_unary*)cond; - while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) + /* ast_istype dereferences cond, should test here for safety */ + while (cond && ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) { cond = unary->operand; unary->operand = NULL; @@ -2644,7 +2755,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { parseerror(parser, "internal error: label stack corrupted"); rv = false; - ast_delete(*out); + /* + * Test for NULL otherwise ast_delete dereferences null pointer + * and boom. + */ + if (*out) + ast_delete(*out); *out = NULL; } else { @@ -3512,7 +3628,8 @@ static bool parse_goto(parser_t *parser, ast_expression **out) if (!(expression = parse_expression(parser, false, true)) || !(*out = parse_goto_computed(parser, &expression))) { parseerror(parser, "invalid goto expression"); - ast_unref(expression); + if(expression) + ast_unref(expression); return false; } @@ -4341,7 +4458,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } vec_push(func->blocks, block); - + parser->function = old; if (!parser_leaveblock(parser)) @@ -4919,32 +5036,44 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var) return NULL; } - cexp = parse_expression_leave(parser, true, false, false); + if (parser->tok != ']') { + cexp = parse_expression_leave(parser, true, false, false); - if (!cexp || !ast_istype(cexp, ast_value)) { - if (cexp) - ast_unref(cexp); - ast_delete(var); - parseerror(parser, "expected array-size as constant positive integer"); - return NULL; + if (!cexp || !ast_istype(cexp, ast_value)) { + if (cexp) + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "expected array-size as constant positive integer"); + return NULL; + } + cval = (ast_value*)cexp; + } + else { + cexp = NULL; + cval = NULL; } - cval = (ast_value*)cexp; tmp = ast_value_new(ctx, "", TYPE_ARRAY); tmp->expression.next = (ast_expression*)var; var = tmp; - if (cval->expression.vtype == TYPE_INTEGER) - tmp->expression.count = cval->constval.vint; - else if (cval->expression.vtype == TYPE_FLOAT) - tmp->expression.count = cval->constval.vfloat; - else { + if (cval) { + if (cval->expression.vtype == TYPE_INTEGER) + tmp->expression.count = cval->constval.vint; + else if (cval->expression.vtype == TYPE_FLOAT) + tmp->expression.count = cval->constval.vfloat; + else { + ast_unref(cexp); + ast_delete(var); + parseerror(parser, "array-size must be a positive integer constant"); + return NULL; + } + ast_unref(cexp); - ast_delete(var); - parseerror(parser, "array-size must be a positive integer constant"); - return NULL; + } else { + var->expression.count = -1; + var->expression.flags |= AST_FLAG_ARRAY_INIT; } - ast_unref(cexp); if (parser->tok != ']') { ast_delete(var); @@ -5194,6 +5323,75 @@ static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, cons return true; } +static bool create_array_accessors(parser_t *parser, ast_value *var) +{ + char name[1024]; + util_snprintf(name, sizeof(name), "%s##SET", var->name); + if (!parser_create_array_setter(parser, var, name)) + return false; + util_snprintf(name, sizeof(name), "%s##GET", var->name); + if (!parser_create_array_getter(parser, var, var->expression.next, name)) + return false; + return true; +} + +static bool parse_array(parser_t *parser, ast_value *array) +{ + size_t i; + if (array->initlist) { + parseerror(parser, "array already initialized elsewhere"); + return false; + } + if (!parser_next(parser)) { + parseerror(parser, "parse error in array initializer"); + return false; + } + i = 0; + while (parser->tok != '}') { + ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false); + if (!v) + return false; + if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) { + ast_unref(v); + parseerror(parser, "initializing element must be a compile time constant"); + return false; + } + vec_push(array->initlist, v->constval); + if (v->expression.vtype == TYPE_STRING) { + array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring); + ++i; + } + ast_unref(v); + if (parser->tok == '}') + break; + if (parser->tok != ',' || !parser_next(parser)) { + parseerror(parser, "expected comma or '}' in element list"); + return false; + } + } + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after initializer, got %s"); + return false; + } + /* + if (!parser_next(parser)) { + parseerror(parser, "parse error after initializer"); + return false; + } + */ + + if (array->expression.flags & AST_FLAG_ARRAY_INIT) { + if (array->expression.count != (size_t)-1) { + parseerror(parser, "array `%s' has already been initialized with %u elements", + array->name, (unsigned)array->expression.count); + } + array->expression.count = vec_size(array->initlist); + if (!create_array_accessors(parser, array)) + return false; + } + return true; +} + 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) { ast_value *var; @@ -5627,13 +5825,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield * deal with arrays */ if (var->expression.vtype == TYPE_ARRAY) { - char name[1024]; - util_snprintf(name, sizeof(name), "%s##SET", var->name); - if (!parser_create_array_setter(parser, var, name)) - goto cleanup; - util_snprintf(name, sizeof(name), "%s##GET", var->name); - if (!parser_create_array_getter(parser, var, var->expression.next, name)) - goto cleanup; + if (var->expression.count != (size_t)-1) { + if (!create_array_accessors(parser, var)) + goto cleanup; + } } else if (!localblock && !nofields && var->expression.vtype == TYPE_FIELD && @@ -5696,8 +5891,16 @@ skipvar: if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) { if (parser->tok != '=') { - parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); - break; + if (!strcmp(parser_tokval(parser), "break")) { + if (!parser_next(parser)) { + parseerror(parser, "error parsing break definition"); + break; + } + (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)"); + } else { + parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); + break; + } } if (!parser_next(parser)) { @@ -5805,11 +6008,10 @@ skipvar: parseerror(parser, "TODO: initializers for local arrays"); break; } - /* -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); -*/ - parseerror(parser, "TODO: initializing global arrays is not supported yet!"); - break; + + var->hasvalue = true; + if (!parse_array(parser, var)) + break; } else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '[')) { @@ -6014,7 +6216,7 @@ static uint16_t progdefs_crc_both(uint16_t old, const char *str) return old; } -static void generate_checksum(parser_t *parser) +static void generate_checksum(parser_t *parser, ir_builder *ir) { uint16_t crc = 0xFFFF; size_t i; @@ -6068,8 +6270,7 @@ static void generate_checksum(parser_t *parser) crc = progdefs_crc_both(crc, ";\n"); } crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); - - parser->code->crc = crc; + ir->code->crc = crc; } parser_t *parser_create() @@ -6084,11 +6285,6 @@ parser_t *parser_create() memset(parser, 0, sizeof(*parser)); - if (!(parser->code = code_init())) { - mem_d(parser); - return NULL; - } - for (i = 0; i < operator_count; ++i) { if (operators[i].id == opid1('=')) { parser->assign_op = operators+i; @@ -6114,8 +6310,9 @@ parser_t *parser_create() vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); - empty_ctx.file = ""; - empty_ctx.line = 0; + empty_ctx.file = ""; + empty_ctx.line = 0; + empty_ctx.column = 0; parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); parser->nil->cvq = CV_CONST; if (OPTS_FLAG(UNTYPED_NIL)) @@ -6151,7 +6348,7 @@ static bool parser_compile(parser_t *parser) { if (!parser_global_statement(parser)) { if (parser->tok == TOKEN_EOF) - parseerror(parser, "unexpected eof"); + parseerror(parser, "unexpected end of file"); else if (compile_errors) parseerror(parser, "there have been errors, bailing out"); lex_close(parser->lex); @@ -6243,7 +6440,6 @@ static void parser_remove_ast(parser_t *parser) vec_free(parser->correct_variables); vec_free(parser->correct_variables_score); - for (i = 0; i < vec_size(parser->_typedefs); ++i) ast_delete(parser->_typedefs[i]); vec_free(parser->_typedefs); @@ -6264,6 +6460,9 @@ static void parser_remove_ast(parser_t *parser) ast_value_delete(parser->const_vec[0]); ast_value_delete(parser->const_vec[1]); ast_value_delete(parser->const_vec[2]); + + if (parser->reserved_version) + ast_value_delete(parser->reserved_version); util_htdel(parser->aliases); intrin_intrinsics_destroy(parser); @@ -6272,8 +6471,6 @@ static void parser_remove_ast(parser_t *parser) void parser_cleanup(parser_t *parser) { parser_remove_ast(parser); - code_cleanup(parser->code); - mem_d(parser); } @@ -6431,7 +6628,8 @@ bool parser_finish(parser_t *parser, const char *output) } } - generate_checksum(parser); + generate_checksum(parser, ir); + if (OPTS_OPTION_BOOL(OPTION_DUMP)) ir_builder_dump(ir, con_out); for (i = 0; i < vec_size(parser->functions); ++i) { @@ -6453,7 +6651,7 @@ bool parser_finish(parser_t *parser, const char *output) if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) ir_builder_dump(ir, con_out); - if (!ir_builder_generate(parser->code, ir, output)) { + if (!ir_builder_generate(ir, output)) { con_out("*** failed to generate output file\n"); ir_builder_delete(ir); return false;