#define PARSER_HT_SIZE 512
#define TYPEDEF_HT_SIZE 512
-static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
-
static void parser_enterblock(parser_t *parser);
static bool parser_leaveblock(parser_t *parser);
static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
return NULL;
}
-static ast_expression* parser_find_global(parser_t *parser, const char *name)
+ast_expression* parser_find_global(parser_t *parser, const char *name)
{
ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser));
if (var)
return NULL;
}
-/* include intrinsics */
-#include "intrin.h"
-
typedef struct
{
size_t etype; /* 0 = expression, others are operators */
#define NotSameType(T) \
(exprs[0]->vtype != exprs[1]->vtype || \
exprs[0]->vtype != T)
-
- /* preform any constant folding on operator usage first */
- /*if ((out = fold_op(parser->fold, op, exprs)))*/
- /*goto complete;*/
-
switch (op->id)
{
default:
return false;
} else if (!(out = fold_op(parser->fold, op, exprs))) {
/* generate a call to __builtin_mod */
- ast_expression *mod = intrin_func(parser, "mod");
+ ast_expression *mod = intrin_func(parser->intrin, "mod");
ast_call *call = NULL;
if (!mod) return false; /* can return null for missing floor */
case opid1('|'):
case opid1('&'):
- if (NotSameType(TYPE_FLOAT)) {
- 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 (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx,
- (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
- exprs[0], exprs[1]);
- break;
case opid1('^'):
- /*
- * 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))
* since scalar ^ vector is not allowed.
*/
if (exprs[0]->vtype == TYPE_FLOAT) {
- ast_binary *expr = ast_binary_new(
- ctx,
- INSTR_SUB_F,
- (ast_expression*)parser->fold->imm_float[2],
- (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
- );
+ out = (ast_expression*)ast_binary_new(ctx,
+ (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
+ exprs[0], exprs[1]);
} else {
/*
- * The first is a vector: vector is allowed to xor with vector and
+ * The first is a vector: vector is allowed to bitop 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
+ * Bitop all the values of the vector components against the
* vectors components in question.
*/
- compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
- return false;
+ out = (ast_expression*)ast_binary_new(ctx,
+ (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
+ exprs[0], exprs[1]);
} else {
- compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
- return false;
+ out = (ast_expression*)ast_binary_new(ctx,
+ (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
+ exprs[0], exprs[1]);
}
}
}
compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
return false;
}
+ break;
case opid2('|','|'):
generated_op += 1; /* INSTR_OR */
ty1, ty2);
return false;
}
-
+
if (!(out = fold_op(parser->fold, op, exprs))) {
- ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
+ ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser->intrin, "pow"));
vec_push(gencall->params, exprs[0]);
vec_push(gencall->params, exprs[1]);
out = (ast_expression*)gencall;
ty1, ty2);
return false;
- }
+ }
if (!(out = fold_op(parser->fold, op, exprs))) {
ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
break;
case opid2('&','='):
case opid2('|','='):
- if (NotSameType(TYPE_FLOAT)) {
+ case opid2('^','='):
+ if (NotSameType(TYPE_FLOAT) && 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 expression: %s and %s",
assignop = type_storep_instr[exprs[0]->vtype];
else
assignop = type_store_instr[exprs[0]->vtype];
- out = (ast_expression*)ast_binstore_new(ctx, assignop,
- (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
- exprs[0], exprs[1]);
+ if (exprs[0]->vtype == TYPE_FLOAT)
+ out = (ast_expression*)ast_binstore_new(ctx, assignop,
+ (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
+ exprs[0], exprs[1]);
+ else
+ out = (ast_expression*)ast_binstore_new(ctx, assignop,
+ (op->id == opid2('^','=') ? VINSTR_BITXOR_V : op->id == opid2('&','=') ? VINSTR_BITAND_V : VINSTR_BITOR_V),
+ exprs[0], exprs[1]);
break;
case opid3('&','~','='):
/* This is like: a &= ~(b);
* But QC has no bitwise-not, so we implement it as
* a -= a & (b);
*/
- if (NotSameType(TYPE_FLOAT)) {
+ if (NotSameType(TYPE_FLOAT) && 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 expression: %s and %s",
assignop = type_storep_instr[exprs[0]->vtype];
else
assignop = type_store_instr[exprs[0]->vtype];
- out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+ if (exprs[0]->vtype == TYPE_FLOAT)
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+ else
+ 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);
}
- asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+ if (exprs[0]->vtype == TYPE_FLOAT)
+ asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+ else
+ asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_V, exprs[0], out);
asbinstore->keep_dest = true;
out = (ast_expression*)asbinstore;
break;
case opid2('~', 'P'):
- if (exprs[0]->vtype != TYPE_FLOAT) {
+ if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
return false;
}
- if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+ if (!(out = fold_op(parser->fold, op, exprs))) {
+ if (exprs[0]->vtype == TYPE_FLOAT) {
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+ } else {
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+ }
+ }
break;
}
#undef NotSameType
-/*complete:*/
if (!out) {
compile_error(ctx, "failed to apply operator %s", op->op);
return false;
return false;
}
- fun = sy->out[fid].out;
-
- if (fun == intrinsic_debug_typestring) {
+ /*
+ * TODO handle this at the intrinsic level with an ast_intrinsic
+ * node and codegen.
+ */
+ if ((fun = sy->out[fid].out) == intrin_debug_typestring(parser->intrin)) {
char ty[1024];
if (fid+2 != vec_size(sy->out) ||
vec_last(sy->out).block)
vec_shrinkby(sy->out, 1);
return true;
}
-
call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
+
if (!call)
return false;
}
/* not to be exposed */
-extern bool ftepp_predef_exists(const char *name);
-
+bool ftepp_predef_exists(const char *name);
static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
{
if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
/* a_vector.{x,y,z} */
if (!vec_size(sy->ops) ||
!vec_last(sy->ops).etype ||
- operators[vec_last(sy->ops).etype-1].id != opid1('.') ||
- (prev >= intrinsic_debug_typestring &&
- prev <= intrinsic_debug_typestring))
+ operators[vec_last(sy->ops).etype-1].id != opid1('.'))
{
/* When adding more intrinsics, fix the above condition */
prev = NULL;
if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
var = (ast_expression*)fold_constgen_string(parser->fold, parser->function->name, false);
if (!var) {
- /* intrinsics */
- if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
- var = (ast_expression*)intrinsic_debug_typestring;
- }
- /* now we try for the real intrinsic hashtable. If the string
+ /*
+ * now we try for the real intrinsic hashtable. If the string
* begins with __builtin, we simply skip past it, otherwise we
* use the identifier as is.
*/
- else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
- var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
+ if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+ var = intrin_func(parser->intrin, parser_tokval(parser));
}
if (!var) {
self_think = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
time_plus_1 = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
- gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1));
+ gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1f));
if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
if (self_frame) ast_delete(self_frame);
parseerror(parser, "error parsing break definition");
break;
}
- (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+ (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
} else {
parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
break;
parser->reserved_version = NULL;
}
- parser->fold = fold_init(parser);
+ parser->fold = fold_init (parser);
+ parser->intrin = intrin_init(parser);
return parser;
}
ast_value_delete(parser->reserved_version);
util_htdel(parser->aliases);
- intrin_intrinsics_destroy(parser);
fold_cleanup(parser->fold);
+ intrin_cleanup(parser->intrin);
}
void parser_cleanup(parser_t *parser)