]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
parse arrays
[xonotic/gmqcc.git] / parser.c
index 24bc01b28b3666a9f0d40c8a2ac6a1180a9c5cd8..f1466a1b3a4abfce274150812f3c0b1249e1b0c3 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -32,6 +32,9 @@ typedef struct {
 
     size_t errors;
 
+    /* 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
@@ -747,7 +750,14 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
         case opid1('='):
             if (ast_istype(exprs[0], ast_entfield)) {
                 ast_expression *field = ((ast_entfield*)exprs[0])->field;
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
+                    exprs[0]->expression.vtype == TYPE_FIELD &&
+                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                {
+                    assignop = type_storep_instr[TYPE_VECTOR];
+                }
+                else
+                    assignop = type_storep_instr[exprs[0]->expression.vtype];
                 if (!ast_compare_type(field->expression.next, exprs[1])) {
                     char ty1[1024];
                     char ty2[1024];
@@ -769,7 +779,14 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             }
             else
             {
-                assignop = type_store_instr[exprs[0]->expression.vtype];
+                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
+                    exprs[0]->expression.vtype == TYPE_FIELD &&
+                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                {
+                    assignop = type_store_instr[TYPE_VECTOR];
+                }
+                else
+                    assignop = type_store_instr[exprs[0]->expression.vtype];
                 if (!ast_compare_type(exprs[0], exprs[1])) {
                     char ty1[1024];
                     char ty2[1024];
@@ -1131,6 +1148,10 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             parseerror(parser, "internal error: '(' should be classified as operator");
             goto onerr;
         }
+        else if (parser->tok == '[') {
+            parseerror(parser, "internal error: '[' should be classified as operator");
+            goto onerr;
+        }
         else if (parser->tok == ')') {
             if (wantop) {
                 DEBUGSHUNTDO(printf("do[op] )\n"));
@@ -1583,7 +1604,7 @@ onerr:
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
 {
-    if (parser->tok == TOKEN_TYPENAME)
+    if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
         /* local variable */
         if (!block) {
@@ -2245,6 +2266,60 @@ on_error:
     return NULL;
 }
 
+static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
+{
+    ast_expression *cexp;
+    ast_value      *cval, *tmp;
+    lex_ctx ctx;
+
+    ctx = parser_ctx(parser);
+
+    if (!parser_next(parser)) {
+        ast_delete(var);
+        parseerror(parser, "expected array-size");
+        return NULL;
+    }
+
+    cexp = parse_expression_leave(parser, true);
+
+    if (!cexp || !ast_istype(cexp, ast_value)) {
+        if (cexp)
+            ast_delete(cexp);
+        ast_delete(var);
+        parseerror(parser, "expected array-size as constant positive integer");
+        return NULL;
+    }
+    cval = (ast_value*)cexp;
+
+    tmp = ast_value_new(ctx, "<type[]>", TYPE_ARRAY);
+    tmp->expression.next = (ast_expression*)var;
+    var = tmp;
+
+    if (cval->expression.vtype == TYPE_INTEGER)
+        tmp->expression.count = cval->constval.vint;
+    else if (cval->expression.vtype == TYPE_FLOAT)
+        tmp->expression.count = cval->constval.vfloat;
+    else {
+        ast_delete(cexp);
+        ast_delete(var);
+        parseerror(parser, "array-size must be a positive integer constant");
+        return NULL;
+    }
+    ast_delete(cexp);
+
+    if (parser->tok != ']') {
+        ast_delete(var);
+        parseerror(parser, "expected ']' after array-size");
+        return NULL;
+    }
+    if (!parser_next(parser)) {
+        ast_delete(var);
+        parseerror(parser, "error after parsing array size");
+        return NULL;
+    }
+    return var;
+}
+
 /* Parse a complete typename.
  * for single-variables (ie. function parameters or typedefs) storebase should be NULL
  * but when parsing variables separated by comma
@@ -2297,29 +2372,25 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
         return NULL;
     }
 
-    /* an opening paren now starts the parameter-list of a function */
+    /* an opening paren now starts the parameter-list of a function
+     * this is where original-QC has parameter lists.
+     * We allow a single parameter list here.
+     * Much like fteqcc we don't allow `float()() x`
+     */
     if (parser->tok == '(') {
         var = parse_parameter_list(parser, var);
         if (!var)
             return NULL;
     }
-    /* This is the point where we can turn it into a field */
-    if (isfield) {
-        /* turn it into a field if desired */
-        tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
-        tmp->expression.next = (ast_expression*)var;
-        var = tmp;
-    }
-
-    while (parser->tok == '(') {
-        var = parse_parameter_list(parser, var);
-        if (!var)
-            return NULL;
-    }
 
     /* store the base if requested */
     if (storebase) {
         *storebase = ast_value_copy(var);
+        if (isfield) {
+            tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
+            tmp->expression.next = (ast_expression*)*storebase;
+            *storebase = tmp;
+        }
     }
 
     /* there may be a name now */
@@ -2327,11 +2398,27 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
         name = util_strdup(parser_tokval(parser));
         /* parse on */
         if (!parser_next(parser)) {
+            ast_delete(var);
             parseerror(parser, "error after variable or field declaration");
             return NULL;
         }
     }
 
