*/
#include <string.h>
#include <math.h>
+
#include "parser.h"
#define PARSER_HT_LOCALS 2
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;
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('?',':')) &&
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'):
}
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 = (ast_expression*)ast_binary_new(
+ 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));
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],
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
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_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);
- }
+ (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
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
{
(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;
* 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()'
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",
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;
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);
if (ast_istype(old, ast_value))
- var = (ast_value*)old;
+ 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->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;
}