X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=6216997879e461e4c759cb17ec2959b9e362f5aa;hp=7ba3782e3b8d8d445b5667a28b5ddf94fc0b9497;hb=fe296de42f96780b4a3c7c2647775ec3d8a5e59d;hpb=5694c77d160eba94e11af74cc429efee0839a5c2 diff --git a/parser.c b/parser.c index 7ba3782..6216997 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; @@ -1035,48 +1032,126 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid1('^'): /* - * ^ can be implemented as: - * (LHS | RHS) & ~(LHS & RHS) - * to implement ~ we need to use -1-X, as you can see the - * whole process ends up becoming: - * (LHS | RHS) & (-1 - (LHS & RHS)) + * 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 (NotSameType(TYPE_FLOAT)) { + 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(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( + /* + * 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_BITAND, + INSTR_SUB_F, + (ast_expression*)parser_const_float_neg1(parser), (ast_expression*)ast_binary_new( ctx, - INSTR_BITOR, + INSTR_BITAND, exprs[0], exprs[1] - ), - (ast_expression*)expr + ) ); + 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('<','<'): @@ -1732,6 +1807,8 @@ static bool parser_close_paren(parser_t *parser, shunt *sy) static void parser_reclassify_token(parser_t *parser) { size_t i; + if (parser->tok >= TOKEN_START) + return; for (i = 0; i < operator_count; ++i) { if (!strcmp(parser_tokval(parser), operators[i].op)) { parser->tok = TOKEN_OPERATOR; @@ -3545,7 +3622,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; } @@ -5807,8 +5885,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)) { @@ -6124,7 +6210,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; @@ -6178,8 +6264,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() @@ -6194,11 +6279,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; @@ -6224,8 +6304,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)) @@ -6353,7 +6434,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); @@ -6374,6 +6454,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); @@ -6382,8 +6465,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); } @@ -6541,7 +6622,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) { @@ -6563,7 +6645,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;