ast_value **imm_string;
ast_value **imm_vector;
+ /* must be deleted first, they reference immediates and values */
+ ast_value **accessors;
+
ast_value *imm_float_zero;
ast_value *imm_vector_zero;
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);
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));
(ast_istype((A), ast_value) && ((ast_value*)(A))->isconst)
#define CanConstFold(A, B) \
(CanConstFold1(A) && CanConstFold1(B))
+#define CanConstFold3(A, B, C) \
+ (CanConstFold1(A) && CanConstFold1(B) && CanConstFold1(C))
#define ConstV(i) (asvalue[(i)]->constval.vvec)
#define ConstF(i) (asvalue[(i)]->constval.vfloat)
#define ConstS(i) (asvalue[(i)]->constval.vstring)
out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
break;
+ case opid2('?',':'):
+ 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;
}
}
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;
+
+ 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);
+
+ 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);
+
+ /* 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 *block;
+
+ 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;
+ }
+ block = ast_block_new(parser_ctx(parser));
+ if (!block) {
+ if (swcase.value) ast_unref(swcase.value);
+ ast_delete(switchnode);
+ return false;
+ }
+ swcase.code = (ast_expression*)block;
+ 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, block, &expr, true)) {
+ ast_delete(switchnode);
+ return false;
+ }
+ if (!expr)
+ continue;
+ vec_push(block->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;
}
{
ast_function *func = NULL;
ast_value *fval = NULL;
+ ast_block *body = NULL;
fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
if (!fval) {
return false;
}
+ body = ast_block_new(ast_ctx(array));
+ if (!body) {
+ parseerror(parser, "failed to create block for array accessor");
+ ast_delete(fval);
+ ast_delete(func);
+ return false;
+ }
+
+ vec_push(func->blocks, body);
*out = fval;
+ vec_push(parser->accessors, fval);
+
return true;
}
static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
{
ast_expression *root = NULL;
- ast_block *body = NULL;
ast_value *index = NULL;
ast_value *value = NULL;
ast_function *func;
func = fval->constval.vfunc;
fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
- body = ast_block_new(ast_ctx(array));
- if (!body) {
- parseerror(parser, "failed to create block for array accessor");
- goto cleanup;
- }
-
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
value = ast_value_copy((ast_value*)array->expression.next);
goto cleanup;
}
- vec_push(body->exprs, root);
- vec_push(func->blocks, body);
+ vec_push(func->blocks[0]->exprs, root);
array->setter = fval;
return true;
cleanup:
- if (body) ast_delete(body);
if (index) ast_delete(index);
if (value) ast_delete(value);
if (root) ast_delete(root);
static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
{
ast_expression *root = NULL;
- ast_block *body = NULL;
ast_value *entity = NULL;
ast_value *index = NULL;
ast_value *value = NULL;
func = fval->constval.vfunc;
fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
- body = ast_block_new(ast_ctx(array));
- if (!body) {
- parseerror(parser, "failed to create block for array accessor");
- goto cleanup;
- }
-
entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY);
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
value = ast_value_copy((ast_value*)array->expression.next);
goto cleanup;
}
- vec_push(body->exprs, root);
- vec_push(func->blocks, body);
+ vec_push(func->blocks[0]->exprs, root);
array->setter = fval;
return true;
cleanup:
- if (body) ast_delete(body);
if (entity) ast_delete(entity);
if (index) ast_delete(index);
if (value) ast_delete(value);
static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
{
ast_expression *root = NULL;
- ast_block *body = NULL;
ast_value *index = NULL;
ast_value *fval;
ast_function *func;
func = fval->constval.vfunc;
fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
- body = ast_block_new(ast_ctx(array));
- if (!body) {
- parseerror(parser, "failed to create block for array accessor");
- goto cleanup;
- }
-
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
if (!index) {
goto cleanup;
}
- vec_push(body->exprs, root);
- vec_push(func->blocks, body);
+ vec_push(func->blocks[0]->exprs, root);
array->getter = fval;
return true;
cleanup:
- if (body) ast_delete(body);
if (index) ast_delete(index);
if (root) ast_delete(root);
ast_delete(func);
}
/* sanity check */
- if (vec_size(params) > 8)
- parseerror(parser, "more than 8 parameters are currently not supported");
+ if (vec_size(params) > 8 && opts_standard == COMPILER_QCC)
+ (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
/* parse-out */
if (!parser_next(parser)) {
{
parser->lex = lex_open(filename);
if (!parser->lex) {
- con_out("failed to open file \"%s\"\n", filename);
+ con_err("failed to open file \"%s\"\n", filename);
+ return false;
+ }
+ return parser_compile();
+}
+
+bool parser_compile_string_len(const char *name, const char *str, size_t len)
+{
+ parser->lex = lex_open_string(str, len, name);
+ if (!parser->lex) {
+ con_err("failed to create lexer for string \"%s\"\n", name);
return false;
}
return parser_compile();
{
parser->lex = lex_open_string(str, strlen(str), name);
if (!parser->lex) {
- con_out("failed to create lexer for string \"%s\"\n", name);
+ con_err("failed to create lexer for string \"%s\"\n", name);
return false;
}
return parser_compile();
void parser_cleanup()
{
size_t i;
+ for (i = 0; i < vec_size(parser->accessors); ++i) {
+ ast_delete(parser->accessors[i]->constval.vfunc);
+ parser->accessors[i]->constval.vfunc = NULL;
+ ast_delete(parser->accessors[i]);
+ }
for (i = 0; i < vec_size(parser->functions); ++i) {
ast_delete(parser->functions[i]);
}
ast_delete(parser->globals[i].var);
mem_d(parser->globals[i].name);
}
+ vec_free(parser->accessors);
vec_free(parser->functions);
vec_free(parser->imm_vector);
vec_free(parser->imm_string);