]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merging in new-syntax
authorWolfgang (Blub) Bumiller <blub@speed.at>
Wed, 14 Nov 2012 20:39:16 +0000 (21:39 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Wed, 14 Nov 2012 20:39:16 +0000 (21:39 +0100)
Makefile
con.c [new file with mode: 0644]
exec.c
gmqcc.h
lexer.c
lexer.h
main.c
mem.c [new file with mode: 0644]
parser.c
util.c

index a44b6ea6322af4c92148a32a813ed2610154f0c9..ce0bc8697702db02caf715af8c13360dfa3dd5d1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,11 +24,12 @@ OBJ     = \
           code.o      \
           ast.o       \
           ir.o        \
-          error.o
+          error.o     \
+          con.o
 OBJ_A = test/ast-test.o
 OBJ_I = test/ir-test.o
 OBJ_C = main.o lexer.o parser.o
-OBJ_X = exec-standalone.o util.o
+OBJ_X = exec-standalone.o util.o con.o
 
 #default is compiler only
 default: gmqcc
diff --git a/con.c b/con.c
new file mode 100644 (file)
index 0000000..623cb99
--- /dev/null
+++ b/con.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2012
+ *     Dale Weiler
+ *
+ * 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"
+
+/*
+ * isatty/STDERR_FILENO/STDOUT_FILNO
+ * + some other things likewise.
+ */
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#define GMQCC_IS_STDOUT(X) ((X) == stdout)
+#define GMQCC_IS_STDERR(X) ((X) == stderr)
+#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
+
+typedef struct {
+    FILE *handle_err;
+    FILE *handle_out;
+    
+    int   color_err;
+    int   color_out;
+} con_t;
+
+/*
+ * Doing colored output on windows is fucking stupid.  The linux way is
+ * the real way. So we emulate it on windows :)
+ */
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/*
+ * Windows doesn't have constants for FILENO, sadly but the docs tell
+ * use the constant values.
+ */
+#undef  STDERR_FILENO
+#undef  STDOUT_FILENO
+#define STDERR_FILENO 2
+#define STDOUT_FILENO 1
+
+/*
+ * Windows and it's posix underscore bullshit.  We simply fix this
+ * with yay, another macro :P
+ */
+#define isatty _isatty
+enum {
+    RESET = 0,
+    BOLD  = 1,
+    BLACK = 30,
+    RED,
+    GREEN,
+    YELLOW,
+    BLUE,
+    MAGENTA,
+    CYAN,
+    GRAY,
+    WHITE
+};
+
+enum {
+    WBLACK,
+    WBLUE,
+    WGREEN   = 2,
+    WRED     = 4,
+    WINTENSE = 8,
+    WCYAN    = WBLUE  | WGREEN,
+    WMAGENTA = WBLUE  | WRED,
+    WYELLOW  = WGREEN | WRED,
+    WWHITE   = WBLUE  | WGREEN | WRED
+}
+
+static const ansi2win[] = {
+    WBLACK,
+    WRED,
+    WGREEN,
+    WYELLOW,
+    WBLUE,
+    WMAGENTA,
+    WCYAN,
+    WWHITE
+};
+
+static void win_fputs(char *str, FILE *f) {
+    /* state for translate */
+    int acolor;
+    int wcolor;
+    int icolor;
+    
+    int state;
+    int place;
+    
+    /* attributes */
+    int intense  =  -1;
+    int colors[] = {-1, -1 };
+    int colorpos = 1;
+    
+    CONSOLE_SCREEN_BUFFER_INFO cinfo;
+    GetConsoleScreenBufferInfo(
+        (h == stdout) ?
+            GetStdHandle(STD_OUTPUT_HANDLE) :
+            GetStdHandle(STD_ERROR_HANDLE), &cinfo
+    );
+    icolor = cinfo.wAttributes;
+    
+    while (*str) {
+        if (*str == '\e')
+            state = '\e';
+        else if (state == '\e' && *str == '[')
+            state = '[';
+        else if (state == '[') {
+            if (*str != 'm') {
+                colors[colorpos] = *str;
+                colorpos--;
+            } else {
+                int find;
+                int mult;
+                for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) {
+                    acolor += (colors[find] - 48) * mult;
+                    mult   *= 10;
+                }
+                
+                /* convert to windows color */
+                if (acolor == BOLD)
+                    intense = WINTENSE;
+                else if (acolor == RESET) {
+                    intense = WBLACK;
+                    wcolor  = icolor;
+                }
+                else if (BLACK < acolor && acolor <= WHITE)
+                    wcolor = ansi2win[acolor - 30];
+                else if (acolor == 90) {
+                    /* special gray really white man */
+                    wcolor  = WWHITE;
+                    intense = WBLACK;
+                }
+                
+                SetConsoleTextattribute(
+                    (h == stdout) ?
+                    GetStdHandle(STD_OUTPUT_HANDLE) :
+                    GetStdHandle(STD_ERROR_HANDLE),
+                    
+                    wcolor | intense | (icolor & 0xF0)
+                );
+                colorpos =  1;
+                state    = -1;
+            }
+        } else {
+            fputc(*str, h);
+        }
+    }
+    /* restore */
+    SetConsoleTextAttribute(
+        (h == stdout) ?
+        GetStdHandle(STD_OUTPUT_HANDLE) :
+        GetStdHandle(STD_ERROR_HANDLE),
+        icolor
+    );
+}
+#endif
+
+/*
+ * We use standard files as default. These can be changed at any time
+ * with con_change(F, F)
+ */
+static con_t console;
+
+/*
+ * Enables color on output if supported.
+ * NOTE: The support for checking colors is NULL.  On windows this will
+ * always work, on *nix it depends if the term has colors.
+ * 
+ * NOTE: This prevents colored output to piped stdout/err via isatty
+ * checks.
+ */
+static void con_enablecolor() {
+    if (console.handle_err == stderr || console.handle_err == stdout)
+        console.color_err = !!(isatty(STDERR_FILENO));
+    if (console.handle_out == stderr || console.handle_out == stdout)
+        console.color_out = !!(isatty(STDOUT_FILENO));
+}
+
+/*
+ * Does a write to the handle with the format string and list of
+ * arguments.  This colorizes for windows as well via translate
+ * step.
+ */
+static int con_write(FILE *handle, const char *fmt, va_list va) {
+    int      ln;
+    #ifndef _WIN32
+    ln = vfprintf(handle, fmt, va);
+    #else
+    {
+        char *data = NULL;
+        ln   = _vscprintf(fmt, va);
+        data = malloc(ln + 1);
+        data[ln] = 0;
+        vsprintf(data, fmt, va);
+        if (GMQCC_IS_DEFINE(handle))
+            ln = win_fputs(data, handle);
+        else
+            ln = fputs(data, handle);
+        free(data);
+    }
+    #endif
+    return ln;
+}
+
+/**********************************************************************
+ * EXPOSED INTERFACE BEGINS
+ *********************************************************************/
+
+void con_close() {
+    if (!GMQCC_IS_DEFINE(console.handle_err))
+        fclose(console.handle_err);
+    if (!GMQCC_IS_DEFINE(console.handle_out))
+        fclose(console.handle_out);
+}
+
+void con_color(int state) {
+    if (state)
+        con_enablecolor();
+    else {
+        console.color_err = 0;
+        console.color_out = 0;
+    }
+}
+
+void con_init() {
+    console.handle_err = stderr;
+    console.handle_out = stdout;
+    con_enablecolor();
+}
+
+void con_reset() {
+    con_close();
+    con_init ();
+}
+
+/*
+ * This is clever, say you want to change the console to use two
+ * files for out/err.  You pass in two strings, it will properly
+ * close the existing handles (if they're not std* handles) and
+ * open them.  Now say you want TO use stdout and stderr, this
+ * allows you to do that so long as you cast them to (char*).
+ * Say you need stdout for out, but want a file for error, you can
+ * do this too, just cast the stdout for (char*) and stick to a
+ * string for the error file.
+ */
+int con_change(const char *out, const char *err) {
+    con_close();
+    
+    if (GMQCC_IS_DEFINE((FILE*)out)) {
+        console.handle_out = (((FILE*)err) == stdout) ? stdout : stderr;
+        con_enablecolor();
+    } else if (!(console.handle_out = fopen(out, "w"))) return 0;
+    
+    if (GMQCC_IS_DEFINE((FILE*)err)) {
+        console.handle_err = (((FILE*)err) == stdout) ? stdout : stderr;
+        con_enablecolor();
+    } else if (!(console.handle_err = fopen(err, "w"))) return 0;
+    
+    return 1;
+}
+
+int con_verr(const char *fmt, va_list va) {
+    return con_write(console.handle_err, fmt, va);
+}
+int con_vout(const char *fmt, va_list va) {
+    return con_write(console.handle_out, fmt, va);
+}
+
+int con_err(const char *fmt, ...) {
+    va_list  va;
+    int      ln = 0;
+    va_start(va, fmt);
+    con_verr(fmt, va);
+    va_end  (va);
+    return   ln;
+}
+int con_out(const char *fmt, ...) {
+    va_list  va;
+    int      ln = 0;
+    va_start(va, fmt);
+    con_vout(fmt, va);
+    va_end  (va);
+    return   ln;
+}
diff --git a/exec.c b/exec.c
index 71b8eba2c1486f5ffe40f5f465362826333822b1..872475befa00ac994caef9d899db9fb6172db755 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -1040,9 +1040,9 @@ void prog_disasm_function(qc_program *prog, size_t id)
 #endif
 
 while (1) {
-       prog_section_function  *newf;
-       qcany          *ed;
-       qcany          *ptr;
+    prog_section_function  *newf;
+    qcany          *ed;
+    qcany          *ptr;
 
     ++st;
 
@@ -1060,12 +1060,12 @@ while (1) {
             qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
             goto cleanup;
 
-               case INSTR_DONE:
-               case INSTR_RETURN:
-                       /* TODO: add instruction count to function profile count */
-                       GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
-                       GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
-                       GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
+        case INSTR_DONE:
+        case INSTR_RETURN:
+            /* TODO: add instruction count to function profile count */
+            GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
+            GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
+            GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
 
             st = prog->code + prog_leavefunction(prog);
             if (!prog->stack_count)
@@ -1073,299 +1073,299 @@ while (1) {
 
             break;
 
-               case INSTR_MUL_F:
-                       OPC->_float = OPA->_float * OPB->_float;
-                       break;
-               case INSTR_MUL_V:
-                       OPC->_float = OPA->vector[0]*OPB->vector[0] +
-                                     OPA->vector[1]*OPB->vector[1] +
-                                     OPA->vector[2]*OPB->vector[2];
-                       break;
-               case INSTR_MUL_FV:
-                       OPC->vector[0] = OPA->_float * OPB->vector[0];
-                       OPC->vector[1] = OPA->_float * OPB->vector[1];
-                       OPC->vector[2] = OPA->_float * OPB->vector[2];
-                       break;
-               case INSTR_MUL_VF:
-                       OPC->vector[0] = OPB->_float * OPA->vector[0];
-                       OPC->vector[1] = OPB->_float * OPA->vector[1];
-                       OPC->vector[2] = OPB->_float * OPA->vector[2];
-                       break;
-               case INSTR_DIV_F:
-                       if (OPB->_float != 0.0f)
-                               OPC->_float = OPA->_float / OPB->_float;
-                       else
-                               OPC->_float = 0;
-                       break;
-
-               case INSTR_ADD_F:
-                       OPC->_float = OPA->_float + OPB->_float;
-                       break;
-               case INSTR_ADD_V:
-                       OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
-                       OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
-                       OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
-                       break;
-               case INSTR_SUB_F:
-                       OPC->_float = OPA->_float - OPB->_float;
-                       break;
-               case INSTR_SUB_V:
-                       OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
-                       OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
-                       OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
-                       break;
-
-               case INSTR_EQ_F:
-                       OPC->_float = (OPA->_float == OPB->_float);
-                       break;
-               case INSTR_EQ_V:
-                       OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
-                                          (OPA->vector[1] == OPB->vector[1]) &&
-                                          (OPA->vector[2] == OPB->vector[2]) );
-                       break;
-               case INSTR_EQ_S:
-                       OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
-                                             prog_getstring(prog, OPB->string));
-                       break;
-               case INSTR_EQ_E:
-                       OPC->_float = (OPA->_int == OPB->_int);
-                       break;
-               case INSTR_EQ_FNC:
-                       OPC->_float = (OPA->function == OPB->function);
-                       break;
-               case INSTR_NE_F:
-                       OPC->_float = (OPA->_float != OPB->_float);
-                       break;
-               case INSTR_NE_V:
-                       OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
-                                      (OPA->vector[1] != OPB->vector[1]) ||
-                                      (OPA->vector[2] != OPB->vector[2]) );
-                       break;
-               case INSTR_NE_S:
-                       OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
-                                              prog_getstring(prog, OPB->string));
-                       break;
-               case INSTR_NE_E:
-                       OPC->_float = (OPA->_int != OPB->_int);
-                       break;
-               case INSTR_NE_FNC:
-                       OPC->_float = (OPA->function != OPB->function);
-                       break;
-
-               case INSTR_LE:
-                       OPC->_float = (OPA->_float <= OPB->_float);
-                       break;
-               case INSTR_GE:
-                       OPC->_float = (OPA->_float >= OPB->_float);
-                       break;
-               case INSTR_LT:
-                       OPC->_float = (OPA->_float < OPB->_float);
-                       break;
-               case INSTR_GT:
-                       OPC->_float = (OPA->_float > OPB->_float);
-                       break;
-
-               case INSTR_LOAD_F:
-               case INSTR_LOAD_S:
-               case INSTR_LOAD_FLD:
-               case INSTR_LOAD_ENT:
-               case INSTR_LOAD_FNC:
-                       if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                           qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
-                               goto cleanup;
-                       }
-                       if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
-                               qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                                         prog->filename,
-                                         OPB->_int);
-                               goto cleanup;
-                       }
-                       ed = prog_getedict(prog, OPA->edict);
-                       OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
-                       break;
-               case INSTR_LOAD_V:
-                       if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                           qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
-                               goto cleanup;
-                       }
-                       if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
-                       {
-                               qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                                         prog->filename,
-                                         OPB->_int + 2);
-                               goto cleanup;
-                       }
-                       ed = prog_getedict(prog, OPA->edict);
-                       OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
-                       OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
-                       OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
-                       break;
-
-               case INSTR_ADDRESS:
-                       if (OPA->edict < 0 || OPA->edict >= prog->entities) {
-                               qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
-                               goto cleanup;
-                       }
-                       if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
-                       {
-                               qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
-                                         prog->filename,
-                                         OPB->_int);
-                               goto cleanup;
-                       }
-
-                       ed = prog_getedict(prog, OPA->edict);
-                       OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
-                       break;
-
-               case INSTR_STORE_F:
-               case INSTR_STORE_S:
-               case INSTR_STORE_ENT:
-               case INSTR_STORE_FLD:
-               case INSTR_STORE_FNC:
-                       OPB->_int = OPA->_int;
-                       break;
-               case INSTR_STORE_V:
-                       OPB->ivector[0] = OPA->ivector[0];
-                       OPB->ivector[1] = OPA->ivector[1];
-                       OPB->ivector[2] = OPA->ivector[2];
-                       break;
-
-               case INSTR_STOREP_F:
-               case INSTR_STOREP_S:
-               case INSTR_STOREP_ENT:
-               case INSTR_STOREP_FLD:
-               case INSTR_STOREP_FNC:
-                       if (OPB->_int < 0 || OPB->_int >= prog->entitydata_count) {
-                               qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
-                               goto cleanup;
-                       }
-                       if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
-                               qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
-                                         prog->filename,
-                                         prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
-                                         OPB->_int);
-                       ptr = (qcany*)(prog->entitydata + OPB->_int);
-                       ptr->_int = OPA->_int;
-                       break;
-               case INSTR_STOREP_V:
-                       if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count) {
-                               qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
-                               goto cleanup;
-                       }
-                       if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
-                               qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
-                                         prog->filename,
-                                         prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
-                                         OPB->_int);
-                       ptr = (qcany*)(prog->entitydata + OPB->_int);
-                       ptr->ivector[0] = OPA->ivector[0];
-                       ptr->ivector[1] = OPA->ivector[1];
-                       ptr->ivector[2] = OPA->ivector[2];
-                       break;
-
-               case INSTR_NOT_F:
-                       OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
-                       break;
-               case INSTR_NOT_V:
-                       OPC->_float = !OPA->vector[0] &&
-                                     !OPA->vector[1] &&
-                                     !OPA->vector[2];
-                       break;
-               case INSTR_NOT_S:
-                       OPC->_float = !OPA->string ||
-                                     !*prog_getstring(prog, OPA->string);
-                       break;
-               case INSTR_NOT_ENT:
-                       OPC->_float = (OPA->edict == 0);
-                       break;
-               case INSTR_NOT_FNC:
-                       OPC->_float = !OPA->function;
-                       break;
-
-               case INSTR_IF:
-                   /* this is consistent with darkplaces' behaviour */
-                       if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
-                       {
-                               st += st->o2.s1 - 1;    /* offset the s++ */
-                               if (++jumpcount >= maxjumps)
-                                       qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
-                       }
-                       break;
-               case INSTR_IFNOT:
-                       if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
-                       {
-                               st += st->o2.s1 - 1;    /* offset the s++ */
-                               if (++jumpcount >= maxjumps)
-                                       qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
-                       }
-                       break;
-
-               case INSTR_CALL0:
-               case INSTR_CALL1:
-               case INSTR_CALL2:
-               case INSTR_CALL3:
-               case INSTR_CALL4:
-               case INSTR_CALL5:
-               case INSTR_CALL6:
-               case INSTR_CALL7:
-               case INSTR_CALL8:
-                       prog->argc = st->opcode - INSTR_CALL0;
-                       if (!OPA->function)
-                               qcvmerror(prog, "NULL function in `%s`", prog->filename);
-
-                       if(!OPA->function || OPA->function >= (unsigned int)prog->functions_count)
-                       {
-                               qcvmerror(prog, "CALL outside the program in `%s` (%i)", prog->filename, (int)OPA->function);
-                               goto cleanup;
-                       }
-
-                       newf = &prog->functions[OPA->function];
-                       newf->profile++;
-
-                       prog->statement = (st - prog->code) + 1;
-
-                       if (newf->entry < 0)
-                       {
-                               /* negative statements are built in functions */
-                               int builtinnumber = -newf->entry;
-                               if (builtinnumber < prog->builtins_count && prog->builtins[builtinnumber])
-                                       prog->builtins[builtinnumber](prog);
-                               else
-                                       qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
-                                                 builtinnumber, prog->filename);
-                       }
-                       else
-                               st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
-                       if (prog->vmerror)
-                               goto cleanup;
-                       break;
-
-               case INSTR_STATE:
-                   qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
-                       break;
-
-               case INSTR_GOTO:
-                       st += st->o1.s1 - 1;    /* offset the s++ */
-                       if (++jumpcount == 10000000)
-                                       qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
-                       break;
-
-               case INSTR_AND:
-                       OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
-                                     FLOAT_IS_TRUE_FOR_INT(OPB->_int);
-                       break;
-               case INSTR_OR:
-                       OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
-                                     FLOAT_IS_TRUE_FOR_INT(OPB->_int);
-                       break;
-
-               case INSTR_BITAND:
-                       OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
-                       break;
-               case INSTR_BITOR:
-                       OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
-                       break;
+        case INSTR_MUL_F:
+            OPC->_float = OPA->_float * OPB->_float;
+            break;
+        case INSTR_MUL_V:
+            OPC->_float = OPA->vector[0]*OPB->vector[0] +
+                          OPA->vector[1]*OPB->vector[1] +
+                          OPA->vector[2]*OPB->vector[2];
+            break;
+        case INSTR_MUL_FV:
+            OPC->vector[0] = OPA->_float * OPB->vector[0];
+            OPC->vector[1] = OPA->_float * OPB->vector[1];
+            OPC->vector[2] = OPA->_float * OPB->vector[2];
+            break;
+        case INSTR_MUL_VF:
+            OPC->vector[0] = OPB->_float * OPA->vector[0];
+            OPC->vector[1] = OPB->_float * OPA->vector[1];
+            OPC->vector[2] = OPB->_float * OPA->vector[2];
+            break;
+        case INSTR_DIV_F:
+            if (OPB->_float != 0.0f)
+                OPC->_float = OPA->_float / OPB->_float;
+            else
+                OPC->_float = 0;
+            break;
+
+        case INSTR_ADD_F:
+            OPC->_float = OPA->_float + OPB->_float;
+            break;
+        case INSTR_ADD_V:
+            OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
+            OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
+            OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
+            break;
+        case INSTR_SUB_F:
+            OPC->_float = OPA->_float - OPB->_float;
+            break;
+        case INSTR_SUB_V:
+            OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
+            OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
+            OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
+            break;
+
+        case INSTR_EQ_F:
+            OPC->_float = (OPA->_float == OPB->_float);
+            break;
+        case INSTR_EQ_V:
+            OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
+                           (OPA->vector[1] == OPB->vector[1]) &&
+                           (OPA->vector[2] == OPB->vector[2]) );
+            break;
+        case INSTR_EQ_S:
+            OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
+                                  prog_getstring(prog, OPB->string));
+            break;
+        case INSTR_EQ_E:
+            OPC->_float = (OPA->_int == OPB->_int);
+            break;
+        case INSTR_EQ_FNC:
+            OPC->_float = (OPA->function == OPB->function);
+            break;
+        case INSTR_NE_F:
+            OPC->_float = (OPA->_float != OPB->_float);
+            break;
+        case INSTR_NE_V:
+            OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
+                           (OPA->vector[1] != OPB->vector[1]) ||
+                           (OPA->vector[2] != OPB->vector[2]) );
+            break;
+        case INSTR_NE_S:
+            OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
+                                   prog_getstring(prog, OPB->string));
+            break;
+        case INSTR_NE_E:
+            OPC->_float = (OPA->_int != OPB->_int);
+            break;
+        case INSTR_NE_FNC:
+            OPC->_float = (OPA->function != OPB->function);
+            break;
+
+        case INSTR_LE:
+            OPC->_float = (OPA->_float <= OPB->_float);
+            break;
+        case INSTR_GE:
+            OPC->_float = (OPA->_float >= OPB->_float);
+            break;
+        case INSTR_LT:
+            OPC->_float = (OPA->_float < OPB->_float);
+            break;
+        case INSTR_GT:
+            OPC->_float = (OPA->_float > OPB->_float);
+            break;
+
+        case INSTR_LOAD_F:
+        case INSTR_LOAD_S:
+        case INSTR_LOAD_FLD:
+        case INSTR_LOAD_ENT:
+        case INSTR_LOAD_FNC:
+            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
+                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
+                goto cleanup;
+            }
+            if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
+                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
+                          prog->filename,
+                          OPB->_int);
+                goto cleanup;
+            }
+            ed = prog_getedict(prog, OPA->edict);
+            OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
+            break;
+        case INSTR_LOAD_V:
+            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
+                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
+                goto cleanup;
+            }
+            if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
+            {
+                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
+                          prog->filename,
+                          OPB->_int + 2);
+                goto cleanup;
+            }
+            ed = prog_getedict(prog, OPA->edict);
+            OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
+            OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
+            OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
+            break;
+
+        case INSTR_ADDRESS:
+            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
+                qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
+                goto cleanup;
+            }
+            if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
+            {
+                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
+                          prog->filename,
+                          OPB->_int);
+                goto cleanup;
+            }
+
+            ed = prog_getedict(prog, OPA->edict);
+            OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
+            break;
+
+        case INSTR_STORE_F:
+        case INSTR_STORE_S:
+        case INSTR_STORE_ENT:
+        case INSTR_STORE_FLD:
+        case INSTR_STORE_FNC:
+            OPB->_int = OPA->_int;
+            break;
+        case INSTR_STORE_V:
+            OPB->ivector[0] = OPA->ivector[0];
+            OPB->ivector[1] = OPA->ivector[1];
+            OPB->ivector[2] = OPA->ivector[2];
+            break;
+
+        case INSTR_STOREP_F:
+        case INSTR_STOREP_S:
+        case INSTR_STOREP_ENT:
+        case INSTR_STOREP_FLD:
+        case INSTR_STOREP_FNC:
+            if (OPB->_int < 0 || OPB->_int >= prog->entitydata_count) {
+                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
+                goto cleanup;
+            }
+            if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
+                qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
+                          prog->filename,
+                          prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
+                          OPB->_int);
+            ptr = (qcany*)(prog->entitydata + OPB->_int);
+            ptr->_int = OPA->_int;
+            break;
+        case INSTR_STOREP_V:
+            if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count) {
+                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
+                goto cleanup;
+            }
+            if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
+                qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
+                          prog->filename,
+                          prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
+                          OPB->_int);
+            ptr = (qcany*)(prog->entitydata + OPB->_int);
+            ptr->ivector[0] = OPA->ivector[0];
+            ptr->ivector[1] = OPA->ivector[1];
+            ptr->ivector[2] = OPA->ivector[2];
+            break;
+
+        case INSTR_NOT_F:
+            OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
+            break;
+        case INSTR_NOT_V:
+            OPC->_float = !OPA->vector[0] &&
+                          !OPA->vector[1] &&
+                          !OPA->vector[2];
+            break;
+        case INSTR_NOT_S:
+            OPC->_float = !OPA->string ||
+                          !*prog_getstring(prog, OPA->string);
+            break;
+        case INSTR_NOT_ENT:
+            OPC->_float = (OPA->edict == 0);
+            break;
+        case INSTR_NOT_FNC:
+            OPC->_float = !OPA->function;
+            break;
+
+        case INSTR_IF:
+            /* this is consistent with darkplaces' behaviour */
+            if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
+            {
+                st += st->o2.s1 - 1;    /* offset the s++ */
+                if (++jumpcount >= maxjumps)
+                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+            }
+            break;
+        case INSTR_IFNOT:
+            if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
+            {
+                st += st->o2.s1 - 1;    /* offset the s++ */
+                if (++jumpcount >= maxjumps)
+                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+            }
+            break;
+
+        case INSTR_CALL0:
+        case INSTR_CALL1:
+        case INSTR_CALL2:
+        case INSTR_CALL3:
+        case INSTR_CALL4:
+        case INSTR_CALL5:
+        case INSTR_CALL6:
+        case INSTR_CALL7:
+        case INSTR_CALL8:
+            prog->argc = st->opcode - INSTR_CALL0;
+            if (!OPA->function)
+                qcvmerror(prog, "NULL function in `%s`", prog->filename);
+
+            if(!OPA->function || OPA->function >= (unsigned int)prog->functions_count)
+            {
+                qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
+                goto cleanup;
+            }
+
+            newf = &prog->functions[OPA->function];
+            newf->profile++;
+
+            prog->statement = (st - prog->code) + 1;
+
+            if (newf->entry < 0)
+            {
+                /* negative statements are built in functions */
+                int builtinnumber = -newf->entry;
+                if (builtinnumber < prog->builtins_count && prog->builtins[builtinnumber])
+                    prog->builtins[builtinnumber](prog);
+                else
+                    qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
+                              builtinnumber, prog->filename);
+            }
+            else
+                st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
+            if (prog->vmerror)
+                goto cleanup;
+            break;
+
+        case INSTR_STATE:
+            qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
+            break;
+
+        case INSTR_GOTO:
+            st += st->o1.s1 - 1;    /* offset the s++ */
+            if (++jumpcount == 10000000)
+                qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
+            break;
+
+        case INSTR_AND:
+            OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
+                          FLOAT_IS_TRUE_FOR_INT(OPB->_int);
+            break;
+        case INSTR_OR:
+            OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
+                          FLOAT_IS_TRUE_FOR_INT(OPB->_int);
+            break;
+
+        case INSTR_BITAND:
+            OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
+            break;
+        case INSTR_BITOR:
+            OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
+            break;
     }
 }
 
