]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Update specification.tex
[xonotic/gmqcc.git] / parser.c
index e9cbe0a031c81dc723cf5ff4cecbb7ab7dc96380..681207c192c01d55b74f565856ae537d78b6fdaa 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -62,6 +62,7 @@ typedef struct {
     size_t crc_fields;
 
     ast_function *function;
+    ht            aliases;
 
     /* All the labels the function defined...
      * Should they be in ast_function instead?
@@ -318,6 +319,9 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name)
 
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
+    ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser));
+    if (var)
+        return var;
     return (ast_expression*)util_htget(parser->htglobals, name);
 }
 
@@ -1085,23 +1089,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 else if (ConstF(0) > ConstF(1))
                     out = (ast_expression*)parser_const_float_1(parser);
             } else {
+                ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
+
+                eq->refs = (ast_binary_ref)false; /* references nothing */
+
                     /* if (lt) { */
                 out = (ast_expression*)ast_ternary_new(ctx,
                         (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]),
-
                         /* out = -1 */
                         (ast_expression*)parser_const_float_neg1(parser),
-
                     /* } else { */
                         /* if (eq) { */
-                        (ast_expression*)ast_ternary_new(ctx,
-                            (ast_expression*)ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]),
-
+                        (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq,
                             /* out = 0 */
                             (ast_expression*)parser_const_float_0(parser),
-
                         /* } else { */
-
                             /* out = 1 */
                             (ast_expression*)parser_const_float_1(parser)
                         /* } */
@@ -1828,8 +1830,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                 var = (ast_expression*)intrinsic_debug_typestring;
             }
-            else
-            {
+                
+            if (!var) {
                 char *correct = NULL;
                 size_t i;
 
@@ -2934,8 +2936,44 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
+                flags   |= AST_FLAG_ALIAS;
+                *message = NULL;
+
+                if (!parser_next(parser)) {
+                    parseerror(parser, "parse error in attribute");
+                    goto argerr;
+                }
+
+                if (parser->tok == '(') {
+                    if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+                        parseerror(parser, "`alias` attribute missing parameter");
+                        goto argerr;
+                    }
+
+                    *message = util_strdup(parser_tokval(parser));
+
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "parse error in attribute");
+                        goto argerr;
+                    }
 
+                    if (parser->tok != ')') {
+                        parseerror(parser, "`alias` attribute expected `)` after parameter");
+                        goto argerr;
+                    }
+
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "parse error in attribute");
+                        goto argerr;
+                    }
+                }
 
+                if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`alias` attribute expected `]]`");
+                    goto argerr;
+                }
+            }
             else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
                 flags   |= AST_FLAG_DEPRECATED;
                 *message = NULL;
@@ -3822,6 +3860,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     has_frame_think = false;
     old = parser->function;
 
+    if (var->expression.flags & AST_FLAG_ALIAS) {
+        parseerror(parser, "function aliases cannot have bodies");
+        return false;
+    }
+
     if (vec_size(parser->gotos) || vec_size(parser->labels)) {
         parseerror(parser, "gotos/labels leaking");
         return false;
@@ -5014,7 +5057,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
         var->cvq = qualifier;
         var->expression.flags |= qflags;
-        if (var->expression.flags & AST_FLAG_DEPRECATED)
+
+        /*
+         * store the vstring back to var for alias and
+         * deprecation messages.
+         */   
+        if (var->expression.flags & AST_FLAG_DEPRECATED ||
+            var->expression.flags & AST_FLAG_ALIAS)
             var->desc = vstring;
 
         /* Part 1:
@@ -5219,10 +5268,85 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     }
                 }
                 else {
-                    parser_addglobal(parser, var->name, (ast_expression*)var);
-                    if (isvector) {
-                        for (i = 0; i < 3; ++i) {
-                            parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+                    if (!(var->expression.flags & AST_FLAG_ALIAS)) {
+                        parser_addglobal(parser, var->name, (ast_expression*)var);
+                        if (isvector) {
+                            for (i = 0; i < 3; ++i) {
+                                parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+                            }
+                        }
+                    } else {
+                        ast_expression *find  = parser_find_global(parser, var->desc);
+
+                        if (!find) {
+                            compile_error(parser_ctx(parser), "undeclared variable `%s` for alias `%s`", var->desc, var->name);
+                            return false;
+                        }
+
+                        if (var->expression.vtype != find->expression.vtype) {
+                            char ty1[1024];
+                            char ty2[1024];
+
+                            ast_type_to_string(find,                  ty1, sizeof(ty1));
+                            ast_type_to_string((ast_expression*)var,  ty2, sizeof(ty2));
+
+                            compile_error(parser_ctx(parser), "incompatible types `%s` and `%s` for alias `%s`",
+                                ty1, ty2, var->name
+                            );
+                            return false;
+                        }
+
+                        /*
+                         * add alias to aliases table and to corrector
+                         * so corrections can apply for aliases as well.
+                         */  
+                        util_htset(parser->aliases, var->name, find);
+
+                        /*
+                         * add to corrector so corrections can work
+                         * even for aliases too.
+                         */ 
+                        correct_add (
+                             vec_last(parser->correct_variables),
+                            &vec_last(parser->correct_variables_score),
+                            var->name
+                        );
+
+                        /* generate aliases for vector components */
+                        if (isvector) {
+                            char *buffer[3];
+
+                            util_asprintf(&buffer[0], "%s_x", var->desc);
+                            util_asprintf(&buffer[1], "%s_y", var->desc);
+                            util_asprintf(&buffer[2], "%s_z", var->desc);
+
+                            util_htset(parser->aliases, me[0]->name, parser_find_global(parser, buffer[0]));
+                            util_htset(parser->aliases, me[1]->name, parser_find_global(parser, buffer[1]));
+                            util_htset(parser->aliases, me[2]->name, parser_find_global(parser, buffer[2]));
+
+                            mem_d(buffer[0]);
+                            mem_d(buffer[1]);
+                            mem_d(buffer[2]);
+
+                            /*
+                             * add to corrector so corrections can work
+                             * even for aliases too.
+                             */  
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[0]->name
+                            );
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[1]->name
+                            );
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[2]->name
+                            );
                         }
                     }
                 }
@@ -5720,6 +5844,8 @@ bool parser_init()
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
 
+    parser->aliases = util_htnew(PARSER_HT_SIZE);
+
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -5870,6 +5996,8 @@ void parser_cleanup()
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
 
+    util_htdel(parser->aliases);
+
     mem_d(parser);
 }