]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Implemented -f[no]enhanced-diagnostics, to enable/disable the usage of enhanced diagn...
[xonotic/gmqcc.git] / parser.c
index c0ca8b59f4ce6588c2600197a6511ecbc994ae2e..e93c7928e76375d189ff0dd3debad579be76e300 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;
@@ -549,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(',')) {
@@ -1604,13 +1618,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));
@@ -1619,6 +1635,29 @@ 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)) {
+                        for (i = 0; i < vec_size(parser->correct_variables); i++) {
+                            correct = correct_str(parser->correct_variables[i], parser_tokval(parser));
+                            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);
+                            goto onerr;
+                        }
+                    }
                     parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
                     goto onerr;
                 }
@@ -1958,6 +1997,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)
@@ -1971,7 +2014,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;
@@ -1998,6 +2045,7 @@ static bool parser_leaveblock(parser_t *parser)
     vec_pop(parser->typedefs);
 
     vec_pop(parser->_block_ctx);
+
     return rv;
 }
 
@@ -2005,6 +2053,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)
@@ -2989,28 +3044,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;
@@ -3035,7 +3097,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;
@@ -3532,6 +3594,7 @@ 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 {
@@ -4773,6 +4836,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);
@@ -4786,6 +4857,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);
@@ -5332,6 +5410,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);