diff --git a/gmqcc.h b/gmqcc.h
index ef1841f713f73948bad62a805dfbb80618a4831e..9770929f8caaa104e04255dd5246bd656f0112c4 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -532,6 +532,19 @@ uint32_t code_genstring   (const char *string);
 uint32_t code_cachedstring(const char *string);
 qcint    code_alloc_field (size_t qcsize);
 
+/*===================================================================*/
+/*============================ con.c ================================*/
+/*===================================================================*/
+void con_close();
+void con_color(int state);
+void con_init ();
+void con_reset();
+int  con_change(const char *out, const char *err);
+int  con_verr  (const char *fmt, va_list va);
+int  con_vout  (const char *fmt, va_list va);
+int  con_err   (const char *fmt, ...);
+int  con_out   (const char *fmt, ...);
+
 /*===================================================================*/
 /*========================= assembler.c =============================*/
 /*===================================================================*/
@@ -609,10 +622,6 @@ static const struct {
 
     { "END"       , 0, 3 } /* virtual assembler instruction */
 };
-
-void asm_init (const char *, FILE **);
-void asm_close(FILE *);
-void asm_parse(FILE *);
 /*===================================================================*/
 /*============================= ast.c ===============================*/
 /*===================================================================*/
@@ -945,10 +954,11 @@ void cprintmsg (lex_ctx ctx, int lvl, const char *msgtype, const char *msg, ...)
 /*===================== parser.c commandline ========================*/
 /*===================================================================*/
 
