]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Whitespace fixes
[xonotic/gmqcc.git] / parser.c
index 84832b8cc2d4fa9238a37da9d6a36b0583e53758..8f3638d6ea5d35920e494cc48dc5c34fab762f8c 100644 (file)
--- a/parser.c
+++ b/parser.c
 #include "gmqcc.h"
 #include "lexer.h"
 
-#define PARSER_HT_FIELDS  0
-#define PARSER_HT_GLOBALS 1
 /* beginning of locals */
 #define PARSER_HT_LOCALS  2
 
 #define PARSER_HT_SIZE    1024
 #define TYPEDEF_HT_SIZE   16
 
+enum parser_pot { POT_PAREN, POT_TERNARY1, POT_TERNARY2 };
 typedef struct {
     lex_file *lex;
     int      tok;
@@ -92,7 +91,7 @@ typedef struct {
      * If we reach a 'comma' operator in a ternary without a paren,
      * we shall trigger -Wternary-precedence.
      */
-    enum { POT_PAREN, POT_TERNARY1, POT_TERNARY2 } *pot;
+    enum parser_pot *pot;
 
     /* pragma flags */
     bool noref;
@@ -114,34 +113,34 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
-       va_list ap;
+  va_list ap;
 
-       parser->errors++;
+  parser->errors++;
 
-       va_start(ap, fmt);
+  va_start(ap, fmt);
     con_vprintmsg(LVL_ERROR, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "parse error", fmt, ap);
-       va_end(ap);
+  va_end(ap);
 }
 
 /* returns true if it counts as an error */
 static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
 {
     bool    r;
-       va_list ap;
-       va_start(ap, fmt);
-       r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
-       va_end(ap);
-       return r;
+  va_list ap;
+  va_start(ap, fmt);
+  r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap);
+  va_end(ap);
+  return r;
 }
 
 static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
 {
     bool    r;
-       va_list ap;
-       va_start(ap, fmt);
-       r = vcompile_warning(ctx, warntype, fmt, ap);
-       va_end(ap);
-       return r;
+  va_list ap;
+  va_start(ap, fmt);
+  r = vcompile_warning(ctx, warntype, fmt, ap);
+  va_end(ap);
+  return r;
 }
 
 /**********************************************************************
@@ -236,7 +235,7 @@ static char *parser_strdup(const char *str)
 {
     if (str && !*str) {
         /* actually dup empty strings */
-        char *out = mem_a(1);
+        char *out = (char*)mem_a(1);
         *out = 0;
         return out;
     }
@@ -298,12 +297,12 @@ static ast_value* parser_const_vector_0(parser_t *parser)
 
 static ast_expression* parser_find_field(parser_t *parser, const char *name)
 {
-    return util_htget(parser->htfields, name);
+    return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
-    return util_htget(parser->htglobals, name);
+    return (ast_expression*)util_htget(parser->htglobals, name);
 }
 
 static ast_expression* parser_find_param(parser_t *parser, const char *name)
@@ -330,7 +329,7 @@ static ast_expression* parser_find_local(parser_t *parser, const char *name, siz
     *isparam = false;
     for (i = vec_size(parser->variables); i > upto;) {
         --i;
-        if ( (e = util_htgeth(parser->variables[i], name, hash)) )
+        if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) )
             return e;
     }
     *isparam = true;
@@ -468,6 +467,32 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
+static bool immediate_is_true(lex_ctx ctx, ast_value *v)
+{
+    switch (v->expression.vtype) {
+        case TYPE_FLOAT:
+            return !!v->constval.vfloat;
+        case TYPE_INTEGER:
+            return !!v->constval.vint;
+        case TYPE_VECTOR:
+            if (OPTS_FLAG(CORRECT_LOGIC))
+                return v->constval.vvec.x &&
+                       v->constval.vvec.y &&
+                       v->constval.vvec.z;
+            else
+                return !!(v->constval.vvec.x);
+        case TYPE_STRING:
+            if (!v->constval.vstring)
+                return false;
+            if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                return true;
+            return !!v->constval.vstring[0];
+        default:
+            compile_error(ctx, "internal error: immediate_is_true on invalid type");
+            return !!v->constval.vfunc;
+    }
+}
+
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -526,7 +551,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
              (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
               exprs[0]->expression.vtype != T)
 #define CanConstFold1(A) \
-             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST))
+             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
+              (A)->expression.vtype != TYPE_FUNCTION)
 #define CanConstFold(A, B) \
              (CanConstFold1(A) && CanConstFold1(B))
 #define ConstV(i) (asvalue[(i)]->constval.vvec)
