#include "gmqcc.h"
#include "lexer.h"
-#define PARSER_HT_FIELDS 0
-#define PARSER_HT_GLOBALS 1
/* beginning of locals */
#define PARSER_HT_LOCALS 2
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;
}
+ for (i = 0; i < 2; ++i) {
+ if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) {
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
+ if (!out) break;
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+ if (!out) break;
+ exprs[i] = out; out = NULL;
+ if (OPTS_FLAG(PERL_LOGIC)) {
+ /* here we want to keep the right expressions' type */
+ break;
+ }
+ }
+ else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) {
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
+ if (!out) break;
+ out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+ if (!out) break;
+ exprs[i] = out; out = NULL;
+ if (OPTS_FLAG(PERL_LOGIC)) {
+ /* here we want to keep the right expressions' type */
+ 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) && cond->expression.vtype == TYPE_VECTOR)
+ {
+ /* vector types need to be cast to true booleans */
+ ast_binary *bin = (ast_binary*)cond;
+ if (!OPTS_FLAG(PERL_LOGIC) || !ast_istype(cond, ast_binary) || !(bin->op == INSTR_AND || bin->op == INSTR_OR))
+ {
+ /* in perl-logic, AND and OR take care of the -fcorrect-logic */
+ prev = cond;
+ cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_V, 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)
+ {
+ 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);
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;
}
else if (parser->tok == '{' || parser->tok == '[')
{
- size_t i;
if (localblock) {
parseerror(parser, "cannot declare functions within functions");
break;