-bool parser_init   ();
-bool parser_compile(const char *filename);
-bool parser_finish (const char *output);
-void parser_cleanup();
+bool parser_init          ();
+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       ();
 
 /*===================================================================*/
 /*======================= main.c commandline ========================*/
diff --git a/lexer.c b/lexer.c
index a555895e854929404c586e86ab4a41b2e180207d..e190da0779ca3349d69ecae7a0eae9f5a2eacbca 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -162,6 +162,34 @@ lex_file* lex_open(const char *file)
     return lex;
 }
 
+lex_file* lex_open_string(const char *str, size_t len, const char *name)
+{
+    lex_file *lex;
+
+    lex = (lex_file*)mem_a(sizeof(*lex));
+    if (!lex) {
+        lexerror(NULL, "out of memory\n");
+        return NULL;
+    }
+
+    memset(lex, 0, sizeof(*lex));
+
+    lex->file = NULL;
+    lex->open_string        = str;
+    lex->open_string_length = len;
+    lex->open_string_pos    = 0;
+
+    lex->name = util_strdup(name ? name : "<string-source>");
+    lex->line = 1; /* we start counting at 1 */
+
+    lex->peekpos = 0;
+    lex->eof = false;
+
+    lex_filenames_add(lex->name);
+
+    return lex;
+}
+
 void lex_cleanup(void)
 {
     size_t i;
@@ -192,6 +220,18 @@ void lex_close(lex_file *lex)
     mem_d(lex);
 }
 