@@ -846,8 +872,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 #endif
             if (CanConstFold(exprs[0], exprs[1]))
             {
-                out = (ast_expression*)parser_const_float(parser,
-                    (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
+                if (OPTS_FLAG(PERL_LOGIC)) {
+                    if (immediate_is_true(ctx, asvalue[0]))
+                        out = exprs[1];
+                }
+                else
+                    out = (ast_expression*)parser_const_float(parser,
+                          ( (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);
             }
             else
             {
@@ -857,23 +891,28 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
                     return false;
                 }
-                if (OPTS_FLAG(CORRECT_LOGIC)) {
-                    /* non-floats need to be NOTed */
-                    for (i = 0; i < 2; ++i) {
-                        if (exprs[i]->expression.vtype != TYPE_FLOAT) {
-                            if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) {
-                                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                                parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2);
-                                return false;
-                            }
-                            out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]);
-                            if (!out)
-                                break;
-                            exprs[i] = out; out = NULL;
+                for (i = 0; i < 2; ++i) {
+                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) {
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
+                        if (!out) break;
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+                        if (!out) break;
+                        exprs[i] = out; out = NULL;
+                        if (OPTS_FLAG(PERL_LOGIC)) {
+                            /* here we want to keep the right expressions' type */
+                            break;
                         }
-                        if (OPTS_FLAG(PERL_LOGIC))
+                    }
+                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) {
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
+                        if (!out) break;
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+                        if (!out) break;
+                        exprs[i] = out; out = NULL;
+                        if (OPTS_FLAG(PERL_LOGIC)) {
+                            /* here we want to keep the right expressions' type */
                             break;
+                        }
                     }
                 }
                 out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
@@ -1441,7 +1480,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             val = parser_const_string(parser, parser_tokval(parser), true);
             wantop = true;
             if (!val)
-                return false;
+                return NULL;
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push string\n"));
 
@@ -1518,7 +1557,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
             val = parser_const_float(parser, (parser_token(parser)->constval.f));
             if (!val)
-                return false;
+                return NULL;
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
         }
@@ -1531,7 +1570,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
             val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
             if (!val)
-                return false;
+                return NULL;
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i));
         }
@@ -1544,7 +1583,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
             val = parser_const_string(parser, parser_tokval(parser), false);
             if (!val)
-                return false;
+                return NULL;
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push string\n"));
         }
@@ -1557,7 +1596,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
             val = parser_const_vector(parser, parser_token(parser)->constval.v);
             if (!val)
-                return false;
+                return NULL;
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push '%g %g %g'\n",
                                 parser_token(parser)->constval.v.x,
@@ -1859,10 +1898,62 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression *
     util_htset(vec_last(parser->variables), name, (void*)e);
 }
 
+static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
+{
+    bool       ifnot = false;
+    ast_unary *unary;
+    ast_expression *prev;
+
+    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
+    {
+        prev = cond;
+        cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
+        if (!cond) {
+            ast_unref(prev);
+            parseerror(parser, "internal error: failed to process condition");
+            return NULL;
+        }
+        ifnot = !ifnot;
+    }
+    else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR)
+    {
+        /* vector types need to be cast to true booleans */
+        ast_binary *bin = (ast_binary*)cond;
+        if (!OPTS_FLAG(PERL_LOGIC) || !ast_istype(cond, ast_binary) || !(bin->op == INSTR_AND || bin->op == INSTR_OR))
+        {
+            /* in perl-logic, AND and OR take care of the -fcorrect-logic */
+            prev = cond;
+            cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_V, cond);
+            if (!cond) {
+                ast_unref(prev);
+                parseerror(parser, "internal error: failed to process condition");
+                return NULL;
+            }
+            ifnot = !ifnot;
+        }
+    }
+
+    unary = (ast_unary*)cond;
+    while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+    {
+        cond = unary->operand;
+        unary->operand = NULL;
+        ast_delete(unary);
+        ifnot = !ifnot;
+        unary = (ast_unary*)cond;
+    }
+
+    if (!cond)
+        parseerror(parser, "internal error: failed to process condition");
+
+    if (ifnot) *_ifnot = !*_ifnot;
+    return cond;
+}
+
 static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_ifthen *ifthen;
-    ast_expression *cond, *ontrue, *onfalse = NULL;
+    ast_expression *cond, *ontrue = NULL, *onfalse = NULL;
     bool ifnot = false;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -1926,6 +2017,13 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         }
     }
 
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        if (ontrue)  ast_delete(ontrue);
+        if (onfalse) ast_delete(onfalse);
+        return false;
+    }
+
     if (ifnot)
         ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue);
     else
@@ -1939,6 +2037,8 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -1974,7 +2074,12 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -1984,6 +2089,8 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -2043,7 +2150,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -2053,7 +2165,9 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     ast_loop       *aloop;
     ast_expression *initexpr, *cond, *increment, *ontrue;
     ast_value      *typevar;
-    bool   retval = true;
+
+    bool retval = true;
+    bool ifnot  = false;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2146,7 +2260,12 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     if (!parse_statement_or_block(parser, &ontrue))
         goto onerr;
 
