]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge branch 'master' into cooking
authorDale Weiler <killfieldengine@gmail.com>
Mon, 17 Jun 2013 20:14:26 +0000 (20:14 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Mon, 17 Jun 2013 20:14:26 +0000 (20:14 +0000)
Conflicts:
opts.def

1  2 
opts.def
parser.c

diff --combined opts.def
index 4e1aa18883075a73eaf631b58b942ebbce266c5a,071b11a951373978388a5f967d4bb65b157408dd..61226ae3719a52d7bbc2dbe3d2890b86322f26bb
+++ b/opts.def
@@@ -52,7 -52,6 +52,7 @@@
      GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
      GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
      GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
 +    GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
  #endif
  
  /* warning flags */
@@@ -90,7 -89,7 +90,8 @@@
      GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
      GMQCC_DEFINE_FLAG(DEPRECATED)
      GMQCC_DEFINE_FLAG(PARENTHESIS)
 +    GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
+     GMQCC_DEFINE_FLAG(BREAKDEF)
  #endif
  
  #ifdef GMQCC_TYPE_OPTIMIZATIONS
diff --combined parser.c
index 71242ac79a81850d57209a2f7234323e478d390f,8a7cf876948e06119fa1730340f5b0be22e6742b..38ec85e37b46ee5f8531d63c271e10e7946b3b93
+++ b/parser.c
@@@ -1034,128 -1034,8 +1034,128 @@@ static bool parser_sy_apply_operator(pa
                      exprs[0], exprs[1]);
              break;
          case opid1('^'):
 -            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
 -            return false;
 +            /*
 +             * Okay lets designate what the hell is an acceptable use
 +             * of the ^ operator. In many vector processing units, XOR
 +             * is allowed to be used on vectors, but only if the first
 +             * operand is a vector, the second operand can be a float
 +             * or vector. It's never legal for the first operand to be
 +             * a float, and then the following operand to be a vector.
 +             * Further more, the only time it is legal to do XOR otherwise
 +             * is when both operand are floats. This nicely crafted if
 +             * statement catches them all.
 +             * 
 +             * In the event that the first operand is a vector, two
 +             * possible situations can arise, thus, each element of
 +             * vector A (operand A) is exclusive-ORed with the corresponding
 +             * element of vector B (operand B), If B is scalar, the
 +             * scalar value is first replicated for each element.
 +             * 
 +             * The QCVM itself lacks a BITXOR instruction. Thus emulating
 +             * the mathematics of it is required. The following equation
 +             * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the
 +             * QCVM also lacking a BITNEG instruction, we need to emulate
 +             * ~FOO with -1 - FOO, the whole process becoming this nicely
 +             * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)).
 +             * 
 +             * When A is not scalar, this process is repeated for all
 +             * components of vector A with the value in operand B,
 +             * only if operand B is scalar. When A is not scalar, and B
 +             * is also not scalar, this process is repeated for all
 +             * components of the vector A with the components of vector B.
 +             * Finally when A is scalar and B is scalar, this process is
 +             * simply used once for A and B being LHS and RHS respectfully.
 +             * 
 +             * Yes the semantics are a bit strange (no pun intended).
 +             * But then again BITXOR is strange itself, consdering it's
 +             * commutative, assocative, and elements of the BITXOR operation
 +             * are their own inverse.
 +             */
 +            if ( !(exprs[0]->vtype == TYPE_FLOAT  && exprs[1]->vtype == TYPE_FLOAT) &&
 +                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) &&
 +                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR))
 +            {
 +                compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
 +                              type_name[exprs[0]->vtype],
 +                              type_name[exprs[1]->vtype]);
 +                return false;
 +            }
 +
 +            /*
 +             * IF the first expression is float, the following will be too
 +             * since scalar ^ vector is not allowed.
 +             */
 +            if (exprs[0]->vtype == TYPE_FLOAT) {
 +                if(CanConstFold(exprs[0], exprs[1])) {
 +                    out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
 +                } else {
 +                    ast_binary *expr = ast_binary_new(
 +                        ctx,
 +                        INSTR_SUB_F,
 +                        (ast_expression*)parser_const_float_neg1(parser),
 +                        (ast_expression*)ast_binary_new(
 +                            ctx,
 +                            INSTR_BITAND,
 +                            exprs[0],
 +                            exprs[1]
 +                        )
 +                    );
 +                    expr->refs = AST_REF_NONE;
 +                    
 +                    out = (ast_expression*)
 +                        ast_binary_new(
 +                            ctx,
 +                            INSTR_BITAND,
 +                            (ast_expression*)ast_binary_new(
 +                                ctx,
 +                                INSTR_BITOR,
 +                                exprs[0],
 +                                exprs[1]
 +                            ),
 +                            (ast_expression*)expr
 +                        );
 +                }
 +            } else {
 +                /*
 +                 * The first is a vector: vector is allowed to xor with vector and
 +                 * with scalar, branch here for the second operand.
 +                 */
 +                if (exprs[1]->vtype == TYPE_VECTOR) {
 +                    /*
 +                     * Xor all the values of the vector components against the
 +                     * vectors components in question.
 +                     */
 +                    if (CanConstFold(exprs[0], exprs[1])) {
 +                        out = (ast_expression*)parser_const_vector_f(
 +                            parser,
 +                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))),
 +                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))),
 +                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z)))
 +                        );
 +                    } else {
 +                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
 +                        return false;
 +                    }
 +                } else {
 +                    /*
 +                     * Xor all the values of the vector components against the
 +                     * scalar in question.
 +                     */
 +                    if (CanConstFold(exprs[0], exprs[1])) {
 +                        out = (ast_expression*)parser_const_vector_f(
 +                            parser,
 +                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))),
 +                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))),
 +                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1))))
 +                        );
 +                    } else {
 +                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
 +                        return false;
 +                    }
 +                }
 +            }
 +                
 +            break;
  
          case opid2('<','<'):
          case opid2('>','>'):
              } else {
                  ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
  
 -                eq->refs = (ast_binary_ref)false; /* references nothing */
 +                eq->refs = AST_REF_NONE;
  
                      /* if (lt) { */
                  out = (ast_expression*)ast_ternary_new(ctx,
@@@ -1688,7 -1568,7 +1688,7 @@@ static bool parser_close_call(parser_t 
      for (i = 0; i < paramcount; ++i)
          vec_push(call->params, sy->out[fid+1 + i].out);
      vec_shrinkby(sy->out, paramcount);
 -    (void)!ast_call_check_types(call);
 +    (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam);
      if (parser->max_param_count < paramcount)
          parser->max_param_count = paramcount;
  
@@@ -1825,12 -1705,8 +1825,12 @@@ static ast_expression* parse_vararg_do(
      ast_expression *idx, *out;
      ast_value      *typevar;
      ast_value      *funtype = parser->function->vtype;
 +    lex_ctx         ctx     = parser_ctx(parser);
  
 -    lex_ctx ctx = parser_ctx(parser);
 +    if (!parser->function->varargs) {
 +        parseerror(parser, "function has no variable argument list");
 +        return NULL;
 +    }
  
      if (!parser_next(parser) || parser->tok != '(') {
          parseerror(parser, "expected parameter index and type in parenthesis");
          return NULL;
  
      if (parser->tok != ',') {
 -        ast_unref(idx);
 -        parseerror(parser, "expected comma after parameter index");
 -        return NULL;
 +        if (parser->tok != ')') {
 +            ast_unref(idx);
 +            parseerror(parser, "expected comma after parameter index");
 +            return NULL;
 +        }
 +        /* vararg piping: ...(start) */
 +        out = (ast_expression*)ast_argpipe_new(ctx, idx);
 +        return out;
      }
  
      if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) {
          return NULL;
      }
  
 -#if 0
 -    if (!parser_next(parser)) {
 -        ast_unref(idx);
 -        ast_delete(typevar);
 -        parseerror(parser, "parse error after vararg");
 -        return NULL;
 -    }
 -#endif
 -
 -    if (!parser->function->varargs) {
 -        ast_unref(idx);
 -        ast_delete(typevar);
 -        parseerror(parser, "function has no variable argument list");
 -        return NULL;
 -    }
 -
      if (funtype->expression.varparam &&
          !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
      {
@@@ -2040,7 -1927,7 +2040,7 @@@ static bool parse_sya_operand(parser_t 
                   * it in the predef table.  And diagnose it better :)
                   */
                  if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
 -                    parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
 +                    parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
                      return false;
                  }
  
                      correct_free(&corr);
  
                      if (correct) {
 -                        parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
 +                        parseerror(parser, "unexpected identifier: %s (did you mean %s?)", parser_tokval(parser), correct);
                          mem_d(correct);
                          return false;
                      }
                  }
 -                parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
 +                parseerror(parser, "unexpected identifier: %s", parser_tokval(parser));
                  return false;
              }
          }
@@@ -2119,7 -2006,7 +2119,7 @@@ static ast_expression* parse_expression
      while (true)
      {
          if (parser->tok == TOKEN_TYPENAME) {
 -            parseerror(parser, "unexpected typename");
 +            parseerror(parser, "unexpected typename `%s`", parser_tokval(parser));
              goto onerr;
          }
  
@@@ -4454,7 -4341,7 +4454,7 @@@ static bool parse_function_body(parser_
      }
  
      vec_push(func->blocks, block);
 -    
 +
  
      parser->function = old;
      if (!parser_leaveblock(parser))
@@@ -5032,44 -4919,32 +5032,44 @@@ static ast_value *parse_arraysize(parse
          return NULL;
      }
  
 -    cexp = parse_expression_leave(parser, true, false, false);
 +    if (parser->tok != ']') {
 +        cexp = parse_expression_leave(parser, true, false, false);
  
 -    if (!cexp || !ast_istype(cexp, ast_value)) {
 -        if (cexp)
 -            ast_unref(cexp);
 -        ast_delete(var);
 -        parseerror(parser, "expected array-size as constant positive integer");
 -        return NULL;
 +        if (!cexp || !ast_istype(cexp, ast_value)) {
 +            if (cexp)
 +                ast_unref(cexp);
 +            ast_delete(var);
 +            parseerror(parser, "expected array-size as constant positive integer");
 +            return NULL;
 +        }
 +        cval = (ast_value*)cexp;
 +    }
 +    else {
 +        cexp = NULL;
 +        cval = 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 {
 +    if (cval) {
 +        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_unref(cexp);
 +            ast_delete(var);
 +            parseerror(parser, "array-size must be a positive integer constant");
 +            return NULL;
 +        }
 +
          ast_unref(cexp);
 -        ast_delete(var);
 -        parseerror(parser, "array-size must be a positive integer constant");
 -        return NULL;
 +    } else {
 +        var->expression.count = -1;
 +        var->expression.flags |= AST_FLAG_ARRAY_INIT;
      }
 -    ast_unref(cexp);
  
      if (parser->tok != ']') {
          ast_delete(var);
@@@ -5319,75 -5194,6 +5319,75 @@@ static bool parser_check_qualifiers(par
      return true;
  }
  
 +static bool create_array_accessors(parser_t *parser, ast_value *var)
 +{
 +    char name[1024];
 +    util_snprintf(name, sizeof(name), "%s##SET", var->name);
 +    if (!parser_create_array_setter(parser, var, name))
 +        return false;
 +    util_snprintf(name, sizeof(name), "%s##GET", var->name);
 +    if (!parser_create_array_getter(parser, var, var->expression.next, name))
 +        return false;
 +    return true;
 +}
 +
 +static bool parse_array(parser_t *parser, ast_value *array)
 +{
 +    size_t i;
 +    if (array->initlist) {
 +        parseerror(parser, "array already initialized elsewhere");
 +        return false;
 +    }
 +    if (!parser_next(parser)) {
 +        parseerror(parser, "parse error in array initializer");
 +        return false;
 +    }
 +    i = 0;
 +    while (parser->tok != '}') {
 +        ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false);
 +        if (!v)
 +            return false;
 +        if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) {
 +            ast_unref(v);
 +            parseerror(parser, "initializing element must be a compile time constant");
 +            return false;
 +        }
 +        vec_push(array->initlist, v->constval);
 +        if (v->expression.vtype == TYPE_STRING) {
 +            array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring);
 +            ++i;
 +        }
 +        ast_unref(v);
 +        if (parser->tok == '}')
 +            break;
 +        if (parser->tok != ',' || !parser_next(parser)) {
 +            parseerror(parser, "expected comma or '}' in element list");
 +            return false;
 +        }
 +    }
 +    if (!parser_next(parser) || parser->tok != ';') {
 +        parseerror(parser, "expected semicolon after initializer, got %s");
 +        return false;
 +    }
 +    /*
 +    if (!parser_next(parser)) {
 +        parseerror(parser, "parse error after initializer");
 +        return false;
 +    }
 +    */
 +
 +    if (array->expression.flags & AST_FLAG_ARRAY_INIT) {
 +        if (array->expression.count != (size_t)-1) {
 +            parseerror(parser, "array `%s' has already been initialized with %u elements",
 +                       array->name, (unsigned)array->expression.count);
 +        }
 +        array->expression.count = vec_size(array->initlist);
 +        if (!create_array_accessors(parser, array))
 +            return false;
 +    }
 +    return true;
 +}
 +
  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)
  {
      ast_value *var;
           * deal with arrays
           */
          if (var->expression.vtype == TYPE_ARRAY) {
 -            char name[1024];
 -            util_snprintf(name, sizeof(name), "%s##SET", var->name);
 -            if (!parser_create_array_setter(parser, var, name))
 -                goto cleanup;
 -            util_snprintf(name, sizeof(name), "%s##GET", var->name);
 -            if (!parser_create_array_getter(parser, var, var->expression.next, name))
 -                goto cleanup;
 +            if (var->expression.count != (size_t)-1) {
 +                if (!create_array_accessors(parser, var))
 +                    goto cleanup;
 +            }
          }
          else if (!localblock && !nofields &&
                   var->expression.vtype == TYPE_FIELD &&
@@@ -5887,8 -5696,16 +5887,16 @@@ skipvar
  
          if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
              if (parser->tok != '=') {
-                 parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
-                 break;
+                 if (!strcmp(parser_tokval(parser), "break")) {
+                     if (!parser_next(parser)) {
+                         parseerror(parser, "error parsing break definition");
+                         break;
+                     }
+                     (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+                 } else {
+                     parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
+                     break;
+                 }
              }
  
              if (!parser_next(parser)) {
                  parseerror(parser, "TODO: initializers for local arrays");
                  break;
              }
 -            /*
 -static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
 -*/
 -            parseerror(parser, "TODO: initializing global arrays is not supported yet!");
 -            break;
 +
 +            var->hasvalue = true;
 +            if (!parse_array(parser, var))
 +                break;
          }
          else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
          {
@@@ -6341,7 -6159,7 +6349,7 @@@ static bool parser_compile(parser_t *pa
          {
              if (!parser_global_statement(parser)) {
                  if (parser->tok == TOKEN_EOF)
 -                    parseerror(parser, "unexpected eof");
 +                    parseerror(parser, "unexpected end of file");
                  else if (compile_errors)
                      parseerror(parser, "there have been errors, bailing out");
                  lex_close(parser->lex);
@@@ -6433,6 -6251,7 +6441,6 @@@ static void parser_remove_ast(parser_t 
      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);
      ast_value_delete(parser->const_vec[0]);
      ast_value_delete(parser->const_vec[1]);
      ast_value_delete(parser->const_vec[2]);
 +    
 +    if (parser->reserved_version)
 +        ast_value_delete(parser->reserved_version);
  
      util_htdel(parser->aliases);
      intrin_intrinsics_destroy(parser);