#include "gmqcc.h"
#include "lexer.h"
+typedef struct {
+ char *name;
+ ast_expression *var;
+} varentry_t;
+
typedef struct {
lex_file *lex;
int tok;
- MEM_VECTOR_MAKE(ast_value*, globals);
+ MEM_VECTOR_MAKE(varentry_t, globals);
MEM_VECTOR_MAKE(ast_function*, functions);
MEM_VECTOR_MAKE(ast_value*, imm_float);
MEM_VECTOR_MAKE(ast_value*, imm_string);
MEM_VECTOR_MAKE(ast_value*, imm_vector);
ast_function *function;
- MEM_VECTOR_MAKE(ast_value*, locals);
+ MEM_VECTOR_MAKE(varentry_t, locals);
size_t blocklocal;
size_t errors;
} parser_t;
-MEM_VEC_FUNCTIONS(parser_t, ast_value*, globals)
+MEM_VEC_FUNCTIONS(parser_t, varentry_t, globals)
MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_float)
MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_string)
MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_vector)
-MEM_VEC_FUNCTIONS(parser_t, ast_value*, locals)
+MEM_VEC_FUNCTIONS(parser_t, varentry_t, locals)
MEM_VEC_FUNCTIONS(parser_t, ast_function*, functions)
void parseerror(parser_t *parser, const char *fmt, ...)
return out;
}
-ast_value* parser_find_global(parser_t *parser, const char *name)
+ast_expression* parser_find_global(parser_t *parser, const char *name)
{
size_t i;
for (i = 0; i < parser->globals_count; ++i) {
- if (!strcmp(parser->globals[i]->name, name))
- return parser->globals[i];
+ if (!strcmp(parser->globals[i].name, name))
+ return parser->globals[i].var;
}
return NULL;
}
-ast_value* parser_find_local(parser_t *parser, const char *name, size_t upto)
+ast_expression* parser_find_local(parser_t *parser, const char *name, size_t upto)
{
size_t i;
ast_value *fun;
for (i = parser->locals_count; i > upto;) {
--i;
- if (!strcmp(parser->locals[i]->name, name))
- return parser->locals[i];
+ if (!strcmp(parser->locals[i].name, name))
+ return parser->locals[i].var;
}
fun = parser->function->vtype;
for (i = 0; i < fun->expression.params_count; ++i) {
if (!strcmp(fun->expression.params[i]->name, name))
- return fun->expression.params[i];
+ return (ast_expression*)(fun->expression.params[i]);
}
return NULL;
}
-ast_value* parser_find_var(parser_t *parser, const char *name)
+ast_expression* parser_find_var(parser_t *parser, const char *name)
{
- ast_value *v;
+ ast_expression *v;
v = parser_find_local(parser, name, 0);
if (!v) v = parser_find_global(parser, name);
return v;
shunt sy;
bool wantop = false;
+ /* count the parens because an if starts with one, so the
+ * end of a condition is an unmatched closing paren
+ */
+ int parens = 0;
+
MEM_VECTOR_INIT(&sy, out);
MEM_VECTOR_INIT(&sy, ops);
if (parser->tok == TOKEN_IDENT)
{
/* variable */
- ast_value *var = parser_find_var(parser, parser_tokval(parser));
+ ast_expression *var = parser_find_var(parser, parser_tokval(parser));
if (!var) {
parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
goto onerr;
}
- if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)var))) {
+ if (!shunt_out_add(&sy, syexp(parser_ctx(parser), var))) {
parseerror(parser, "out of memory");
goto onerr;
}
}
}
else if (parser->tok == '(') {
+ ++parens;
nextwant = false; /* not expecting an operator next */
if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) {
parseerror(parser, "out of memory");
}
}
else if (parser->tok == ')') {
+ --parens;
+ if (parens < 0)
+ break;
/* allowed for function calls */
if (!parser_close_paren(parser, &sy, true))
goto onerr;
parser->lex->flags.noops = !wantop;
} else {
if (parser->tok == '(') {
+ ++parens;
/* we expected an operator, this is the function-call operator */
if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 'f', sy.out_count-1))) {
parseerror(parser, "out of memory");
}
}
else if (parser->tok == ')') {
+ --parens;
+ if (parens < 0)
+ break;
/* we do expect an operator next */
/* closing an opening paren */
if (!parser_close_paren(parser, &sy, false))
break;
}
}
- if (!parser_next(parser)) {
+ if (parens >= 0 && !parser_next(parser)) {
parseerror(parser, "Unexpected end of file");
goto onerr;
}
}
static bool parser_variable(parser_t *parser, ast_block *localblock);
-static bool parser_body_do(parser_t *parser, ast_block *block)
+static ast_block* parser_parse_block(parser_t *parser);
+static ast_expression* parser_parse_statement_or_block(parser_t *parser);
+static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
{
if (parser->tok == TOKEN_TYPENAME)
{
/* local variable */
+ if (!block) {
+ parseerror(parser, "cannot declare a variable from here");
+ return false;
+ }
if (!parser_variable(parser, block))
return false;
+ *out = NULL;
return true;
}
else if (parser->tok == TOKEN_KEYWORD)
return false;
}
- if (!ast_block_exprs_add(block, (ast_expression*)ret)) {
- ast_delete(ret);
- return false;
- }
+ *out = (ast_expression*)ret;
} else if (!parser_next(parser)) {
parseerror(parser, "expected semicolon");
if (expected->expression.next->expression.vtype != TYPE_VOID) {
}
return true;
}
+ else if (!strcmp(parser_tokval(parser), "if"))
+ {
+ ast_ifthen *ifthen;
+ ast_expression *cond, *ontrue, *onfalse = NULL;
+
+ lex_ctx ctx = parser_ctx(parser);
+
+ /* skip the 'if' and check for opening paren */
+ if (!parser_next(parser) || parser->tok != '(') {
+ parseerror(parser, "expected 'if' condition in parenthesis");
+ return false;
+ }
+ /* parse into the expression */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected 'if' condition after opening paren");
+ return false;
+ }
+ /* parse the condition */
+ cond = parser_expression(parser);
+ if (!cond)
+ return false;
+ /* closing paren */
+ if (parser->tok != ')') {
+ parseerror(parser, "expected closing paren after 'if' condition");
+ ast_delete(cond);
+ return false;
+ }
+ /* parse into the 'then' branch */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected statement for on-true branch of 'if'");
+ ast_delete(cond);
+ return false;
+ }
+ ontrue = parser_parse_statement_or_block(parser);
+ if (!ontrue) {
+ ast_delete(cond);
+ return false;
+ }
+ /* check for an else */
+ if (!strcmp(parser_tokval(parser), "else")) {
+ /* parse into the 'else' branch */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected on-false branch after 'else'");
+ ast_delete(ontrue);
+ ast_delete(cond);
+ return false;
+ }
+ onfalse = parser_parse_statement_or_block(parser);
+ if (!onfalse) {
+ ast_delete(ontrue);
+ ast_delete(cond);
+ return false;
+ }
+ }
+
+ ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse);
+ *out = (ast_expression*)ifthen;
+ return true;
+ }
parseerror(parser, "Unexpected keyword");
return false;
}
else if (parser->tok == '{')
{
- /* a block */
- parseerror(parser, "TODO: inner blocks: %s", parser_tokval(parser));
- return false;
+ ast_block *inner;
+ inner = parser_parse_block(parser);
+ if (!inner)
+ return false;
+ *out = (ast_expression*)inner;
+ return true;
}
else
{
ast_expression *exp = parser_expression(parser);
if (!exp)
return false;
- if (!ast_block_exprs_add(block, exp)) {
- ast_delete(exp);
- return false;
- }
+ *out = exp;
return true;
}
}
+static void parser_pop_local(parser_t *parser)
+{
+ parser->locals_count--;
+ mem_d(parser->locals[parser->locals_count].name);
+}
+
static ast_block* parser_parse_block(parser_t *parser)
{
size_t oldblocklocal;
while (parser->tok != TOKEN_EOF && parser->tok < TOKEN_ERROR)
{
+ ast_expression *expr;
if (parser->tok == '}')
break;
- if (!parser_body_do(parser, block)) {
+ if (!parser_parse_statement(parser, block, &expr)) {
+ ast_block_delete(block);
+ block = NULL;
+ goto cleanup;
+ }
+ if (!expr)
+ continue;
+ if (!ast_block_exprs_add(block, expr)) {
+ ast_delete(expr);
ast_block_delete(block);
block = NULL;
goto cleanup;
cleanup:
parser->blocklocal = oldblocklocal;
+ /* unroll the local vector */
+ while (parser->locals_count > parser->blocklocal)
+ parser_pop_local(parser);
return block;
}
+static ast_expression* parser_parse_statement_or_block(parser_t *parser)
+{
+ ast_expression *expr;
+ if (parser->tok == '{')
+ return (ast_expression*)parser_parse_block(parser);
+ if (!parser_parse_statement(parser, NULL, &expr))
+ return NULL;
+ return expr;
+}
+
static bool parser_variable(parser_t *parser, ast_block *localblock)
{
bool isfunc = false;
ast_function *func = NULL;
lex_ctx ctx;
ast_value *var;
+ varentry_t varent;
int basetype = parser_token(parser)->constval.t;
var = fval;
}
- if ( (!localblock && !parser_t_globals_add(parser, var)) ||
- ( localblock && !parser_t_locals_add(parser, var)) )
+ varent.name = util_strdup(var->name);
+ varent.var = (ast_expression*)var;
+ if (var->expression.vtype == TYPE_VECTOR)
{
- ast_value_delete(var);
- return false;
+ size_t len = strlen(varent.name);
+ varentry_t vx, vy, vz;
+ vx.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 0);
+ vy.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 1);
+ vz.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 2);
+ vx.name = mem_a(len+3);
+ vy.name = mem_a(len+3);
+ vz.name = mem_a(len+3);
+ strcpy(vx.name, varent.name);
+ strcpy(vy.name, varent.name);
+ strcpy(vz.name, varent.name);
+ vx.name[len] = vy.name[len] = vz.name[len] = '_';
+ vx.name[len+1] = 'x';
+ vy.name[len+1] = 'y';
+ vz.name[len+1] = 'z';
+ vx.name[len+2] = vy.name[len+2] = vz.name[len+2] = 0;
+
+ if (!localblock) {
+ (void)!parser_t_globals_add(parser, varent);
+ (void)!parser_t_globals_add(parser, vx);
+ (void)!parser_t_globals_add(parser, vy);
+ (void)!parser_t_globals_add(parser, vz);
+ } else {
+ (void)!parser_t_locals_add(parser, varent);
+ (void)!parser_t_locals_add(parser, vx);
+ (void)!parser_t_locals_add(parser, vy);
+ (void)!parser_t_locals_add(parser, vz);
+ }
+ }
+ else
+ {
+ if ( (!localblock && !parser_t_globals_add(parser, varent)) ||
+ ( localblock && !parser_t_locals_add(parser, varent)) )
+ {
+ ast_value_delete(var);
+ return false;
+ }
}
if (localblock && !ast_block_locals_add(localblock, var))
{
- parser->locals_count--;
+ parser_pop_local(parser);
ast_value_delete(var);
return false;
}
ast_delete(parser->imm_float[i]);
}
for (i = 0; i < parser->globals_count; ++i) {
- ast_delete(parser->globals[i]);
+ ast_delete(parser->globals[i].var);
+ mem_d(parser->globals[i].name);
}
MEM_VECTOR_CLEAR(parser, globals);
}
}
for (i = 0; i < parser->globals_count; ++i) {
- if (!ast_global_codegen(parser->globals[i], ir)) {
- printf("failed to generate global %s\n", parser->globals[i]->name);
+ if (!ast_istype(parser->globals[i].var, ast_value))
+ continue;
+ if (!ast_global_codegen((ast_value*)(parser->globals[i].var), ir)) {
+ printf("failed to generate global %s\n", parser->globals[i].name);
ir_builder_delete(ir);
return false;
}