+    /* now this may be an array */
+    if (parser->tok == '[') {
+        var = parse_arraysize(parser, var);
+        if (!var)
+            return NULL;
+    }
+
+    /* This is the point where we can turn it into a field */
+    if (isfield) {
+        /* turn it into a field if desired */
+        tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
+        tmp->expression.next = (ast_expression*)var;
+        var = tmp;
+    }
+
     /* now there may be function parens again */
     if (parser->tok == '(' && opts_standard == COMPILER_QCC)
         parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
@@ -2340,6 +2427,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
         if (!var) {
             if (name)
                 mem_d((void*)name);
+            ast_delete(var);
             return NULL;
         }
     }
@@ -2371,6 +2459,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
     bool      isparam   = false;
     bool      isvector  = false;
     bool      cleanvar  = true;
+    bool      wasarray  = false;
 
     varentry_t varent, ve[3];
 
@@ -2387,8 +2476,32 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
     while (true) {
         proto = NULL;
+        wasarray = false;
 
         /* Part 0: finish the type */
+        if (parser->tok == '(') {
+            if (opts_standard == COMPILER_QCC)
+                parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
+            var = parse_parameter_list(parser, var);
+            if (!var) {
+                retval = false;
+                goto cleanup;
+            }
+        }
+        /* we only allow 1-dimensional arrays */
+        if (parser->tok == '[') {
+            wasarray = true;
+            var = parse_arraysize(parser, var);
+            if (!var) {
+                retval = false;
+                goto cleanup;
+            }
+        }
+        if (parser->tok == '(' && wasarray) {
+            parseerror(parser, "functions cannot return arrays");
+            /* we'll still parse the type completely for now */
+        }
+        /* for functions returning functions */
         while (parser->tok == '(') {
             if (opts_standard == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
@@ -2715,17 +2828,43 @@ skipvar:
             if (!cexp)
                 break;
 
-            cval = (ast_value*)cexp;
-            if (!ast_istype(cval, ast_value) || !cval->isconst)
-                parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
-            else
-            {
-                var->isconst = true;
-                if (cval->expression.vtype == TYPE_STRING)
-                    var->constval.vstring = parser_strdup(cval->constval.vstring);
+            if (!localblock) {
+                cval = (ast_value*)cexp;
+                if (!ast_istype(cval, ast_value) || !cval->isconst)
+                    parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
                 else
-                    memcpy(&var->constval, &cval->constval, sizeof(var->constval));
-                ast_unref(cval);
+                {
+                    var->isconst = true;
+                    if (cval->expression.vtype == TYPE_STRING)
+                        var->constval.vstring = parser_strdup(cval->constval.vstring);
+                    else
+                        memcpy(&var->constval, &cval->constval, sizeof(var->constval));
+                    ast_unref(cval);
+                }
+            } else {
+                shunt sy;
+                MEM_VECTOR_INIT(&sy, out);
+                MEM_VECTOR_INIT(&sy, ops);
+                if (!shunt_out_add(&sy, syexp(ast_ctx(var), (ast_expression*)var)) ||
+                    !shunt_out_add(&sy, syexp(ast_ctx(cexp), (ast_expression*)cexp)) ||
+                    !shunt_ops_add(&sy, syop(ast_ctx(var), parser->assign_op)))
+                {
+                    parseerror(parser, "internal error: failed to prepare initializer");
+                    ast_unref(cexp);
+                }
+                else if (!parser_sy_pop(parser, &sy))
+                    ast_unref(cexp);
+                else {
+                    if (sy.out_count != 1 && sy.ops_count != 0)
+                        parseerror(parser, "internal error: leaked operands");
+                    else if (!ast_block_exprs_add(localblock, (ast_expression*)sy.out[0].out)) {
+                        parseerror(parser, "failed to create intializing expression");
+                        ast_unref(sy.out[0].out);
+                        ast_unref(cexp);
+                    }
+                }
+                MEM_VECTOR_CLEAR(&sy, out);
+                MEM_VECTOR_CLEAR(&sy, ops);
             }
         }
 
@@ -2820,11 +2959,24 @@ static parser_t *parser;
 
 bool parser_init()
 {
+    size_t i;
     parser = (parser_t*)mem_a(sizeof(parser_t));
     if (!parser)
         return false;
 
     memset(parser, 0, sizeof(*parser));
+
+    for (i = 0; i < operator_count; ++i) {
+        if (operators[i].id == opid1('=')) {
+            parser->assign_op = operators+i;
+            break;
+        }
+    }
+    if (!parser->assign_op) {
+        printf("internal error: initializing parser: failed to find assign operator\n");
+        mem_d(parser);
+        return false;
+    }
     return true;
 }