]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
correction fix: don't try to create edits for an empty string
[xonotic/gmqcc.git] / parser.c
index cb02e742b5a117acc87bf7dc5d5e084fbb7cb477..c457d1b12a1ac728863d53063c3c8f2ffb75f53c 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -74,6 +74,10 @@ typedef struct {
     ht htglobals;
     ht *typedefs;
 
+    /* same as above but for the spelling corrector */
+    correct_trie_t  **correct_variables;
+    size_t         ***correct_variables_score;  /* vector of vector of size_t* */
+
     /* not to be used directly, we use the hash table */
     ast_expression **_locals;
     size_t          *_blocklocals;
@@ -84,18 +88,15 @@ typedef struct {
     /* we store the '=' operator info */
     const oper_info *assign_op;
 
-    /* TYPE_FIELD -> parser_find_fields is used instead of find_var
-     * TODO: TYPE_VECTOR -> x, y and z are accepted in the gmqcc standard
-     * anything else: type error
-     */
-    qcint  memberof;
-
     /* Keep track of our ternary vs parenthesis nesting state.
      * If we reach a 'comma' operator in a ternary without a paren,
      * we shall trigger -Wternary-precedence.
      */
     enum parser_pot *pot;
 
+    /* magic values */
+    ast_value *const_vec[3];
+
     /* pragma flags */
     bool noref;
 } parser_t;
@@ -105,6 +106,7 @@ static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10;
 static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
 static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring);
 static ast_block* parse_block(parser_t *parser);
@@ -551,7 +553,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         asvalue[i] = (ast_value*)exprs[i];
 
         if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
-            !(i != 0 && op->id == opid2('?',':')))
+            !(i != 0 && op->id == opid2('?',':')) &&
+            !(i == 1 && op->id == opid1('.')))
         {
             if (ast_istype(exprs[i], ast_label))
                 compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier");
@@ -584,7 +587,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             return false;
 
         case opid1('.'):
-            if (exprs[0]->expression.vtype == TYPE_ENTITY) {
+            if (exprs[0]->expression.vtype == TYPE_VECTOR &&
+                exprs[1]->expression.vtype == TYPE_NOEXPR)
+            {
+                if      (exprs[1] == (ast_expression*)parser->const_vec[0])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
+                else if (exprs[1] == (ast_expression*)parser->const_vec[1])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
+                else if (exprs[1] == (ast_expression*)parser->const_vec[2])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
+                else {
+                    parseerror(parser, "access to invalid vector component");
+                    return false;
+                }
+            }
+            else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
                 if (exprs[1]->expression.vtype != TYPE_FIELD) {
                     parseerror(parser, "type error: right hand of member-operand should be an entity-field");
                     return false;
@@ -592,7 +609,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]);
             }
             else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
-                parseerror(parser, "internal error: vector access is not supposed to be handled at this point");
+                parseerror(parser, "vectors cannot be accessed this way");
                 return false;
             }
             else {
@@ -924,14 +941,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     exprs[0], exprs[1]);
             break;
         case opid1('^'):
-            parseerror(parser, "TODO: bitxor");
+            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
             return false;
 
         case opid2('<','<'):
         case opid2('>','>'):
+            if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
+                if (op->id == opid2('<','<'))
+                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
+                else
+                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1))));
+                break;
+            }
         case opid3('<','<','='):
         case opid3('>','>','='):
-            parseerror(parser, "TODO: shifts");
+            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
             return false;
 
         case opid2('|','|'):
@@ -1507,7 +1531,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     ast_expression *expr = NULL;
     shunt sy;
     bool wantop = false;
-    bool gotmemberof = false;
     /* only warn once about an assignment in a truth value because the current code
      * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
      */
