* anything else: type error
*/
qcint memberof;
+
+ /* Keep track of our ternary vs parenthesis nesting state.
+ * If we reach a 'comma' operator in a ternary without a paren,
+ * we shall trigger -Wternary-precedence.
+ */
+ enum { POT_PAREN, POT_TERNARY1, POT_TERNARY2 } *pot;
} parser_t;
static void parser_enterblock(parser_t *parser);
static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef);
static ast_block* parse_block(parser_t *parser);
static bool parse_block_into(parser_t *parser, ast_block *block);
-static ast_expression* parse_statement_or_block(parser_t *parser);
+static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
#define SY_PAREN_EXPR '('
#define SY_PAREN_FUNC 'f'
#define SY_PAREN_INDEX '['
+#define SY_PAREN_TERNARY '?'
static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
sy_elem e;
vec_shrinkby(sy->ops, 1);
+ /* op(:?) has no input and no output */
+ if (!op->operands)
+ return true;
+
vec_shrinkby(sy->out, op->operands);
for (i = 0; i < op->operands; ++i) {
exprs[i] = sy->out[vec_size(sy->out)+i].out;
out = (ast_expression*)ast_array_index_new(ctx, exprs[0], exprs[1]);
if (rotate_entfield_array_index_nodes(&out))
{
+#if 0
+ /* This is not broken in fteqcc anymore */
if (opts_standard != COMPILER_GMQCC) {
/* this error doesn't need to make us bail out */
(void)!parsewarning(parser, WARN_EXTENSIONS,
"accessing array-field members of an entity without parenthesis\n"
" -> this is an extension from -std=gmqcc");
}
+#endif
}
break;
break;
case opid2('?',':'):
+ if (vec_last(parser->pot) != POT_TERNARY2) {
+ parseerror(parser, "mismatched parenthesis/ternary");
+ return false;
+ }
+ vec_pop(parser->pot);
if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
ast_type_to_string(exprs[1], ty1, sizeof(ty1));
ast_type_to_string(exprs[2], ty2, sizeof(ty2));
return false;
return true;
}
+ if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_TERNARY) {
+ if (functions_only)
+ return false;
+ if (vec_last(parser->pot) != POT_TERNARY1) {
+ parseerror(parser, "mismatched colon in ternary expression (missing closing paren?)");
+ return false;
+ }
+ vec_last(parser->pot) = POT_TERNARY2;
+ /* pop off the parenthesis */
+ vec_shrinkby(sy->ops, 1);
+ return true;
+ }
if (!parser_sy_pop(parser, sy))
return false;
}
/* closing an opening paren */
if (!parser_close_paren(parser, &sy, false))
goto onerr;
+ if (vec_last(parser->pot) != POT_PAREN) {
+ parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+ goto onerr;
+ }
+ vec_pop(parser->pot);
} else {
DEBUGSHUNTDO(con_out("do[nop] )\n"));
--parens;
/* allowed for function calls */
if (!parser_close_paren(parser, &sy, true))
goto onerr;
+ if (vec_last(parser->pot) != POT_PAREN) {
+ parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+ goto onerr;
+ }
+ vec_pop(parser->pot);
}
wantop = true;
}
break;
if (!parser_close_paren(parser, &sy, false))
goto onerr;
+ if (vec_last(parser->pot) != POT_PAREN) {
+ parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
+ goto onerr;
+ }
+ vec_pop(parser->pot);
wantop = true;
}
else if (parser->tok != TOKEN_OPERATOR) {
break;
}
+ if (op->id == opid1(',')) {
+ if (vec_size(parser->pot) && vec_last(parser->pot) == POT_TERNARY2) {
+ (void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression");
+ }
+ }
+
if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
olast = &operators[vec_last(sy.ops).etype-1];
if (wantop) {
size_t sycount = vec_size(sy.out);
DEBUGSHUNTDO(con_out("push [op] (\n"));
- ++parens;
+ ++parens; vec_push(parser->pot, POT_PAREN);
/* we expected an operator, this is the function-call operator */
vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_FUNC, sycount-1));
} else {
- ++parens;
+ ++parens; vec_push(parser->pot, POT_PAREN);
vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0));
DEBUGSHUNTDO(con_out("push [nop] (\n"));
}
parseerror(parser, "unexpected array subscript");
goto onerr;
}
- ++parens;
+ ++parens; vec_push(parser->pot, POT_PAREN);
/* push both the operator and the paren, this makes life easier */
vec_push(sy.ops, syop(parser_ctx(parser), op));
vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
} else if (op->id == opid2('?',':')) {
wantop = false;
vec_push(sy.ops, syop(parser_ctx(parser), op));
+ vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0));
wantop = false;
- --ternaries;
+ ++ternaries;
+ vec_push(parser->pot, POT_TERNARY1);
} else if (op->id == opid2(':','?')) {
- /* we don't push this operator */
+ if (!vec_size(parser->pot)) {
+ parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
+ goto onerr;
+ }
+ if (vec_last(parser->pot) != POT_TERNARY1) {
+ parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
+ goto onerr;
+ }
+ if (!parser_close_paren(parser, &sy, false))
+ goto onerr;
+ vec_push(sy.ops, syop(parser_ctx(parser), op));
wantop = false;
- ++ternaries;
+ --ternaries;
} else {
DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
vec_push(sy.ops, syop(parser_ctx(parser), op));
vec_free(sy.out);
vec_free(sy.ops);
DEBUGSHUNTDO(con_out("shunt done\n"));
+ if (vec_size(parser->pot)) {
+ parseerror(parser, "internal error: vec_size(parser->pot) = %lu", (unsigned long)vec_size(parser->pot));
+ return NULL;
+ }
+ vec_free(parser->pot);
return expr;
onerr:
ast_delete(cond);
return false;
}
- ontrue = parse_statement_or_block(parser);
- if (!ontrue) {
+ if (!parse_statement_or_block(parser, &ontrue)) {
ast_delete(cond);
return false;
}
ast_delete(cond);
return false;
}
- onfalse = parse_statement_or_block(parser);
- if (!onfalse) {
+ if (!parse_statement_or_block(parser, &onfalse)) {
ast_delete(ontrue);
ast_delete(cond);
return false;
ast_delete(cond);
return false;
}
- ontrue = parse_statement_or_block(parser);
- if (!ontrue) {
+ if (!parse_statement_or_block(parser, &ontrue)) {
ast_delete(cond);
return false;
}
parseerror(parser, "expected loop body");
return false;
}
- ontrue = parse_statement_or_block(parser);
- if (!ontrue)
+ if (!parse_statement_or_block(parser, &ontrue))
return false;
/* expect the "while" */
parseerror(parser, "expected for-loop body");
goto onerr;
}
- ontrue = parse_statement_or_block(parser);
- if (!ontrue) {
+ if (!parse_statement_or_block(parser, &ontrue))
goto onerr;
- }
aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue);
*out = (ast_expression*)aloop;
ast_return *ret = NULL;
ast_value *expected = parser->function->vtype;
+ lex_ctx ctx = parser_ctx(parser);
+
(void)block; /* not touching */
if (!parser_next(parser)) {
else
parseerror(parser, "return without value");
}
- ret = ast_return_new(parser_ctx(parser), NULL);
+ ret = ast_return_new(ctx, NULL);
}
*out = (ast_expression*)ret;
return true;
{
ast_expression *operand;
ast_value *opval;
+ ast_value *typevar;
ast_switch *switchnode;
ast_switch_case swcase;
return false;
}
+ /* new block; allow some variables to be declared here */
+ parser_enterblock(parser);
+ while (true) {
+ typevar = NULL;
+ if (parser->tok == TOKEN_IDENT)
+ typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
+ if (typevar || parser->tok == TOKEN_TYPENAME) {
+ if (!parse_variable(parser, block, false, CV_NONE, typevar)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ continue;
+ }
+ if (!strcmp(parser_tokval(parser), "var") ||
+ !strcmp(parser_tokval(parser), "local"))
+ {
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected variable declaration");
+ ast_delete(switchnode);
+ return false;
+ }
+ if (!parse_variable(parser, block, false, CV_VAR, NULL)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ continue;
+ }
+ if (!strcmp(parser_tokval(parser), "const")) {
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected variable declaration");
+ ast_delete(switchnode);
+ return false;
+ }
+ if (!strcmp(parser_tokval(parser), "var")) {
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected variable declaration");
+ ast_delete(switchnode);
+ return false;
+ }
+ }
+ if (!parse_variable(parser, block, false, CV_CONST, NULL)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ continue;
+ }
+ break;
+ }
+
/* case list! */
while (parser->tok != '}') {
ast_block *caseblock;
}
}
+ parser_leaveblock(parser);
+
/* closing paren */
if (parser->tok != '}') {
ast_delete(switchnode);
return block;
}
-static ast_expression* parse_statement_or_block(parser_t *parser)
+static bool parse_statement_or_block(parser_t *parser, ast_expression **out)
{
- ast_expression *expr = NULL;
- if (parser->tok == '{')
- return (ast_expression*)parse_block(parser);
- if (!parse_statement(parser, NULL, &expr, false))
- return NULL;
- return expr;
+ if (parser->tok == '{') {
+ *out = (ast_expression*)parse_block(parser);
+ return !!*out;
+ }
+ return parse_statement(parser, NULL, out, false);
}
static bool create_vector_members(ast_value *var, ast_member **me)
if (!localblock) {
/* deal with global variables, fields, functions */
- if (!nofields && var->expression.vtype == TYPE_FIELD) {
+ if (!nofields && var->expression.vtype == TYPE_FIELD && parser->tok != '=') {
+ var->isfield = true;
vec_push(parser->fields, (ast_expression*)var);
util_htset(parser->htfields, var->name, var);
if (isvector) {
if (parser->tok == ',')
goto another;
+ /*
if (!var || (!localblock && !nofields && basetype->expression.vtype == TYPE_FIELD)) {
+ */
+ if (!var) {
parseerror(parser, "missing comma or semicolon while parsing variables");
break;
}
parseerror(parser, "builtin number must be an integer constant");
break;
}
- if (parser_token(parser)->constval.i <= 0) {
+ if (parser_token(parser)->constval.i < 0) {
parseerror(parser, "builtin number must be an integer greater than zero");
break;
}
}
vec_push(parser->functions, func);
- func->builtin = -parser_token(parser)->constval.i;
+ func->builtin = -parser_token(parser)->constval.i-1;
}
if (!parser_next(parser)) {
if (!localblock) {
cval = (ast_value*)cexp;
- if (!ast_istype(cval, ast_value) || !cval->hasvalue || cval->cvq != CV_CONST)
+ if (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
else
{
var->hasvalue = true;
if (cval->expression.vtype == TYPE_STRING)
var->constval.vstring = parser_strdup(cval->constval.vstring);
+ else if (cval->expression.vtype == TYPE_FIELD)
+ var->constval.vfield = cval;
else
memcpy(&var->constval, &cval->constval, sizeof(var->constval));
ast_unref(cval);
parseerror(parser, "expected variable declaration after 'var'");
return false;
}
+ if (parser->tok == TOKEN_KEYWORD && !strcmp(parser_tokval(parser), "const")) {
+ (void)!parsewarning(parser, WARN_CONST_VAR, "ignoring `const` after 'var' qualifier");
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected variable declaration after 'const var'");
+ return false;
+ }
+ }
return parse_variable(parser, NULL, true, CV_VAR, NULL);
}
}