]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Got rid of all the memleaks. We can now merge with master.
[xonotic/gmqcc.git] / parser.c
index 2ed7398bbbbbf7e0383912dfc79f036f4a39110f..b7831d4e1d9f65dfc05147f9107bdf38defa9d2b 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 */
+    ht       *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;
@@ -111,8 +115,8 @@ static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
 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, bool truthvalue);
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -300,11 +304,9 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name)
     return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
-static ast_expression* parser_find_label(parser_t *parser, const char *name) {
+static ast_expression* parser_find_label(parser_t *parser, const char *name)
+{
     size_t i;
-    if (!parser->labels)
-        return NULL;
     for(i = 0; i < vec_size(parser->labels); i++)
         if (!strcmp(parser->labels[i]->name, name))
             return (ast_expression*)parser->labels[i];
@@ -551,6 +553,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
         asvalue[i] = (ast_value*)exprs[i];
+
+        if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+            !(i != 0 && op->id == opid2('?',':')))
+        {
+            if (ast_istype(exprs[i], ast_label))
+                compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier");
+            else
+                compile_error(ast_ctx(exprs[i]), "not an expression");
+            (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i);
+        }
     }
 
     if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) {
@@ -1494,7 +1506,7 @@ static void parser_reclassify_token(parser_t *parser)
     }
 }
 
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
 {
     ast_expression *expr = NULL;
     shunt sy;
@@ -1584,14 +1596,20 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     parseerror(parser, "namespace for member not found");
                     goto onerr;
                 }
-                else if (!(var = parser_find_var(parser, parser_tokval(parser))))
-                    var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+                else
+                    var = parser_find_var(parser, parser_tokval(parser));
             } else {
                 var = parser_find_var(parser, parser_tokval(parser));
                 if (!var)
                     var = parser_find_field(parser, parser_tokval(parser));
-                if (!var)
-                    var = parser_find_label(parser, parser_tokval(parser));
+            }
+            if (!var && with_labels) {
+                var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+                if (!with_labels) {
+                    ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
+                    var = (ast_expression*)lbl;
+                    vec_push(parser->labels, lbl);
+                }
             }
             if (!var) {
                 /* intrinsics */
@@ -1600,7 +1618,46 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
                 else
                 {
-                    parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                    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)) {
+                        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));
+                                goto onerr;
+                            }
+                        }
+                    }
+
+                    /*
+                     * 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.
+                     */
+                    for (i = 0; i < vec_size(parser->correct_variables); i++) {
+                        correct = correct_str(parser->correct_variables[i], "ello");
+                        if (strcmp(correct, parser_tokval(parser))) {
+                            break;
+                        } else if (correct) {
+                            mem_d(correct);
+                        }
+                    }
+
+                    if (correct) {
+                        parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                        mem_d(correct);
+                    } else {
+                        parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                    }
+
                     goto onerr;
                 }
             }
@@ -1920,9 +1977,9 @@ onerr:
     return NULL;
 }
 
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels)
 {
-    ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
+    ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels);
     if (!e)
         return NULL;
     if (!parser_next(parser)) {
@@ -1939,6 +1996,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, util_htnew(PARSER_HT_SIZE));
+    vec_push(parser->correct_variables_score, NULL);
 }
 
 static bool parser_leaveblock(parser_t *parser)
@@ -1952,7 +2013,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;
@@ -1979,6 +2044,7 @@ static bool parser_leaveblock(parser_t *parser)
     vec_pop(parser->typedefs);
 
     vec_pop(parser->_block_ctx);
+
     return rv;
 }
 
@@ -1986,6 +2052,13 @@ 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 ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
@@ -2072,7 +2145,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2193,7 +2266,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2308,7 +2381,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2437,7 +2510,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     }
     else if (parser->tok != ';')
     {
-        initexpr = parse_expression_leave(parser, false, false);
+        initexpr = parse_expression_leave(parser, false, false, false);
         if (!initexpr)
             goto onerr;
     }
@@ -2454,7 +2527,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the condition */
     if (parser->tok != ';') {
-        cond = parse_expression_leave(parser, false, true);
+        cond = parse_expression_leave(parser, false, true, false);
         if (!cond)
             goto onerr;
     }
@@ -2471,7 +2544,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
-        increment = parse_expression_leave(parser, false, false);
+        increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
@@ -2528,7 +2601,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     }
 
     if (parser->tok != ';') {
-        exp = parse_expression(parser, false);
+        exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
 
@@ -2814,7 +2887,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         return false;
     }
     /* parse the operand */
-    operand = parse_expression_leave(parser, false, false);
+    operand = parse_expression_leave(parser, false, false, false);
     if (!operand)
         return false;
 
@@ -2878,7 +2951,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected expression for case");
                 return false;
             }