@@ -1528,11 +1551,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
     while (true)
     {
-        if (gotmemberof)
-            gotmemberof = false;
-        else
-            parser->memberof = 0;
-
         if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
             parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_"))
         {
@@ -1568,32 +1586,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         }
         else if (parser->tok == TOKEN_IDENT)
         {
+            const char     *ctoken = parser_tokval(parser);
+            ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL;
             ast_expression *var;
             if (wantop) {
                 parseerror(parser, "expected operator or end of statement");
                 goto onerr;
             }
             wantop = true;
-            /* variable */
-            if (opts.standard == COMPILER_GMQCC)
+            /* a_vector.{x,y,z} */
+            if (!vec_size(sy.ops) ||
+                !vec_last(sy.ops).etype ||
+                operators[vec_last(sy.ops).etype-1].id != opid1('.') ||
+                (prev >= intrinsic_debug_typestring &&
+                 prev <= intrinsic_debug_typestring))
             {
-                if (parser->memberof == TYPE_ENTITY) {
-                    /* still get vars first since there could be a fieldpointer */
-                    var = parser_find_var(parser, parser_tokval(parser));
-                    if (!var)
-                        var = parser_find_field(parser, parser_tokval(parser));
-                }
-                else if (parser->memberof == TYPE_VECTOR)
-                {
-                    parseerror(parser, "TODO: implement effective vector member access");
-                    goto onerr;
-                }
-                else if (parser->memberof) {
-                    parseerror(parser, "namespace for member not found");
-                    goto onerr;
-                }
-                else
-                    var = parser_find_var(parser, parser_tokval(parser));
+                /* When adding more intrinsics, fix the above condition */
+                prev = NULL;
+            }
+            if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+            {
+                var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
             } else {
                 var = parser_find_var(parser, parser_tokval(parser));
                 if (!var)
@@ -1614,13 +1627,15 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
                 else
                 {
+                    size_t i;
+                    char  *correct = NULL;
+
                     /*
                      * sometimes people use preprocessing predefs without enabling them
                      * i've done this thousands of times already myself.  Lets check for
                      * it in the predef table.  And diagnose it better :)
                      */
                     if (!OPTS_FLAG(FTEPP_PREDEFS)) {
-                        size_t i;
                         for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
                             if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
                                 parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
@@ -1629,6 +1644,34 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                         }
                     }
 
+                    /*
+                     * TODO: determine the best score for the identifier: be it
+                     * a variable, a field.
+                     *
+                     * We should also consider adding correction tables for
+                     * other things as well.
+                     */
+                    if (OPTS_FLAG(ENHANCED_DIAGNOSTICS)) {
+                        correction_t corr;
+                        correct_init(&corr);
+
+                        for (i = 0; i < vec_size(parser->correct_variables); i++) {
+                            correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
+                            if (strcmp(correct, parser_tokval(parser))) {
+                                break;
+                            } else if (correct) {
+                                mem_d(correct);
+                                correct = NULL;
+                            }
+                        }
+                        correct_free(&corr);
+
+                        if (correct) {
+                            parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                            mem_d(correct);
+                            goto onerr;
+                        }
+                    }
                     parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
                     goto onerr;
                 }
@@ -1844,24 +1887,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     olast = NULL;
             }
 
-            if (op->id == opid1('.') && opts.standard == COMPILER_GMQCC) {
-                /* for gmqcc standard: open up the namespace of the previous type */
-                ast_expression *prevex = vec_last(sy.out).out;
-                if (!prevex) {
-                    parseerror(parser, "unexpected member operator");
-                    goto onerr;
-                }
-                if (prevex->expression.vtype == TYPE_ENTITY)
-                    parser->memberof = TYPE_ENTITY;
-                else if (prevex->expression.vtype == TYPE_VECTOR)
-                    parser->memberof = TYPE_VECTOR;
-                else {
-                    parseerror(parser, "type error: type has no members");
-                    goto onerr;
-                }
-                gotmemberof = true;
-            }
-
             if (op->id == opid1('(')) {
                 if (wantop) {
                     size_t sycount = vec_size(sy.out);
@@ -1915,7 +1940,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             goto onerr;
         }
         if (parser->tok == ';' ||
-            (!parens && parser->tok == ']'))
+            (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
         {
             break;
         }
@@ -1954,8 +1979,13 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool
     ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels);
     if (!e)
         return NULL;
+    if (parser->tok != ';') {
+        parseerror(parser, "semicolon expected after expression");
+        ast_unref(e);
+        return NULL;
+    }
     if (!parser_next(parser)) {
-        ast_delete(e);
+        ast_unref(e);
         return NULL;
     }
     return e;
@@ -1968,6 +1998,10 @@ static void parser_enterblock(parser_t *parser)
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
     vec_push(parser->_block_ctx, parser_ctx(parser));
+
+    /* corrector */
+    vec_push(parser->correct_variables, correct_trie_new());
+    vec_push(parser->correct_variables_score, NULL);
 }
 
 static bool parser_leaveblock(parser_t *parser)
@@ -1981,7 +2015,11 @@ static bool parser_leaveblock(parser_t *parser)
     }
 
     util_htdel(vec_last(parser->variables));