+static int lex_fgetc(lex_file *lex)
+{
+    if (lex->file)
+        return fgetc(lex->file);
+    if (lex->open_string) {
+        if (lex->open_string_pos >= lex->open_string_length)
+            return EOF;
+        return lex->open_string[lex->open_string_pos++];
+    }
+    return EOF;
+}
+
 /* Get or put-back data
  * The following to functions do NOT understand what kind of data they
  * are working on.
@@ -201,13 +241,13 @@ static void lex_ungetch(lex_file *lex, int ch);
 static int lex_try_trigraph(lex_file *lex, int old)
 {
     int c2, c3;
-    c2 = fgetc(lex->file);
+    c2 = lex_fgetc(lex);
     if (c2 != '?') {
         lex_ungetch(lex, c2);
         return old;
     }
 
-    c3 = fgetc(lex->file);
+    c3 = lex_fgetc(lex);
     switch (c3) {
         case '=': return '#';
         case '/': return '\\';
@@ -228,7 +268,7 @@ static int lex_try_trigraph(lex_file *lex, int old)
 static int lex_try_digraph(lex_file *lex, int ch)
 {
     int c2;
-    c2 = fgetc(lex->file);
+    c2 = lex_fgetc(lex);
     if      (ch == '<' && c2 == ':')
         return '[';
     else if (ch == ':' && c2 == '>')
@@ -254,7 +294,7 @@ static int lex_getch(lex_file *lex)
         return lex->peek[lex->peekpos];
     }
 
-    ch = fgetc(lex->file);
+    ch = lex_fgetc(lex);
     if (ch == '\n')
         lex->line++;
     else if (ch == '?')
diff --git a/lexer.h b/lexer.h
index 8a93d233baf08060ddd1644590fc721136f19dba..a3b9833f3cab1727d66bbe85ffdb4e198e318fd5 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -100,6 +100,10 @@ typedef struct {
 
 typedef struct {
        FILE   *file;
+       const char *open_string;
+       size_t      open_string_length;
+       size_t      open_string_pos;
+
        char   *name;
        size_t  line;
        size_t  sline; /* line at the start of a token */