-    aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue);
+    if (cond) {
+        cond = process_condition(parser, cond, &ifnot);
+        if (!cond)
+            goto onerr;
+    }
+    aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
     *out = (ast_expression*)aloop;
 
     if (!parser_leaveblock(parser))
@@ -2231,9 +2350,48 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
     bool had_var   = false;
     bool had_noref = false;
     bool had_noreturn = false;
+    bool had_attrib = false;
 
+    *cvq = CV_NONE;
     for (;;) {
-        if (!strcmp(parser_tokval(parser), "const"))
+        if (parser->tok == TOKEN_ATTRIBUTE_OPEN) {
+            had_attrib = true;
+            /* parse an attribute */
+            if (!parser_next(parser)) {
+                parseerror(parser, "expected attribute after `[[`");
+                *cvq = CV_WRONG;
+                return false;
+            }
+            if (!strcmp(parser_tokval(parser), "noreturn")) {
+                had_noreturn = true;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
+            else if (!strcmp(parser_tokval(parser), "noref")) {
+                had_noref = true;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
+            else
+            {
+                /* Skip tokens until we hit a ]] */
+                (void)!parsewarning(parser, WARN_UNKNOWN_ATTRIBUTE, "unknown attribute starting with `%s`", parser_tokval(parser));
+                while (parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "error inside attribute");
+                        *cvq = CV_WRONG;
+                        return false;
+                    }
+                }
+            }
+        }
+        else if (!strcmp(parser_tokval(parser), "const"))
             had_const = true;
         else if (!strcmp(parser_tokval(parser), "var"))
             had_var = true;
@@ -2241,9 +2399,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo
             had_var = true;
         else if (!strcmp(parser_tokval(parser), "noref"))
             had_noref = true;
-        else if (!strcmp(parser_tokval(parser), "noreturn"))
-            had_noreturn = true;
-        else if (!had_const && !had_var && !had_noref && !had_noreturn) {
+        else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib) {
             return false;
         }
         else
@@ -2811,7 +2967,7 @@ static bool create_vector_members(ast_value *var, ast_member **me)
     size_t len = strlen(var->name);
 
     for (i = 0; i < 3; ++i) {
-        char *name = mem_a(len+3);
+        char *name = (char*)mem_a(len+3);
         memcpy(name, var->name, len);
         name[len+0] = '_';
         name[len+1] = 'x'+i;
@@ -3548,9 +3704,9 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
                 goto on_error;
             vec_push(params, param);
             if (param->expression.vtype >= TYPE_VARIANT) {
-                char typename[1024];
-                ast_type_to_string((ast_expression*)param, typename, sizeof(typename));
-                parseerror(parser, "type not supported as part of a parameter list: %s", typename);
+                char tname[1024]; /* typename is reserved in C++ */
+                ast_type_to_string((ast_expression*)param, tname, sizeof(tname));
+                parseerror(parser, "type not supported as part of a parameter list: %s", tname);
                 goto on_error;
             }
         }
@@ -4252,7 +4408,6 @@ skipvar:
         }
         else if (parser->tok == '{' || parser->tok == '[')
         {
-            size_t i;
             if (localblock) {
                 parseerror(parser, "cannot declare functions within functions");
                 break;
@@ -4445,56 +4600,56 @@ static void generate_checksum(parser_t *parser)
     size_t     i;
     ast_value *value;
 
-       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], ast_value))
-               continue;
-           value = (ast_value*)(parser->globals[i]);
-           switch (value->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, value->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], ast_value))
-               continue;
-           value = (ast_value*)(parser->fields[i]);
-           switch (value->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, value->name);
-           crc = progdefs_crc_both(crc, ";\n");
-       }
-       crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
-       code_crc = crc;
+  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], ast_value))
+          continue;
+      value = (ast_value*)(parser->globals[i]);
+      switch (value->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, value->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], ast_value))
+          continue;
+      value = (ast_value*)(parser->fields[i]);
+      switch (value->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, value->name);
+      crc = progdefs_crc_both(crc, ";\n");
+  }
+  crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
+
+  code_crc = crc;
 }
 
 static parser_t *parser;
@@ -4570,7 +4725,7 @@ bool parser_compile_file(const char *filename)
     return parser_compile();
 }
 
-bool parser_compile_string_len(const char *name, const char *str, size_t len)
+bool parser_compile_string(const char *name, const char *str, size_t len)
 {
     parser->lex = lex_open_string(str, len, name);
     if (!parser->lex) {
@@ -4580,16 +4735,6 @@ bool parser_compile_string_len(const char *name, const char *str, size_t len)
     return parser_compile();
 }
 
-bool parser_compile_string(const char *name, const char *str)
-{
-    parser->lex = lex_open_string(str, strlen(str), name);
-    if (!parser->lex) {
-        con_err("failed to create lexer for string \"%s\"\n", name);
-        return false;
-    }
-    return parser_compile();
-}
-
 void parser_cleanup()
 {
     size_t i;