+    correct_del(vec_last(parser->correct_variables), vec_last(parser->correct_variables_score));
+
     vec_pop(parser->variables);
+    vec_pop(parser->correct_variables);
+    vec_pop(parser->correct_variables_score);
     if (!vec_size(parser->_blocklocals)) {
         parseerror(parser, "internal error: parser_leaveblock with no block (2)");
         return false;
@@ -2008,6 +2046,7 @@ static bool parser_leaveblock(parser_t *parser)
     vec_pop(parser->typedefs);
 
     vec_pop(parser->_block_ctx);
+
     return rv;
 }
 
@@ -2015,6 +2054,26 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression *
 {
     vec_push(parser->_locals, e);
     util_htset(vec_last(parser->variables), name, (void*)e);
+
+    /* corrector */
+    correct_add (
+         vec_last(parser->correct_variables),
+        &vec_last(parser->correct_variables_score),
+        name
+    );
+}
+
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e)
+{
+    vec_push(parser->globals, e);
+    util_htset(parser->htglobals, name, e);
+
+    /* corrector */
+    correct_add (
+         parser->correct_variables[0],
+        &parser->correct_variables_score[0],
+        name
+    );
 }
 
 static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
@@ -2120,6 +2179,8 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         ast_delete(cond);
         return false;
     }
+    if (!ontrue)
+        ontrue = (ast_expression*)ast_block_new(parser_ctx(parser));
     /* check for an else */
     if (!strcmp(parser_tokval(parser), "else")) {
         /* parse into the 'else' branch */
@@ -2597,6 +2658,13 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
         return false;
     }
 
+    if (!vec_size(loops)) {
+        if (is_continue)
+            parseerror(parser, "`continue` can only be used inside loops");
+        else
+            parseerror(parser, "`break` can only be used inside loops or switches");
+    }
+
     if (parser->tok == TOKEN_IDENT) {
         if (!OPTS_FLAG(LOOP_LABELS))
             parseerror(parser, "labeled loops not activated, try using -floop-labels");
@@ -2999,28 +3067,35 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
 }
 
 /* parse computed goto sides */
-static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *side) {
+static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
     ast_expression *on_true;
     ast_expression *on_false;
+    ast_expression *cond;
 
-    if (!side)
+    if (!*side)
         return NULL;
 
-    if (ast_istype(side, ast_ternary)) {
-        on_true  = parse_goto_computed(parser, ((ast_ternary*)side)->on_true);
-        on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false);
+    if (ast_istype(*side, ast_ternary)) {
+        ast_ternary *tern = (ast_ternary*)*side;
+        on_true  = parse_goto_computed(parser, &tern->on_true);
+        on_false = parse_goto_computed(parser, &tern->on_false);
 
         if (!on_true || !on_false) {
             parseerror(parser, "expected label or expression in ternary");
-            if (((ast_ternary*)side)->on_false) ast_unref(((ast_ternary*)side)->on_false);
-            if (((ast_ternary*)side)->on_true)  ast_unref(((ast_ternary*)side)->on_true);
+            if (on_true) ast_unref(on_true);
+            if (on_false) ast_unref(on_false);
             return NULL;
         }
 
-        return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->cond, on_true, on_false);
-    } else if (ast_istype(side, ast_label)) {
-        ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)side)->name);
-        ast_goto_set_label(gt, ((ast_label*)side));
+        cond = tern->cond;
+        tern->cond = NULL;
+        ast_delete(tern);
+        *side = NULL;
+        return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false);
+    } else if (ast_istype(*side, ast_label)) {
+        ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name);
+        ast_goto_set_label(gt, ((ast_label*)*side));
+        *side = NULL;
         return (ast_expression*)gt;
     }
     return NULL;
