/* collected information */
size_t max_param_count;
-
- /* code generator */
- code_t *code;
} parser_t;
static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
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.
*/
- #define TRY_TYPE(I) \
- if (exprs[(I)]->vtype != TYPE_FLOAT) { \
- ast_type_to_string(exprs[(I)], ty1, sizeof(ty1)); \
- compile_error ( \
- ast_ctx(exprs[(I)]), \
- "invalid type `%s` for bit-xor in %s", \
- ty1, \
- ((I) == 0) \
- ? "left-hand-side of expression" \
- : "right-hand-side of expression" \
- ); \
- return false; \
- }
- TRY_TYPE(0)
- TRY_TYPE(1)
- #undef TRY_TYPE
+ 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 {
- 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*)ast_binary_new(
+ )
+ );
+ expr->refs = AST_REF_NONE;
+
+ out = (ast_expression*)
+ ast_binary_new(
ctx,
- INSTR_SUB_F,
- (ast_expression*)parser_const_float_neg1(parser),
+ INSTR_BITAND,
(ast_expression*)ast_binary_new(
ctx,
- INSTR_BITAND,
+ 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('<','<'):
} 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,
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;
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;
}
}
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;
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 {
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;
}
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)) {
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;
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()
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;
vec_push(parser->correct_variables, correct_trie_new());
vec_push(parser->correct_variables_score, NULL);
- empty_ctx.file = "<internal>";
- empty_ctx.line = 0;
+ empty_ctx.file = "<internal>";
+ 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))
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);
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);
void parser_cleanup(parser_t *parser)
{
parser_remove_ast(parser);
- code_cleanup(parser->code);
-
mem_d(parser);
}
}
}
- 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) {
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;