]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
handling if/else, shunting yard now leaves unmatched closing paren-tokens on the...
[xonotic/gmqcc.git] / parser.c
index bb85edf3c75bb9e5907154f4f39a3e057071bf82..94486d62fb38cc648f258f398bae7ad7bb6eb016 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -4,28 +4,33 @@
 #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, ...)
@@ -121,36 +126,36 @@ ast_value* parser_const_vector(parser_t *parser, vector v)
     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;
@@ -567,6 +572,11 @@ static ast_expression* parser_expression(parser_t *parser)
     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);
 
@@ -578,12 +588,12 @@ static ast_expression* parser_expression(parser_t *parser)
             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;
                 }
@@ -625,6 +635,7 @@ static ast_expression* parser_expression(parser_t *parser)
                 }
             }
             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");
@@ -632,6 +643,9 @@ static ast_expression* parser_expression(parser_t *parser)
                 }
             }
             else if (parser->tok == ')') {
+                --parens;
+                if (parens < 0)
+                    break;
                 /* allowed for function calls */
                 if (!parser_close_paren(parser, &sy, true))
                     goto onerr;
@@ -645,6 +659,7 @@ static ast_expression* parser_expression(parser_t *parser)
             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");
@@ -652,6 +667,9 @@ static ast_expression* parser_expression(parser_t *parser)
                 }
             }
             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))
@@ -708,7 +726,7 @@ static ast_expression* parser_expression(parser_t *parser)
             break;
         }
     }
-    if (!parser_next(parser)) {
+    if (parens >= 0 && !parser_next(parser)) {
         parseerror(parser, "Unexpected end of file");
         goto onerr;
     }
@@ -736,13 +754,20 @@ 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)
@@ -773,10 +798,7 @@ static bool parser_body_do(parser_t *parser, ast_block *block)
                     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) {
@@ -785,28 +807,93 @@ static bool parser_body_do(parser_t *parser, ast_block *block)
             }
             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;
@@ -824,10 +911,19 @@ static ast_block* parser_parse_block(parser_t *parser)
 
     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;
@@ -843,15 +939,29 @@ static ast_block* parser_parse_block(parser_t *parser)
 
 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;
 
@@ -922,15 +1032,51 @@ static bool parser_variable(parser_t *parser, ast_block *localblock)
             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;
         }
@@ -1114,7 +1260,8 @@ void parser_cleanup()
         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);
 
@@ -1156,8 +1303,10 @@ bool parser_finish(const char *output)
             }
         }
         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;
             }