* 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);
#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;
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_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);
}
vec_push(parser->functions, func);
- func->builtin = -parser_token(parser)->constval.i;
+ func->builtin = -parser_token(parser)->constval.i-1;
}
if (!parser_next(parser)) {
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);
}
}