@@ -125,6 +129,7 @@ typedef struct {
 MEM_VECTOR_PROTO(lex_file, char, token);
 
 lex_file* lex_open (const char *file);
+lex_file* lex_open_string(const char *str, size_t len, const char *name);
 void      lex_close(lex_file   *lex);
 int       lex_do   (lex_file   *lex);
 void      lex_cleanup(void);
diff --git a/main.c b/main.c
index 773b08ed711e815c89da14e1c64235f649b49a2f..0b9d726744a4169220e92916c96899835ad3716f 100644 (file)
--- a/main.c
+++ b/main.c
@@ -55,28 +55,28 @@ VECTOR_MAKE(argitem, items);
 static const char *app_name;
 
 static int usage() {
-    printf("usage: %s [options] [files...]", app_name);
-    printf("options:\n"
+    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");
-    printf("  -o, --output=file      output file, defaults to progs.dat\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");
-    printf("  -E                     stop after preprocessing\n");
-    printf("  -f<flag>               enable a flag\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");
-    printf("  -W<warning>            enable a warning\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");
-    printf("  -force-crc=num         force a specific checksum into the header\n");
-    printf("\n");
-    printf("flags:\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"
            );
@@ -205,7 +205,7 @@ static bool options_parse(int argc, char **argv) {
                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                     opts_standard = COMPILER_QCCX;
                 } else {
-                    printf("Unknown standard: %s\n", argarg);
+                    con_out("Unknown standard: %s\n", argarg);
                     return false;
                 }
                 continue;
@@ -243,31 +243,31 @@ static bool options_parse(int argc, char **argv) {
                 case 'f':
                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
                     if (!strcmp(argv[0]+2, "HELP")) {
-                        printf("Possible flags:\n");
+                        con_out("Possible flags:\n");
                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
-                            printf(" -f%s\n", buffer);
+                            con_out(" -f%s\n", buffer);
                         }
                         exit(0);
                     }
                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
                         if (!options_setflag(argv[0]+5, false)) {
-                            printf("unknown flag: %s\n", argv[0]+2);
+                            con_out("unknown flag: %s\n", argv[0]+2);
                             return false;
                         }
                     }
                     else if (!options_setflag(argv[0]+2, true)) {
-                        printf("unknown flag: %s\n", argv[0]+2);
+                        con_out("unknown flag: %s\n", argv[0]+2);
                         return false;
                     }
                     break;
                 case 'W':
                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
                     if (!strcmp(argv[0]+2, "HELP")) {
-                        printf("Possible warnings:\n");
+                        con_out("Possible warnings:\n");
                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
-                            printf(" -W%s\n", buffer);
+                            con_out(" -W%s\n", buffer);
                         }
                         exit(0);
                     }
@@ -291,19 +291,19 @@ static bool options_parse(int argc, char **argv) {
                     }
                     if (!strncmp(argv[0]+2, "NO_", 3)) {
                         if (!options_setwarn(argv[0]+5, false)) {
-                            printf("unknown warning: %s\n", argv[0]+2);
+                            con_out("unknown warning: %s\n", argv[0]+2);
                             return false;
                         }
                     }
                     else if (!options_setwarn(argv[0]+2, true)) {
-                        printf("unknown warning: %s\n", argv[0]+2);
+                        con_out("unknown warning: %s\n", argv[0]+2);
                         return false;
                     }
                     break;
 
                 case 'O':
                     if (!options_witharg(&argc, &argv, &argarg)) {
-                        printf("option -O requires a numerical argument\n");
+                        con_out("option -O requires a numerical argument\n");
                         return false;
                     }
                     opts_O = atoi(argarg);
@@ -311,7 +311,7 @@ static bool options_parse(int argc, char **argv) {
 
                 case 'o':
                     if (!options_witharg(&argc, &argv, &argarg)) {
-                        printf("option -o requires an argument: the output file name\n");
+                        con_out("option -o requires an argument: the output file name\n");
                         return false;
                     }
                     opts_output = argarg;
@@ -322,7 +322,7 @@ static bool options_parse(int argc, char **argv) {
                 case 's':
                     item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
                     if (!options_witharg(&argc, &argv, &argarg)) {
-                        printf("option -a requires a filename %s\n",
+                        con_out("option -a requires a filename %s\n",
                                 (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
                         return false;
                     }
@@ -347,14 +347,14 @@ static bool options_parse(int argc, char **argv) {
                             opts_output = argarg;
                             opts_output_wasset = true;
                         } else {
-                            printf("Unknown parameter: %s\n", argv[0]);
+                            con_out("Unknown parameter: %s\n", argv[0]);
                             return false;
                         }
                     }
                     break;
 
                 default:
-                    printf("Unknown parameter: %s\n", argv[0]);
+                    con_out("Unknown parameter: %s\n", argv[0]);
                     return false;
             }
         }
@@ -403,6 +403,7 @@ int main(int argc, char **argv) {
     bool opts_output_free = false;
 
     app_name = argv[0];
+    con_init();
 
     /* default options / warn flags */
     options_set(opts_warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
@@ -438,18 +439,18 @@ int main(int argc, char **argv) {
 
     if (opts_dump) {
         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
-            printf("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
+            con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
         }
         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
-            printf("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
+            con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
         }
-        printf("output = %s\n", opts_output);
-        printf("optimization level = %i\n", (int)opts_O);
-        printf("standard = %i\n", opts_standard);
+        con_out("output = %s\n", opts_output);
+        con_out("optimization level = %i\n", (int)opts_O);
+        con_out("standard = %i\n", opts_standard);
     }
 
     if (!parser_init()) {
-        printf("failed to initialize parser\n");
+        con_out("failed to initialize parser\n");
         retval = 1;
         goto cleanup;
     }
@@ -457,17 +458,18 @@ int main(int argc, char **argv) {
     util_debug("COM", "starting ...\n");
 
     if (items_elements) {
-        printf("Mode: manual\n");
-        printf("There are %lu items to compile:\n", (unsigned long)items_elements);
+        con_out("Mode: manual\n");
+        con_out("There are %lu items to compile:\n", (unsigned long)items_elements);
         for (itr = 0; itr < items_elements; ++itr) {
-            printf("  item: %s (%s)\n",
+            con_out("  item: %s (%s)\n",
                    items_data[itr].filename,
                    ( (items_data[itr].type == TYPE_QC ? "qc" :
                      (items_data[itr].type == TYPE_ASM ? "asm" :
                      (items_data[itr].type == TYPE_SRC ? "progs.src" :
                      ("unknown"))))));
 
-            if (!parser_compile(items_data[itr].filename)) {
+        if (!parser_compile_file(items_data[itr].filename))
+        {
                 retval = 1;
                 goto cleanup;
             }
@@ -483,17 +485,17 @@ int main(int argc, char **argv) {
         char *line;
         size_t linelen = 0;
 
-        printf("Mode: progs.src\n");
+        con_out("Mode: progs.src\n");
         src = util_fopen("progs.src", "rb");
         if (!src) {
-            printf("failed to open `progs.src` for reading\n");
+            con_out("failed to open `progs.src` for reading\n");
             retval = 1;
             goto cleanup;
         }
 
         line = NULL;
         if (!progs_nextline(&line, &linelen, src) || !line[0]) {
-            printf("illformatted progs.src file: expected output filename in first line\n");
+            con_out("illformatted progs.src file: expected output filename in first line\n");
             retval = 1;
             goto srcdone;
         }
@@ -506,8 +508,8 @@ int main(int argc, char **argv) {
         while (progs_nextline(&line, &linelen, src)) {
             if (!line[0] || (line[0] == '/' && line[1] == '/'))
                 continue;
-            printf("  src: %s\n", line);
-            if (!parser_compile(line)) {
+            con_out("  src: %s\n", line);
+            if (!parser_compile_file(line)) {
                 retval = 1;
                 goto srcdone;
             }
diff --git a/mem.c b/mem.c
new file mode 100644 (file)
index 0000000..ac63886
--- /dev/null
+++ b/mem.c
@@ -0,0 +1,360 @@
+#include "gmqcc.h"
+#include <assert.h>
+/*
+ * GMQCC does a lot of allocations on shortly lived objects all of which
+ * call down to malloc/free internally.  The overhead involved with these
+ * allocations makes GMQCC slow. To combat this, a special allocator was
+ * in need.  This is an implementation of a user-space buddy allocator
+ * that sits ontop of malloc/free.  I'd like to thank Lee Salzman for
+ * guiding me in the right direction for designing this.
+ */
+#define GMQCC_MEM_USED  0xEDCA10A1EDCA10A1
+#define GMQCC_MEM_FREE  0xEEF8EEF8EEF8EEF8
+#define GMQCC_MEM_BSL  -1
+#define GMQCC_MEM_BSR   1
+#define GMQCC_MEM_DEBUG 1
+
+/* debug info for dumping heap contents nicely */
+#define GMQCC_MEM_DEBUG_BPL 32 /* bytes per line    */
+#define GMQCC_MEM_DEBUG_BIG 8  /* bytes in group    */
+#define GMQCC_MEM_DEBUG_BSG 4  /* bytes split group */
+
+#ifdef  GMQCC_MEM_DEBUG
+#   include <stdio.h>
+#   define GMQCC_MEM_TRACE(TAG, ...)            \
+    do {                                        \
+        printf("[mem:%s]: %s ", TAG, __func__); \
+        printf(__VA_ARGS__);                    \
+        printf("\n");                           \
+    } while (0)
+#else
+#   define GMQCC_MEM_TRACE(TAG, ...)
+#endif
+
+typedef unsigned long int mem_addr;
+
+static void   *mem_heap = NULL;
+static size_t  mem_look = 0;  /* lookup table offset */
+static size_t  mem_size = 0;  /* heap size           */
+
+/* read or write to heap */
+#define GMQCC_MEM_WRITEHEAP(OFFSET, TYPE, VALUE)                \
+    do {                                                        \
+        TYPE *T = (TYPE*)((unsigned char*)mem_heap + (OFFSET)); \
+        *T = VALUE;                                             \
+    } while (0)
+#define GMQCC_MEM_READHEAP(OFFSET, TYPE)  ((TYPE)*((TYPE *)(((unsigned char*)mem_heap + (OFFSET)))))
+
+/* read of write first block to heap */
+#define GMQCC_MEM_WRITEFBA(SIZE, ADDR) GMQCC_MEM_WRITEHEAP(mem_look + (SIZE) * sizeof(mem_addr), mem_addr, (ADDR))
+#define GMQCC_MEM_READFBA(SIZE)        GMQCC_MEM_READHEAP (mem_look + (SIZE) * sizeof(mem_addr), mem_addr)
+
+/* read and write block sizes from heap */
+#define GMQCC_MEM_WRITEBS(ADDR, SIZE) GMQCC_MEM_WRITEHEAP(ADDR, mem_addr, (SIZE))
+#define GMQCC_MEM_READBS(ADDR)        GMQCC_MEM_READHEAP (ADDR, mem_addr);
+    
+/*
+ * Getting address of previous/following siblings, as well as
+ * setting address of previous/following siblings.
+ */
+#define GMQCC_MEM_GETADDROFPS(ADDR)   GMQCC_MEM_READHEAP ((ADDR) + 2 * sizeof(mem_addr), mem_addr)
+#define GMQCC_MEM_GETADDROFFS(ADDR)   GMQCC_MEM_READHEAP ((ADDR) + 3 * sizeof(mem_addr), mem_addr)
+#define GMQCC_MEM_SETADDROFPS(ADDR,V) GMQCC_MEM_WRITEHEAP((ADDR) + 2 * sizeof(mem_addr), mem_addr, V)
+#define GMQCC_MEM_SETADDROFFS(ADDR,V) GMQCC_MEM_WRITEHEAP((ADDR) + 3 * sizeof(mem_addr), mem_addr, V)
+
+/* Marking blocks as used or free */
+#define GMQCC_MEM_MARKUSED(ADDR) GMQCC_MEM_WRITEHEAP((ADDR) + 1 * sizeof(mem_addr), mem_addr, GMQCC_MEM_USED)
+#define GMQCC_MEM_MARKFREE(ADDR) GMQCC_MEM_WRITEHEAP((ADDR) + 1 * sizeof(mem_addr), mem_addr, GMQCC_MEM_FREE)
+
+/* Has block? */
+#define GMQCC_MEM_HASBLOCK(SIZE) (GMQCC_MEM_READFBA(SIZE) != 0)
+
+/* Block free? */
+#define GMQCC_MEM_BLOCKFREE(ADDR) ((GMQCC_MEM_READHEAP((ADDR) + 1 * sizeof(mem_addr), mem_addr)) == GMQCC_MEM_FREE)
+
+/*
+ * Must be first since it's used internally, but also should be exposed
+ * to the outside, statics exist after this.
+ */
+void mem_dump() {
+    size_t         addr = 0;
+    unsigned char *ptr  = (unsigned char*)mem_heap;
+    
+    while (addr < mem_size) {
+        size_t offset = 0;
+        printf("% 8X:  ", addr);
+        while (offset < GMQCC_MEM_DEBUG_BPL) {
+            if (addr + offset >= mem_size)
+                break;
+                
+            ptr    ++;
+            offset ++;
+            
+            printf (
+                !(offset%GMQCC_MEM_DEBUG_BSG) && 
+                 (offset%GMQCC_MEM_DEBUG_BIG) ? "%02X  "   :
+                !(offset%GMQCC_MEM_DEBUG_BIG) ? "%02X    " : "%02X ",
+                *ptr
+            );
+        }
+        printf("\n");
+        addr += GMQCC_MEM_DEBUG_BPL;
+    }
+}
+
+static void mem_init_table(size_t size) {
+    size_t i;
+    GMQCC_MEM_TRACE("flow", "(%lu)", size);
+    
+    mem_look = 8 * ((mem_addr)1 << (size - 1)) + sizeof(mem_addr);
+    
+    GMQCC_MEM_WRITEHEAP(0,        mem_addr, mem_look);
+    GMQCC_MEM_WRITEHEAP(mem_look, mem_addr, size);
+    
+    /* write pointers to first free bock of said size */
+    for (i = 1; i < size; i++)
+        GMQCC_MEM_WRITEHEAP(mem_look + sizeof(mem_addr) * i, mem_addr, 0);
+        
+    GMQCC_MEM_WRITEHEAP(mem_look + sizeof(mem_addr) * size, mem_addr, sizeof(mem_addr));
+    GMQCC_MEM_WRITEHEAP(sizeof(mem_addr), mem_addr, size);
+    GMQCC_MEM_MARKFREE (sizeof(mem_addr) * 2);
+    GMQCC_MEM_WRITEHEAP(sizeof(mem_addr) * 3, mem_addr, 0);
+    GMQCC_MEM_WRITEHEAP(sizeof(mem_addr) * 4, mem_addr, 0);
+}
+
+/* get needed block size */
+static size_t mem_getnbs(const size_t need) {
+    size_t b = 8;
+    size_t s = 1;
+    
+    while (need > b) {
+        b *= 2;
+        s ++;
+    }
+    
+    return s;
+}
+
+static void mem_removeblock(mem_addr a, size_t size) {
+    mem_addr p = GMQCC_MEM_GETADDROFPS(a);
+    mem_addr n = GMQCC_MEM_GETADDROFFS(a);
+    
+    GMQCC_MEM_TRACE("flow", "(%lu, %lu)", a, size);
+    
+    GMQCC_MEM_SETADDROFPS(a, ~((mem_addr)0));
+    GMQCC_MEM_SETADDROFFS(a, ~((mem_addr)0));
+    
+    /* handle singles in list */
+    if ((p == 0) && (n == 0)) {
+        GMQCC_MEM_WRITEFBA(size, 0);
+        return;
+    }
+    
+    /* first in list has different sibling semantics */
+    if (p == 0) {
+        GMQCC_MEM_WRITEFBA   (size, n);
+        GMQCC_MEM_SETADDROFPS(n, 0);
+        return;
+    }
+    
+    /* last item also has special meaning :) */
+    if (n == 0) {
+        GMQCC_MEM_SETADDROFFS(p, 0);
+        return;
+    }
+    
+    /* middle of list */
+    GMQCC_MEM_SETADDROFPS(n, p);
+    GMQCC_MEM_SETADDROFFS(p, n);
+}
+
+static int mem_createblock(const size_t size) {
+    mem_addr parent;
+    int      test;
+    
+    GMQCC_MEM_TRACE("flow", "(%lu)", size);
+    if (GMQCC_MEM_HASBLOCK(size))
+        return 0;
+        
+    if (size > GMQCC_MEM_READHEAP(mem_look, mem_addr))
+        abort();
+
+    /* recrusive ... */
+    test = mem_createblock(size + 1);
+    if (test != 0)
+        return test;
+        
+    /* safe splits assured */
+    parent = GMQCC_MEM_READFBA(size + 1);
+    mem_removeblock(parent, size + 1);
+    
+    /* split it */
+    GMQCC_MEM_WRITEFBA(size, parent);
+    {
+        /* find center and split */
+        mem_addr block = parent + 8 * ((mem_addr)1 << (size - 1));
+        mem_addr left  = parent;
+        mem_addr right = block;
+        
+        GMQCC_MEM_TRACE(
+            "dump",
+            "left: %lu right: %lu parent: %lu",
+            left, right, parent
+        );
+        
+        /* left half  */
+        GMQCC_MEM_WRITEHEAP  (left, mem_addr, size);
+        GMQCC_MEM_MARKFREE   (left);
+        GMQCC_MEM_SETADDROFPS(left, 0);
+        GMQCC_MEM_SETADDROFFS(left, right);
+        /* right half */
+        GMQCC_MEM_WRITEHEAP  (right, mem_addr, size);
+        GMQCC_MEM_MARKFREE   (right);
+        GMQCC_MEM_SETADDROFPS(right, left);
+        GMQCC_MEM_SETADDROFPS(right, 0);
+    }
+    mem_dump();
+    return 0;
+}
+
+static mem_addr mem_allocblock(const size_t size) {
+    GMQCC_MEM_TRACE("flow", "(%lu)", size);
+    int      test = mem_createblock(size);
+    mem_addr first;
+    mem_addr next;
+    
+    if (test != 0)
+        return 0;
+    
+    /* first free one */
+    first = GMQCC_MEM_READFBA    (size);
+    next  = GMQCC_MEM_GETADDROFFS(first);
+    
+    mem_removeblock(first, size);
+    
+    GMQCC_MEM_WRITEFBA(next, size);
+    GMQCC_MEM_MARKUSED(first);
+    
+    return first;
+}
+
+static int mem_getside(mem_addr addr, const size_t size) {
+    size_t start = addr - sizeof(mem_addr);
+    size_t test  = 0;
+    start /= 8;
+    test   = ((mem_addr)1 << (size));
+    
+    return ((start % test) == 0) ? GMQCC_MEM_BSL : GMQCC_MEM_BSR;
+}
+
+static mem_addr mem_getaddr(mem_addr start, const size_t size) {
+    size_t length = ((mem_addr)1 << (size - 1));
+    length *= 8;
+    
+    switch (mem_getside(start, size)) {
+        case GMQCC_MEM_BSL: return start + length;
+        case GMQCC_MEM_BSR: return start - length;
+    }
+    /* if reached blow up */
+    return (abort(), 1);
+}
+
+static void mem_addblock(mem_addr a, size_t s) {
+    mem_addr first = GMQCC_MEM_READFBA(s);
+    if (first == 0) {
+        /* only block */
+        GMQCC_MEM_WRITEFBA   (s, a);
+        GMQCC_MEM_SETADDROFPS(a, 0);
+        GMQCC_MEM_SETADDROFFS(a, 0);
+    } else {
+        /* add to front */
+        GMQCC_MEM_WRITEFBA   (s, a);
+        GMQCC_MEM_SETADDROFPS(a, 0);
+        GMQCC_MEM_SETADDROFFS(a, first);
+        GMQCC_MEM_SETADDROFPS(first, a);
+    }
+}
+
+void mem_init(size_t size) {
+    size_t alloc = size;
+    size_t count = 1;
+    size_t block = 1;
+    
+    /* blow up if too small */
+    assert (sizeof(void*) == sizeof(mem_addr));
+    
+    if (!(mem_heap = malloc(size)))
+        abort();
+    
+    memset(mem_heap, 170, size);
+    mem_size = size;
+    alloc    -= 2 * sizeof(mem_addr);
+    
+    while (alloc + sizeof(mem_addr) > 8 * block) {
+        alloc -= sizeof(mem_addr);
+        block *= 2;
+        count ++;
+    }
+    
+    /* over shot ? */
+    block /= 2;
+    count --;
+    
+    mem_init_table(count);
+}
+
+/* doesn't get any simpler :-) */
+void mem_destroy() {
+    free(mem_heap);
+    mem_heap = NULL;
+}
+
+void *mem_alloc(size_t amount) {
+    GMQCC_MEM_TRACE("flow", "(%lu)", amount);
+    size_t   need  = amount + 4 * sizeof(mem_addr);
+    size_t   size  = mem_getnbs    (need);
+    mem_addr block = mem_allocblock(size);
+    
+    GMQCC_MEM_TRACE("dump", "will allocate %lu size block", size);
+    /* standard behaviour */
+    if (block == 0)
+        return NULL;
+    GMQCC_MEM_TRACE("dump", "returning offset %lu", block);
+    return mem_heap + block + 4 * sizeof(mem_addr);
+}
+
+void mem_free(void *ptr) {
+    mem_addr start = (mem_addr)(ptr - mem_heap) - 4 * sizeof(mem_addr);
+    size_t   size  = GMQCC_MEM_READHEAP(start, mem_addr);
+    mem_addr addr  = mem_getaddr(start, size);
+    int      side  = mem_getside(start, size);
+    
+    
+    GMQCC_MEM_TRACE (
+        "dump",
+        "deallocating %s buddy (neighbour at %lu)",
+        (side == GMQCC_MEM_BSL) ? "left" : "right",
+        addr
+    );
+    GMQCC_MEM_MARKFREE(start);
+    
+    /* while free block merge */
+    while (GMQCC_MEM_BLOCKFREE(addr)) {
+        GMQCC_MEM_TRACE("dump", "merging ...");
+        mem_removeblock(addr, size);
+        
+        /* find new start */
+        start = addr < start ? addr : start;
+        size ++;
+        
+        if (size == GMQCC_MEM_READHEAP(mem_look, mem_addr))
+            break; /* blow up */
+            
+        addr = mem_getaddr(start, size);
+        GMQCC_MEM_TRACE("dump", "new block start is %lu, buddy at %lu", start, addr);
+    }
+    
+    /* add it */
+    GMQCC_MEM_WRITEBS(start, size);
+    mem_addblock     (start, size);
+}
index 0f354a7b1ce6c1605e971464bc3074dd43c664c1..534f007db4d2ba65d0071a8408088ec8e5346f2a 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -445,7 +445,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
     op = &operators[sy->ops[sy->ops_count-1].etype - 1];
     ctx = sy->ops[sy->ops_count-1].ctx;
 
-    DEBUGSHUNTDO(printf("apply %s\n", op->op));
+    DEBUGSHUNTDO(con_out("apply %s\n", op->op));
 
     if (sy->out_count < op->operands) {
         parseerror(parser, "internal error: not enough operands: %i (operator %s (%i))", sy->out_count,
@@ -775,7 +775,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 return false;
             }
             if (opts_standard == COMPILER_GMQCC)
-                printf("TODO: early out logic\n");
+                con_out("TODO: early out logic\n");
             if (CanConstFold(exprs[0], exprs[1]))
                 out = (ast_expression*)parser_const_float(parser,
                     (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
@@ -922,7 +922,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
         return false;
     }
 
-    DEBUGSHUNTDO(printf("applied %s\n", op->op));
+    DEBUGSHUNTDO(con_out("applied %s\n", op->op));
     sy->out[sy->out_count++] = syexp(ctx, out);
     return true;
 }
@@ -1159,7 +1159,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %s\n", parser_tokval(parser)));
+            DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
         }
         else if (parser->tok == TOKEN_FLOATCONST) {
             ast_value *val;
@@ -1175,7 +1175,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %g\n", parser_token(parser)->constval.f));
+            DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
         }
         else if (parser->tok == TOKEN_INTCONST) {
             ast_value *val;
@@ -1191,7 +1191,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %i\n", parser_token(parser)->constval.i));
+            DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i));
         }
         else if (parser->tok == TOKEN_STRINGCONST) {
             ast_value *val;
@@ -1207,7 +1207,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push string\n"));
+            DEBUGSHUNTDO(con_out("push string\n"));
         }
         else if (parser->tok == TOKEN_VECTORCONST) {
             ast_value *val;
@@ -1223,7 +1223,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push '%g %g %g'\n",
+            DEBUGSHUNTDO(con_out("push '%g %g %g'\n",
                                 parser_token(parser)->constval.v.x,
                                 parser_token(parser)->constval.v.y,
                                 parser_token(parser)->constval.v.z));
@@ -1238,7 +1238,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         }
         else if (parser->tok == ')') {
             if (wantop) {
-                DEBUGSHUNTDO(printf("do[op] )\n"));
+                DEBUGSHUNTDO(con_out("do[op] )\n"));
                 --parens;
                 if (parens < 0)
                     break;
@@ -1247,7 +1247,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 if (!parser_close_paren(parser, &sy, false))
                     goto onerr;
             } else {
-                DEBUGSHUNTDO(printf("do[nop] )\n"));
+                DEBUGSHUNTDO(con_out("do[nop] )\n"));
                 --parens;
                 if (parens < 0)
                     break;
@@ -1338,7 +1338,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
             if (op->id == opid1('(')) {
                 if (wantop) {
-                    DEBUGSHUNTDO(printf("push [op] (\n"));
+                    DEBUGSHUNTDO(con_out("push [op] (\n"));
                     ++parens;
                     /* we expected an operator, this is the function-call operator */
                     if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), SY_PAREN_FUNC, sy.out_count-1))) {
@@ -1351,7 +1351,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                         parseerror(parser, "out of memory");
                         goto onerr;
                     }
-                    DEBUGSHUNTDO(printf("push [nop] (\n"));
+                    DEBUGSHUNTDO(con_out("push [nop] (\n"));
                 }
                 wantop = false;
             } else if (op->id == opid1('[')) {
@@ -1367,7 +1367,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     goto onerr;
                 wantop = false;
             } else {
-                DEBUGSHUNTDO(printf("push operator %s\n", op->op));
+                DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
                 if (!shunt_ops_add(&sy, syop(parser_ctx(parser), op)))
                     goto onerr;
                 wantop = false;
@@ -1394,7 +1394,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         expr = sy.out[0].out;
     MEM_VECTOR_CLEAR(&sy, out);
     MEM_VECTOR_CLEAR(&sy, ops);
-    DEBUGSHUNTDO(printf("shunt done\n"));
+    DEBUGSHUNTDO(con_out("shunt done\n"));
     return expr;
 
 onerr:
@@ -3545,14 +3545,8 @@ bool parser_init()
     return true;
 }
 
-bool parser_compile(const char *filename)
+bool parser_compile()
 {
-    parser->lex = lex_open(filename);
-    if (!parser->lex) {
-        printf("failed to open file \"%s\"\n", filename);
-        return false;
-    }
-
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
 
@@ -3583,6 +3577,26 @@ bool parser_compile(const char *filename)
     return !parser->errors;
 }
 
+bool parser_compile_file(const char *filename)
+{
+    parser->lex = lex_open(filename);
+    if (!parser->lex) {
+        con_out("failed to open file \"%s\"\n", filename);
+        return false;
+    }
+    return parser_compile();
+}
+
+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);
+        return false;
+    }
+    return parser_compile();
+}
+
 void parser_cleanup()
 {
     size_t i;
@@ -3699,7 +3713,7 @@ bool parser_finish(const char *output)
     {
         ir = ir_builder_new("gmqcc_out");
         if (!ir) {
-            printf("failed to allocate builder\n");
+            con_out("failed to allocate builder\n");
             return false;
         }
 
@@ -3712,7 +3726,7 @@ bool parser_finish(const char *output)
             isconst = field->isconst;
             field->isconst = false;
             if (!ast_global_codegen((ast_value*)field, ir, true)) {
-                printf("failed to generate field %s\n", field->name);
+                con_out("failed to generate field %s\n", field->name);
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3743,28 +3757,28 @@ bool parser_finish(const char *output)
                 }
             }
             if (!ast_global_codegen(asvalue, ir, false)) {
-                printf("failed to generate global %s\n", parser->globals[i].name);
+                con_out("failed to generate global %s\n", parser->globals[i].name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_float_count; ++i) {
             if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
-                printf("failed to generate global %s\n", parser->imm_float[i]->name);
+                con_out("failed to generate global %s\n", parser->imm_float[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_string_count; ++i) {
             if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
-                printf("failed to generate global %s\n", parser->imm_string[i]->name);
+                con_out("failed to generate global %s\n", parser->imm_string[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_vector_count; ++i) {
             if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
-                printf("failed to generate global %s\n", parser->imm_vector[i]->name);
+                con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3826,12 +3840,12 @@ bool parser_finish(const char *output)
         }
         for (i = 0; i < parser->functions_count; ++i) {
             if (!ast_function_codegen(parser->functions[i], ir)) {
-                printf("failed to generate function %s\n", parser->functions[i]->name);
+                con_out("failed to generate function %s\n", parser->functions[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
             if (!ir_function_finalize(parser->functions[i]->ir_func)) {
-                printf("failed to finalize function %s\n", parser->functions[i]->name);
+                con_out("failed to finalize function %s\n", parser->functions[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3839,12 +3853,12 @@ bool parser_finish(const char *output)
 
         if (retval) {
             if (opts_dump)
-                ir_builder_dump(ir, printf);
+                ir_builder_dump(ir, con_out);
 
             generate_checksum(parser);
 
             if (!ir_builder_generate(ir, output)) {
-                printf("*** failed to generate output file\n");
+                con_out("*** failed to generate output file\n");
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3854,6 +3868,6 @@ bool parser_finish(const char *output)
         return retval;
     }
 
-    printf("*** there were compile errors\n");
+    con_out("*** there were compile errors\n");
     return false;
 }
diff --git a/util.c b/util.c
index ec115cdddb4097897e49d6fd0bf64ca914f35506..2689bc062e5389cf1682c6bfbfbf3e12fdf89a0c 100644 (file)
--- a/util.c
+++ b/util.c
@@ -242,11 +242,8 @@ void util_debug(const char *area, const char *ms, ...) {
         return;
 
     va_start(va, ms);
-    fprintf (stdout, "DEBUG: ");
-    fputc   ('[',  stdout);
-    fprintf(stdout, "%s", area);
-    fputs   ("] ", stdout);
-    vfprintf(stdout, ms, va);
+    con_out ("[%s] ", area);
+    con_vout(ms, va); 
     va_end  (va);
 }