static bool parse_block_into(parser_t *parser, ast_block *block);
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_leave(parser_t *parser, bool stopatcomma, bool truthvalue);
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
static void parseerror(parser_t *parser, const char *fmt, ...)
( (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);
+ ? 1 : 0);
}
else
{
return false;
}
vec_pop(parser->pot);
- if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
+ if (!ast_compare_type(exprs[1], exprs[2])) {
ast_type_to_string(exprs[1], ty1, sizeof(ty1));
ast_type_to_string(exprs[2], ty2, sizeof(ty2));
parseerror(parser, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
return false;
}
if (CanConstFold1(exprs[0]))
- out = (ConstF(0) ? exprs[1] : exprs[2]);
+ out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]);
else
out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
break;
}
else
assignop = type_storep_instr[exprs[0]->expression.vtype];
- if (assignop == AINSTR_END ||
- !ast_compare_type(field->expression.next, exprs[1]))
+ if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
{
ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
}
- else if (exprs[1]->expression.vtype != TYPE_NIL &&
- !ast_compare_type(exprs[0], exprs[1]))
+ else if (!ast_compare_type(exprs[0], exprs[1]))
{
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
vec_size(fun->expression.params) < paramcount))
{
const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
- if (opts.standard == COMPILER_GMQCC)
- {
- if (fval)
- parseerror(parser, "too %s parameters for call to %s: expected %i, got %i\n"
- " -> `%s` has been declared here: %s:%i",
- fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
- fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
- else
- parseerror(parser, "too %s parameters for function call: expected %i, got %i\n"
- " -> it has been declared here: %s:%i",
- fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
- ast_ctx(fun).file, (int)ast_ctx(fun).line);
- return false;
- }
+ if (fval)
+ return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+ "too %s parameters for call to %s: expected %i, got %i\n"
+ " -> `%s` has been declared here: %s:%i",
+ fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+ fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
else
- {
- if (fval)
- return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
- "too %s parameters for call to %s: expected %i, got %i\n"
- " -> `%s` has been declared here: %s:%i",
- fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
- fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
- else
- return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
- "too %s parameters for function call: expected %i, got %i\n"
- " -> it has been declared here: %s:%i",
- fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
- ast_ctx(fun).file, (int)ast_ctx(fun).line);
- }
+ return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+ "too %s parameters for function call: expected %i, got %i\n"
+ " -> it has been declared here: %s:%i",
+ fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+ ast_ctx(fun).file, (int)ast_ctx(fun).line);
}
}
}
}
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
{
ast_expression *expr = NULL;
shunt sy;
bool wantop = false;
bool gotmemberof = false;
+ /* only warn once about an assignment in a truth value because the current code
+ * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
+ */
+ bool warn_truthvalue = true;
/* count the parens because an if starts with one, so the
* end of a condition is an unmatched closing paren
if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
olast = &operators[vec_last(sy.ops).etype-1];
+#define IsAssignOp(x) (\
+ (x) == opid1('=') || \
+ (x) == opid2('+','=') || \
+ (x) == opid2('-','=') || \
+ (x) == opid2('*','=') || \
+ (x) == opid2('/','=') || \
+ (x) == opid2('%','=') || \
+ (x) == opid2('&','=') || \
+ (x) == opid2('|','=') || \
+ (x) == opid3('&','~','=') \
+ )
+ if (warn_truthvalue) {
+ if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
+ (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
+ (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+ )
+ {
+ (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
+ warn_truthvalue = false;
+ }
+ }
+
while (olast && (
(op->prec < olast->prec) ||
(op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) )
static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
{
- ast_expression *e = parse_expression_leave(parser, stopatcomma);
+ ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
if (!e)
return NULL;
if (!parser_next(parser)) {
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
return false;
}
/* parse the condition */
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
return false;
/* closing paren */
typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
if (typevar || parser->tok == TOKEN_TYPENAME) {
+#if 0
if (opts.standard != COMPILER_GMQCC) {
if (parsewarning(parser, WARN_EXTENSIONS,
"current standard does not allow variable declarations in for-loop initializers"))
goto onerr;
}
+#endif
if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL))
goto onerr;
}
else if (parser->tok != ';')
{
- initexpr = parse_expression_leave(parser, false);
+ initexpr = parse_expression_leave(parser, false, false);
if (!initexpr)
goto onerr;
}
/* parse the condition */
if (parser->tok != ';') {
- cond = parse_expression_leave(parser, false);
+ cond = parse_expression_leave(parser, false, true);
if (!cond)
goto onerr;
}
/* parse the incrementor */
if (parser->tok != ')') {
- increment = parse_expression_leave(parser, false);
+ increment = parse_expression_leave(parser, false, false);
if (!increment)
goto onerr;
if (!ast_side_effects(increment)) {
if (!exp)
return false;
- if (exp->expression.vtype != expected->expression.next->expression.vtype) {
+ if (exp->expression.vtype != TYPE_NIL &&
+ exp->expression.vtype != expected->expression.next->expression.vtype)
+ {
parseerror(parser, "return with invalid expression");
}
if (!parser_next(parser))
parseerror(parser, "parse error");
if (expected->expression.next->expression.vtype != TYPE_VOID) {
- if (opts.standard != COMPILER_GMQCC)
- (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
- else
- parseerror(parser, "return without value");
+ (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
}
ret = ast_return_new(ctx, NULL);
}
return false;
}
/* parse the operand */
- operand = parse_expression_leave(parser, false);
+ operand = parse_expression_leave(parser, false, false);
if (!operand)
return false;
parseerror(parser, "expected expression for case");
return false;
}
- swcase.value = parse_expression_leave(parser, false);
+ swcase.value = parse_expression_leave(parser, false, false);
if (!swcase.value) {
ast_delete(switchnode);
parseerror(parser, "expected expression for case");
if (!parser_next(parser))
return false;
- framenum = parse_expression_leave(parser, true);
+ framenum = parse_expression_leave(parser, true, false);
if (!framenum) {
parseerror(parser, "expected a framenumber constant in[frame,think] notation");
return false;
nextthink = (ast_expression*)thinkfunc;
} else {
- nextthink = parse_expression_leave(parser, true);
+ nextthink = parse_expression_leave(parser, true, false);
if (!nextthink) {
ast_unref(framenum);
parseerror(parser, "expected a think-function in [frame,think] notation");
return NULL;
}
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false);
if (!cexp || !ast_istype(cexp, ast_value)) {
if (cexp)
}
av = (var ->expression.flags & AST_FLAG_NORETURN);
ao = (proto->expression.flags & AST_FLAG_NORETURN);
- if (av != ao) {
+ if (!av != !ao) {
return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES,
"`%s` declared with different attributes%s\n"
" -> previous declaration here: %s:%i",
{
/* other globals */
if (old) {
- if (opts.standard == COMPILER_GMQCC) {
- parseerror(parser, "global `%s` already declared here: %s:%i",
- var->name, ast_ctx(old).file, ast_ctx(old).line);
+ if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
+ "global `%s` already declared here: %s:%i",
+ var->name, ast_ctx(old).file, ast_ctx(old).line))
+ {
retval = false;
goto cleanup;
- } else {
- if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
- "global `%s` already declared here: %s:%i",
- var->name, ast_ctx(old).file, ast_ctx(old).line))
- {
- retval = false;
- goto cleanup;
- }
- proto = (ast_value*)old;
- if (!ast_istype(old, ast_value)) {
- parseerror(parser, "internal error: not an ast_value");
- retval = false;
- proto = NULL;
- goto cleanup;
- }
- if (!parser_check_qualifiers(parser, var, proto)) {
- retval = false;
- proto = NULL;
- goto cleanup;
- }
- proto->expression.flags |= var->expression.flags;
- ast_delete(var);
- var = proto;
}
+ proto = (ast_value*)old;
+ if (!ast_istype(old, ast_value)) {
+ parseerror(parser, "internal error: not an ast_value");
+ retval = false;
+ proto = NULL;
+ goto cleanup;
+ }
+ if (!parser_check_qualifiers(parser, var, proto)) {
+ retval = false;
+ proto = NULL;
+ goto cleanup;
+ }
+ proto->expression.flags |= var->expression.flags;
+ ast_delete(var);
+ var = proto;
}
if (opts.standard == COMPILER_QCC &&
(old = parser_find_field(parser, var->name)))
ast_expression *cexp;
ast_value *cval;
- cexp = parse_expression_leave(parser, true);
+ cexp = parse_expression_leave(parser, true, false);
if (!cexp)
break;
}
else
{
- if (opts.standard != COMPILER_GMQCC &&
- !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+ if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
qualifier != CV_VAR)
{
var->cvq = CV_CONST;