Merge branch 'master' into test-suite
authorDale Weiler <killfieldengine@gmail.com>
Mon, 19 Nov 2012 02:13:46 +0000 (02:13 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Mon, 19 Nov 2012 02:13:46 +0000 (02:13 +0000)
Conflicts:
Makefile
ir.c

14 files changed:
INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile
README [new file with mode: 0644]
exec.c
ftepp.c [new file with mode: 0644]
gmqcc.h
ir.c
ir.h
lexer.c
lexer.h
main.c
opts.def
parser.c

diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..9283835
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,39 @@
+                           Installing gmqcc
+
+1. Prerequisites
+    - A C-Compiler such as gcc or clang
+    - GNU Make. This document will assume GNU-Make to be executed via
+      `make'. On BSD systems you probably have to use `gmake' instead.
+
+2. Compilation
+    Run the GNU make program `make' or `gmake'.
+
+        make
+
+    If no error appears, the following binary files will have been
+    created:
+        - gmqcc
+        - qcvm
+
+3. Installation
+    The `install' target will install the 2 binaries to /usr/local/bin
+    by default.
+    The Makefile honors the following variables:
+
+        - DESTDIR: The installation directory root.
+        - PREFIX:  The installation prefix, default: /usr/local
+        - BINDIR:  Directory for binary executables,
+                   deafult: $PREFIX/bin
+
+    To install to /usr/local run:
+
+        make install
+
+    To install to /usr run:
+
+        make PREFIX=/usr install
+
+    To install to a package-staging directory such as $pkgdir when
+    writing an ArchLinux PKGBUILD file:
+
+        make DESTDIR=$pkgdir install
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..029afc5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2012 Dale Weiler, Wolfgang Bumiller
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
index 89168d7..aa5ef11 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,9 @@ OBJ     =             \
           code.o      \
           ast.o       \
           ir.o        \
-          con.o
-          
+          con.o       \
+          ftepp.o
+
 OBJ_T = test.o util.o con.o
 OBJ_C = main.o lexer.o parser.o
 OBJ_X = exec-standalone.o util.o con.o
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..2999a47
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+                                                  The gmqcc Quake C Compiler
+
+For licensing, see the LICENSE file.
+
+For installation notes, see the INSTALL file.
diff --git a/exec.c b/exec.c
index ee61c1a..f50eac1 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -334,16 +334,15 @@ static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
     def = prog_getdef(prog, glob);
     value = (qcany*)(&prog->globals[glob]);
 
+    len = printf("[@%u] ", glob);
     if (def) {
         const char *name = prog_getstring(prog, def->name);
         if (name[0] == '#')
-            len = printf("$");
+            len += printf("$");
         else
-            len = printf("%s ", name);
+            len += printf("%s ", name);
         vtype = def->type & DEF_TYPEMASK;
     }
-    else
-        len = printf("[@%u] ", glob);
 
     switch (vtype) {
         case TYPE_VOID:
diff --git a/ftepp.c b/ftepp.c
new file mode 100644 (file)
index 0000000..1d73430
--- /dev/null
+++ b/ftepp.c
@@ -0,0 +1,1280 @@
+/*
+ * Copyright (C) 2012
+ *     Wolfgang Bumiller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "gmqcc.h"
+#include "lexer.h"
+
+typedef struct {
+    bool on;
+    bool was_on;
+    bool had_else;
+} ppcondition;
+
+typedef struct {
+    int   token;
+    char *value;
+    /* a copy from the lexer */
+    union {
+        vector v;
+        int    i;
+        double f;
+        int    t; /* type */
+    } constval;
+} pptoken;
+
+typedef struct {
+    lex_ctx ctx;
+
+    char   *name;
+    char  **params;
+    /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
+    bool    has_params;
+
+    pptoken **output;
+} ppmacro;
+
+typedef struct {
+    lex_file    *lex;
+    int          token;
+    bool         newline;
+    unsigned int errors;
+
+    bool         output_on;
+    ppcondition *conditions;
+    ppmacro    **macros;
+
+    char        *output_string;
+
+    char        *itemname;
+} ftepp_t;
+
+#define ftepp_tokval(f) ((f)->lex->tok.value)
+#define ftepp_ctx(f)    ((f)->lex->tok.ctx)
+
+static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
+{
+    va_list ap;
+
+    ftepp->errors++;
+
+    va_start(ap, fmt);
+    con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap);
+    va_end(ap);
+}
+
+static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
+{
+    va_list ap;
+
+    ftepp->errors++;
+
+    va_start(ap, fmt);
+    con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap);
+    va_end(ap);
+}
+
+static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
+{
+    va_list ap;
+    int lvl = LVL_WARNING;
+
+    if (!OPTS_WARN(warntype))
+        return false;
+
+    if (opts_werror) {
+           lvl = LVL_ERROR;
+        ftepp->errors++;
+    }
+
+    va_start(ap, fmt);
+    con_vprintmsg(lvl, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap);
+    va_end(ap);
+    return opts_werror;
+}
+
+static pptoken *pptoken_make(ftepp_t *ftepp)
+{
+    pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
+    token->token = ftepp->token;
+#if 0
+    if (token->token == TOKEN_WHITE)
+        token->value = util_strdup(" ");
+    else
+#else
+        token->value = util_strdup(ftepp_tokval(ftepp));
+#endif
+    memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
+    return token;
+}
+
+static void pptoken_delete(pptoken *self)
+{
+    mem_d(self->value);
+    mem_d(self);
+}
+
+static ppmacro *ppmacro_new(lex_ctx ctx, const char *name)
+{
+    ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
+    memset(macro, 0, sizeof(*macro));
+    macro->name = util_strdup(name);
+    return macro;
+}
+
+static void ppmacro_delete(ppmacro *self)
+{
+    size_t i;
+    for (i = 0; i < vec_size(self->params); ++i)
+        mem_d(self->params[i]);
+    vec_free(self->params);
+    for (i = 0; i < vec_size(self->output); ++i)
+        pptoken_delete(self->output[i]);
+    vec_free(self->output);
+    mem_d(self->name);
+    mem_d(self);
+}
+
+static ftepp_t* ftepp_new()
+{
+    ftepp_t *ftepp;
+
+    ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
+    memset(ftepp, 0, sizeof(*ftepp));
+
+    ftepp->output_on = true;
+
+    return ftepp;
+}
+
+static void ftepp_delete(ftepp_t *self)
+{
+    size_t i;
+    if (self->itemname)
+        mem_d(self->itemname);
+    for (i = 0; i < vec_size(self->macros); ++i)
+        ppmacro_delete(self->macros[i]);
+    vec_free(self->macros);
+    vec_free(self->conditions);
+    if (self->lex)
+        lex_close(self->lex);
+    mem_d(self);
+}
+
+static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
+{
+    if (ignore_cond || ftepp->output_on)
+    {
+        size_t len;
+        char  *data;
+        len = strlen(str);
+        data = vec_add(ftepp->output_string, len);
+        memcpy(data, str, len);
+    }
+}
+
+static void ftepp_update_output_condition(ftepp_t *ftepp)
+{
+    size_t i;
+    ftepp->output_on = true;
+    for (i = 0; i < vec_size(ftepp->conditions); ++i)
+        ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
+}
+
+static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
+{
+    size_t i;
+    for (i = 0; i < vec_size(ftepp->macros); ++i) {
+        if (!strcmp(name, ftepp->macros[i]->name))
+            return ftepp->macros[i];
+    }
+    return NULL;
+}
+
+static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
+{
+    size_t i;
+    for (i = 0; i < vec_size(ftepp->macros); ++i) {
+        if (!strcmp(name, ftepp->macros[i]->name)) {
+            vec_remove(ftepp->macros, i, 1);
+            return;
+        }
+    }
+}
+
+static inline int ftepp_next(ftepp_t *ftepp)
+{
+    return (ftepp->token = lex_do(ftepp->lex));
+}
+
+/* Important: this does not skip newlines! */
+static bool ftepp_skipspace(ftepp_t *ftepp)
+{
+    if (ftepp->token != TOKEN_WHITE)
+        return true;
+    while (ftepp_next(ftepp) == TOKEN_WHITE) {}
+    if (ftepp->token >= TOKEN_EOF) {
+        ftepp_error(ftepp, "unexpected end of preprocessor directive");
+        return false;
+    }
+    return true;
+}
+
+/* this one skips EOLs as well */
+static bool ftepp_skipallwhite(ftepp_t *ftepp)
+{
+    if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
+        return true;
+    do {
+        ftepp_next(ftepp);
+    } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
+    if (ftepp->token >= TOKEN_EOF) {
+        ftepp_error(ftepp, "unexpected end of preprocessor directive");
+        return false;
+    }
+    return true;
+}
+
+/**
+ * The huge macro parsing code...
+ */
+static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
+{
+    do {
+        ftepp_next(ftepp);
+        if (!ftepp_skipspace(ftepp))
+            return false;
+        if (ftepp->token == ')')
+            break;
+        switch (ftepp->token) {
+            case TOKEN_IDENT:
+            case TOKEN_TYPENAME:
+            case TOKEN_KEYWORD:
+                break;
+            default:
+                ftepp_error(ftepp, "unexpected token in parameter list");
+                return false;
+        }
+        vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
+        ftepp_next(ftepp);
+        if (!ftepp_skipspace(ftepp))
+            return false;
+    } while (ftepp->token == ',');
+    if (ftepp->token != ')') {
+        ftepp_error(ftepp, "expected closing paren after macro parameter list");
+        return false;
+    }
+    ftepp_next(ftepp);
+    /* skipspace happens in ftepp_define */
+    return true;
+}
+
+static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
+{
+    pptoken *ptok;
+    while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
+        ptok = pptoken_make(ftepp);
+        vec_push(macro->output, ptok);
+        ftepp_next(ftepp);
+    }
+    /* recursive expansion can cause EOFs here */
+    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
+        ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
+        return false;
+    }
+    return true;
+}
+
+static bool ftepp_define(ftepp_t *ftepp)
+{
+    ppmacro *macro;
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    switch (ftepp->token) {
+        case TOKEN_IDENT:
+        case TOKEN_TYPENAME:
+        case TOKEN_KEYWORD:
+            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+            if (macro && ftepp->output_on) {
+                if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp)))
+                    return false;
+                ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
+            }
+            macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
+            break;
+        default:
+            ftepp_error(ftepp, "expected macro name");
+            return false;
+    }
+
+    (void)ftepp_next(ftepp);
+
+    if (ftepp->token == '(') {
+        macro->has_params = true;
+        if (!ftepp_define_params(ftepp, macro))
+            return false;
+    }
+
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    if (!ftepp_define_body(ftepp, macro))
+        return false;
+
+    if (ftepp->output_on)
+        vec_push(ftepp->macros, macro);
+    else {
+        ppmacro_delete(macro);
+    }
+    return true;
+}
+
+/**
+ * When a macro is used we have to handle parameters as well
+ * as special-concatenation via ## or stringification via #
+ *
+ * Note: parenthesis can nest, so FOO((a),b) is valid, but only
+ * this kind of parens. Curly braces or [] don't count towards the
+ * paren-level.
+ */
+typedef struct {
+    pptoken **tokens;
+} macroparam;
+
+static void macroparam_clean(macroparam *self)
+{
+    size_t i;
+    for (i = 0; i < vec_size(self->tokens); ++i)
+        pptoken_delete(self->tokens[i]);
+    vec_free(self->tokens);
+}
+
+/* need to leave the last token up */
+static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
+{
+    macroparam *params = NULL;
+    pptoken    *ptok;
+    macroparam  mp;
+    size_t      parens = 0;
+    size_t      i;
+
+    if (!ftepp_skipallwhite(ftepp))
+        return false;
+    while (ftepp->token != ')') {
+        mp.tokens = NULL;
+        if (!ftepp_skipallwhite(ftepp))
+            return false;
+        while (parens || ftepp->token != ',') {
+            if (ftepp->token == '(')
+                ++parens;
+            else if (ftepp->token == ')') {
+                if (!parens)
+                    break;
+                --parens;
+            }
+            ptok = pptoken_make(ftepp);
+            vec_push(mp.tokens, ptok);
+            if (ftepp_next(ftepp) >= TOKEN_EOF) {
+                ftepp_error(ftepp, "unexpected EOF in macro call");
+                goto on_error;
+            }
+        }
+        vec_push(params, mp);
+        mp.tokens = NULL;
+        if (ftepp->token == ')')
+            break;
+        if (ftepp->token != ',') {
+            ftepp_error(ftepp, "expected closing paren or comma in macro call");
+            goto on_error;
+        }
+        if (ftepp_next(ftepp) >= TOKEN_EOF) {
+            ftepp_error(ftepp, "unexpected EOF in macro call");
+            goto on_error;
+        }
+    }
+    /* need to leave that up
+    if (ftepp_next(ftepp) >= TOKEN_EOF) {
+        ftepp_error(ftepp, "unexpected EOF in macro call");
+        goto on_error;
+    }
+    */
+    *out_params = params;
+    return true;
+
+on_error:
+    if (mp.tokens)
+        macroparam_clean(&mp);
+    for (i = 0; i < vec_size(params); ++i)
+        macroparam_clean(&params[i]);
+    vec_free(params);
+    return false;
+}
+
+static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
+{
+    size_t i;
+    for (i = 0; i < vec_size(macro->params); ++i) {
+        if (!strcmp(macro->params[i], name)) {
+            *idx = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
+{
+    char        chs[2];
+    const char *ch;
+    chs[1] = 0;
+    switch (token->token) {
+        case TOKEN_STRINGCONST:
+            ch = token->value;
+            while (*ch) {
+                /* in preprocessor mode strings already are string,
+                 * so we don't get actual newline bytes here.
+                 * Still need to escape backslashes and quotes.
+                 */
+                switch (*ch) {
+                    case '\\': ftepp_out(ftepp, "\\\\", false); break;
+                    case '"':  ftepp_out(ftepp, "\\\"", false); break;
+                    default:
+                        chs[0] = *ch;
+                        ftepp_out(ftepp, chs, false);
+                        break;
+                }
+                ++ch;
+            }
+            break;
+        case TOKEN_WHITE:
+            ftepp_out(ftepp, " ", false);
+            break;
+        case TOKEN_EOL:
+            ftepp_out(ftepp, "\\n", false);
+            break;
+        default:
+            ftepp_out(ftepp, token->value, false);
+            break;
+    }
+}
+
+static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
+{
+    size_t i;
+    ftepp_out(ftepp, "\"", false);
+    for (i = 0; i < vec_size(param->tokens); ++i)
+        ftepp_stringify_token(ftepp, param->tokens[i]);
+    ftepp_out(ftepp, "\"", false);
+}
+
+static void ftepp_recursion_header(ftepp_t *ftepp)
+{
+    ftepp_out(ftepp, "\n#pragma push(line)\n", false);
+}
+
+static void ftepp_recursion_footer(ftepp_t *ftepp)
+{
+    ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
+}
+
+static bool ftepp_preprocess(ftepp_t *ftepp);
+static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params)
+{
+    char     *old_string = ftepp->output_string;
+    lex_file *old_lexer = ftepp->lex;
+    bool retval = true;
+
+    size_t    o, pi, pv;
+    lex_file *inlex;
+
+    int nextok;
+
+    /* really ... */
+    if (!vec_size(macro->output))
+        return true;
+
+    ftepp->output_string = NULL;
+    for (o = 0; o < vec_size(macro->output); ++o) {
+        pptoken *out = macro->output[o];
+        switch (out->token) {
+            case TOKEN_IDENT:
+            case TOKEN_TYPENAME:
+            case TOKEN_KEYWORD:
+                if (!macro_params_find(macro, out->value, &pi)) {
+                    ftepp_out(ftepp, out->value, false);
+                    break;
+                } else {
+                    for (pv = 0; pv < vec_size(params[pi].tokens); ++pv) {
+                        out = params[pi].tokens[pv];
+                        if (out->token == TOKEN_EOL)
+                            ftepp_out(ftepp, "\n", false);
+                        else
+                            ftepp_out(ftepp, out->value, false);
+                    }
+                }
+                break;
+            case '#':
+                if (o + 1 < vec_size(macro->output)) {
+                    nextok = macro->output[o+1]->token;
+                    if (nextok == '#') {
+                        /* raw concatenation */
+                        ++o;
+                        break;
+                    }
+                    if ( (nextok == TOKEN_IDENT    ||
+                          nextok == TOKEN_KEYWORD  ||
+                          nextok == TOKEN_TYPENAME) &&
+                        macro_params_find(macro, macro->output[o+1]->value, &pi))
+                    {
+                        ++o;
+                        ftepp_stringify(ftepp, &params[pi]);
+                        break;
+                    }
+                }
+                ftepp_out(ftepp, "#", false);
+                break;
+            case TOKEN_EOL:
+                ftepp_out(ftepp, "\n", false);
+                break;
+            default:
+                ftepp_out(ftepp, out->value, false);
+                break;
+        }
+    }
+    vec_push(ftepp->output_string, 0);
+    /* Now run the preprocessor recursively on this string buffer */
+    /*
+    printf("__________\n%s\n=========\n", ftepp->output_string);
+    */
+    inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
+    if (!inlex) {
+        ftepp_error(ftepp, "internal error: failed to instantiate lexer");
+        retval = false;
+        goto cleanup;
+    }
+    ftepp->output_string = old_string;
+    ftepp->lex = inlex;
+    ftepp_recursion_header(ftepp);
+    if (!ftepp_preprocess(ftepp)) {
+        lex_close(ftepp->lex);
+        retval = false;
+        goto cleanup;
+    }
+    ftepp_recursion_footer(ftepp);
+    old_string = ftepp->output_string;
+
+cleanup:
+    ftepp->lex           = old_lexer;
+    ftepp->output_string = old_string;
+    return retval;
+}
+
+static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
+{
+    size_t     o;
+    macroparam *params = NULL;
+    bool        retval = true;
+
+    if (!macro->has_params) {
+        if (!ftepp_macro_expand(ftepp, macro, NULL))
+            return false;
+        ftepp_next(ftepp);
+        return true;
+    }
+    ftepp_next(ftepp);
+
+    if (!ftepp_skipallwhite(ftepp))
+        return false;
+
+    if (ftepp->token != '(') {
+        ftepp_error(ftepp, "expected macro parameters in parenthesis");
+        return false;
+    }
+
+    ftepp_next(ftepp);
+    if (!ftepp_macro_call_params(ftepp, &params))
+        return false;
+
+    if (vec_size(params) != vec_size(macro->params)) {
+        ftepp_error(ftepp, "macro %s expects %u paramteters, %u provided", macro->name,
+                    (unsigned int)vec_size(macro->params),
+                    (unsigned int)vec_size(params));
+        retval = false;
+        goto cleanup;
+    }
+
+    if (!ftepp_macro_expand(ftepp, macro, params))
+        retval = false;
+    ftepp_next(ftepp);
+
+cleanup:
+    for (o = 0; o < vec_size(params); ++o)
+        macroparam_clean(&params[o]);
+    vec_free(params);
+    return retval;
+}
+
+/**
+ * #if - the FTEQCC way:
+ *    defined(FOO) => true if FOO was #defined regardless of parameters or contents
+ *    <numbers>    => True if the number is not 0
+ *    !<factor>    => True if the factor yields false
+ *    !!<factor>   => ERROR on 2 or more unary nots
+ *    <macro>      => becomes the macro's FIRST token regardless of parameters
+ *    <e> && <e>   => True if both expressions are true
+ *    <e> || <e>   => True if either expression is true
+ *    <string>     => False
+ *    <ident>      => False (remember for macros the <macro> rule applies instead)
+ * Unary + and - are weird and wrong in fteqcc so we don't allow them
+ * parenthesis in expressions are allowed
+ * parameter lists on macros are errors
+ * No mathematical calculations are executed
+ */
+static bool ftepp_if_expr(ftepp_t *ftepp, bool *out)
+{
+    ppmacro *macro;
+    bool     wasnot = false;
+
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    while (ftepp->token == '!') {
+        wasnot = true;
+        ftepp_next(ftepp);
+        if (!ftepp_skipspace(ftepp))
+            return false;
+    }
+
+    switch (ftepp->token) {
+        case TOKEN_IDENT:
+        case TOKEN_TYPENAME:
+        case TOKEN_KEYWORD:
+            if (!strcmp(ftepp_tokval(ftepp), "defined")) {
+                ftepp_next(ftepp);
+                if (!ftepp_skipspace(ftepp))
+                    return false;
+                if (ftepp->token != '(') {
+                    ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
+                    return false;
+                }
+                ftepp_next(ftepp);
+                if (!ftepp_skipspace(ftepp))
+                    return false;
+                if (ftepp->token != TOKEN_IDENT &&
+                    ftepp->token != TOKEN_TYPENAME &&
+                    ftepp->token != TOKEN_KEYWORD)
+                {
+                    ftepp_error(ftepp, "defined() used on an unexpected token type");
+                    return false;
+                }
+                macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+                *out = !!macro;
+                ftepp_next(ftepp);
+                if (!ftepp_skipspace(ftepp))
+                    return false;
+                if (ftepp->token != ')') {
+                    ftepp_error(ftepp, "expected closing paren");
+                    return false;
+                }
+                break;
+            }
+
+            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+            if (!macro || !vec_size(macro->output)) {
+                *out = false;
+            } else {
+                /* This does not expand recursively! */
+                switch (macro->output[0]->token) {
+                    case TOKEN_INTCONST:
+                        *out = !!(macro->output[0]->constval.f);
+                        break;
+                    case TOKEN_FLOATCONST:
+                        *out = !!(macro->output[0]->constval.f);
+                        break;
+                    default:
+                        *out = false;
+                        break;
+                }
+            }
+            break;
+        case TOKEN_STRINGCONST:
+            *out = false;
+            break;
+        case TOKEN_INTCONST:
+            *out = !!(ftepp->lex->tok.constval.i);
+            break;
+        case TOKEN_FLOATCONST:
+            *out = !!(ftepp->lex->tok.constval.f);
+            break;
+
+        case '(':
+            ftepp_next(ftepp);
+            if (!ftepp_if_expr(ftepp, out))
+                return false;
+            if (ftepp->token != ')') {
+                ftepp_error(ftepp, "expected closing paren in #if expression");
+                return false;
+            }
+            break;
+
+        default:
+            ftepp_error(ftepp, "junk in #if");
+            return false;
+    }
+    if (wasnot)
+        *out = !*out;
+
+    ftepp->lex->flags.noops = false;
+    ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    ftepp->lex->flags.noops = true;
+
+    if (ftepp->token == ')')
+        return true;
+
+    if (ftepp->token != TOKEN_OPERATOR)
+        return true;
+
+    if (!strcmp(ftepp_tokval(ftepp), "&&") ||
+        !strcmp(ftepp_tokval(ftepp), "||"))
+    {
+        bool next = false;
+        char opc  = ftepp_tokval(ftepp)[0];
+
+        ftepp_next(ftepp);
+        if (!ftepp_if_expr(ftepp, &next))
+            return false;
+
+        if (opc == '&')
+            *out = *out && next;
+        else
+            *out = *out || next;
+        return true;
+    }
+    else {
+        ftepp_error(ftepp, "junk after #if");
+        return false;
+    }
+}
+
+static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
+{
+    bool result = false;
+
+    memset(cond, 0, sizeof(*cond));
+    (void)ftepp_next(ftepp);
+
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    if (ftepp->token == TOKEN_EOL) {
+        ftepp_error(ftepp, "expected expression for #if-directive");
+        return false;
+    }
+
+    if (!ftepp_if_expr(ftepp, &result))
+        return false;
+
+    cond->on = result;
+    return true;
+}
+
+/**
+ * ifdef is rather simple
+ */
+static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
+{
+    ppmacro *macro;
+    memset(cond, 0, sizeof(*cond));
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    switch (ftepp->token) {
+        case TOKEN_IDENT:
+        case TOKEN_TYPENAME:
+        case TOKEN_KEYWORD:
+            macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+            break;
+        default:
+            ftepp_error(ftepp, "expected macro name");
+            return false;
+    }
+
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    /* relaxing this condition
+    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
+        ftepp_error(ftepp, "stray tokens after #ifdef");
+        return false;
+    }
+    */
+    cond->on = !!macro;
+    return true;
+}
+
+/**
+ * undef is also simple
+ */
+static bool ftepp_undef(ftepp_t *ftepp)
+{
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    if (ftepp->output_on) {
+        switch (ftepp->token) {
+            case TOKEN_IDENT:
+            case TOKEN_TYPENAME:
+            case TOKEN_KEYWORD:
+                ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
+                break;
+            default:
+                ftepp_error(ftepp, "expected macro name");
+                return false;
+        }
+    }
+
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    /* relaxing this condition
+    if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
+        ftepp_error(ftepp, "stray tokens after #ifdef");
+        return false;
+    }
+    */
+    return true;
+}
+
+/* Special unescape-string function which skips a leading quote
+ * and stops at a quote, not just at \0
+ */
+static void unescape(const char *str, char *out) {
+    ++str;
+    while (*str && *str != '"') {
+        if (*str == '\\') {
+            ++str;
+            switch (*str) {
+                case '\\': *out++ = *str; break;
+                case '"':  *out++ = *str; break;
+                case 'a':  *out++ = '\a'; break;
+                case 'b':  *out++ = '\b'; break;
+                case 'r':  *out++ = '\r'; break;
+                case 'n':  *out++ = '\n'; break;
+                case 't':  *out++ = '\t'; break;
+                case 'f':  *out++ = '\f'; break;
+                case 'v':  *out++ = '\v'; break;
+                default:
+                    *out++ = '\\';
+                    *out++ = *str;
+                    break;
+            }
+            ++str;
+            continue;
+        }
+
+        *out++ = *str++;
+    }
+    *out = 0;
+}
+
+static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
+{
+    char *filename = NULL;
+    size_t len;
+
+    if (ftepp->itemname) {
+        const char *last_slash;
+        last_slash = strrchr(ftepp->itemname, '/');
+        if (last_slash) {
+            len = last_slash - ftepp->itemname;
+            memcpy(vec_add(filename, len), ftepp->itemname, len);
+            vec_push(filename, '/');
+        }
+        else {
+            len = strlen(ftepp->itemname);
+            memcpy(vec_add(filename, len), ftepp->itemname, len);
+            if (vec_last(filename) != '/')
+                vec_push(filename, '/');
+        }
+    }
+    len = strlen(file);
+    memcpy(vec_add(filename, len), file, len);
+    vec_push(filename, 0);
+    return filename;
+}
+
+/**
+ * Include a file.
+ * FIXME: do we need/want a -I option?
+ * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
+ */
+static bool ftepp_include(ftepp_t *ftepp)
+{
+    lex_file *old_lexer = ftepp->lex;
+    lex_file *inlex;
+    lex_ctx  ctx;
+    char     lineno[128];
+    char     *filename;
+
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    if (ftepp->token != TOKEN_STRINGCONST) {
+        ftepp_error(ftepp, "expected filename to include");
+        return false;
+    }
+
+    ctx = ftepp_ctx(ftepp);
+
+    unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
+
+    ftepp_out(ftepp, "\n#pragma file(", false);
+    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+    ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
+
+    filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
+    inlex = lex_open(filename);
+    if (!inlex) {
+        ftepp_error(ftepp, "failed to open include file `%s`", filename);
+        vec_free(filename);
+        return false;
+    }
+    vec_free(filename);
+    ftepp->lex = inlex;
+    if (!ftepp_preprocess(ftepp)) {
+        lex_close(ftepp->lex);
+        ftepp->lex = old_lexer;
+        return false;
+    }
+    lex_close(ftepp->lex);
+    ftepp->lex = old_lexer;
+
+    ftepp_out(ftepp, "\n#pragma file(", false);
+    ftepp_out(ftepp, ctx.file, false);
+    snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
+    ftepp_out(ftepp, lineno, false);
+
+    /* skip the line */
+    (void)ftepp_next(ftepp);
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    if (ftepp->token != TOKEN_EOL) {
+        ftepp_error(ftepp, "stray tokens after #include");
+        return false;
+    }
+    (void)ftepp_next(ftepp);
+
+    return true;
+}
+
+/* Basic structure handlers */
+static bool ftepp_else_allowed(ftepp_t *ftepp)
+{
+    if (!vec_size(ftepp->conditions)) {
+        ftepp_error(ftepp, "#else without #if");
+        return false;
+    }
+    if (vec_last(ftepp->conditions).had_else) {
+        ftepp_error(ftepp, "multiple #else for a single #if");
+        return false;
+    }
+    return true;
+}
+
+static bool ftepp_hash(ftepp_t *ftepp)
+{
+    ppcondition cond;
+    ppcondition *pc;
+
+    lex_ctx ctx = ftepp_ctx(ftepp);
+
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    switch (ftepp->token) {
+        case TOKEN_KEYWORD:
+        case TOKEN_IDENT:
+        case TOKEN_TYPENAME:
+            if (!strcmp(ftepp_tokval(ftepp), "define")) {
+                return ftepp_define(ftepp);
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
+                return ftepp_undef(ftepp);
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
+                if (!ftepp_ifdef(ftepp, &cond))
+                    return false;
+                cond.was_on = cond.on;
+                vec_push(ftepp->conditions, cond);
+                ftepp->output_on = ftepp->output_on && cond.on;
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
+                if (!ftepp_ifdef(ftepp, &cond))
+                    return false;
+                cond.on = !cond.on;
+                cond.was_on = cond.on;
+                vec_push(ftepp->conditions, cond);
+                ftepp->output_on = ftepp->output_on && cond.on;
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
+                if (!ftepp_else_allowed(ftepp))
+                    return false;
+                if (!ftepp_ifdef(ftepp, &cond))
+                    return false;
+                pc = &vec_last(ftepp->conditions);
+                pc->on     = !pc->was_on && cond.on;
+                pc->was_on = pc->was_on || pc->on;
+                ftepp_update_output_condition(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
+                if (!ftepp_else_allowed(ftepp))
+                    return false;
+                if (!ftepp_ifdef(ftepp, &cond))
+                    return false;
+                cond.on = !cond.on;
+                pc = &vec_last(ftepp->conditions);
+                pc->on     = !pc->was_on && cond.on;
+                pc->was_on = pc->was_on || pc->on;
+                ftepp_update_output_condition(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
+                if (!ftepp_else_allowed(ftepp))
+                    return false;
+                if (!ftepp_if(ftepp, &cond))
+                    return false;
+                pc = &vec_last(ftepp->conditions);
+                pc->on     = !pc->was_on && cond.on;
+                pc->was_on = pc->was_on  || pc->on;
+                ftepp_update_output_condition(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "if")) {
+                if (!ftepp_if(ftepp, &cond))
+                    return false;
+                cond.was_on = cond.on;
+                vec_push(ftepp->conditions, cond);
+                ftepp->output_on = ftepp->output_on && cond.on;
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "else")) {
+                if (!ftepp_else_allowed(ftepp))
+                    return false;
+                pc = &vec_last(ftepp->conditions);
+                pc->on = !pc->was_on;
+                pc->had_else = true;
+                ftepp_next(ftepp);
+                ftepp_update_output_condition(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
+                if (!vec_size(ftepp->conditions)) {
+                    ftepp_error(ftepp, "#endif without #if");
+                    return false;
+                }
+                vec_pop(ftepp->conditions);
+                ftepp_next(ftepp);
+                ftepp_update_output_condition(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "include")) {
+                return ftepp_include(ftepp);
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
+                ftepp_out(ftepp, "#", false);
+                break;
+            }
+            else {
+                ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
+                return false;
+            }
+            break;
+        default:
+            ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
+            return false;
+        case TOKEN_EOL:
+            ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
+            return false;
+        case TOKEN_EOF:
+            ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
+            return false;
+
+        /* Builtins! Don't forget the builtins! */
+        case TOKEN_INTCONST:
+        case TOKEN_FLOATCONST:
+            ftepp_out(ftepp, "#", false);
+            return true;
+    }
+    if (!ftepp_skipspace(ftepp))
+        return false;
+    return true;
+}
+
+static bool ftepp_preprocess(ftepp_t *ftepp)
+{
+    ppmacro *macro;
+    bool     newline = true;
+
+    ftepp->lex->flags.preprocessing = true;
+    ftepp->lex->flags.mergelines    = false;
+    ftepp->lex->flags.noops         = true;
+
+    ftepp_next(ftepp);
+    do
+    {
+        if (ftepp->token >= TOKEN_EOF)
+            break;
+#if 0
+        ftepp->newline = newline;
+        newline = false;
+#else
+        /* For the sake of FTE compatibility... FU, really */
+        ftepp->newline = newline = true;
+#endif
+
+        switch (ftepp->token) {
+            case TOKEN_KEYWORD:
+            case TOKEN_IDENT:
+            case TOKEN_TYPENAME:
+                macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+                if (!macro) {
+                    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+                    ftepp_next(ftepp);
+                    break;
+                }
+                if (!ftepp_macro_call(ftepp, macro))
+                    ftepp->token = TOKEN_ERROR;
+                break;
+            case '#':
+                if (!ftepp->newline) {
+                    ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+                    ftepp_next(ftepp);
+                    break;
+                }
+                ftepp->lex->flags.mergelines = true;
+                if (ftepp_next(ftepp) >= TOKEN_EOF) {
+                    ftepp_error(ftepp, "error in preprocessor directive");
+                    ftepp->token = TOKEN_ERROR;
+                    break;
+                }
+                if (!ftepp_hash(ftepp))
+                    ftepp->token = TOKEN_ERROR;
+                ftepp->lex->flags.mergelines = false;
+                break;
+            case TOKEN_EOL:
+                newline = true;
+                ftepp_out(ftepp, "\n", true);
+                ftepp_next(ftepp);
+                break;
+            default:
+                ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+                ftepp_next(ftepp);
+                break;
+        }
+    } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
+
+    newline = ftepp->token == TOKEN_EOF;
+    return newline;
+}
+
+/* Like in parser.c - files keep the previous state so we have one global
+ * preprocessor. Except here we will want to warn about dangling #ifs.
+ */
+static ftepp_t *ftepp;
+
+static bool ftepp_preprocess_done()
+{
+    bool retval = true;
+    lex_close(ftepp->lex);
+    ftepp->lex = NULL;
+    if (vec_size(ftepp->conditions)) {
+        if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
+            retval = false;
+    }
+    if (ftepp->itemname) {
+        mem_d(ftepp->itemname);
+        ftepp->itemname = NULL;
+    }
+    return retval;
+}
+
+bool ftepp_preprocess_file(const char *filename)
+{
+    ftepp->lex = lex_open(filename);
+    ftepp->itemname = util_strdup(filename);
+    if (!ftepp->lex) {
+        con_out("failed to open file \"%s\"\n", filename);
+        return false;
+    }
+    if (!ftepp_preprocess(ftepp)) {
+        ftepp_delete(ftepp);
+        return false;
+    }
+    return ftepp_preprocess_done();
+}
+
+bool ftepp_preprocess_string(const char *name, const char *str)
+{
+    ftepp->lex = lex_open_string(str, strlen(str), name);
+    ftepp->itemname = util_strdup(name);
+    if (!ftepp->lex) {
+        con_out("failed to create lexer for string \"%s\"\n", name);
+        return false;
+    }
+    if (!ftepp_preprocess(ftepp)) {
+        ftepp_delete(ftepp);
+        return false;
+    }
+    return ftepp_preprocess_done();
+}
+
+bool ftepp_init()
+{
+    ftepp = ftepp_new();
+    return !!ftepp;
+}
+
+const char *ftepp_get()
+{
+    return ftepp->output_string;
+}
+
+void ftepp_flush()
+{
+    vec_free(ftepp->output_string);
+}
+
+void ftepp_finish()
+{
+    if (!ftepp)
+        return;
+    ftepp_delete(ftepp);
+    ftepp = NULL;
+}
diff --git a/gmqcc.h b/gmqcc.h
index 5136903..265bee5 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -752,6 +752,18 @@ bool parser_compile_file  (const char *filename);
 bool parser_compile_string(const char *name, const char *str);
 bool parser_finish        (const char *output);
 void parser_cleanup       ();
+/* There's really no need to strlen() preprocessed files */
+bool parser_compile_string_len(const char *name, const char *str, size_t len);
+
+/*===================================================================*/
+/*====================== ftepp.c commandline ========================*/
+/*===================================================================*/
+bool ftepp_init             ();
+bool ftepp_preprocess_file  (const char *filename);
+bool ftepp_preprocess_string(const char *name, const char *str);
+void ftepp_finish           ();
+const char *ftepp_get       ();
+void ftepp_flush            ();
 
 /*===================================================================*/
 /*======================= main.c commandline ========================*/
diff --git a/ir.c b/ir.c
index 5f037ec..9907b35 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -259,6 +259,7 @@ ir_builder* ir_builder_new(const char *modulename)
     self->functions   = NULL;
     self->globals     = NULL;
     self->fields      = NULL;
+    self->extparams   = NULL;
     self->filenames   = NULL;
     self->filestrings = NULL;
 
@@ -280,6 +281,10 @@ void ir_builder_delete(ir_builder* self)
         ir_function_delete_quick(self->functions[i]);
     }
     vec_free(self->functions);
+    for (i = 0; i != vec_size(self->extparams); ++i) {
+        ir_value_delete(self->extparams[i]);
+    }
+    vec_free(self->extparams);
     for (i = 0; i != vec_size(self->globals); ++i) {
         ir_value_delete(self->globals[i]);
     }
@@ -2500,10 +2505,13 @@ tailcall:
              *      generation already. This would even include later
              *      reuse.... probably... :)
              */
-            size_t p;
+            size_t p, first;
             ir_value *retvalue;
 
-            for (p = 0; p < vec_size(instr->params); ++p)
+            first = vec_size(instr->params);
+            if (first > 8)
+                first = 8;
+            for (p = 0; p < first; ++p)
             {
                 ir_value *param = instr->params[p];
 
@@ -2518,6 +2526,33 @@ tailcall:
                 stmt.o2.u1 = OFS_PARM0 + 3 * p;
                 vec_push(code_statements, stmt);
             }
+            /* No whandle extparams */
+            first = vec_size(instr->params);
+            for (; p < first; ++p)
+            {
+                ir_builder *ir = func->owner;
+                ir_value *param = instr->params[p];
+                ir_value *target;
+
+                if (p-8 >= vec_size(ir->extparams)) {
+                    irerror(instr->context, "Not enough extparam-globals have been created");
+                    return false;
+                }
+
+                target = ir->extparams[p-8];
+
+                stmt.opcode = INSTR_STORE_F;
+                stmt.o3.u1 = 0;
+
+                if (param->vtype == TYPE_FIELD)
+                    stmt.opcode = field_store_instr[param->fieldtype];
+                else
+                    stmt.opcode = type_store_instr[param->vtype];
+                stmt.o1.u1 = ir_value_code_addr(param);
+                stmt.o2.u1 = ir_value_code_addr(target);
+                vec_push(code_statements, stmt);
+            }
+
             stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
             if (stmt.opcode > INSTR_CALL8)
                 stmt.opcode = INSTR_CALL8;
@@ -2652,6 +2687,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     fun.file    = ir_builder_filestring(ir, global->context.file);
     fun.profile = 0; /* always 0 */
     fun.nargs   = vec_size(irfun->params);
+    if (fun.nargs > 8)
+        fun.nargs = 8;
 
     for (i = 0;i < 8; ++i) {
         if (i >= fun.nargs)
@@ -2698,6 +2735,63 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
     return true;
 }
 
+static void ir_gen_extparam(ir_builder *ir)
+{
+    prog_section_def def;
+    ir_value        *global;
+    char             name[128];
+
+    snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparams)+8));
+    global = ir_value_var(name, store_global, TYPE_VECTOR);
+
+    def.name = code_genstring(name);
+    def.type = TYPE_VECTOR;
+    def.offset = vec_size(code_globals);
+
+    vec_push(code_defs, def);
+    ir_value_code_setaddr(global, def.offset);
+    vec_push(code_globals, 0);
+    vec_push(code_globals, 0);
+    vec_push(code_globals, 0);
+
+    vec_push(ir->extparams, global);
+}
+
+static bool gen_function_extparam_copy(ir_function *self)
+{
+    size_t i, ext, numparams;
+
+    ir_builder *ir = self->owner;
+    ir_value   *ep;
+    prog_section_statement stmt;
+
+    numparams = vec_size(self->params);
+    if (!numparams)
+        return true;
+
+    stmt.opcode = INSTR_STORE_F;
+    stmt.o3.s1 = 0;
+    for (i = 8; i < numparams; ++i) {
+        ext = i - 8;
+        if (ext >= vec_size(ir->extparams))
+            ir_gen_extparam(ir);
+
+        ep = ir->extparams[ext];
+
+        stmt.opcode = type_store_instr[self->locals[i]->vtype];
+        if (self->locals[i]->vtype == TYPE_FIELD &&
+            self->locals[i]->fieldtype == TYPE_VECTOR)
+        {
+            stmt.opcode = INSTR_STORE_V;
+        }
+        stmt.o1.u1 = ir_value_code_addr(ep);
+        stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
+        vec_push(code_statements, stmt);
+    }
+
+    return true;
+}
+
 static bool gen_global_function_code(ir_builder *ir, ir_value *global)
 {
     prog_section_function *fundef;
@@ -2721,6 +2815,10 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
     fundef = &code_functions[irfun->code_function_def];
 
     fundef->entry = vec_size(code_statements);
+    if (!gen_function_extparam_copy(irfun)) {
+        irerror(irfun->context, "Failed to generate extparam-copy code for function %s", irfun->name);
+        return false;
+    }
     if (!gen_function_code(irfun)) {
         irerror(irfun->context, "Failed to generate code for function %s", irfun->name);
         return false;
@@ -2972,7 +3070,8 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
     stmt.o3.u1 = 0;
     vec_push(code_statements, stmt);
 
-    con_out("writing '%s'...\n", filename);
+    if (!opts_pp_only)
+        con_out("writing '%s'...\n", filename);
     return code_write(filename);
 }
 
diff --git a/ir.h b/ir.h
index 9b5171d..06efa5b 100644 (file)
--- a/ir.h
+++ b/ir.h
@@ -290,6 +290,8 @@ typedef struct ir_builder_s
     ir_value    **globals;
     ir_value    **fields;
 
+    ir_value    **extparams;
+
     const char **filenames;
     qcint       *filestrings;
     /* we cache the #IMMEDIATE string here */
diff --git a/lexer.c b/lexer.c
index 513499a..a71e50f 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -10,32 +10,32 @@ char* *lex_filenames;
 
 void lexerror(lex_file *lex, const char *fmt, ...)
 {
-       va_list ap;
+    va_list ap;
 
-       va_start(ap, fmt);
-       if (lex)
+    va_start(ap, fmt);
+    if (lex)
         con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
     else
         con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
-       va_end(ap);
+    va_end(ap);
 }
 
 bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
 {
-       va_list ap;
-       int lvl = LVL_WARNING;
+    va_list ap;
+    int lvl = LVL_WARNING;
 
     if (!OPTS_WARN(warntype))
         return false;
 
     if (opts_werror)
-           lvl = LVL_ERROR;
+        lvl = LVL_ERROR;
 
-       va_start(ap, fmt);
+    va_start(ap, fmt);
     con_vprintmsg(lvl, lex->name, lex->sline, "warning", fmt, ap);
-       va_end(ap);
+    va_end(ap);
 
-       return opts_werror;
+    return opts_werror;
 }
 
 
@@ -289,13 +289,13 @@ static int lex_getch(lex_file *lex)
 
     if (lex->peekpos) {
         lex->peekpos--;
-        if (lex->peek[lex->peekpos] == '\n')
+        if (!lex->push_line && lex->peek[lex->peekpos] == '\n')
             lex->line++;
         return lex->peek[lex->peekpos];
     }
 
     ch = lex_fgetc(lex);
-    if (ch == '\n')
+    if (!lex->push_line && ch == '\n')
         lex->line++;
     else if (ch == '?')
         return lex_try_trigraph(lex, ch);
@@ -307,7 +307,7 @@ static int lex_getch(lex_file *lex)
 static void lex_ungetch(lex_file *lex, int ch)
 {
     lex->peek[lex->peekpos++] = ch;
-    if (ch == '\n')
+    if (!lex->push_line && ch == '\n')
         lex->line--;
 }
 
@@ -347,6 +347,115 @@ static void lex_endtoken(lex_file *lex)
     vec_shrinkby(lex->tok.value, 1);
 }
 
+static bool lex_try_pragma(lex_file *lex)
+{
+    int ch;
+    char *pragma  = NULL;
+    char *command = NULL;
+    char *param   = NULL;
+    size_t line;
+
+    if (lex->flags.preprocessing)
+        return false;
+
+    line = lex->line;
+
+    ch = lex_getch(lex);
+    if (ch != '#') {
+        lex_ungetch(lex, ch);
+        return false;
+    }
+
+    for (ch = lex_getch(lex); vec_size(pragma) < 8 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
+        vec_push(pragma, ch);
+    vec_push(pragma, 0);
+
+    if (ch != ' ' || strcmp(pragma, "pragma")) {
+        lex_ungetch(lex, ch);
+        goto unroll;
+    }
+
+    for (ch = lex_getch(lex); vec_size(command) < 32 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
+        vec_push(command, ch);
+    vec_push(command, 0);
+
+    if (ch != '(') {
+        lex_ungetch(lex, ch);
+        goto unroll;
+    }
+
+    for (ch = lex_getch(lex); vec_size(param) < 32 && ch != ')' && ch != '\n'; ch = lex_getch(lex))
+        vec_push(param, ch);
+    vec_push(param, 0);
+
+    if (ch != ')') {
+        lex_ungetch(lex, ch);
+        goto unroll;
+    }
+
+    if (!strcmp(command, "push")) {
+        if (!strcmp(param, "line")) {
+            lex->push_line++;
+            --line;
+        }
+        else
+            goto unroll;
+    }
+    else if (!strcmp(command, "pop")) {
+        if (!strcmp(param, "line")) {
+            if (lex->push_line)
+                lex->push_line--;
+            --line;
+        }
+        else
+            goto unroll;
+    }
+    else if (!strcmp(command, "file")) {
+        lex->name = util_strdup(param);
+        vec_push(lex_filenames, lex->name);
+    }
+    else if (!strcmp(command, "line")) {
+        line = strtol(param, NULL, 0)-1;
+    }
+    else
+        goto unroll;
+
+    lex->line = line;
+    while (ch != '\n' && ch != EOF)
+        ch = lex_getch(lex);
+    return true;
+
+unroll:
+    if (command) {
+        vec_pop(command);
+        while (vec_size(command)) {
+            lex_ungetch(lex, vec_last(command));
+            vec_pop(command);
+        }
+        vec_free(command);
+    }
+    if (command) {
+        vec_pop(command);
+        while (vec_size(command)) {
+            lex_ungetch(lex, vec_last(command));
+            vec_pop(command);
+        }
+        vec_free(command);
+    }
+    if (pragma) {
+        vec_pop(pragma);
+        while (vec_size(pragma)) {
+            lex_ungetch(lex, vec_last(pragma));
+            vec_pop(pragma);
+        }
+        vec_free(pragma);
+    }
+    lex_ungetch(lex, '#');
+
+    lex->line = line;
+    return false;
+}
+
 /* Skip whitespace and comments and return the first
  * non-white character.
  * As this makes use of the above getch() ungetch() functions,
@@ -388,6 +497,10 @@ static int lex_skipwhite(lex_file *lex)
     {
         ch = lex_getch(lex);
         while (ch != EOF && isspace(ch)) {
+            if (ch == '\n') {
+                if (lex_try_pragma(lex))
+                    continue;
+            }
             if (lex->flags.preprocessing) {
                 if (ch == '\n') {
                     /* end-of-line */
@@ -415,13 +528,17 @@ static int lex_skipwhite(lex_file *lex)
 
                 if (lex->flags.preprocessing) {
                     haswhite = true;
+                    /*
                     lex_tokench(lex, '/');
                     lex_tokench(lex, '/');
+                    */
+                    lex_tokench(lex, ' ');
+                    lex_tokench(lex, ' ');
                 }
 
                 while (ch != EOF && ch != '\n') {
                     if (lex->flags.preprocessing)
-                        lex_tokench(lex, ch);
+                        lex_tokench(lex, ' '); /* ch); */
                     ch = lex_getch(lex);
                 }
                 if (lex->flags.preprocessing) {
@@ -436,8 +553,12 @@ static int lex_skipwhite(lex_file *lex)
                 /* multiline comment */
                 if (lex->flags.preprocessing) {
                     haswhite = true;
+                    /*
                     lex_tokench(lex, '/');
                     lex_tokench(lex, '*');
+                    */
+                    lex_tokench(lex, ' ');
+                    lex_tokench(lex, ' ');
                 }
 
                 while (ch != EOF)
@@ -447,14 +568,18 @@ static int lex_skipwhite(lex_file *lex)
                         ch = lex_getch(lex);
                         if (ch == '/') {
                             if (lex->flags.preprocessing) {
+                                /*
                                 lex_tokench(lex, '*');
                                 lex_tokench(lex, '/');
+                                */
+                                lex_tokench(lex, ' ');
+                                lex_tokench(lex, ' ');
                             }
                             break;
                         }
                     }
                     if (lex->flags.preprocessing) {
-                        lex_tokench(lex, ch);
+                        lex_tokench(lex, ' '); /* ch); */
                     }
                 }
                 ch = ' '; /* cause TRUE in the isspace check */
@@ -561,7 +686,17 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
         if (ch == quote)
             return TOKEN_STRINGCONST;
 
-        if (!lex->flags.preprocessing && ch == '\\') {
+        if (lex->flags.preprocessing && ch == '\\') {
+            lex_tokench(lex, ch);
+            ch = lex_getch(lex);
+            if (ch == EOF) {
+                lexerror(lex, "unexpected end of file");
+                lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
+                return (lex->tok.ttype = TOKEN_ERROR);
+            }
+            lex_tokench(lex, ch);
+        }
+        else if (ch == '\\') {
             ch = lex_getch(lex);
             if (ch == EOF) {
                 lexerror(lex, "unexpected end of file");
@@ -571,6 +706,8 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
 
             switch (ch) {
             case '\\': break;
+            case '\'': break;
+            case '"':  break;
             case 'a':  ch = '\a'; break;
             case 'b':  ch = '\b'; break;
             case 'r':  ch = '\r'; break;
@@ -678,7 +815,21 @@ int lex_do(lex_file *lex)
         return TOKEN_FATAL;
 #endif
 
-    ch = lex_skipwhite(lex);
+    while (true) {
+        ch = lex_skipwhite(lex);
+        if (!lex->flags.mergelines || ch != '\\')
+            break;
+        ch = lex_getch(lex);
+        if (ch != '\n') {
+            lex_ungetch(lex, ch);
+            ch = '\\';
+            break;
+        }
+        /* we reached a linemerge */
+        lex_tokench(lex, '\n');
+        continue;
+    }
+
     lex->sline = lex->line;
     lex->tok.ctx.line = lex->sline;
     lex->tok.ctx.file = lex->name;
@@ -851,6 +1002,7 @@ int lex_do(lex_file *lex)
                 return (lex->tok.ttype = TOKEN_OPERATOR);
         case ')':
         case ';':
+        case ':':
         case '{':
         case '}':
         case ']':
diff --git a/lexer.h b/lexer.h
index 76d2b7a..561148c 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -119,11 +119,14 @@ typedef struct {
            bool noops;
            bool nodigraphs; /* used when lexing string constants */
            bool preprocessing; /* whitespace and EOLs become actual tokens */
+           bool mergelines; /* backslash at the end of a line escapes the newline */
        } flags;
 
     int framevalue;
        frame_macro *frames;
        char *modelname;
+
+       size_t push_line;
 } lex_file;
 
 lex_file* lex_open (const char *file);
diff --git a/main.c b/main.c
index b483db9..4c8062d 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2012
  *     Dale Weiler
+ *     Wolfgang Bumiller
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  * this software and associated documentation files (the "Software"), to deal in
@@ -57,28 +58,28 @@ static const char *app_name;
 static int usage() {
     con_out("usage: %s [options] [files...]", app_name);
     con_out("options:\n"
-           "  -h, --help             show this help message\n"
-           "  -debug                 turns on compiler debug messages\n"
-           "  -memchk                turns on compiler memory leak check\n");
+            "  -h, --help             show this help message\n"
+            "  -debug                 turns on compiler debug messages\n"
+            "  -memchk                turns on compiler memory leak check\n");
     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
-           "  -a filename            add an asm file to be assembled\n"
-           "  -s filename            add a progs.src file to be used\n");
+            "  -a filename            add an asm file to be assembled\n"
+            "  -s filename            add a progs.src file to be used\n");
     con_out("  -E                     stop after preprocessing\n");
     con_out("  -f<flag>               enable a flag\n"
-           "  -fno-<flag>            disable a flag\n"
-           "  -std standard          select one of the following standards\n"
-           "       -std=qcc          original QuakeC\n"
-           "       -std=fteqcc       fteqcc QuakeC\n"
-           "       -std=gmqcc        this compiler (default)\n");
+            "  -fno-<flag>            disable a flag\n"
+            "  -std standard          select one of the following standards\n"
+            "       -std=qcc          original QuakeC\n"
+            "       -std=fteqcc       fteqcc QuakeC\n"
+            "       -std=gmqcc        this compiler (default)\n");
     con_out("  -W<warning>            enable a warning\n"
-           "  -Wno-<warning>         disable a warning\n"
-           "  -Wall                  enable all warnings\n"
-           "  -Werror                treat warnings as errors\n");
+            "  -Wno-<warning>         disable a warning\n"
+            "  -Wall                  enable all warnings\n"
+            "  -Werror                treat warnings as errors\n");
     con_out("  -force-crc=num         force a specific checksum into the header\n");
     con_out("\n");
     con_out("flags:\n"
-           "  -fadjust-vector-fields\n"
-           "            when assigning a vector field, its _y and _z fields also get assigned\n"
+            "  -fadjust-vector-fields\n"
+            "            when assigning a vector field, its _y and _z fields also get assigned\n"
            );
     return -1;
 }
@@ -202,6 +203,7 @@ static bool options_parse(int argc, char **argv) {
                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                     opts_standard = COMPILER_QCC;
                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
+                    options_set(opts_flags, FTEPP,                true);
                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                     opts_standard = COMPILER_FTEQCC;
                 } else if (!strcmp(argarg, "qccx")) {
@@ -416,6 +418,8 @@ int main(int argc, char **argv) {
     size_t itr;
     int retval = 0;
     bool opts_output_free = false;
+    bool progs_src = false;
+    FILE *outfile = NULL;
 
     app_name = argv[0];
     con_init();
@@ -436,8 +440,11 @@ int main(int argc, char **argv) {
     options_set(opts_warn, WARN_EFFECTLESS_STATEMENT, true);
     options_set(opts_warn, WARN_END_SYS_FIELDS, true);
     options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true);
+    options_set(opts_warn, WARN_PREPROCESSOR, true);
+    options_set(opts_warn, WARN_MULTIFILE_IF, true);
 
     options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
+    options_set(opts_flags, FTEPP, false);
 
     if (!options_parse(argc, argv)) {
         return usage();
@@ -464,53 +471,53 @@ int main(int argc, char **argv) {
         con_out("standard = %i\n", opts_standard);
     }
 
-    if (!parser_init()) {
-        con_out("failed to initialize parser\n");
-        retval = 1;
-        goto cleanup;
-    }
-
-    util_debug("COM", "starting ...\n");
-
-    if (vec_size(items)) {
-        con_out("Mode: manual\n");
-        con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
-        for (itr = 0; itr < vec_size(items); ++itr) {
-            con_out("  item: %s (%s)\n",
-                   items[itr].filename,
-                   ( (items[itr].type == TYPE_QC ? "qc" :
-                     (items[itr].type == TYPE_ASM ? "asm" :
-                     (items[itr].type == TYPE_SRC ? "progs.src" :
-                     ("unknown"))))));
-
-            if (!parser_compile_file(items[itr].filename))
-            {
-                    retval = 1;
-                    goto cleanup;
+    if (opts_pp_only) {
+        if (opts_output_wasset) {
+            outfile = util_fopen(opts_output, "wb");
+            if (!outfile) {
+                con_err("failed to open `%s` for writing\n", opts_output);
+                retval = 1;
+                goto cleanup;
             }
         }
+        else
+            outfile = stdout;
+    }
 
-        if (!parser_finish(opts_output)) {
+    if (!opts_pp_only) {
+        if (!parser_init()) {
+            con_err("failed to initialize parser\n");
             retval = 1;
             goto cleanup;
         }
+    }
+    if (opts_pp_only || OPTS_FLAG(FTEPP)) {
+        if (!ftepp_init()) {
+            con_err("failed to initialize parser\n");
+            retval = 1;
+            goto cleanup;
+        }
+    }
 
-    } else {
+    util_debug("COM", "starting ...\n");
+
+    if (!vec_size(items)) {
         FILE *src;
         char *line;
         size_t linelen = 0;
 
-        con_out("Mode: progs.src\n");
+        progs_src = true;
+
         src = util_fopen("progs.src", "rb");
         if (!src) {
-            con_out("failed to open `progs.src` for reading\n");
+            con_err("failed to open `progs.src` for reading\n");
             retval = 1;
             goto cleanup;
         }
 
         line = NULL;
         if (!progs_nextline(&line, &linelen, src) || !line[0]) {
-            con_out("illformatted progs.src file: expected output filename in first line\n");
+            con_err("illformatted progs.src file: expected output filename in first line\n");
             retval = 1;
             goto srcdone;
         }
@@ -521,30 +528,92 @@ int main(int argc, char **argv) {
         }
 
         while (progs_nextline(&line, &linelen, src)) {
+            argitem item;
             if (!line[0] || (line[0] == '/' && line[1] == '/'))
                 continue;
-            con_out("  src: %s\n", line);
-            if (!parser_compile_file(line)) {
-                retval = 1;
-                goto srcdone;
-            }
+            item.filename = util_strdup(line);
+            item.type     = TYPE_QC;
+            vec_push(items, item);
         }
 
-        parser_finish(opts_output);
-
 srcdone:
         fclose(src);
         mem_d(line);
     }
 
+    if (retval)
+        goto cleanup;
+
+    if (vec_size(items)) {
+        if (!opts_pp_only) {
+            con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
+            con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
+        }
+        for (itr = 0; itr < vec_size(items); ++itr) {
+            if (!opts_pp_only) {
+                con_out("  item: %s (%s)\n",
+                       items[itr].filename,
+                       ( (items[itr].type == TYPE_QC ? "qc" :
+                         (items[itr].type == TYPE_ASM ? "asm" :
+                         (items[itr].type == TYPE_SRC ? "progs.src" :
+                         ("unknown"))))));
+            }
+
+            if (opts_pp_only) {
+                if (!ftepp_preprocess_file(items[itr].filename)) {
+                    retval = 1;
+                    goto cleanup;
+                }
+                fprintf(outfile, "%s", ftepp_get());
+                ftepp_flush();
+            }
+            else {
+                if (OPTS_FLAG(FTEPP)) {
+                    const char *data;
+                    if (!ftepp_preprocess_file(items[itr].filename)) {
+                        retval = 1;
+                        goto cleanup;
+                    }
+                    data = ftepp_get();
+                    if (!parser_compile_string_len(items[itr].filename, data, vec_size(data)-1)) {
+                        retval = 1;
+                        goto cleanup;
+                    }
+                    ftepp_flush();
+                }
+                else {
+                    if (!parser_compile_file(items[itr].filename)) {
+                        retval = 1;
+                        goto cleanup;
+                    }
+                }
+            }
+
+            if (progs_src) {
+                mem_d(items[itr].filename);
+                items[itr].filename = NULL;
+            }
+        }
+
+        ftepp_finish();
+        if (!opts_pp_only) {
+            if (!parser_finish(opts_output)) {
+                retval = 1;
+                goto cleanup;
+            }
+        }
+    }
+
     /* stuff */
 
 cleanup:
     util_debug("COM", "cleaning ...\n");
+    ftepp_finish();
     con_close();
     vec_free(items);
 
-    parser_cleanup();
+    if (!opts_pp_only)
+        parser_cleanup();
     if (opts_output_free)
         mem_d((char*)opts_output);
 
index e628f43..9b64833 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -31,6 +31,7 @@
     GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
     GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)
     GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
+    GMQCC_DEFINE_FLAG(FTEPP)
 #endif
 
 /* warning flags */
@@ -52,6 +53,8 @@
     GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
     GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
     GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
+    GMQCC_DEFINE_FLAG(PREPROCESSOR)
+    GMQCC_DEFINE_FLAG(MULTIFILE_IF)
 #endif
 
 /* some cleanup so we don't have to */
index 805c28c..25c9c74 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -20,6 +20,9 @@ typedef struct {
     ast_value    **imm_string;
     ast_value    **imm_vector;
 
+    /* must be deleted first, they reference immediates and values */
+    ast_value    **accessors;
+
     ast_value *imm_float_zero;
     ast_value *imm_vector_zero;
 
@@ -2396,6 +2399,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
 {
     ast_function   *func = NULL;
     ast_value      *fval = NULL;
+    ast_block      *body = NULL;
 
     fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
     if (!fval) {
@@ -2410,15 +2414,25 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
         return false;
     }
 
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        ast_delete(fval);
+        ast_delete(func);
+        return false;
+    }
+
+    vec_push(func->blocks, body);
     *out = fval;
 
+    vec_push(parser->accessors, fval);
+
     return true;
 }
 
 static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
 {
     ast_expression *root = NULL;
-    ast_block      *body = NULL;
     ast_value      *index = NULL;
     ast_value      *value = NULL;
     ast_function   *func;
@@ -2434,12 +2448,6 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
     func = fval->constval.vfunc;
     fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
 
-    body = ast_block_new(ast_ctx(array));
-    if (!body) {
-        parseerror(parser, "failed to create block for array accessor");
-        goto cleanup;
-    }
-
     index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
     value = ast_value_copy((ast_value*)array->expression.next);
 
@@ -2457,12 +2465,10 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
         goto cleanup;
     }
 
-    vec_push(body->exprs, root);
-    vec_push(func->blocks, body);
+    vec_push(func->blocks[0]->exprs, root);
     array->setter = fval;
     return true;
 cleanup:
-    if (body)  ast_delete(body);
     if (index) ast_delete(index);
     if (value) ast_delete(value);
     if (root)  ast_delete(root);
@@ -2474,7 +2480,6 @@ cleanup:
 static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
 {
     ast_expression *root = NULL;
-    ast_block      *body = NULL;
     ast_value      *entity = NULL;
     ast_value      *index = NULL;
     ast_value      *value = NULL;
@@ -2491,12 +2496,6 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array,
     func = fval->constval.vfunc;
     fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
 
-    body = ast_block_new(ast_ctx(array));
-    if (!body) {
-        parseerror(parser, "failed to create block for array accessor");
-        goto cleanup;
-    }
-
     entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY);
     index  = ast_value_new(ast_ctx(array), "index",  TYPE_FLOAT);
     value  = ast_value_copy((ast_value*)array->expression.next);
@@ -2515,12 +2514,10 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array,
         goto cleanup;
     }
 
-    vec_push(body->exprs, root);
-    vec_push(func->blocks, body);
+    vec_push(func->blocks[0]->exprs, root);
     array->setter = fval;
     return true;
 cleanup:
-    if (body)   ast_delete(body);
     if (entity) ast_delete(entity);
     if (index)  ast_delete(index);
     if (value)  ast_delete(value);
@@ -2533,7 +2530,6 @@ cleanup:
 static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
 {
     ast_expression *root = NULL;
-    ast_block      *body = NULL;
     ast_value      *index = NULL;
     ast_value      *fval;
     ast_function   *func;
@@ -2551,12 +2547,6 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
     func = fval->constval.vfunc;
     fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
 
-    body = ast_block_new(ast_ctx(array));
-    if (!body) {
-        parseerror(parser, "failed to create block for array accessor");
-        goto cleanup;
-    }
-
     index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
 
     if (!index) {
@@ -2571,12 +2561,10 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
         goto cleanup;
     }
 
-    vec_push(body->exprs, root);
-    vec_push(func->blocks, body);
+    vec_push(func->blocks[0]->exprs, root);
     array->getter = fval;
     return true;
 cleanup:
-    if (body)  ast_delete(body);
     if (index) ast_delete(index);
     if (root)  ast_delete(root);
     ast_delete(func);
@@ -2649,8 +2637,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     }
 
     /* sanity check */
-    if (vec_size(params) > 8)
-        parseerror(parser, "more than 8 parameters are currently not supported");
+    if (vec_size(params) > 8 && opts_standard == COMPILER_QCC)
+        (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
 
     /* parse-out */
     if (!parser_next(parser)) {
@@ -3431,7 +3419,17 @@ bool parser_compile_file(const char *filename)
 {
     parser->lex = lex_open(filename);
     if (!parser->lex) {
-        con_out("failed to open file \"%s\"\n", filename);
+        con_err("failed to open file \"%s\"\n", filename);
+        return false;
+    }
+    return parser_compile();
+}
+
+bool parser_compile_string_len(const char *name, const char *str, size_t len)
+{
+    parser->lex = lex_open_string(str, len, name);
+    if (!parser->lex) {
+        con_err("failed to create lexer for string \"%s\"\n", name);
         return false;
     }
     return parser_compile();
@@ -3441,7 +3439,7 @@ bool parser_compile_string(const char *name, const char *str)
 {
     parser->lex = lex_open_string(str, strlen(str), name);
     if (!parser->lex) {
-        con_out("failed to create lexer for string \"%s\"\n", name);
+        con_err("failed to create lexer for string \"%s\"\n", name);
         return false;
     }
     return parser_compile();
@@ -3450,6 +3448,11 @@ bool parser_compile_string(const char *name, const char *str)
 void parser_cleanup()
 {
     size_t i;
+    for (i = 0; i < vec_size(parser->accessors); ++i) {
+        ast_delete(parser->accessors[i]->constval.vfunc);
+        parser->accessors[i]->constval.vfunc = NULL;
+        ast_delete(parser->accessors[i]);
+    }
     for (i = 0; i < vec_size(parser->functions); ++i) {
         ast_delete(parser->functions[i]);
     }
@@ -3470,6 +3473,7 @@ void parser_cleanup()
         ast_delete(parser->globals[i].var);
         mem_d(parser->globals[i].name);
     }
+    vec_free(parser->accessors);
     vec_free(parser->functions);
     vec_free(parser->imm_vector);
     vec_free(parser->imm_string);