-            swcase.value = parse_expression_leave(parser, false, false);
+            swcase.value = parse_expression_leave(parser, false, false, false);
             if (!swcase.value) {
                 ast_delete(switchnode);
                 parseerror(parser, "expected expression for case");
@@ -2981,15 +3054,10 @@ static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *sid
         on_true  = parse_goto_computed(parser, ((ast_ternary*)side)->on_true);
         on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false);
 
-        if (!on_true) {
-            parseerror(parser, "expected label or expression in ternary");
-            ast_unref(((ast_ternary*)side)->on_false);
-            return NULL;
-        }
-
-        if (!on_false) {
+        if (!on_true || !on_false) {
             parseerror(parser, "expected label or expression in ternary");
-            ast_unref(((ast_ternary*)side)->on_true);
+            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);
             return NULL;
         }
 
@@ -3004,14 +3072,15 @@ static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *sid
 
 static bool parse_goto(parser_t *parser, ast_expression **out)
 {
-    size_t    i;
-    ast_goto *gt = NULL;
+    ast_goto       *gt = NULL;
+    ast_expression *lbl;
 
     if (!parser_next(parser))
         return false;
 
     if (parser->tok != TOKEN_IDENT) {
         ast_expression *expression;
+
         /* could be an expression i.e computed goto :-) */
         if (parser->tok != '(') {
             parseerror(parser, "expected label name after `goto`");
@@ -3019,9 +3088,10 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
         }
 
         /* failed to parse expression for goto */
-        if (!(expression = parse_expression(parser, false)) ||
+        if (!(expression = parse_expression(parser, false, true)) ||
             !(*out = parse_goto_computed(parser, expression))) {
             parseerror(parser, "invalid goto expression");
+            ast_unref(expression);
             return false;
         }
 
@@ -3030,14 +3100,16 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
 
     /* not computed goto */
     gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser));
-
-    for (i = 0; i < vec_size(parser->labels); ++i) {
-        if (!strcmp(parser->labels[i]->name, parser_tokval(parser))) {
-            ast_goto_set_label(gt, parser->labels[i]);
-            break;
+    lbl = parser_find_label(parser, gt->name);
+    if (lbl) {
+        if (!ast_istype(lbl, ast_label)) {
+            parseerror(parser, "internal error: label is not an ast_label");
+            ast_delete(gt);
+            return false;
         }
+        ast_goto_set_label(gt, (ast_label*)lbl);
     }
-    if (i == vec_size(parser->labels))
+    else
         vec_push(parser->gotos, gt);
 
     if (!parser_next(parser) || parser->tok != ';') {
@@ -3272,10 +3344,18 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "label must be an identifier");
             return false;
         }
-        label = ast_label_new(parser_ctx(parser), parser_tokval(parser));
-        if (!label)
-            return false;
-        vec_push(parser->labels, label);
+        label = (ast_label*)parser_find_label(parser, parser_tokval(parser));
+        if (label) {
+            if (!label->undefined) {
+                parseerror(parser, "label `%s` already defined", label->name);
+                return false;
+            }
+            label->undefined = false;
+        }
+        else {
+            label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false);
+            vec_push(parser->labels, label);
+        }
         *out = (ast_expression*)label;
         if (!parser_next(parser)) {
             parseerror(parser, "parse error after label");
@@ -3300,7 +3380,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
-        ast_expression *exp = parse_expression(parser, false);
+        ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
         *out = exp;
@@ -3461,7 +3541,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         if (!parser_next(parser))
             return false;
 
-        framenum = parse_expression_leave(parser, true, false);
+        framenum = parse_expression_leave(parser, true, false, false);
         if (!framenum) {
             parseerror(parser, "expected a framenumber constant in[frame,think] notation");
             return false;
@@ -3506,10 +3586,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
             vec_push(parser->globals, (ast_expression*)thinkfunc);
             util_htset(parser->htglobals, thinkfunc->name, thinkfunc);
+
             nextthink = (ast_expression*)thinkfunc;
 
         } else {
-            nextthink = parse_expression_leave(parser, true, false);
+            nextthink = parse_expression_leave(parser, true, false, false);
             if (!nextthink) {
                 ast_unref(framenum);
                 parseerror(parser, "expected a think-function in [frame,think] notation");
@@ -4175,7 +4256,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
         return NULL;
     }
 
-    cexp = parse_expression_leave(parser, true, false);
+    cexp = parse_expression_leave(parser, true, false, false);
 
     if (!cexp || !ast_istype(cexp, ast_value)) {
         if (cexp)
@@ -4747,6 +4828,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);
@@ -4760,6 +4849,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);
@@ -4941,7 +5037,7 @@ skipvar:
             ast_expression *cexp;
             ast_value      *cval;
 
-            cexp = parse_expression_leave(parser, true, false);
+            cexp = parse_expression_leave(parser, true, false, false);
             if (!cexp)
                 break;
 
@@ -5306,6 +5402,17 @@ 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]);
+    }
+    for (i = 0; i < vec_size(parser->correct_variables_score); ++i) {
+        vec_free(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);