static ast_block* parse_block(parser_t *parser, bool warnreturn);
static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
static ast_expression* parse_statement_or_block(parser_t *parser);
+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);
size_t i;
ast_value *out;
for (i = 0; i < vec_size(parser->imm_float); ++i) {
- if (parser->imm_float[i]->constval.vfloat == d)
+ const double compare = parser->imm_float[i]->constval.vfloat;
+ if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0)
return parser->imm_float[i];
}
out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_FLOAT);
return false;
}
- if (sy->ops[vec_size(sy->ops)-1].paren) {
+ if (vec_last(sy->ops).paren) {
parseerror(parser, "unmatched parenthesis");
return false;
}
- op = &operators[sy->ops[vec_size(sy->ops)-1].etype - 1];
- ctx = sy->ops[vec_size(sy->ops)-1].ctx;
+ op = &operators[vec_last(sy->ops).etype - 1];
+ ctx = vec_last(sy->ops).ctx;
DEBUGSHUNTDO(con_out("apply %s\n", op->op));
out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
break;
+ case opid2('?',':'):
+ 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));
+ parseerror(parser, "iperands 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]);
+ else
+ out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
+ break;
+
case opid1('>'):
generated_op += 1; /* INSTR_GT */
case opid1('<'):
* end of a condition is an unmatched closing paren
*/
int parens = 0;
+ int ternaries = 0;
sy.out = NULL;
sy.ops = NULL;
break;
}
+ /* a colon without a pervious question mark cannot be a ternary */
+ if (!ternaries && op->id == opid2(':','?')) {
+ parser->tok = ':';
+ break;
+ }
+
if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
olast = &operators[vec_last(sy.ops).etype-1];
vec_push(sy.ops, syop(parser_ctx(parser), op));
vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
wantop = false;
+ } else if (op->id == opid2('?',':')) {
+ wantop = false;
+ vec_push(sy.ops, syop(parser_ctx(parser), op));
+ wantop = false;
+ --ternaries;
+ } else if (op->id == opid2(':','?')) {
+ /* we don't push this operator */
+ wantop = false;
+ ++ternaries;
} else {
DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
vec_push(sy.ops, syop(parser_ctx(parser), op));
if (!parser_next(parser)) {
goto onerr;
}
- if (parser->tok == ';' || (!parens && parser->tok == ']')) {
+ if (parser->tok == ';' ||
+ (!parens && parser->tok == ']'))
+ {
break;
}
}
lex_ctx ctx = parser_ctx(parser);
+ (void)block; /* not touching */
+
/* skip the 'if', parse an optional 'not' and check for an opening paren */
if (!parser_next(parser)) {
parseerror(parser, "expected condition or 'not'");
lex_ctx ctx = parser_ctx(parser);
+ (void)block; /* not touching */
+
/* skip the 'while' and check for opening paren */
if (!parser_next(parser) || parser->tok != '(') {
parseerror(parser, "expected 'while' condition in parenthesis");
lex_ctx ctx = parser_ctx(parser);
+ (void)block; /* not touching */
+
/* skip the 'do' and get the body */
if (!parser_next(parser)) {
parseerror(parser, "expected loop body");
return false;
}
-static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
+static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
+{
+ ast_expression *exp = NULL;
+ ast_return *ret = NULL;
+ ast_value *expected = parser->function->vtype;
+
+ (void)block; /* not touching */
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected return expression");
+ return false;
+ }
+
+ if (parser->tok != ';') {
+ exp = parse_expression(parser, false);
+ if (!exp)
+ return false;
+
+ if (exp->expression.vtype != expected->expression.next->expression.vtype) {
+ parseerror(parser, "return with invalid expression");
+ }
+
+ ret = ast_return_new(exp->expression.node.context, exp);
+ if (!ret) {
+ ast_delete(exp);
+ return false;
+ }
+ } else {
+ 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");
+ }
+ ret = ast_return_new(parser_ctx(parser), NULL);
+ }
+ *out = (ast_expression*)ret;
+ return true;
+}
+
+static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
+{
+ lex_ctx ctx = parser_ctx(parser);
+
+ (void)block; /* not touching */
+
+ if (!parser_next(parser) || parser->tok != ';') {
+ parseerror(parser, "expected semicolon");
+ return false;
+ }
+
+ if (!parser_next(parser))
+ parseerror(parser, "parse error");
+
+ *out = (ast_expression*)ast_breakcont_new(ctx, is_continue);
+ return true;
+}
+
+static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out)
+{
+ ast_expression *operand;
+ ast_value *opval;
+ ast_switch *switchnode;
+ ast_switch_case swcase;
+
+ lex_ctx ctx = parser_ctx(parser);
+
+ (void)block; /* not touching */
+
+ /* parse over the opening paren */
+ if (!parser_next(parser) || parser->tok != '(') {
+ parseerror(parser, "expected switch operand in parenthesis");
+ return false;
+ }
+
+ /* parse into the expression */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected switch operand");
+ return false;
+ }
+ /* parse the operand */
+ operand = parse_expression_leave(parser, false);
+ if (!operand)
+ return false;
+
+ if (!OPTS_FLAG(RELAXED_SWITCH)) {
+ opval = (ast_value*)operand;
+ if (!ast_istype(operand, ast_value) || !opval->isconst) {
+ parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch");
+ ast_unref(operand);
+ return false;
+ }
+ }
+
+ switchnode = ast_switch_new(ctx, operand);
+
+ /* closing paren */
+ if (parser->tok != ')') {
+ ast_delete(switchnode);
+ parseerror(parser, "expected closing paren after 'switch' operand");
+ return false;
+ }
+
+ /* parse over the opening paren */
+ if (!parser_next(parser) || parser->tok != '{') {
+ ast_delete(switchnode);
+ parseerror(parser, "expected list of cases");
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ ast_delete(switchnode);
+ parseerror(parser, "expected 'case' or 'default'");
+ return false;
+ }
+
+ /* case list! */
+ while (parser->tok != '}') {
+ ast_block *caseblock;
+
+ if (parser->tok != TOKEN_KEYWORD) {
+ ast_delete(switchnode);
+ parseerror(parser, "expected 'case' or 'default'");
+ return false;
+ }
+ if (!strcmp(parser_tokval(parser), "case")) {
+ if (!parser_next(parser)) {
+ ast_delete(switchnode);
+ parseerror(parser, "expected expression for case");
+ return false;
+ }
+ swcase.value = parse_expression_leave(parser, false);
+ if (!swcase.value) {
+ ast_delete(switchnode);
+ parseerror(parser, "expected expression for case");
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "default")) {
+ swcase.value = NULL;
+ if (!parser_next(parser)) {
+ ast_delete(switchnode);
+ parseerror(parser, "expected colon");
+ return false;
+ }
+ }
+
+ /* Now the colon and body */
+ if (parser->tok != ':') {
+ if (swcase.value) ast_unref(swcase.value);
+ ast_delete(switchnode);
+ parseerror(parser, "expected colon");
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ if (swcase.value) ast_unref(swcase.value);
+ ast_delete(switchnode);
+ parseerror(parser, "expected statements or case");
+ return false;
+ }
+ caseblock = ast_block_new(parser_ctx(parser));
+ if (!caseblock) {
+ if (swcase.value) ast_unref(swcase.value);
+ ast_delete(switchnode);
+ return false;
+ }
+ swcase.code = (ast_expression*)caseblock;
+ vec_push(switchnode->cases, swcase);
+ while (true) {
+ ast_expression *expr;
+ if (parser->tok == '}')
+ break;
+ if (parser->tok == TOKEN_KEYWORD) {
+ if (!strcmp(parser_tokval(parser), "case") ||
+ !strcmp(parser_tokval(parser), "default"))
+ {
+ break;
+ }
+ }
+ if (!parse_statement(parser, caseblock, &expr, true)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ if (!expr)
+ continue;
+ vec_push(caseblock->exprs, expr);
+ }
+ }
+
+ /* closing paren */
+ if (parser->tok != '}') {
+ ast_delete(switchnode);
+ parseerror(parser, "expected closing paren of case list");
+ return false;
+ }
+ if (!parser_next(parser)) {
+ ast_delete(switchnode);
+ parseerror(parser, "parse error after switch");
+ return false;
+ }
+ *out = (ast_expression*)switchnode;
+ return true;
+}
+
+static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
{
if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
{
}
else if (!strcmp(parser_tokval(parser), "return"))
{
- ast_expression *exp = NULL;
- ast_return *ret = NULL;
- ast_value *expected = parser->function->vtype;
-
- if (!parser_next(parser)) {
- parseerror(parser, "expected return expression");
- return false;
- }
-
- if (parser->tok != ';') {
- exp = parse_expression(parser, false);
- if (!exp)
- return false;
-
- if (exp->expression.vtype != expected->expression.next->expression.vtype) {
- parseerror(parser, "return with invalid expression");
- }
-
- ret = ast_return_new(exp->expression.node.context, exp);
- if (!ret) {
- ast_delete(exp);
- return false;
- }
- } else {
- 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");
- }
- ret = ast_return_new(parser_ctx(parser), NULL);
- }
- *out = (ast_expression*)ret;
- return true;
+ return parse_return(parser, block, out);
}
else if (!strcmp(parser_tokval(parser), "if"))
{
}
return parse_for(parser, block, out);
}
+ else if (!strcmp(parser_tokval(parser), "break"))
+ {
+ return parse_break_continue(parser, block, out, false);
+ }
+ else if (!strcmp(parser_tokval(parser), "continue"))
+ {
+ return parse_break_continue(parser, block, out, true);
+ }
+ else if (!strcmp(parser_tokval(parser), "switch"))
+ {
+ return parse_switch(parser, block, out);
+ }
+ else if (!strcmp(parser_tokval(parser), "case") ||
+ !strcmp(parser_tokval(parser), "default"))
+ {
+ if (!allow_cases) {
+ parseerror(parser, "unexpected 'case' label");
+ return false;
+ }
+ return true;
+ }
parseerror(parser, "Unexpected keyword");
return false;
}
varentry_t *ve;
ve = &vec_last(parser->locals);
- if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) {
- if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name))
- rv = false;
+ if (!parser->errors) {
+ if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) {
+ if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name))
+ rv = false;
+ }
}
mem_d(ve->name);
vec_pop(parser->locals);
if (parser->tok == '}')
break;
- if (!parse_statement(parser, block, &expr)) {
+ if (!parse_statement(parser, block, &expr, false)) {
/* parseerror(parser, "parse error"); */
block = NULL;
goto cleanup;
ast_expression *expr = NULL;
if (parser->tok == '{')
return (ast_expression*)parse_block(parser, false);
- if (!parse_statement(parser, NULL, &expr))
+ if (!parse_statement(parser, NULL, &expr, false))
return NULL;
return expr;
}
/* loop method */
-static bool create_vector_members(parser_t *parser, ast_value *var, varentry_t *ve)
+static bool create_vector_members(ast_value *var, varentry_t *ve)
{
size_t i;
size_t len = strlen(var->name);
continue;
}
- if (!create_vector_members(parser, param, ve)) {
+ if (!create_vector_members(param, ve)) {
ast_block_delete(block);
return false;
}
lex_ctx ctx = ast_ctx(array);
if (from+1 == afterend) {
- // set this value
+ /* set this value */
ast_block *block;
ast_return *ret;
ast_array_index *subscript;
+ ast_store *st;
int assignop = type_store_instr[value->expression.vtype];
if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
if (!subscript)
return NULL;
- ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
+ st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
if (!st) {
ast_delete(subscript);
return NULL;
lex_ctx ctx = ast_ctx(array);
if (from+1 == afterend) {
- // set this value
+ /* set this value */
ast_block *block;
ast_return *ret;
ast_entfield *entfield;
ast_array_index *subscript;
+ ast_store *st;
int assignop = type_storep_instr[value->expression.vtype];
if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
return NULL;
}
- ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
+ st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
if (!st) {
ast_delete(entfield);
return NULL;
isvector = true;
if (isvector) {
- if (!create_vector_members(parser, var, ve)) {
+ if (!create_vector_members(var, ve)) {
retval = false;
goto cleanup;
}
return true;
}
+static uint16_t progdefs_crc_sum(uint16_t old, const char *str)
+{
+ return util_crc16(old, str, strlen(str));
+}
+
+static void progdefs_crc_file(const char *str)
+{
+ /* write to progdefs.h here */
+ (void)str;
+}
+
+static uint16_t progdefs_crc_both(uint16_t old, const char *str)
+{
+ old = progdefs_crc_sum(old, str);
+ progdefs_crc_file(str);
+ return old;
+}
+
+static void generate_checksum(parser_t *parser)
+{
+ uint16_t crc = 0xFFFF;
+ size_t i;
+
+ crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
+ crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
+ /*
+ progdefs_crc_file("\tint\tpad;\n");
+ progdefs_crc_file("\tint\tofs_return[3];\n");
+ progdefs_crc_file("\tint\tofs_parm0[3];\n");
+ progdefs_crc_file("\tint\tofs_parm1[3];\n");
+ progdefs_crc_file("\tint\tofs_parm2[3];\n");
+ progdefs_crc_file("\tint\tofs_parm3[3];\n");
+ progdefs_crc_file("\tint\tofs_parm4[3];\n");
+ progdefs_crc_file("\tint\tofs_parm5[3];\n");
+ progdefs_crc_file("\tint\tofs_parm6[3];\n");
+ progdefs_crc_file("\tint\tofs_parm7[3];\n");
+ */
+ for (i = 0; i < parser->crc_globals; ++i) {
+ if (!ast_istype(parser->globals[i].var, ast_value))
+ continue;
+ switch (parser->globals[i].var->expression.vtype) {
+ case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+ case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+ case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+ case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+ default:
+ crc = progdefs_crc_both(crc, "\tint\t");
+ break;
+ }
+ crc = progdefs_crc_both(crc, parser->globals[i].name);
+ crc = progdefs_crc_both(crc, ";\n");
+ }
+ crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
+ for (i = 0; i < parser->crc_fields; ++i) {
+ if (!ast_istype(parser->fields[i].var, ast_value))
+ continue;
+ switch (parser->fields[i].var->expression.next->expression.vtype) {
+ case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break;
+ case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
+ case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
+ case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
+ default:
+ crc = progdefs_crc_both(crc, "\tint\t");
+ break;
+ }
+ crc = progdefs_crc_both(crc, parser->fields[i].name);
+ crc = progdefs_crc_both(crc, ";\n");
+ }
+ crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
+
+ code_crc = crc;
+}
+
static parser_t *parser;
bool parser_init()
mem_d(parser);
}
-static uint16_t progdefs_crc_sum(uint16_t old, const char *str)
-{
- return util_crc16(old, str, strlen(str));
-}
-
-static void progdefs_crc_file(const char *str)
-{
- /* write to progdefs.h here */
-}
-
-static uint16_t progdefs_crc_both(uint16_t old, const char *str)
-{
- old = progdefs_crc_sum(old, str);
- progdefs_crc_file(str);
- return old;
-}
-
-static void generate_checksum(parser_t *parser)
-{
- uint16_t crc = 0xFFFF;
- size_t i;
-
- crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{");
- crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n");
- /*
- progdefs_crc_file("\tint\tpad;\n");
- progdefs_crc_file("\tint\tofs_return[3];\n");
- progdefs_crc_file("\tint\tofs_parm0[3];\n");
- progdefs_crc_file("\tint\tofs_parm1[3];\n");
- progdefs_crc_file("\tint\tofs_parm2[3];\n");
- progdefs_crc_file("\tint\tofs_parm3[3];\n");
- progdefs_crc_file("\tint\tofs_parm4[3];\n");
- progdefs_crc_file("\tint\tofs_parm5[3];\n");
- progdefs_crc_file("\tint\tofs_parm6[3];\n");
- progdefs_crc_file("\tint\tofs_parm7[3];\n");
- */
- for (i = 0; i < parser->crc_globals; ++i) {
- if (!ast_istype(parser->globals[i].var, ast_value))
- continue;
- switch (parser->globals[i].var->expression.vtype) {
- case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break;
- case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
- case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
- case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
- default:
- crc = progdefs_crc_both(crc, "\tint\t");
- break;
- }
- crc = progdefs_crc_both(crc, parser->globals[i].name);
- crc = progdefs_crc_both(crc, ";\n");
- }
- crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n");
- for (i = 0; i < parser->crc_fields; ++i) {
- if (!ast_istype(parser->fields[i].var, ast_value))
- continue;
- switch (parser->fields[i].var->expression.next->expression.vtype) {
- case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break;
- case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
- case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
- case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break;
- default:
- crc = progdefs_crc_both(crc, "\tint\t");
- break;
- }
- crc = progdefs_crc_both(crc, parser->fields[i].name);
- crc = progdefs_crc_both(crc, ";\n");
- }
- crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
- code_crc = crc;
-}
-
bool parser_finish(const char *output)
{
size_t i;
ir_builder_delete(ir);
return false;
}
+ }
+ if (opts_dump)
+ ir_builder_dump(ir, con_out);
+ for (i = 0; i < vec_size(parser->functions); ++i) {
if (!ir_function_finalize(parser->functions[i]->ir_func)) {
con_out("failed to finalize function %s\n", parser->functions[i]->name);
ir_builder_delete(ir);
}
if (retval) {
- if (opts_dump)
+ if (opts_dumpfin)
ir_builder_dump(ir, con_out);
generate_checksum(parser);