*/
#include <string.h>
#include <math.h>
+
#include "parser.h"
#define PARSER_HT_LOCALS 2
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg);
static void parseerror(parser_t *parser, const char *fmt, ...)
{
return true;
}
+static bool check_write_to(lex_ctx_t ctx, ast_expression *expr)
+{
+ if (ast_istype(expr, ast_value)) {
+ ast_value *val = (ast_value*)expr;
+ if (val->cvq == CV_CONST) {
+ if (val->name[0] == '#') {
+ compile_error(ctx, "invalid assignment to a literal constant");
+ return false;
+ }
+ /*
+ * To work around quakeworld we must elide the error and make it
+ * a warning instead.
+ */
+ if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC)
+ compile_error(ctx, "assignment to constant `%s`", val->name);
+ else
+ (void)!compile_warning(ctx, WARN_CONST_OVERWRITE, "assignment to constant `%s`", val->name);
+ return false;
+ }
+ }
+ return true;
+}
+
static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
{
const oper_info *op;
ast_expression *out = NULL;
ast_expression *exprs[3];
ast_block *blocks[3];
- ast_value *asvalue[3];
ast_binstore *asbinstore;
size_t i, assignop, addop, subop;
qcint_t generated_op = 0;
ctx = vec_last(sy->ops).ctx;
if (vec_size(sy->out) < op->operands) {
- compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
- op->op, (int)op->id);
+ if (op->flags & OP_PREFIX)
+ compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id);
+ else /* this should have errored previously already */
+ compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id);
return false;
}
for (i = 0; i < op->operands; ++i) {
exprs[i] = sy->out[vec_size(sy->out)+i].out;
blocks[i] = sy->out[vec_size(sy->out)+i].block;
- asvalue[i] = (ast_value*)exprs[i];
if (exprs[i]->vtype == TYPE_NOEXPR &&
!(i != 0 && op->id == opid2('?',':')) &&
#define NotSameType(T) \
(exprs[0]->vtype != exprs[1]->vtype || \
exprs[0]->vtype != T)
+
switch (op->id)
{
default:
out = exprs[0];
break;
case opid2('-','P'):
- if (!(out = fold_op(parser->fold, op, exprs))) {
- switch (exprs[0]->vtype) {
- case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F,
- (ast_expression*)parser->fold->imm_float[0],
- exprs[0]);
- break;
- case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V,
- (ast_expression*)parser->fold->imm_vector[0],
- exprs[0]);
- break;
- default:
- compile_error(ctx, "invalid types used in expression: cannot negate type %s",
+ if ((out = fold_op(parser->fold, op, exprs)))
+ break;
+
+ if (exprs[0]->vtype != TYPE_FLOAT &&
+ exprs[0]->vtype != TYPE_VECTOR) {
+ compile_error(ctx, "invalid types used in unary expression: cannot negate type %s",
type_name[exprs[0]->vtype]);
- return false;
- }
+ return false;
}
+ out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]);
break;
case opid2('!','P'):
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
if (!(out = fold_op(parser->fold, op, exprs))) {
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
- out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (exprs[1]->vtype == TYPE_VECTOR)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
break;
case TYPE_VECTOR:
if (exprs[1]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
else
- out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
break;
default:
compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
}
if (!(out = fold_op(parser->fold, op, exprs))) {
if (exprs[0]->vtype == TYPE_FLOAT)
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
else {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
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 = fold_binary(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 = fold_binary(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 = fold_binary(ctx,
+ (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
+ exprs[0], exprs[1]);
}
}
}
case opid2('|','|'):
generated_op += 1; /* INSTR_OR */
+ GMQCC_FALLTHROUGH;
case opid2('&','&'):
generated_op += INSTR_AND;
if (!(out = fold_op(parser->fold, op, exprs))) {
}
}
}
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
}
break;
ty1, ty2);
return false;
}
-
+
if (!(out = fold_op(parser->fold, op, exprs))) {
ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser->intrin, "pow"));
vec_push(gencall->params, exprs[0]);
}
break;
+ case opid2('>', '<'):
+ if (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 cross product: %s and %s",
+ ty1, ty2);
+ return false;
+ }
+
+ if (!(out = fold_op(parser->fold, op, exprs))) {
+ out = fold_binary(
+ parser_ctx(parser),
+ VINSTR_CROSS,
+ exprs[0],
+ exprs[1]
+ );
+ }
+
+ break;
+
case opid3('<','=','>'): /* -1, 0, or 1 */
if (NotSameType(TYPE_FLOAT)) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ty1, ty2);
return false;
- }
+ }
if (!(out = fold_op(parser->fold, op, exprs))) {
+ /* This whole block is NOT fold_binary safe */
ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
eq->refs = AST_REF_NONE;
case opid1('>'):
generated_op += 1; /* INSTR_GT */
+ GMQCC_FALLTHROUGH;
case opid1('<'):
generated_op += 1; /* INSTR_LT */
+ GMQCC_FALLTHROUGH;
case opid2('>', '='):
generated_op += 1; /* INSTR_GE */
+ GMQCC_FALLTHROUGH;
case opid2('<', '='):
generated_op += INSTR_LE;
if (NotSameType(TYPE_FLOAT)) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+ out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
break;
case opid2('!', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid2('=', '='):
if (exprs[0]->vtype != exprs[1]->vtype) {
return false;
}
if (!(out = fold_op(parser->fold, op, exprs)))
- out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+ out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid1('='):
compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
}
}
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ctx, exprs[0]);
out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
break;
case opid3('+','+','P'):
addop = INSTR_ADD_F;
else
addop = INSTR_SUB_F;
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
if (ast_istype(exprs[0], ast_entfield)) {
out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
exprs[0],
addop = INSTR_SUB_F;
subop = INSTR_ADD_F;
}
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
if (ast_istype(exprs[0], ast_entfield)) {
out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
exprs[0],
}
if (!out)
return false;
- out = (ast_expression*)ast_binary_new(ctx, subop,
- out,
- (ast_expression*)parser->fold->imm_float[1]);
+ out = fold_binary(ctx, subop,
+ out,
+ (ast_expression*)parser->fold->imm_float[1]);
+
break;
case opid2('+','='):
case opid2('-','='):
ty1, ty2);
return false;
}
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ctx, exprs[0]);
if (ast_istype(exprs[0], ast_entfield))
assignop = type_storep_instr[exprs[0]->vtype];
else
ty1, ty2);
return false;
}
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ctx, exprs[0]);
if (ast_istype(exprs[0], ast_entfield))
assignop = type_storep_instr[exprs[0]->vtype];
else
out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF,
exprs[0], exprs[1]);
} else {
- out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
- (ast_expression*)parser->fold->imm_float[1],
- exprs[1]);
+ out = fold_binary(ctx, INSTR_DIV_F,
+ (ast_expression*)parser->fold->imm_float[1],
+ exprs[1]);
if (!out) {
compile_error(ctx, "internal error: failed to generate division");
return false;
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",
ty1, ty2);
return false;
}
- if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
- compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
- }
+ (void)check_write_to(ctx, exprs[0]);
if (ast_istype(exprs[0], ast_entfield))
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 = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+ else
+ out = fold_binary(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);
+ (void)check_write_to(ctx, exprs[0]);
+ 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 = fold_binary(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+ } else {
+ out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+ }
+ }
break;
}
#undef NotSameType
size_t fid;
size_t paramcount, i;
+ bool fold = true;
fid = vec_last(sy->ops).off;
vec_shrinkby(sy->ops, 1);
return false;
}
- /*
+ /*
* TODO handle this at the intrinsic level with an ast_intrinsic
* node and codegen.
*/
vec_shrinkby(sy->out, 1);
return true;
}
+
+ /*
+ * Now we need to determine if the function that is being called is
+ * an intrinsic so we can evaluate if the arguments to it are constant
+ * and than fruitfully fold them.
+ */
+#define fold_can_1(X) \
+ (ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \
+ ((ast_expression*)(X))->vtype != TYPE_FUNCTION)
+
+ if (fid + 1 < vec_size(sy->out))
+ ++paramcount;
+
+ for (i = 0; i < paramcount; ++i) {
+ if (!fold_can_1((ast_value*)sy->out[fid + 1 + i].out)) {
+ fold = false;
+ break;
+ }
+ }
+
+ /*
+ * All is well which ends well, if we make it into here we can ignore the
+ * intrinsic call and just evaluate it i.e constant fold it.
+ */
+ if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->intrinsic) {
+ ast_expression **exprs = NULL;
+ ast_expression *foldval = NULL;
+
+ for (i = 0; i < paramcount; i++)
+ vec_push(exprs, sy->out[fid+1 + i].out);
+
+ if (!(foldval = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) {
+ vec_free(exprs);
+ goto fold_leave;
+ }
+
+ /*
+ * Blub: what sorts of unreffing and resizing of
+ * sy->out should I be doing here?
+ */
+ sy->out[fid] = syexp(foldval->node.context, foldval);
+ vec_shrinkby(sy->out, paramcount);
+ vec_free(exprs);
+
+ return true;
+ }
+
+ fold_leave:
call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
if (!call)
return false;
- if (fid+1 < vec_size(sy->out))
- ++paramcount;
-
if (fid+1 + paramcount != vec_size(sy->out)) {
parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu",
(unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out));
return NULL;
}
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar) {
ast_unref(idx);
return NULL;
if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
var = (ast_expression*)fold_constgen_string(parser->fold, parser->function->name, false);
if (!var) {
- /*
+ /*
* 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.
/* only warn once about an assignment in a truth value because the current code
* would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
*/
- bool warn_truthvalue = true;
+ bool warn_parenthesis = true;
/* count the parens because an if starts with one, so the
* end of a condition is an unmatched closing paren
}
}
if (o == operator_count) {
- compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+ compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser));
goto onerr;
}
/* found an operator */
if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
olast = &operators[vec_last(sy.ops).etype-1];
+ /* first only apply higher precedences, assoc_left+equal comes after we warn about precedence rules */
+ while (olast && op->prec < olast->prec)
+ {
+ if (!parser_sy_apply_operator(parser, &sy))
+ goto onerr;
+ if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
+ olast = &operators[vec_last(sy.ops).etype-1];
+ else
+ olast = NULL;
+ }
+
#define IsAssignOp(x) (\
(x) == opid1('=') || \
(x) == opid2('+','=') || \
(x) == opid2('|','=') || \
(x) == opid3('&','~','=') \
)
- if (warn_truthvalue) {
+ if (warn_parenthesis) {
if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
(olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
(truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id))
)
{
(void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
- warn_truthvalue = false;
+ warn_parenthesis = false;
+ }
+
+ if (olast && olast->id != op->id) {
+ if ((op->id == opid1('&') || op->id == opid1('|') || op->id == opid1('^')) &&
+ (olast->id == opid1('&') || olast->id == opid1('|') || olast->id == opid1('^')))
+ {
+ (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around bitwise operations");
+ warn_parenthesis = false;
+ }
+ else if ((op->id == opid2('&','&') || op->id == opid2('|','|')) &&
+ (olast->id == opid2('&','&') || olast->id == opid2('|','|')))
+ {
+ (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around logical operations");
+ warn_parenthesis = false;
+ }
}
}
parser->lex->flags.noops = true;
if (vec_size(sy.out) != 1) {
- parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
+ parseerror(parser, "expression expected");
expr = NULL;
} else
expr = sy.out[0].out;
else if (!strcmp(parser_tokval(parser), "inline")) {
flags |= AST_FLAG_INLINE;
if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
- parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+ parseerror(parser, "`inline` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "eraseable")) {
+ flags |= AST_FLAG_ERASEABLE;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "accumulate")) {
+ flags |= AST_FLAG_ACCUMULATE;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`");
*cvq = CV_WRONG;
return false;
}
else
{
(void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
- return false;
+
+ /* skip to eol */
+ while (!parse_eol(parser)) {
+ parser_next(parser);
+ }
+
+ return true;
}
return true;
if (parser->tok == TOKEN_IDENT)
typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
- if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+ if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
{
/* local variable */
if (!block) {
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);
}
if (var->hasvalue) {
- parseerror(parser, "function `%s` declared with multiple bodies", var->name);
- ast_block_delete(block);
- goto enderr;
- }
+ if (!(var->expression.flags & AST_FLAG_ACCUMULATE)) {
+ parseerror(parser, "function `%s` declared with multiple bodies", var->name);
+ ast_block_delete(block);
+ goto enderr;
+ }
+ func = var->constval.vfunc;
- func = ast_function_new(ast_ctx(var), var->name, var);
- if (!func) {
- parseerror(parser, "failed to allocate function for `%s`", var->name);
- ast_block_delete(block);
- goto enderr;
+ if (!func) {
+ parseerror(parser, "internal error: NULL function: `%s`", var->name);
+ ast_block_delete(block);
+ goto enderr;
+ }
+ } else {
+ func = ast_function_new(ast_ctx(var), var->name, var);
+
+ if (!func) {
+ parseerror(parser, "failed to allocate function for `%s`", var->name);
+ ast_block_delete(block);
+ goto enderr;
+ }
+ vec_push(parser->functions, func);
}
- vec_push(parser->functions, func);
parser_enterblock(parser);
}
}
- if (var->argcounter) {
+ if (var->argcounter && !func->argc) {
ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
parser_addlocal(parser, argc->name, (ast_expression*)argc);
func->argc = argc;
}
- if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
+ if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC && !func->varargs) {
char name[1024];
ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY);
varargs->expression.flags |= AST_FLAG_IS_VARARG;
vec_push(func->blocks, block);
-
parser->function = old;
if (!parser_leaveblock(parser))
retval = false;
return parser_create_array_getter_impl(parser, array);
}
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
{
lex_ctx_t ctx;
/* parse variables until we hit a closing paren */
while (parser->tok != ')') {
+ bool is_varargs = false;
+
if (!first) {
/* there must be commas between them */
if (parser->tok != ',') {
}
first = false;
- if (parser->tok == TOKEN_DOTS) {
+ param = parse_typename(parser, NULL, NULL, &is_varargs);
+ if (!param && !is_varargs)
+ goto on_error;
+ if (is_varargs) {
/* '...' indicates a varargs function */
variadic = true;
- if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+ if (parser->tok != ')' && parser->tok != TOKEN_IDENT) {
parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
goto on_error;
}
goto on_error;
}
}
- }
- else
- {
- /* for anything else just parse a typename */
- param = parse_typename(parser, NULL, NULL);
- if (!param)
- goto on_error;
+ } else {
vec_push(params, param);
if (param->expression.vtype >= TYPE_VARIANT) {
char tname[1024]; /* typename is reserved in C++ */
* The base type makes up every bit of type information which comes *before* the
* variable name.
*
+ * NOTE: The value must either be named, have a NULL name, or a name starting
+ * with '<'. In the first case, this will be the actual variable or type
+ * name, in the other cases it is assumed that the name will appear
+ * later, and an error is generated otherwise.
+ *
* The following will be parsed in its entirety:
* void() foo()
* The 'basetype' in this case is 'void()'
* void() foo(), bar
* then the type-information 'void()' can be stored in 'storebase'
*/
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg)
{
ast_value *var, *tmp;
lex_ctx_t ctx;
bool wasarray = false;
size_t morefields = 0;
+ bool vararg = (parser->tok == TOKEN_DOTS);
+
ctx = parser_ctx(parser);
/* types may start with a dot */
- if (parser->tok == '.') {
+ if (parser->tok == '.' || parser->tok == TOKEN_DOTS) {
isfield = true;
+ if (parser->tok == TOKEN_DOTS)
+ morefields += 2;
/* if we parsed a dot we need a typename now */
if (!parser_next(parser)) {
parseerror(parser, "expected typename for field definition");
/* Further dots are handled seperately because they won't be part of the
* basetype
*/
- while (parser->tok == '.') {
- ++morefields;
+ while (true) {
+ if (parser->tok == '.')
+ ++morefields;
+ else if (parser->tok == TOKEN_DOTS)
+ morefields += 3;
+ else
+ break;
+ vararg = false;
if (!parser_next(parser)) {
parseerror(parser, "expected typename for field definition");
return NULL;
if (parser->tok == TOKEN_IDENT)
cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0);
if (!cached_typedef && parser->tok != TOKEN_TYPENAME) {
+ if (vararg && is_vararg) {
+ *is_vararg = true;
+ return NULL;
+ }
parseerror(parser, "expected typename");
return NULL;
}
}
/* there may be a name now */
- if (parser->tok == TOKEN_IDENT) {
+ if (parser->tok == TOKEN_IDENT || parser->tok == TOKEN_KEYWORD) {
+ if (!strcmp(parser_tokval(parser), "break"))
+ (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+ else if (parser->tok == TOKEN_KEYWORD)
+ goto leave;
+
name = util_strdup(parser_tokval(parser));
+
/* parse on */
if (!parser_next(parser)) {
ast_delete(var);
}
}
+ leave:
/* now this may be an array */
if (parser->tok == '[') {
wasarray = true;
ast_value *typevar, *oldtype;
ast_expression *old;
- typevar = parse_typename(parser, NULL, NULL);
+ typevar = parse_typename(parser, NULL, NULL, NULL);
if (!typevar)
return false;
+ /* while parsing types, the ast_value's get named '<something>' */
+ if (!typevar->name || typevar->name[0] == '<') {
+ parseerror(parser, "missing name in typedef");
+ ast_delete(typevar);
+ return false;
+ }
+
if ( (old = parser_find_var(parser, typevar->name)) ) {
parseerror(parser, "cannot define a type with the same name as a variable: %s\n"
" -> `%s` has been declared here: %s:%i",
parseerror(parser, "`static` qualifier is not supported in global scope");
/* get the first complete variable */
- var = parse_typename(parser, &basetype, cached_typedef);
+ var = parse_typename(parser, &basetype, cached_typedef, NULL);
if (!var) {
if (basetype)
ast_delete(basetype);
return false;
}
+ /* while parsing types, the ast_value's get named '<something>' */
+ if (!var->name || var->name[0] == '<') {
+ parseerror(parser, "declaration does not declare anything");
+ if (basetype)
+ ast_delete(basetype);
+ return false;
+ }
+
while (true) {
proto = NULL;
wasarray = false;
var->expression.flags & AST_FLAG_ALIAS)
var->desc = vstring;
+ if (parser_find_global(parser, var->name) && var->expression.flags & AST_FLAG_ALIAS) {
+ parseerror(parser, "function aliases cannot be forward declared");
+ retval = false;
+ goto cleanup;
+ }
+
+
/* Part 1:
* check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
* Also: if there was a prototype, `var` will be deleted and set to `proto` which
retval = false;
goto cleanup;
}
+ /* doing this here as the above is just for a single scope */
old = parser_find_local(parser, var->name, 0, &isparam);
if (old && isparam) {
if (parsewarning(parser, WARN_LOCAL_SHADOWS,
}
if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
ast_delete(var);
- var = NULL;
- goto skipvar;
+ if (ast_istype(old, ast_value))
+ var = proto = (ast_value*)old;
+ else {
+ var = NULL;
+ goto skipvar;
+ }
}
}
}
return false;
}
- if (var->expression.vtype != find->vtype) {
+ if (!ast_compare_type((ast_expression*)var, find)) {
char ty1[1024];
char ty2[1024];
if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
if (parser->tok != '=') {
- 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;
- }
+ parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
+ break;
}
if (!parser_next(parser)) {
if (parser->tok == TOKEN_IDENT)
istype = parser_find_typedef(parser, parser_tokval(parser), 0);
- if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+ if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
{
return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL);
}
}
}
if (!parser->assign_op) {
- printf("internal error: initializing parser: failed to find assign operator\n");
+ con_err("internal error: initializing parser: failed to find assign operator\n");
mem_d(parser);
return NULL;
}
if (parser->reserved_version)
ast_value_delete(parser->reserved_version);
- util_htdel(parser->aliases);
+ util_htdel(parser->aliases);
fold_cleanup(parser->fold);
intrin_cleanup(parser->intrin);
}