@@ -3045,7 +3120,7 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
 
         /* failed to parse expression for goto */
         if (!(expression = parse_expression(parser, false, true)) ||
-            !(*out = parse_goto_computed(parser, expression))) {
+            !(*out = parse_goto_computed(parser, &expression))) {
             parseerror(parser, "invalid goto expression");
             ast_unref(expression);
             return false;
@@ -3348,6 +3423,106 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
 }
 
+static bool parse_enum(parser_t *parser)
+{
+    qcfloat     num = 0;
+    ast_value **values = NULL;
+    ast_value  *var = NULL;
+    ast_value  *asvalue;
+
+    ast_expression *old;
+
+    if (!parser_next(parser) || parser->tok != '{') {
+        parseerror(parser, "expected `{` after `enum` keyword");
+        return false;
+    }
+
+    while (true) {
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            if (parser->tok == '}') {
+                /* allow an empty enum */
+                break;
+            }
+            parseerror(parser, "expected identifier or `}`");
+            goto onerror;
+        }
+
+        old = parser_find_field(parser, parser_tokval(parser));
+        if (!old)
+            old = parser_find_global(parser, parser_tokval(parser));
+        if (old) {
+            parseerror(parser, "value `%s` has already been declared here: %s:%i",
+                       parser_tokval(parser), ast_ctx(old).file, ast_ctx(old).line);
+            goto onerror;
+        }
+
+        var = ast_value_new(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT);
+        vec_push(values, var);
+        var->cvq             = CV_CONST;
+        var->hasvalue        = true;
+        var->constval.vfloat = num++;
+
+        parser_addglobal(parser, var->name, (ast_expression*)var);
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected `=`, `}` or comma after identifier");
+            goto onerror;
+        }
+
+        if (parser->tok == ',')
+            continue;
+        if (parser->tok == '}')
+            break;
+        if (parser->tok != '=') {
+            parseerror(parser, "expected `=`, `}` or comma after identifier");
+            goto onerror;
+        }
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected expression after `=`");
+            goto onerror;
+        }
+
+        /* We got a value! */
+        old = parse_expression_leave(parser, true, false, false);
+        asvalue = (ast_value*)old;
+        if (!ast_istype(old, ast_value) || asvalue->cvq != CV_CONST || !asvalue->hasvalue) {
+            parseerror(parser, "enumeration value for must be a constant");
+            goto onerror;
+        }
+        num = (var->constval.vfloat = asvalue->constval.vfloat) + 1;
+
+        if (parser->tok == '}')
+            break;
+        if (parser->tok != ',') {
+            parseerror(parser, "expected `}` or comma after expression");
+            goto onerror;
+        }
+    }
+
+    if (parser->tok != '}') {
+        parseerror(parser, "internal error: breaking without `}`");
+        goto onerror;
+    }
+
+    if (!parser_next(parser) || parser->tok != ';') {
+        parseerror(parser, "expected semicolon after enumeration");
+        goto onerror;
+    }
+
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error after enumeration");
+        goto onerror;
+    }
+
+    vec_free(values);
+    return true;
+
+onerror:
+    vec_free(values);
+    return false;
+}
+
 static bool parse_block_into(parser_t *parser, ast_block *block)
 {
     bool   retval = true;
@@ -3540,8 +3715,8 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
                 return false;
             }
 
