static bool parser_leaveblock(parser_t *parser);
static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
static bool parse_typedef(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn);
static ast_block* parse_block(parser_t *parser);
static bool parse_block_into(parser_t *parser, ast_block *block);
static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
return true;
}
+static bool immediate_is_true(lex_ctx ctx, ast_value *v)
+{
+ switch (v->expression.vtype) {
+ case TYPE_FLOAT:
+ return !!v->constval.vfloat;
+ case TYPE_INTEGER:
+ return !!v->constval.vint;
+ case TYPE_VECTOR:
+ if (OPTS_FLAG(CORRECT_LOGIC))
+ return v->constval.vvec.x &&
+ v->constval.vvec.y &&
+ v->constval.vvec.z;
+ else
+ return !!(v->constval.vvec.x);
+ case TYPE_STRING:
+ if (!v->constval.vstring)
+ return false;
+ if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
+ return true;
+ return !!v->constval.vstring[0];
+ default:
+ compile_error(ctx, "internal error: immediate_is_true on invalid type");
+ return !!v->constval.vfunc;
+ }
+}
+
static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
{
const oper_info *op;
(exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
exprs[0]->expression.vtype != T)
#define CanConstFold1(A) \
- (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST))
+ (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
+ (A)->expression.vtype != TYPE_FUNCTION)
#define CanConstFold(A, B) \
(CanConstFold1(A) && CanConstFold1(B))
#define ConstV(i) (asvalue[(i)]->constval.vvec)
out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
break;
case TYPE_STRING:
- if (CanConstFold1(exprs[0]))
- out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
- else
- out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+ if (CanConstFold1(exprs[0])) {
+ if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+ out = (ast_expression*)parser_const_float(parser, !ConstS(0));
+ else
+ out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
+ } else {
+ if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
+ else
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+ }
break;
/* we don't constant-fold NOT for these types */
case TYPE_ENTITY:
}
#endif
if (CanConstFold(exprs[0], exprs[1]))
- out = (ast_expression*)parser_const_float(parser,
- (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
+ {
+ if (OPTS_FLAG(PERL_LOGIC)) {
+ if (immediate_is_true(ctx, asvalue[0]))
+ out = exprs[1];
+ }
+ else
+ out = (ast_expression*)parser_const_float(parser,
+ ( (generated_op == INSTR_OR)
+ ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
+ : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
+ ? 0 : 1);
+ }
else
{
if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) {
parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
return false;
}
+ if (OPTS_FLAG(CORRECT_LOGIC)) {
+ /* non-floats need to be NOTed */
+ for (i = 0; i < 2; ++i) {
+ if (exprs[i]->expression.vtype != TYPE_FLOAT) {
+ if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) {
+ ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+ ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+ parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2);
+ return false;
+ }
+ out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]);
+ if (!out)
+ break;
+ exprs[i] = out; out = NULL;
+ }
+ if (OPTS_FLAG(PERL_LOGIC))
+ break;
+ }
+ }
out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
}
break;
util_htset(vec_last(parser->variables), name, (void*)e);
}
+static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
+{
+ bool ifnot = false;
+ ast_unary *unary;
+ ast_expression *prev;
+
+ if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) {
+ prev = cond;
+ cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
+ if (!cond) {
+ ast_unref(prev);
+ parseerror(parser, "internal error: failed to process condition");
+ return NULL;
+ }
+ ifnot = !ifnot;
+ }
+ else if (OPTS_FLAG(CORRECT_LOGIC)) {
+ /* everything must use a NOT_ */
+ unary = (ast_unary*)cond;
+ if (!ast_istype(cond, ast_unary) || unary->op < INSTR_NOT_F || unary->op > INSTR_NOT_FNC)
+ {
+ /* use the right NOT_ */
+ prev = cond;
+ cond = (ast_expression*)ast_unary_new(ast_ctx(cond), type_not_instr[cond->expression.vtype], cond);
+ if (!cond) {
+ ast_unref(prev);
+ parseerror(parser, "internal error: failed to process condition");
+ return NULL;
+ }
+ ifnot = !ifnot;
+ }
+ }
+
+ unary = (ast_unary*)cond;
+ while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+ /*&& unary->operand->expression.vtype != TYPE_STRING) */
+ {
+ cond = unary->operand;
+ unary->operand = NULL;
+ ast_delete(unary);
+ ifnot = !ifnot;
+ unary = (ast_unary*)cond;
+ }
+
+ if (!cond)
+ parseerror(parser, "internal error: failed to process condition");
+
+ if (ifnot) *_ifnot = !*_ifnot;
+ return cond;
+}
+
static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
{
ast_ifthen *ifthen;
- ast_expression *cond, *ontrue, *onfalse = NULL;
+ ast_expression *cond, *ontrue = NULL, *onfalse = NULL;
bool ifnot = false;
lex_ctx ctx = parser_ctx(parser);
}
}
+ cond = process_condition(parser, cond, &ifnot);
+ if (!cond) {
+ if (ontrue) ast_delete(ontrue);
+ if (onfalse) ast_delete(onfalse);
+ return false;
+ }
+
if (ifnot)
ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue);
else
ast_loop *aloop;
ast_expression *cond, *ontrue;
+ bool ifnot = false;
+
lex_ctx ctx = parser_ctx(parser);
(void)block; /* not touching */
return false;
}
- aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue);
+ cond = process_condition(parser, cond, &ifnot);
+ if (!cond) {
+ ast_delete(ontrue);
+ return false;
+ }
+ aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
*out = (ast_expression*)aloop;
return true;
}
ast_loop *aloop;
ast_expression *cond, *ontrue;
+ bool ifnot = false;
+
lex_ctx ctx = parser_ctx(parser);
(void)block; /* not touching */
return false;
}
- aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue);
+ cond = process_condition(parser, cond, &ifnot);
+ if (!cond) {
+ ast_delete(ontrue);
+ return false;
+ }
+ aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue);
*out = (ast_expression*)aloop;
return true;
}
ast_loop *aloop;
ast_expression *initexpr, *cond, *increment, *ontrue;
ast_value *typevar;
- bool retval = true;
+
+ bool retval = true;
+ bool ifnot = false;
lex_ctx ctx = parser_ctx(parser);
"current standard does not allow variable declarations in for-loop initializers"))
goto onerr;
}
- if (!parse_variable(parser, block, true, CV_VAR, typevar, false))
+ if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false))
goto onerr;
}
else if (parser->tok != ';')
if (!parse_statement_or_block(parser, &ontrue))
goto onerr;
- aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue);
+ if (cond) {
+ cond = process_condition(parser, cond, &ifnot);
+ if (!cond)
+ goto onerr;
+ }
+ aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
*out = (ast_expression*)aloop;
if (!parser_leaveblock(parser))
parseerror(parser, "return with invalid expression");
}
- ret = ast_return_new(exp->expression.node.context, exp);
+ ret = ast_return_new(ctx, exp);
if (!ret) {
ast_delete(exp);
return false;
/* returns true when it was a variable qualifier, false otherwise!
* on error, cvq is set to CV_WRONG
*/
-static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref)
+static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn)
{
bool had_const = false;
bool had_var = false;
bool had_noref = false;
+ bool had_noreturn = false;
for (;;) {
if (!strcmp(parser_tokval(parser), "const"))
had_var = true;
else if (!strcmp(parser_tokval(parser), "noref"))
had_noref = true;
- else if (!had_const && !had_var && !had_noref) {
+ else if (!strcmp(parser_tokval(parser), "noreturn"))
+ had_noreturn = true;
+ else if (!had_const && !had_var && !had_noref && !had_noreturn) {
return false;
}
else
*cvq = CV_VAR;
else
*cvq = CV_NONE;
- *noref = had_noref;
+ *noref = had_noref;
+ *noreturn = had_noreturn;
return true;
onerr:
parseerror(parser, "parse error after variable qualifier");
ast_switch_case swcase;
int cvq;
- bool noref;
+ bool noref, noreturn;
lex_ctx ctx = parser_ctx(parser);
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, false)) {
+ if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) {
ast_delete(switchnode);
return false;
}
continue;
}
- if (parse_var_qualifiers(parser, true, &cvq, &noref))
+ if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn))
{
if (cvq == CV_WRONG) {
ast_delete(switchnode);
return false;
}
- if (!parse_variable(parser, block, false, cvq, NULL, noref)) {
+ if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) {
ast_delete(switchnode);
return false;
}
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
{
- bool noref;
+ bool noref, noreturn;
int cvq = CV_NONE;
ast_value *typevar = NULL;
if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
return false;
}
- if (!parse_variable(parser, block, false, CV_NONE, typevar, false))
+ if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false))
return false;
return true;
}
- else if (parse_var_qualifiers(parser, !!block, &cvq, &noref))
+ else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn))
{
if (cvq == CV_WRONG)
return false;
- return parse_variable(parser, block, true, cvq, NULL, noref);
+ return parse_variable(parser, block, true, cvq, NULL, noref, noreturn);
}
else if (parser->tok == TOKEN_KEYWORD)
{
return true;
}
-static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn)
{
ast_value *var;
ast_value *proto;
/* in a noref section we simply bump the usecount */
if (noref || parser->noref)
var->uses++;
+ if (noreturn)
+ var->expression.flags |= AST_FLAG_NORETURN;
/* Part 1:
* check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
static bool parser_global_statement(parser_t *parser)
{
- int cvq = CV_WRONG;
- bool noref = false;
- ast_value *istype = NULL;
+ int cvq = CV_WRONG;
+ bool noref = false;
+ bool noreturn = false;
+ ast_value *istype = NULL;
if (parser->tok == TOKEN_IDENT)
istype = parser_find_typedef(parser, parser_tokval(parser), 0);
if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
{
- return parse_variable(parser, NULL, false, CV_NONE, istype, false);
+ return parse_variable(parser, NULL, false, CV_NONE, istype, false, false);
}
- else if (parse_var_qualifiers(parser, false, &cvq, &noref))
+ else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn))
{
if (cvq == CV_WRONG)
return false;
- return parse_variable(parser, NULL, true, cvq, NULL, noref);
+ return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn);
}
else if (parser->tok == TOKEN_KEYWORD)
{