-            vec_push(parser->globals, (ast_expression*)thinkfunc);
-            util_htset(parser->htglobals, thinkfunc->name, thinkfunc);
+            parser_addglobal(parser, thinkfunc->name, (ast_expression*)thinkfunc);
+
             nextthink = (ast_expression*)thinkfunc;
 
         } else {
@@ -4757,12 +4932,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     }
                 }
                 else {
-                    vec_push(parser->globals, (ast_expression*)var);
-                    util_htset(parser->htglobals, var->name, var);
+                    parser_addglobal(parser, var->name, (ast_expression*)var);
                     if (isvector) {
                         for (i = 0; i < 3; ++i) {
-                            vec_push(parser->globals, (ast_expression*)me[i]);
-                            util_htset(parser->htglobals, me[i]->name, me[i]);
+                            parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
                         }
                     }
                 }
@@ -4783,6 +4956,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
                     /* Add it to the local scope */
                     util_htset(vec_last(parser->variables), var->name, (void*)var);
+
+                    /* corrector */
+                    correct_add (
+                         vec_last(parser->correct_variables),
+                        &vec_last(parser->correct_variables_score),
+                        var->name
+                    );
+
                     /* now rename the global */
                     ln = strlen(var->name);
                     vec_append(defname, ln, var->name);
@@ -4796,6 +4977,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         for (i = 0; i < 3; ++i) {
                             util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i]));
 
+                            /* corrector */
+                            correct_add(
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[i]->name
+                            );
+
                             vec_shrinkto(defname, prefix_len);
                             ln = strlen(me[i]->name);
                             vec_append(defname, ln, me[i]->name);
@@ -5104,6 +5292,10 @@ static bool parser_global_statement(parser_t *parser)
             return false;
         return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring);
     }
+    else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum"))
+    {
+        return parse_enum(parser);
+    }
     else if (parser->tok == TOKEN_KEYWORD)
     {
         if (!strcmp(parser_tokval(parser), "typedef")) {
@@ -5241,12 +5433,20 @@ bool parser_init()
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
 
+    /* corrector */
+    vec_push(parser->correct_variables, correct_trie_new());
+    vec_push(parser->correct_variables_score, NULL);
+
     empty_ctx.file = "<internal>";
     empty_ctx.line = 0;
     parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
     parser->nil->cvq = CV_CONST;
     if (OPTS_FLAG(UNTYPED_NIL))
         util_htset(parser->htglobals, "nil", (void*)parser->nil);
+
+    parser->const_vec[0] = ast_value_new(empty_ctx, "<vector.x>", TYPE_NOEXPR);
+    parser->const_vec[1] = ast_value_new(empty_ctx, "<vector.y>", TYPE_NOEXPR);
+    parser->const_vec[2] = ast_value_new(empty_ctx, "<vector.z>", TYPE_NOEXPR);
     return true;
 }
 
@@ -5342,6 +5542,14 @@ void parser_cleanup()
     vec_free(parser->_blocklocals);
     vec_free(parser->_locals);
 
+    /* corrector */
+    for (i = 0; i < vec_size(parser->correct_variables); ++i) {
+        correct_del(parser->correct_variables[i], parser->correct_variables_score[i]);
+    }
+    vec_free(parser->correct_variables);
+    vec_free(parser->correct_variables_score);
+
+
     for (i = 0; i < vec_size(parser->_typedefs); ++i)
         ast_delete(parser->_typedefs[i]);
     vec_free(parser->_typedefs);
@@ -5359,6 +5567,10 @@ void parser_cleanup()
 
     ast_value_delete(parser->nil);
 
+    ast_value_delete(parser->const_vec[0]);
+    ast_value_delete(parser->const_vec[1]);
+    ast_value_delete(parser->const_vec[2]);
+
     mem_d(parser);
 }