More work on multiple error messages.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 03 Feb 2009 04:14:00 -0500
changeset 542 a56d3bfd2e36
parent 541 0240ccafb610
child 543 98742b1d8a4a
More work on multiple error messages. Removed all the FAIL/NOFAIL nastiness, and let errors flow through. Some of these changes are really dicey, especially in the assembler. We'll see what bugs show up now.
mojoshader.c
mojoshader_assembler.c
mojoshader_internal.h
--- a/mojoshader.c	Tue Feb 03 04:10:50 2009 -0500
+++ b/mojoshader.c	Tue Feb 03 04:14:00 2009 -0500
@@ -9,7 +9,6 @@
 
 // !!! FIXME: this file really needs to be split up.
 // !!! FIXME: I keep changing coding styles for symbols and typedefs.
-// !!! FIXME: this should report all errors, not quit on the first fail().
 
 #define __MOJOSHADER_INTERNAL__ 1
 #include "mojoshader_internal.h"
@@ -108,12 +107,6 @@
     struct ConstantsList *next;
 } ConstantsList;
 
-typedef struct ErrorList
-{
-    MOJOSHADER_error error;
-    struct ErrorList *next;
-} ErrorList;
-
 typedef struct VariableList
 {
     MOJOSHADER_uniformType type;
@@ -250,11 +243,9 @@
     1, &out_of_mem_error, 0, 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0
 };
 
-const char *out_of_mem_str = "Out of memory";
-static inline int out_of_memory(Context *ctx)
+static inline void out_of_memory(Context *ctx)
 {
     ctx->isfail = ctx->out_of_memory = 1;
-    return FAIL;
 } // out_of_memory
 
 static inline void *Malloc(Context *ctx, const size_t len)
@@ -353,12 +344,12 @@
 } // get_scratch_buffer
 
 
-static int failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
-static int failf(Context *ctx, const char *fmt, ...)
+static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
+static void failf(Context *ctx, const char *fmt, ...)
 {
     ctx->isfail = 1;
     if (ctx->out_of_memory)
-        return FAIL;
+        return;
 
     int error_position = 0;
     switch (ctx->parse_phase)
@@ -374,12 +365,12 @@
             break;
         default:
             assert(0 && "Unexpected value");
-            return FAIL;
+            return;
     } // switch
 
     ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
     if (error == NULL)
-        return FAIL;
+        return;
 
     char *scratch = get_scratch_buffer(ctx);
     va_list ap;
@@ -421,19 +412,17 @@
 
         ctx->error_count++;
     } // else
-
-    return FAIL;
 } // failf
 
 
-static inline int fail(Context *ctx, const char *reason)
-{
-    return failf(ctx, "%s", reason);
+static inline void fail(Context *ctx, const char *reason)
+{
+    failf(ctx, "%s", reason);
 } // fail
 
 
-static int output_line(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
-static int output_line(Context *ctx, const char *fmt, ...)
+static void output_line(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
+static void output_line(Context *ctx, const char *fmt, ...)
 {
     OutputListNode *item = NULL;
 
@@ -453,13 +442,13 @@
 
     item = (OutputListNode *) Malloc(ctx, sizeof (OutputListNode));
     if (item == NULL)
-        return FAIL;
+        return;
 
     item->str = (char *) Malloc(ctx, len + 1);
     if (item->str == NULL)
     {
         Free(ctx, item);
-        return FAIL;
+        return;
     } // if
 
     // If we overflowed our scratch buffer, that's okay. We were going to
@@ -481,15 +470,13 @@
     ctx->output->tail->next = item;
     ctx->output->tail = item;
     ctx->output_len += len + ctx->endline_len;
-
-    return 0;
 } // output_line
 
 
 // this is just to stop gcc whining.
-static inline int output_blank_line(Context *ctx)
-{
-    return output_line(ctx, "%s", "");
+static inline void output_blank_line(Context *ctx)
+{
+    output_line(ctx, "%s", "");
 } // output_blank_line
 
 
@@ -5030,12 +5017,11 @@
 static int parse_destination_token(Context *ctx, DestArgInfo *info)
 {
     // !!! FIXME: recheck against the spec for ranges (like RASTOUT values, etc).
-
-    if (isfail(ctx))
-        return FAIL;  // already failed elsewhere.
-
     if (ctx->tokencount == 0)
-        return fail(ctx, "Out of tokens in destination parameter");
+    {
+        fail(ctx, "Out of tokens in destination parameter");
+        return 0;
+    } // if
 
     const uint32 token = SWAP32(*(ctx->tokens));
     const int reserved1 = (int) ((token >> 14) & 0x3); // bits 14 through 15
@@ -5083,57 +5069,56 @@
     ctx->tokencount--;  // swallow token for now, for multiple calls in a row.
 
     if (reserved1 != 0x0)
-        return fail(ctx, "Reserved bit #1 in destination token must be zero");
+        fail(ctx, "Reserved bit #1 in destination token must be zero");
 
     if (reserved2 != 0x1)
-        return fail(ctx, "Reserved bit #2 in destination token must be one");
+        fail(ctx, "Reserved bit #2 in destination token must be one");
 
     if (info->relative)
     {
         if (!shader_is_vertex(ctx))
-            return fail(ctx, "Relative addressing in non-vertex shader");
-        else if (!shader_version_atleast(ctx, 3, 0))
-            return fail(ctx, "Relative addressing in vertex shader version < 3.0");
-        else if (!ctx->have_ctab)  // it's hard to do this efficiently without!
-            return fail(ctx, "relative addressing unsupported without a CTAB");
+            fail(ctx, "Relative addressing in non-vertex shader");
+        if (!shader_version_atleast(ctx, 3, 0))
+            fail(ctx, "Relative addressing in vertex shader version < 3.0");
+        if (!ctx->have_ctab)  // it's hard to do this efficiently without!
+            fail(ctx, "relative addressing unsupported without a CTAB");
+
         // !!! FIXME: I don't have a shader that has a relative dest currently.
-        return fail(ctx, "Relative addressing of dest tokens is unsupported");
+        fail(ctx, "Relative addressing of dest tokens is unsupported");
+        return 2;
     } // if
 
     const int s = info->result_shift;
     if (s != 0)
     {
         if (!shader_is_pixel(ctx))
-            return fail(ctx, "Result shift scale in non-pixel shader");
-        else if (shader_version_atleast(ctx, 2, 0))
-            return fail(ctx, "Result shift scale in pixel shader version >= 2.0");
-        else if ( ! (((s >= 1) && (s <= 3)) || ((s >= 0xD) && (s <= 0xF))) )
-            return fail(ctx, "Result shift scale isn't 1 to 3, or 13 to 15.");
+            fail(ctx, "Result shift scale in non-pixel shader");
+        if (shader_version_atleast(ctx, 2, 0))
+            fail(ctx, "Result shift scale in pixel shader version >= 2.0");
+        if ( ! (((s >= 1) && (s <= 3)) || ((s >= 0xD) && (s <= 0xF))) )
+            fail(ctx, "Result shift scale isn't 1 to 3, or 13 to 15.");
     } // if
 
     if (info->result_mod & MOD_PP)  // Partial precision (pixel shaders only)
     {
         if (!shader_is_pixel(ctx))
-            return fail(ctx, "Partial precision result mod in non-pixel shader");
+            fail(ctx, "Partial precision result mod in non-pixel shader");
     } // if
 
     if (info->result_mod & MOD_CENTROID)  // Centroid (pixel shaders only)
     {
         if (!shader_is_pixel(ctx))
-            return fail(ctx, "Centroid result mod in non-pixel shader");
+            fail(ctx, "Centroid result mod in non-pixel shader");
         else if (!ctx->centroid_allowed)  // only on DCL opcodes!
-            return fail(ctx, "Centroid modifier not allowed here");
+            fail(ctx, "Centroid modifier not allowed here");
     } // if
 
     if ((info->regtype < 0) || (info->regtype > REG_TYPE_MAX))
-        return fail(ctx, "Register type is out of range");
-
-    // !!! FIXME: from msdn:
-    //  "_sat cannot be used with instructions writing to output o# registers."
-    // !!! FIXME: actually, just go over this page:
-    //  http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/graphics/reference/shaders/ps_instructionmodifiers.asp
-
-    set_used_register(ctx, info->regtype, info->regnum);
+        fail(ctx, "Register type is out of range");
+
+    if (!isfail(ctx))
+        set_used_register(ctx, info->regtype, info->regnum);
+
     return 1;
 } // parse_destination_token
 
@@ -5270,11 +5255,11 @@
 {
     int retval = 1;
 
-    if (isfail(ctx))
-        return FAIL;  // already failed elsewhere.
-
     if (ctx->tokencount == 0)
-        return fail(ctx, "Out of tokens in source parameter");
+    {
+        fail(ctx, "Out of tokens in source parameter");
+        return 0;
+    } // if
 
     const uint32 token = SWAP32(*(ctx->tokens));
     const int reserved1 = (int) ((token >> 14) & 0x3); // bits 14 through 15
@@ -5315,18 +5300,21 @@
     ctx->tokencount--;  // swallow token for now, for multiple calls in a row.
 
     if (reserved1 != 0x0)
-        return fail(ctx, "Reserved bits #1 in source token must be zero");
+        fail(ctx, "Reserved bits #1 in source token must be zero");
 
     if (reserved2 != 0x1)
-        return fail(ctx, "Reserved bit #2 in source token must be one");
+        fail(ctx, "Reserved bit #2 in source token must be one");
+
+    if ((info->relative) && (ctx->tokencount == 0))
+    {
+        fail(ctx, "Out of tokens in relative source parameter");
+        info->relative = 0;  // don't try to process it.
+    } // if
 
     if (info->relative)
     {
         if ( (shader_is_pixel(ctx)) && (!shader_version_atleast(ctx, 3, 0)) )
-            return fail(ctx, "Relative addressing in pixel shader version < 3.0");
-
-        if (ctx->tokencount == 0)
-            return fail(ctx, "Out of tokens in relative source parameter");
+            fail(ctx, "Relative addressing in pixel shader version < 3.0");
 
         const uint32 reltoken = SWAP32(*(ctx->tokens));
         ctx->tokens++;  // swallow token for now, for multiple calls in a row.
@@ -5339,10 +5327,10 @@
                                     ((reltoken >> 8) & 0x18));
 
         if (((reltoken >> 31) & 0x1) == 0)
-            return fail(ctx, "bit #31 in relative address must be set");
+            fail(ctx, "bit #31 in relative address must be set");
 
         if ((reltoken & 0xF00E000) != 0)  // usused bits.
-            return fail(ctx, "relative address reserved bit must be zero");
+            fail(ctx, "relative address reserved bit must be zero");
 
         switch (info->relative_regtype)
         {
@@ -5350,48 +5338,51 @@
             case REG_TYPE_ADDRESS:
                 break;
             default:
-                return fail(ctx, "invalid register for relative address");
+                fail(ctx, "invalid register for relative address");
                 break;
         } // switch
 
         if (info->relative_regnum != 0)  // true for now.
-            return fail(ctx, "invalid register for relative address");
+            fail(ctx, "invalid register for relative address");
 
         if (!replicate_swizzle(relswiz))
-            return fail(ctx, "relative address needs replicate swizzle");
+            fail(ctx, "relative address needs replicate swizzle");
 
         if (info->regtype == REG_TYPE_INPUT)
         {
             if ( (shader_is_pixel(ctx)) || (!shader_version_atleast(ctx, 3, 0)) )
-                return fail(ctx, "relative addressing of input registers not supported in this shader model");
+                fail(ctx, "relative addressing of input registers not supported in this shader model");
             ctx->have_relative_input_registers = 1;
         } // if
         else if (info->regtype == REG_TYPE_CONST)
         {
             // figure out what array we're in...
             if (!ctx->have_ctab)  // it's hard to do this efficiently without!
-                return fail(ctx, "relative addressing unsupported without a CTAB");
-            determine_constants_arrays(ctx);
-
-            VariableList *var;
-            const int reltarget = info->regnum;
-            for (var = ctx->variables; var != NULL; var = var->next)
+                fail(ctx, "relative addressing unsupported without a CTAB");
+            else
             {
-                const int lo = var->index;
-                if ( (reltarget >= lo) && (reltarget < (lo + var->count)) )
-                    break;  // match!
-            } // for
-
-            if (var == NULL)
-                return fail(ctx, "relative addressing of indeterminate array");
-
-            var->used = 1;
-            info->relative_array = var;
-            set_used_register(ctx, info->relative_regtype, info->relative_regnum);
+                determine_constants_arrays(ctx);
+
+                VariableList *var;
+                const int reltarget = info->regnum;
+                for (var = ctx->variables; var != NULL; var = var->next)
+                {
+                    const int lo = var->index;
+                    if ( (reltarget >= lo) && (reltarget < (lo + var->count)) )
+                        break;  // match!
+                } // for
+
+                if (var == NULL)
+                    fail(ctx, "relative addressing of indeterminate array");
+
+                var->used = 1;
+                info->relative_array = var;
+                set_used_register(ctx, info->relative_regtype, info->relative_regnum);
+            } // else
         } // else if
         else
         {
-            return fail(ctx, "relative addressing of invalid register");
+            fail(ctx, "relative addressing of invalid register");
         } // else
 
         retval++;
@@ -5416,19 +5407,19 @@
         case SRCMOD_DZ:
         case SRCMOD_DW:
             if (shader_version_atleast(ctx, 2, 0))
-                return fail(ctx, "illegal source mod for this Shader Model.");
+                fail(ctx, "illegal source mod for this Shader Model.");
             break;
 
         case SRCMOD_NOT:  // !!! FIXME: I _think_ this is right...
             if (shader_version_atleast(ctx, 2, 0))
             {
                 if (info->regtype != REG_TYPE_PREDICATE)
-                    return fail(ctx, "NOT only allowed on predicate register.");
+                    fail(ctx, "NOT only allowed on predicate register.");
             } // if
             break;
 
         default:
-            return fail(ctx, "Unknown source modifier");
+            fail(ctx, "Unknown source modifier");
     } // switch
 
     // !!! FIXME: docs say this for sm3 ... check these!
@@ -5440,7 +5431,9 @@
     //    All of the constant floating-point registers must use the abs modifier.
     //    None of the constant floating-point registers can use the abs modifier.
 
-    set_used_register(ctx, info->regtype, info->regnum);
+    if (!isfail(ctx))
+        set_used_register(ctx, info->regtype, info->regnum);
+
     return retval;
 } // parse_source_token
 
@@ -5448,16 +5441,15 @@
 static int parse_predicated_token(Context *ctx)
 {
     SourceArgInfo *arg = &ctx->predicate_arg;
-    if (parse_source_token(ctx, arg) == FAIL)
-        return FAIL;
-    else if (arg->regtype != REG_TYPE_PREDICATE)
-        return fail(ctx, "Predicated instruction but not predicate register!");
-    else if ((arg->src_mod != SRCMOD_NONE) && (arg->src_mod != SRCMOD_NOT))
-        return fail(ctx, "Predicated instruction register is not NONE or NOT");
-    else if ( !no_swizzle(arg->swizzle) && !replicate_swizzle(arg->swizzle) )
-        return fail(ctx, "Predicated instruction register has wrong swizzle");
-    else if (arg->relative)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "relative addressing in predicated token");
+    parse_source_token(ctx, arg);
+    if (arg->regtype != REG_TYPE_PREDICATE)
+        fail(ctx, "Predicated instruction but not predicate register!");
+    if ((arg->src_mod != SRCMOD_NONE) && (arg->src_mod != SRCMOD_NOT))
+        fail(ctx, "Predicated instruction register is not NONE or NOT");
+    if ( !no_swizzle(arg->swizzle) && !replicate_swizzle(arg->swizzle) )
+        fail(ctx, "Predicated instruction register has wrong swizzle");
+    if (arg->relative)  // I'm pretty sure this is illegal...?
+        fail(ctx, "relative addressing in predicated token");
 
     return 1;
 } // parse_predicated_token
@@ -5465,18 +5457,17 @@
 
 static int parse_args_NULL(Context *ctx)
 {
-    return (isfail(ctx) ? FAIL : 1);
+    return 1;
 } // parse_args_NULL
 
 
 static int parse_args_DEF(Context *ctx)
 {
-    if (parse_destination_token(ctx, &ctx->dest_arg) == FAIL)
-        return FAIL;
-    else if (ctx->dest_arg.regtype != REG_TYPE_CONST)
-        return fail(ctx, "DEF using non-CONST register");
-    else if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "relative addressing in DEF");
+    parse_destination_token(ctx, &ctx->dest_arg);
+    if (ctx->dest_arg.regtype != REG_TYPE_CONST)
+        fail(ctx, "DEF using non-CONST register");
+    if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
+        fail(ctx, "relative addressing in DEF");
 
     ctx->dwords[0] = SWAP32(ctx->tokens[0]);
     ctx->dwords[1] = SWAP32(ctx->tokens[1]);
@@ -5489,12 +5480,11 @@
 
 static int parse_args_DEFI(Context *ctx)
 {
-    if (parse_destination_token(ctx, &ctx->dest_arg) == FAIL)
-        return FAIL;
-    else if (ctx->dest_arg.regtype != REG_TYPE_CONSTINT)
-        return fail(ctx, "DEFI using non-CONSTING register");
-    else if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "relative addressing in DEFI");
+    parse_destination_token(ctx, &ctx->dest_arg);
+    if (ctx->dest_arg.regtype != REG_TYPE_CONSTINT)
+        fail(ctx, "DEFI using non-CONSTING register");
+    if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
+        fail(ctx, "relative addressing in DEFI");
 
     ctx->dwords[0] = SWAP32(ctx->tokens[0]);
     ctx->dwords[1] = SWAP32(ctx->tokens[1]);
@@ -5507,12 +5497,11 @@
 
 static int parse_args_DEFB(Context *ctx)
 {
-    if (parse_destination_token(ctx, &ctx->dest_arg) == FAIL)
-        return FAIL;
-    else if (ctx->dest_arg.regtype != REG_TYPE_CONSTBOOL)
-        return fail(ctx, "DEFB using non-CONSTBOOL register");
-    else if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "relative addressing in DEFB");
+    parse_destination_token(ctx, &ctx->dest_arg);
+    if (ctx->dest_arg.regtype != REG_TYPE_CONSTBOOL)
+        fail(ctx, "DEFB using non-CONSTBOOL register");
+    if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
+        fail(ctx, "relative addressing in DEFB");
 
     ctx->dwords[0] = *(ctx->tokens) ? 1 : 0;
 
@@ -5543,20 +5532,18 @@
     uint32 reserved_mask = 0x00000000;
 
     if (reserved1 != 0x1)
-        return fail(ctx, "Bit #31 in DCL token must be one");
+        fail(ctx, "Bit #31 in DCL token must be one");
 
     ctx->centroid_allowed = 1;
     ctx->tokens++;
     ctx->tokencount--;
-    const int parse_dest_rc = parse_destination_token(ctx, &ctx->dest_arg);
+    parse_destination_token(ctx, &ctx->dest_arg);
     ctx->centroid_allowed = 0;
-    if (parse_dest_rc == FAIL)
-        return FAIL;
 
     if (ctx->dest_arg.result_shift != 0)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "shift scale in DCL");
-    else if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
-        return fail(ctx, "relative addressing in DCL");
+        fail(ctx, "shift scale in DCL");
+    if (ctx->dest_arg.relative)  // I'm pretty sure this is illegal...?
+        fail(ctx, "relative addressing in DCL");
 
     const RegisterType regtype = ctx->dest_arg.regtype;
     const int regnum = ctx->dest_arg.regnum;
@@ -5580,11 +5567,11 @@
             {
                 reserved_mask = 0x7FFFFFFF;
                 if (!writemask_xyzw(ctx->dest_arg.orig_writemask))
-                    return fail(ctx, "DCL face writemask must be full");
-                else if (ctx->dest_arg.result_mod != 0)
-                    return fail(ctx, "DCL face result modifier must be zero");
-                else if (ctx->dest_arg.result_shift != 0)
-                    return fail(ctx, "DCL face shift scale must be zero");
+                    fail(ctx, "DCL face writemask must be full");
+                if (ctx->dest_arg.result_mod != 0)
+                    fail(ctx, "DCL face result modifier must be zero");
+                if (ctx->dest_arg.result_shift != 0)
+                    fail(ctx, "DCL face shift scale must be zero");
             } // else if
             else
             {
@@ -5602,16 +5589,16 @@
             if (usage == MOJOSHADER_USAGE_TEXCOORD)
             {
                 if (index > 7)
-                    return fail(ctx, "DCL texcoord usage must have 0-7 index");
+                    fail(ctx, "DCL texcoord usage must have 0-7 index");
             } // if
             else if (usage == MOJOSHADER_USAGE_COLOR)
             {
                 if (index != 0)
-                    return fail(ctx, "DCL color usage must have 0 index");
+                    fail(ctx, "DCL color usage must have 0 index");
             } // else if
             else
             {
-                return fail(ctx, "Invalid DCL texture usage");
+                fail(ctx, "Invalid DCL texture usage");
             } // else
 
             reserved_mask = 0x7FF0FFE0;
@@ -5623,7 +5610,7 @@
         {
             const uint32 ttype = ((token >> 27) & 0xF);
             if (!valid_texture_type(ttype))
-                return fail(ctx, "unknown sampler texture type");
+                fail(ctx, "unknown sampler texture type");
             reserved_mask = 0x7FFFFFF;
             ctx->dwords[0] = ttype;
         } // else if
@@ -5652,7 +5639,7 @@
         {
             const uint32 ttype = ((token >> 27) & 0xF);
             if (!valid_texture_type(ttype))
-                return fail(ctx, "unknown sampler texture type");
+                fail(ctx, "unknown sampler texture type");
             reserved_mask = 0x7FFFFFF;
             ctx->dwords[0] = ttype;
         } // else if
@@ -5700,10 +5687,10 @@
     } // else
 
     if (unsupported)
-        return fail(ctx, "invalid DCL register type for this shader model");
+        fail(ctx, "invalid DCL register type for this shader model");
 
     if ((token & reserved_mask) != 0)
-        return fail(ctx, "reserved bits in DCL dword aren't zero");
+        fail(ctx, "reserved bits in DCL dword aren't zero");
 
     return 3;
 } // parse_args_DCL
@@ -5713,7 +5700,7 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx, &ctx->dest_arg);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_D
 
 
@@ -5721,7 +5708,7 @@
 {
     int retval = 1;
     retval += parse_source_token(ctx, &ctx->source_args[0]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_S
 
 
@@ -5730,7 +5717,7 @@
     int retval = 1;
     retval += parse_source_token(ctx, &ctx->source_args[0]);
     retval += parse_source_token(ctx, &ctx->source_args[1]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_SS
 
 
@@ -5739,7 +5726,7 @@
     int retval = 1;
     retval += parse_destination_token(ctx, &ctx->dest_arg);
     retval += parse_source_token(ctx, &ctx->source_args[0]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DS
 
 
@@ -5749,7 +5736,7 @@
     retval += parse_destination_token(ctx, &ctx->dest_arg);
     retval += parse_source_token(ctx, &ctx->source_args[0]);
     retval += parse_source_token(ctx, &ctx->source_args[1]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSS
 
 
@@ -5760,7 +5747,7 @@
     retval += parse_source_token(ctx, &ctx->source_args[0]);
     retval += parse_source_token(ctx, &ctx->source_args[1]);
     retval += parse_source_token(ctx, &ctx->source_args[2]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSSS
 
 
@@ -5772,7 +5759,7 @@
     retval += parse_source_token(ctx, &ctx->source_args[1]);
     retval += parse_source_token(ctx, &ctx->source_args[2]);
     retval += parse_source_token(ctx, &ctx->source_args[3]);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSSSS
 
 
@@ -6047,25 +6034,20 @@
         fail(ctx, "REP without ENDREP");
 } // state_RET
 
-static int check_label_register(Context *ctx, int arg, const char *opcode)
+static void check_label_register(Context *ctx, int arg, const char *opcode)
 {
     const SourceArgInfo *info = &ctx->source_args[arg];
     const RegisterType regtype = info->regtype;
     const int regnum = info->regnum;
 
     if (regtype != REG_TYPE_LABEL)
-        return failf(ctx, "%s with a non-label register specified", opcode);
-
-    else if (!shader_version_atleast(ctx, 2, 0))
-        return failf(ctx, "%s not supported in Shader Model 1", opcode);
-
-    else if ((shader_version_atleast(ctx, 2, 255)) && (regnum > 2047))
-        return failf(ctx, "label register number must be <= 2047");
-
-    else if (regnum > 15)
-        return failf(ctx, "label register number must be <= 15");
-
-    return 0;
+        failf(ctx, "%s with a non-label register specified", opcode);
+    if (!shader_version_atleast(ctx, 2, 0))
+        failf(ctx, "%s not supported in Shader Model 1", opcode);
+    if ((shader_version_atleast(ctx, 2, 255)) && (regnum > 2047))
+        fail(ctx, "label register number must be <= 2047");
+    if (regnum > 15)
+        fail(ctx, "label register number must be <= 15");
 } // check_label_register
 
 static void state_LABEL(Context *ctx)
@@ -6100,8 +6082,8 @@
 
 static void state_CALL(Context *ctx)
 {
-    if (check_label_register(ctx, 0, "CALL") != FAIL)
-        check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
+    check_label_register(ctx, 0, "CALL");
+    check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
 } // state_CALL
 
 static void state_CALLNZ(Context *ctx)
@@ -6109,8 +6091,8 @@
     const RegisterType regtype = ctx->source_args[1].regtype;
     if ((regtype != REG_TYPE_CONSTBOOL) && (regtype != REG_TYPE_PREDICATE))
         fail(ctx, "CALLNZ argument isn't constbool or predicate register");
-    else if (check_label_register(ctx, 0, "CALLNZ") != FAIL)
-        check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
+    check_label_register(ctx, 0, "CALLNZ");
+    check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
 } // state_CALLNZ
 
 static void state_MOVA(Context *ctx)
@@ -6444,7 +6426,7 @@
 
 static int parse_instruction_token(Context *ctx)
 {
-    int retval = NOFAIL;
+    int retval = 0;
     const uint32 *start_tokens = ctx->tokens;
     const uint32 start_tokencount = ctx->tokencount;
     const uint32 token = SWAP32(*(ctx->tokens));
@@ -6461,24 +6443,28 @@
     const emit_function emitter = instruction->emitter[ctx->profileid];
 
     if ((token & 0x80000000) != 0)
-        return fail(ctx, "instruction token high bit must be zero.");  // so says msdn.
-    else if (instruction->opcode_string == NULL)
-        return fail(ctx, "Unknown opcode.");
+        fail(ctx, "instruction token high bit must be zero.");  // so says msdn.
+
+    if (instruction->opcode_string == NULL)
+    {
+        fail(ctx, "Unknown opcode.");
+        return 1;  // pray that you resync later.
+    } // if
 
     if (coissue)
     {
         if (!shader_is_pixel(ctx))
-            return fail(ctx, "coissue instruction on non-pixel shader");
-        else if (shader_version_atleast(ctx, 2, 0))
-            return fail(ctx, "coissue instruction in Shader Model >= 2.0");
+            fail(ctx, "coissue instruction on non-pixel shader");
+        if (shader_version_atleast(ctx, 2, 0))
+            fail(ctx, "coissue instruction in Shader Model >= 2.0");
         // !!! FIXME: I'm not sure what this actually means, yet.
-        return fail(ctx, "coissue instructions unsupported");
+        fail(ctx, "coissue instructions unsupported");
     } // if
 
     if ((ctx->shader_type & instruction->shader_types) == 0)
     {
-        return failf(ctx, "opcode '%s' not available in this shader type.",
-                     instruction->opcode_string);
+        failf(ctx, "opcode '%s' not available in this shader type.",
+                instruction->opcode_string);
     } // if
 
     memset(ctx->dwords, '\0', sizeof (ctx->dwords));
@@ -6489,52 +6475,40 @@
     ctx->tokens++;
     ctx->tokencount--;
     retval = instruction->parse_args(ctx);
-    assert((isfail(ctx)) || (retval >= 0));
-
-    if ( (!isfail(ctx)) && (predicated) )
-    {
-        if (parse_predicated_token(ctx) != FAIL)
-            retval++;  // one more token.
-    } // if
+
+    if (predicated)
+        retval += parse_predicated_token(ctx);
 
     // parse_args() moves these forward for convenience...reset them.
     ctx->tokens = start_tokens;
     ctx->tokencount = start_tokencount;
 
-    if (!isfail(ctx))
-    {
-        if (instruction->state != NULL)
-            instruction->state(ctx);
-    } // if
+    if (instruction->state != NULL)
+        instruction->state(ctx);
 
     ctx->instruction_count += instruction->slots;
 
-    if (isfail(ctx))
-        retval = FAIL;
-    else
+    if (!isfail(ctx))
         emitter(ctx);  // call the profile's emitter.
 
     ctx->previous_opcode = opcode;
     ctx->scratch_registers = 0;  // reset after every instruction.
 
-    if (!isfail(ctx))
-    {
-        if (!shader_version_atleast(ctx, 2, 0))
-        {
-            if (insttoks != 0)  // reserved field in shaders < 2.0 ...
-                return fail(ctx, "instruction token count must be zero");
-        } // if
-        else
+    if (!shader_version_atleast(ctx, 2, 0))
+    {
+        if (insttoks != 0)  // reserved field in shaders < 2.0 ...
+            fail(ctx, "instruction token count must be zero");
+    } // if
+    else
+    {
+        if (retval != (insttoks+1))
         {
-            if (retval != (insttoks+1))
-            {
-                return failf(ctx,
-                        "wrong token count (%u, not %u) for opcode '%s'.",
-                        (uint) retval, (uint) (insttoks+1),
-                        instruction->opcode_string);
-            } // if
-        } // else
-    } // if
+            failf(ctx, "wrong token count (%u, not %u) for opcode '%s'.",
+                    (uint) retval, (uint) (insttoks+1),
+                    instruction->opcode_string);
+            retval = insttoks + 1;  // try to keep sync.
+        } // if
+    } // else
 
     return retval;
 } // parse_instruction_token
@@ -6543,7 +6517,10 @@
 static int parse_version_token(Context *ctx, const char *profilestr)
 {
     if (ctx->tokencount == 0)
-        return fail(ctx, "Expected version token, got none at all.");
+    {
+        fail(ctx, "Expected version token, got none at all.");
+        return 0;
+    } // if
 
     const uint32 token = SWAP32(*(ctx->tokens));
     const uint32 shadertype = ((token >> 16) & 0xFFFF);
@@ -6565,7 +6542,7 @@
     } // else if
     else  // geometry shader? Bogus data?
     {
-        return fail(ctx, "Unsupported shader type or not a shader at all");
+        fail(ctx, "Unsupported shader type or not a shader at all");
     } // else
 
     ctx->major_ver = major;
@@ -6573,11 +6550,13 @@
 
     if (!shader_version_supported(major, minor))
     {
-        return failf(ctx, "Shader Model %u.%u is currently unsupported.",
-                     (uint) major, (uint) minor);
+        failf(ctx, "Shader Model %u.%u is currently unsupported.",
+                (uint) major, (uint) minor);
     } // if
 
-    ctx->profile->start_emitter(ctx, profilestr);
+    if (!isfail(ctx))
+        ctx->profile->start_emitter(ctx, profilestr);
+
     return 1;  // ate one token.
 } // parse_version_token
 
@@ -6666,18 +6645,13 @@
     const uint32 token = SWAP32(*(ctx->tokens));
     if ((token & 0xFFFF) != 0xFFFE)
         return 0;  // not a comment token.
-    else if ((token & 0x80000000) != 0)
-        return fail(ctx, "comment token high bit must be zero.");  // so says msdn.
-    else
-    {
-        const uint32 commenttoks = ((token >> 16) & 0xFFFF);
-        if ((commenttoks >= 8) && (commenttoks < ctx->tokencount))
-            parse_constant_table(ctx, commenttoks * 4);
-        return commenttoks + 1;  // comment data plus the initial token.
-    } // else
-
-    // shouldn't hit this.
-    return failf(ctx, "Logic error at %s:%d", __FILE__, __LINE__);
+    if ((token & 0x80000000) != 0)
+        fail(ctx, "comment token high bit must be zero.");  // so says msdn.
+
+    const uint32 commenttoks = ((token >> 16) & 0xFFFF);
+    if ((commenttoks >= 8) && (commenttoks < ctx->tokencount))
+        parse_constant_table(ctx, commenttoks * 4);
+    return commenttoks + 1;  // comment data plus the initial token.
 } // parse_comment_token
 
 
@@ -6687,11 +6661,12 @@
         return 0;  // not us, eat no tokens.
 
     if (ctx->tokencount != 1)  // we _must_ be last. If not: fail.
-        return fail(ctx, "end token before end of stream");
-
-    ctx->profile->end_emitter(ctx);
-
-    return END_OF_STREAM;
+        fail(ctx, "end token before end of stream");
+
+    if (!isfail(ctx))
+        ctx->profile->end_emitter(ctx);
+
+    return 1;
 } // parse_end_token
 
 
@@ -6700,9 +6675,13 @@
     // !!! FIXME: needs state; allow only one phase token per shader, I think?
     if (SWAP32(*(ctx->tokens)) != 0x0000FFFD) // phase token always 0x0000FFFD.
         return 0;  // not us, eat no tokens.
-    else if ( (!shader_is_pixel(ctx)) || (!shader_version_exactly(ctx, 1, 4)) )
-        return fail(ctx, "phase token only available in 1.4 pixel shaders");
-    ctx->profile->phase_emitter(ctx);
+
+    if ( (!shader_is_pixel(ctx)) || (!shader_version_exactly(ctx, 1, 4)) )
+        fail(ctx, "phase token only available in 1.4 pixel shaders");
+
+    if (!isfail(ctx))
+        ctx->profile->phase_emitter(ctx);
+
     return 1;
 } // parse_phase_token
 
@@ -6711,11 +6690,10 @@
 {
     int rc = 0;
 
-    if (ctx->output_stack_len != 0)
-        return fail(ctx, "BUG: output stack isn't empty on new token!");
-
-    else if (ctx->tokencount == 0)
-        return fail(ctx, "unexpected end of shader.");
+    assert(ctx->output_stack_len == 0);
+
+    if (ctx->tokencount == 0)
+        fail(ctx, "unexpected end of shader.");
 
     else if ((rc = parse_comment_token(ctx)) != 0)
         return rc;
@@ -6729,7 +6707,8 @@
     else if ((rc = parse_instruction_token(ctx)) != 0)
         return rc;
 
-    return failf(ctx, "unknown token (%u)", (uint) *ctx->tokens);
+    failf(ctx, "unknown token (%u)", (uint) *ctx->tokens);
+    return 1;  // good luck!
 } // parse_token
 
 
@@ -7486,7 +7465,7 @@
 {
     MOJOSHADER_parseData *retval = NULL;
     Context *ctx = NULL;
-    int rc = FAIL;
+    int rc = 0;
     int failed = 0;
 
     if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
@@ -7499,14 +7478,11 @@
     verify_swizzles(ctx);
 
     // Version token always comes first.
-    if (!isfail(ctx))
-    {
-        ctx->parse_phase = MOJOSHADER_PARSEPHASE_WORKING;
-        rc = parse_version_token(ctx, profile);
-    } // if
+    ctx->parse_phase = MOJOSHADER_PARSEPHASE_WORKING;
+    rc = parse_version_token(ctx, profile);
 
     // parse out the rest of the tokens after the version token...
-    while (rc > 0)
+    while (ctx->tokencount > 0)
     {
         // reset for each token.
         if (isfail(ctx))
--- a/mojoshader_assembler.c	Tue Feb 03 04:10:50 2009 -0500
+++ b/mojoshader_assembler.c	Tue Feb 03 04:14:00 2009 -0500
@@ -7,8 +7,6 @@
  *  This file written by Ryan C. Gordon.
  */
 
-// !!! FIXME: this should report all errors, not quit on the first fail().
-
 #define __MOJOSHADER_INTERNAL__ 1
 #include "mojoshader_internal.h"
 
@@ -37,10 +35,14 @@
 // Context...this is state that changes as we assemble a shader...
 typedef struct Context
 {
+    int isfail;
+    int out_of_memory;
+    int eof;
     MOJOSHADER_malloc malloc;
     MOJOSHADER_free free;
     void *malloc_data;
-    const char *failstr;
+    int error_count;
+    ErrorList *errors;
     TokenizerContext tctx;
     MOJOSHADER_parsePhase parse_phase;
     MOJOSHADER_shaderType shader_type;
@@ -62,11 +64,9 @@
 
 // Convenience functions for allocators...
 
-static inline int out_of_memory(Context *ctx)
+static inline void out_of_memory(Context *ctx)
 {
-    if (ctx->failstr == NULL)
-        ctx->failstr = out_of_mem_str;  // fail() would call malloc().
-    return FAIL;
+    ctx->isfail = ctx->out_of_memory = 1;
 } // out_of_memory
 
 static inline void *Malloc(Context *ctx, const size_t len)
@@ -77,44 +77,100 @@
     return retval;
 } // Malloc
 
+static inline char *StrDup(Context *ctx, const char *str)
+{
+    char *retval = (char *) Malloc(ctx, strlen(str) + 1);
+    if (retval == NULL)
+        out_of_memory(ctx);
+    else
+        strcpy(retval, str);
+    return retval;
+} // StrDup
+
 static inline void Free(Context *ctx, void *ptr)
 {
     if (ptr != NULL)  // check for NULL in case of dumb free() impl.
         ctx->free(ptr, ctx->malloc_data);
 } // Free
 
-static int failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
-static int failf(Context *ctx, const char *fmt, ...)
+static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
+static void failf(Context *ctx, const char *fmt, ...)
 {
-    if (ctx->failstr == NULL)  // don't change existing error.
+    const char *fname = NULL;
+    unsigned int linenum = 0;
+    int error_position = 0;
+
+    switch (ctx->parse_phase)
     {
-        char scratch = 0;
-        va_list ap;
+        case MOJOSHADER_PARSEPHASE_NOTSTARTED:
+            error_position = -2;
+            break;
+        case MOJOSHADER_PARSEPHASE_WORKING:
+            // !!! FIXME: fname == base source file if output_pos == 0.
+            if (ctx->output_len > 0)
+            {
+                const size_t idx = ctx->output_len - 1;
+                linenum = ctx->token_to_source[idx].line;
+                fname = ctx->token_to_source[idx].filename;
+            } // if
+            error_position = linenum;
+            break;
+        case MOJOSHADER_PARSEPHASE_DONE:
+            error_position = -1;
+            break;
+        default:
+            assert(0 && "Unexpected value");
+            return;
+    } // switch
+
+    ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
+    if (error == NULL)
+        return;
+
+    char scratch = 0;
+    va_list ap;
+    va_start(ap, fmt);
+    const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap);
+    va_end(ap);
+
+    char *failstr = (char *) Malloc(ctx, len + 1);
+    if (failstr == NULL)
+        Free(ctx, error);
+    else
+    {
         va_start(ap, fmt);
-        const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap);
+        vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
         va_end(ap);
 
-        char *failstr = (char *) Malloc(ctx, len + 1);
-        if (failstr != NULL)
+        error->error.error = failstr;
+        error->error.filename = fname ? StrDup(ctx, fname) : NULL;
+        error->error.error_position = error_position;
+
+        ErrorList *prev = NULL;
+        error->next = ctx->errors;
+        while (error->next != NULL)
         {
-            va_start(ap, fmt);
-            vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
-            va_end(ap);
-            ctx->failstr = failstr;
-        } // if
-    } // if
+            prev = error->next;
+            error->next = error->next->next;
+        } // while
 
-    return FAIL;
+        if (prev != NULL)
+            prev->next = error;
+        else
+            ctx->errors = error;
+
+        ctx->error_count++;
+    } // else
 } // failf
 
-static inline int fail(Context *ctx, const char *reason)
+static inline void fail(Context *ctx, const char *reason)
 {
-    return failf(ctx, "%s", reason);
+    failf(ctx, "%s", reason);
 } // fail
 
 static inline int isfail(const Context *ctx)
 {
-    return (ctx->failstr != NULL);
+    return ctx->isfail;
 } // isfail
 
 
@@ -243,13 +299,10 @@
 {
     int idx = 0;
 
-    if (isfail(ctx))
-        return FAIL;
-
     if (tctx->pushedback)
     {
         tctx->pushedback = 0;
-        return NOFAIL;
+        return 1;
     } // if
 
     if (tctx->on_endline)
@@ -262,7 +315,10 @@
     {
         // !!! FIXME: carefully crafted (but legal) comments can trigger this.
         if (idx >= sizeof (tctx->token))
-            return fail(ctx, "buffer overflow");
+        {
+            fail(ctx, "buffer overflow");
+            return 0;
+        } // if
 
         char ch = *tctx->source;
         if (ch == '\t')
@@ -280,7 +336,7 @@
             if ((idx > 0) && ((tctx->prevchar < '0') || (tctx->prevchar > '9')))
             {
                 tctx->token[idx++] = '\0';
-                return NOFAIL;
+                return 1;
             } // if
         } // if
         else
@@ -289,7 +345,7 @@
             if ((idx > 0) && ((tctx->prevchar >= '0') && (tctx->prevchar <= '9')))
             {
                 tctx->token[idx++] = '\0';
-                return NOFAIL;
+                return 1;
             } // if
         } // else
 
@@ -310,7 +366,7 @@
                     } // if
                     tctx->token[idx++] = '\0';
                 } // else
-                return NOFAIL;
+                return 1;
 
             case ' ':
                 if (tctx->prevchar == ' ')
@@ -338,13 +394,14 @@
                     tctx->token[idx++] = ch;
                     tctx->token[idx++] = '\0';
                 } // else
-                return NOFAIL;
+                return 1;
 
             case '\0':
                 tctx->token[idx] = '\0';
                 if (idx != 0)  // had any chars? It's a token.
-                    return NOFAIL;
-                return END_OF_STREAM;
+                    return 1;
+                ctx->eof = 1;
+                return 0;
 
             default:
                 tctx->source++;
@@ -355,7 +412,8 @@
         tctx->prevchar = ch;
     } // while
 
-    return fail(ctx, "???");  // shouldn't hit this.
+    assert(0 && "Shouldn't hit this code");
+    return 0;
 } // tokenize_ctx
 
 
@@ -364,10 +422,7 @@
     const int rc = tokenize_ctx(ctx, &ctx->tctx);
 
     #if DEBUG_TOKENIZER
-    printf("TOKENIZE: %s '%s'\n",
-           (rc == END_OF_STREAM) ? "END_OF_STREAM" :
-           (rc == FAIL) ? "FAIL" :
-           (rc == NOFAIL) ? "NOFAIL" : "???",
+    printf("TOKENIZE: %d '%s'\n", rc,
            (ctx->tctx.token[0] == '\n') ? "\\n" : ctx->tctx.token);
     #endif
 
@@ -375,28 +430,19 @@
 } // tokenize
 
 
-static int pushback_ctx(Context *ctx, TokenizerContext *tctx)
+static void pushback_ctx(Context *ctx, TokenizerContext *tctx)
 {
-    if (tctx->pushedback)
-        return fail(ctx, "BUG: Double pushback in parser");
-    else
-        tctx->pushedback = 1;
+    assert(!tctx->pushedback);
+    tctx->pushedback = 1;
+} // pushback_ctx
 
-    return NOFAIL;
-}
 
-static inline int pushback(Context *ctx)
+static inline void pushback(Context *ctx)
 {
-    const int rc = pushback_ctx(ctx, &ctx->tctx);
-
+    pushback_ctx(ctx, &ctx->tctx);
     #if DEBUG_TOKENIZER
-    printf("PUSHBACK: %s\n",
-           (rc == END_OF_STREAM) ? "END_OF_STREAM" :
-           (rc == FAIL) ? "FAIL" :
-           (rc == NOFAIL) ? "NOFAIL" : "???");
+    printf("PUSHBACK\n");
     #endif
-
-    return rc;
 } // pushback
 
 
@@ -404,16 +450,17 @@
                      const int ignoreeol, const int ignorewhitespace,
                      const int eolok, const int eosok)
 {
-    int rc = NOFAIL;
-
-    while ((rc = tokenize_ctx(ctx, tctx)) == NOFAIL)
+    while (tokenize_ctx(ctx, tctx))
     {
         if (tokeq(tctx, "\n"))
         {
             if (ignoreeol)
                 continue;
             else if (!eolok)
-                return fail(ctx, "Unexpected EOL");
+            {
+                fail(ctx, "Unexpected EOL");
+                return 0;
+            } // else if
         } // if
 
         else if (tokeq(tctx, " "))
@@ -425,7 +472,7 @@
         // skip comments...
         else if (tokeq(tctx, "//") || tokeq(tctx, ";"))
         {
-            while ((rc = tokenize_ctx(ctx, tctx)) == NOFAIL)
+            while (tokenize_ctx(ctx, tctx))
             {
                 if (tokeq(tctx, "\n"))
                 {
@@ -439,10 +486,13 @@
         break;
     } // while
 
-    if ((rc == END_OF_STREAM) && (!eosok))
-        return fail(ctx, "Unexpected EOF");
+    if ((ctx->eof) && (!eosok))
+    {
+        fail(ctx, "Unexpected EOF");
+        return 0;
+    } // if
 
-    return rc;
+    return 1;
 } // nexttoken_ctx
 
 
@@ -454,10 +504,7 @@
                                  ignorewhitespace, eolok, eosok);
 
     #if DEBUG_TOKENIZER
-    printf("NEXTTOKEN: %s '%s'\n",
-           (rc == END_OF_STREAM) ? "END_OF_STREAM" :
-           (rc == FAIL) ? "FAIL" :
-           (rc == NOFAIL) ? "NOFAIL" : "???",
+    printf("NEXTTOKEN: %d '%s'\n", rc,
            (ctx->tctx.token[0] == '\n') ? "\\n" : ctx->tctx.token);
     #endif
 
@@ -465,17 +512,30 @@
 } // nexttoken
 
 
-static int require_endline(Context *ctx)
+static void skip_line(Context *ctx)
+{
+    if (!tokeq(&ctx->tctx, "\n"))
+    {
+        while (nexttoken(ctx, 0, 1, 1, 1))
+        {
+            if (tokeq(&ctx->tctx, "\n"))
+                break;
+        } // while
+    } // if
+} // skip_line
+
+
+static void require_endline(Context *ctx)
 {
     TokenizerContext *tctx = &ctx->tctx;
     const int rc = nexttoken(ctx, 0, 1, 1, 1);
-    if (rc == FAIL)
-        return FAIL;
-    else if (rc == END_OF_STREAM)
-        return NOFAIL;  // we'll call this an EOL.
-    else if (!tokeq(tctx, "\n"))
-        return fail(ctx, "Endline expected");
-    return NOFAIL;
+    if (ctx->eof)
+        return;  // we'll call this an EOL.
+    else if ((rc == 0) || (!tokeq(tctx, "\n")))
+    {
+        fail(ctx, "Endline expected");
+        skip_line(ctx);
+    } // else if
 } // require_endline
 
 
@@ -483,19 +543,20 @@
 {
     TokenizerContext *tctx = &ctx->tctx;
     const int rc = nexttoken(ctx, 0, 1, 0, 0);
-    if (rc == FAIL)
-        return FAIL;
-    else if (!tokeq(tctx, ","))
-        return fail(ctx, "Comma expected");
-    return NOFAIL;
+    if ((rc == 0) || (!tokeq(tctx, ",")))
+    {
+        fail(ctx, "Comma expected");
+        return 0;
+    } // if
+    return 1;
 } // require_comma
 
 
 static int parse_register_name(Context *ctx, RegisterType *rtype, int *rnum)
 {
     TokenizerContext *tctx = &ctx->tctx;
-    if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 0;
 
     int neednum = 1;
     int regnum = 0;
@@ -533,25 +594,25 @@
     else if (tokeq(tctx, "o"))
     {
         if (!shader_is_vertex(ctx) || !shader_version_atleast(ctx, 3, 0))
-            return fail(ctx, "Output register not valid in this shader type");
+            fail(ctx, "Output register not valid in this shader type");
         regtype = REG_TYPE_OUTPUT;
     } // else if
     else if (tokeq(tctx, "oT"))
     {
         if (shader_is_vertex(ctx) && shader_version_atleast(ctx, 3, 0))
-            return fail(ctx, "Output register not valid in this shader type");
+            fail(ctx, "Output register not valid in this shader type");
         regtype = REG_TYPE_OUTPUT;
     } // else if
     else if (tokeq(tctx, "a"))
     {
         if (!shader_is_vertex(ctx))
-            return fail(ctx, "Address register only valid in vertex shaders.");
+            fail(ctx, "Address register only valid in vertex shaders.");
         regtype = REG_TYPE_ADDRESS;
     } // else if
     else if (tokeq(tctx, "t"))
     {
         if (!shader_is_pixel(ctx))
-            return fail(ctx, "Address register only valid in pixel shaders.");
+            fail(ctx, "Address register only valid in pixel shaders.");
         regtype = REG_TYPE_ADDRESS;
     } // else if
     else if (tokeq(tctx, "vPos"))
@@ -589,7 +650,10 @@
 
     else
     {
-        return fail(ctx, "expected register type");
+        fail(ctx, "expected register type");
+        regtype = REG_TYPE_CONST;
+        regnum = 0;
+        neednum = 0;
     } // else
 
     if (neednum)
@@ -599,20 +663,20 @@
         //  that whitespace back.
         TokenizerContext tmptctx;
         memcpy(&tmptctx, tctx, sizeof (TokenizerContext));
-        if (nexttoken_ctx(ctx, &tmptctx, 0, 1, 1, 1) == FAIL)
-            return FAIL;
+        if (!nexttoken_ctx(ctx, &tmptctx, 0, 1, 1, 1))
+            return 0;
         else if (tokeq(&tmptctx, "["))
             neednum = 0;
     } // if
 
     if (neednum)
     {
-        if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 0, 0, 0))
+            return 0;
 
         uint32 ui32 = 0;
         if (!ui32fromstr(tctx->token, &ui32))
-            return fail(ctx, "Invalid register index");
+            fail(ctx, "Invalid register index");
         regnum = (int) ui32;
     } // if
 
@@ -641,23 +705,22 @@
         } // if
         else
         {
-            return fail(ctx, "Invalid const register index");
+            fail(ctx, "Invalid const register index");
         } // else
     } // if
 
     *rtype = regtype;
     *rnum = regnum;
 
-    return NOFAIL;
+    return 1;
 } // parse_register_name
 
 
-static int set_result_shift(Context *ctx, DestArgInfo *info, const int val)
+static void set_result_shift(Context *ctx, DestArgInfo *info, const int val)
 {
     if (info->result_shift != 0)
-        return fail(ctx, "Multiple result shift modifiers");
+        fail(ctx, "Multiple result shift modifiers");
     info->result_shift = val;
-    return NOFAIL;
 } // set_result_shift
 
 
@@ -671,19 +734,19 @@
     // See if there are destination modifiers on the instruction itself...
     while (1)
     {
-        if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 0, 0, 0))
+            return 1;
         else if (tokeq(tctx, " "))
             break;  // done with modifiers.
         else if (!tokeq(tctx, "_"))
-            return fail(ctx, "Expected modifier or whitespace");
-        else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-            return FAIL;
+            fail(ctx, "Expected modifier or whitespace");
+        else if (!nexttoken(ctx, 0, 0, 0, 0))
+            return 1;
         // !!! FIXME: this can be cleaned up when tokenizer is fixed.
         else if (tokeq(tctx, "x"))
         {
-            if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-                return FAIL;
+            if (!nexttoken(ctx, 0, 0, 0, 0))
+                return 1;
             else if (tokeq(tctx, "2"))
                 set_result_shift(ctx, info, 0x1);
             else if (tokeq(tctx, "4"))
@@ -691,13 +754,13 @@
             else if (tokeq(tctx, "8"))
                 set_result_shift(ctx, info, 0x3);
             else
-                return fail(ctx, "Expected modifier");
+                fail(ctx, "Expected modifier");
         } // else if
         // !!! FIXME: this can be cleaned up when tokenizer is fixed.
         else if (tokeq(tctx, "d"))
         {
-            if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-                return FAIL;
+            if (!nexttoken(ctx, 0, 0, 0, 0))
+                return 1;
             else if (tokeq(tctx, "8"))
                 set_result_shift(ctx, info, 0xD);
             else if (tokeq(tctx, "4"))
@@ -705,7 +768,7 @@
             else if (tokeq(tctx, "2"))
                 set_result_shift(ctx, info, 0xF);
             else
-                return fail(ctx, "Expected modifier");
+                fail(ctx, "Expected modifier");
         } // else if
         else if (tokeq(tctx, "sat"))
             info->result_mod |= MOD_SATURATE;
@@ -714,25 +777,26 @@
         else if (tokeq(tctx, "centroid"))
             info->result_mod |= MOD_CENTROID;
         else
-            return fail(ctx, "Expected modifier");
+            fail(ctx, "Expected modifier");
     } // while
 
-    if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 1;
 
     // !!! FIXME: predicates.
     if (tokeq(tctx, "("))
-        return fail(ctx, "Predicates unsupported at this time");
+        fail(ctx, "Predicates unsupported at this time");  // !!! FIXME: ...
+
     pushback(ctx);  // parse_register_name calls nexttoken().
 
-    if (parse_register_name(ctx, &info->regtype, &info->regnum) == FAIL)
-        return FAIL;
+    parse_register_name(ctx, &info->regtype, &info->regnum);
 
-    if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return 1;
 
     // !!! FIXME: can dest registers do relative addressing?
 
+    int invalid_writemask = 0;
     int implicit_writemask = 0;
     if (!tokeq(tctx, "."))
     {
@@ -744,11 +808,11 @@
     // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
     //else if (scalar_register(ctx->shader_type, info->regtype, info->regnum))
     else if ( (scalar_register(ctx->shader_type, info->regtype, info->regnum)) && (info->regtype != REG_TYPE_DEPTHOUT) )
-        return fail(ctx, "Writemask specified for scalar register");
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+        fail(ctx, "Writemask specified for scalar register");
+    else if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 1;
     else if (tokeq(tctx, ""))
-        return fail(ctx, "Invalid writemask");
+        invalid_writemask = 1;
     else
     {
         char *ptr = tctx->token;
@@ -766,7 +830,7 @@
         } // if
 
         if (*ptr != '\0')
-            return fail(ctx, "Invalid writemask");
+            invalid_writemask = 1;
 
         info->writemask = ( ((info->writemask0 & 0x1) << 0) |
                             ((info->writemask1 & 0x1) << 1) |
@@ -774,18 +838,24 @@
                             ((info->writemask3 & 0x1) << 3) );
     } // else
 
+    if (invalid_writemask)
+        fail(ctx, "Invalid writemask");
+
     // !!! FIXME: Cg generates code with oDepth.z ... this is a bug, I think.
     if (info->regtype == REG_TYPE_DEPTHOUT)
     {
         if ( (!implicit_writemask) && ((info->writemask0 + info->writemask1 +
                info->writemask2 + info->writemask3) > 1) )
-            return fail(ctx, "Writemask specified for scalar register");
+            fail(ctx, "Writemask specified for scalar register");
     } // if
 
     info->orig_writemask = info->writemask;
 
     if (ctx->tokenbufpos >= STATICARRAYLEN(ctx->tokenbuf))
-        return fail(ctx, "Too many tokens");
+    {
+        fail(ctx, "Too many tokens");
+        return 1;
+    } // if
 
     ctx->tokenbuf[ctx->tokenbufpos++] =
             ( ((((uint32) 1)) << 31) |
@@ -818,21 +888,25 @@
     int retval = 1;
 
     if (ctx->tokenbufpos >= STATICARRAYLEN(ctx->tokenbuf))
-        return fail(ctx, "Too many tokens");
+    {
+        fail(ctx, "Too many tokens");
+        return 0;
+    } // if
 
     // mark this now, so optional relative addressing token is placed second.
     uint32 *token = &ctx->tokenbuf[ctx->tokenbufpos++];
+    *token = 0;
 
     SourceMod srcmod = SRCMOD_NONE;
     int negate = 0;
-    if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 1;
     else if (tokeq(tctx, "1"))
     {
-        if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 1, 0, 0))
+            return 1;
         else if (!tokeq(tctx, "-"))
-            return fail(ctx, "Unexpected value");
+            fail(ctx, "Unexpected value");
         else
             srcmod = SRCMOD_COMPLEMENT;
     } // else
@@ -845,14 +919,13 @@
 
     RegisterType regtype;
     int regnum;
-    if (parse_register_name(ctx, &regtype, &regnum) == FAIL)
-        return FAIL;
-    else if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    parse_register_name(ctx, &regtype, &regnum);
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return 1;
     else if (!tokeq(tctx, "_"))
         pushback(ctx);
-    else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+    else if (!nexttoken(ctx, 0, 0, 0, 0))
+        return 1;
     else if (tokeq(tctx, "bias"))
         set_source_mod(ctx, negate, SRCMOD_BIAS, SRCMOD_BIASNEGATE, &srcmod);
     else if (tokeq(tctx, "bx2"))
@@ -866,48 +939,49 @@
     else if (tokeq(tctx, "abs"))
         set_source_mod(ctx, negate, SRCMOD_ABS, SRCMOD_ABSNEGATE, &srcmod);
     else
-        return fail(ctx, "Invalid source modifier");
+        fail(ctx, "Invalid source modifier");
 
-    if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return 1;
 
     uint32 relative = 0;
     if (!tokeq(tctx, "["))
         pushback(ctx);  // not relative addressing?
-    else if (!relok)
-        return fail(ctx, "Relative addressing not permitted here.");
     else
     {
-        const int rc = parse_source_token_maybe_relative(ctx, 0);
-        if (rc == FAIL)
-            return FAIL;
-        retval += rc;
+        if (!relok)
+            fail(ctx, "Relative addressing not permitted here.");
+        else
+            retval++;
+
+        parse_source_token_maybe_relative(ctx, 0);
         relative = 1;
-        if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 1, 0, 0))
+            return retval;
         else if (!tokeq(tctx, "+"))
             pushback(ctx);
-        else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        else if (!nexttoken(ctx, 0, 1, 0, 0))
+            return retval;
         else
         {
             if (regnum != 0)  // !!! FIXME: maybe c3[a0.x + 5] is legal and becomes c[a0.x + 8] ?
                 fail(ctx, "Relative addressing with explicit register number.");
             uint32 ui32 = 0;
             if (!ui32fromstr(tctx->token, &ui32))
-                return fail(ctx, "Invalid relative addressing offset");
+                fail(ctx, "Invalid relative addressing offset");
             regnum += (int) ui32;
         } // else
 
-        if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 1, 0, 0))
+            return retval;
         else if (!tokeq(tctx, "]"))
-            return fail(ctx, "Expected ']'");
+            fail(ctx, "Expected ']'");
     } // else
 
-    if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return retval;
 
+    int invalid_swizzle = 0;
     uint32 swizzle = 0;
     if (!tokeq(tctx, "."))
     {
@@ -915,11 +989,11 @@
         pushback(ctx);  // no explicit writemask; do full mask.
     } // if
     else if (scalar_register(ctx->shader_type, regtype, regnum))
-        return fail(ctx, "Swizzle specified for scalar register");
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+        fail(ctx, "Swizzle specified for scalar register");
+    else if (!nexttoken(ctx, 0, 1, 0, 0))
+        return retval;
     else if (tokeq(tctx, ""))
-        return fail(ctx, "Invalid swizzle");
+        invalid_swizzle = 1;
     else
     {
         // deal with shortened form (.x = .xxxx, etc).
@@ -930,10 +1004,10 @@
         else if (tctx->token[3] == '\0')
             tctx->token[3] = tctx->token[2];
         else if (tctx->token[4] != '\0')
-            return fail(ctx, "Invalid swizzle");
+            invalid_swizzle = 1;
         tctx->token[4] = '\0';
 
-        uint32 val;
+        uint32 val = 0;
         int saw_xyzw = 0;
         int saw_rgba = 0;
         int i;
@@ -950,15 +1024,18 @@
                 case 'g': val = 1; saw_rgba = 1; break;
                 case 'b': val = 2; saw_rgba = 1; break;
                 case 'a': val = 3; saw_rgba = 1; break;
-                default: return fail(ctx, "Invalid swizzle");
+                default: invalid_swizzle = 1; break;
             } // switch
             swizzle |= (val << (i * 2));
         } // for
 
         if (saw_xyzw && saw_rgba)
-            return fail(ctx, "Invalid swizzle");
+            invalid_swizzle = 1;
     } // else
 
+    if (invalid_swizzle)
+        fail(ctx, "Invalid swizzle");
+
     *token = ( ((((uint32) 1)) << 31) |
                ((((uint32) regnum) & 0x7ff) << 0) |
                ((((uint32) relative) & 0x1) << 13) |
@@ -979,7 +1056,7 @@
 
 static int parse_args_NULL(Context *ctx)
 {
-    return (isfail(ctx) ? FAIL : 1);
+    return 1;
 } // parse_args_NULL
 
 
@@ -990,41 +1067,43 @@
     union { float f; int32 si32; uint32 ui32; } cvt;
     cvt.si32 = 0;
 
-    if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    *token = 0;
+
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 0;
     else if (tokeq(tctx, "-"))
         negative = -1;
     else
         pushback(ctx);
 
     uint32 val = 0;
-    if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 0;
     else if (!ui32fromstr(tctx->token, &val))
-        return fail(ctx, "Expected number");
+        fail(ctx, "Expected number");
 
     uint32 fraction = 0;
-    if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return 0;
     else if (!tokeq(tctx, "."))
         pushback(ctx);  // whole number
     else if (!floatok)
-        return fail(ctx, "Expected whole number");
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+        fail(ctx, "Expected whole number");
+    else if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 0;
     else if (!ui32fromstr(tctx->token, &fraction))
-        return fail(ctx, "Expected number");
+        fail(ctx, "Expected number");
 
     uint32 exponent = 0;
     int negexp = 0;
-    if (nexttoken(ctx, 0, 1, 1, 1) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 1, 1, 1))
+        return 0;
     else if (!tokeq(tctx, "e"))
         pushback(ctx);
     else if (!floatok)
-        return fail(ctx, "Exponent on whole number");  // !!! FIXME: illegal?
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+        fail(ctx, "Exponent on whole number");  // !!! FIXME: illegal?
+    else if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 0;
     else
     {
         if (!tokeq(tctx, "-"))
@@ -1032,10 +1111,10 @@
         else
             negexp = 1;
 
-        if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 1, 0, 0))
+            return 0;
         else if (!ui32fromstr(tctx->token, &exponent))
-            return fail(ctx, "Expected exponent");
+            fail(ctx, "Expected exponent");
     } // else
 
     if (!floatok)
@@ -1066,30 +1145,21 @@
     } // else
 
     *token = cvt.ui32;
-    return NOFAIL;
+    return 1;
 } // parse_num
 
 
 static int parse_args_DEFx(Context *ctx, const int isflt)
 {
-    if (parse_destination_token(ctx) == FAIL)
-        return FAIL;
-    else if (require_comma(ctx) == FAIL)
-        return FAIL;
-    else if (parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]) == FAIL)
-        return FAIL;
-    else if (require_comma(ctx) == FAIL)
-        return FAIL;
-    else if (parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]) == FAIL)
-        return FAIL;
-    else if (require_comma(ctx) == FAIL)
-        return FAIL;
-    else if (parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]) == FAIL)
-        return FAIL;
-    else if (require_comma(ctx) == FAIL)
-        return FAIL;
-    else if (parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]) == FAIL)
-        return FAIL;
+    parse_destination_token(ctx);
+    require_comma(ctx);
+    parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+    require_comma(ctx);
+    parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+    require_comma(ctx);
+    parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
+    require_comma(ctx);
+    parse_num(ctx, isflt, &ctx->tokenbuf[ctx->tokenbufpos++]);
     return 6;
 } // parse_args_DEFx
 
@@ -1109,20 +1179,16 @@
 static int parse_args_DEFB(Context *ctx)
 {
     TokenizerContext *tctx = &ctx->tctx;
-    if (parse_destination_token(ctx) == FAIL)
-        return FAIL;
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
-    else if (!tokeq(tctx, ","))
-        return fail(ctx, "Expected ','");
-    else if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-        return FAIL;
+    parse_destination_token(ctx);
+    require_comma(ctx);
+    if (!nexttoken(ctx, 0, 1, 0, 0))
+        return 1;
     else if (tokeq(tctx, "true"))
         ctx->tokenbuf[ctx->tokenbufpos++] = 1;
     else if (tokeq(tctx, "false"))
         ctx->tokenbuf[ctx->tokenbufpos++] = 0;
     else
-        return fail(ctx, "Expected 'true' or 'false'");
+        fail(ctx, "Expected 'true' or 'false'");
     return 3;
 } // parse_args_DEFB
 
@@ -1144,8 +1210,8 @@
     strcpy(token, tctx->token);
     if (tokeq(tctx, "2"))  // "2d" is two tokens.
     {
-        if (nexttoken(ctx, 0, 0, 1, 1) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 0, 1, 1))
+            return 0;
         else if (!tokeq(tctx, "d") != 0)
             pushback(ctx);
         else
@@ -1158,7 +1224,7 @@
         {
             *issampler = 0;
             *val = i;
-            return NOFAIL;
+            return 1;
         } // if
     } // for
 
@@ -1168,7 +1234,7 @@
         {
             *issampler = 1;
             *val = i + 2;
-            return NOFAIL;
+            return 1;
         } // if
     } // for
 
@@ -1182,11 +1248,11 @@
             tctx->source -= strlen(token);  // !!! FIXME: hack to move back
             strcpy(tctx->token, "_");  // !!! FIXME: hack to move back
             pushback(ctx);  // !!! FIXME: hack to move back
-            return NOFAIL;  // if you have "dcl_pp", then "_pp" isn't a usage.
+            return 1;  // if you have "dcl_pp", then "_pp" isn't a usage.
         } // if
     } // for
 
-    return FAIL;
+    return 0;
 } // parse_dcl_usage
 
 
@@ -1198,31 +1264,31 @@
     uint32 index = 0;
 
     ctx->tokenbufpos++;  // save a spot for the usage/index token.
+    ctx->tokenbuf[0] = 0;
 
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 0, 0, 0))
+        return 1;
     else if (tokeq(tctx, " "))
         pushback(ctx);
     else if (!tokeq(tctx, "_"))
-        return fail(ctx, "Expected register or usage");
-    else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
-    else if (parse_dcl_usage(ctx, &usage, &issampler) == FAIL)
-        return FAIL;
+        fail(ctx, "Expected register or usage");
+    else if (!nexttoken(ctx, 0, 0, 0, 0))
+        return 1;
+    else
+        parse_dcl_usage(ctx, &usage, &issampler);
 
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 0, 0, 0))
+        return 1;
     else if (tokeq(tctx, " ") || tokeq(tctx, "_"))
         pushback(ctx);  // parse_destination_token() wants these.
     else if (!ui32fromstr(tctx->token, &index))
-        return fail(ctx, "Expected usage index or register");
+        fail(ctx, "Expected usage index or register");
 
-    if (parse_destination_token(ctx) == FAIL)
-        return FAIL;
+    parse_destination_token(ctx);
 
     const int samplerreg = (ctx->dest_arg.regtype == REG_TYPE_SAMPLER);
     if (issampler != samplerreg)
-        return fail(ctx, "Invalid usage");
+        fail(ctx, "Invalid usage");
     else if (samplerreg)
         ctx->tokenbuf[0] = (usage << 27) | 0x80000000;
     else
@@ -1236,7 +1302,7 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_D
 
 
@@ -1244,7 +1310,7 @@
 {
     int retval = 1;
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_S
 
 
@@ -1252,9 +1318,9 @@
 {
     int retval = 1;
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_SS
 
 
@@ -1262,9 +1328,9 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DS
 
 
@@ -1272,11 +1338,11 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSS
 
 
@@ -1284,13 +1350,13 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSSS
 
 
@@ -1298,15 +1364,15 @@
 {
     int retval = 1;
     retval += parse_destination_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    if (require_comma(ctx) == FAIL) return FAIL;
+    require_comma(ctx);
     retval += parse_source_token(ctx);
-    return isfail(ctx) ? FAIL : retval;
+    return retval;
 } // parse_args_DSSSS
 
 
@@ -1365,7 +1431,7 @@
 static int parse_condition(Context *ctx, uint32 *controls)
 {
     TokenizerContext *tctx = &ctx->tctx;
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
+    if (!nexttoken(ctx, 0, 0, 0, 0))
         return 0;
     else if (!tokeq(tctx, "_"))
     {
@@ -1373,7 +1439,7 @@
         return 0;
     } // else if
 
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
+    if (!nexttoken(ctx, 0, 0, 0, 0))
         return 0;
     else
     {
@@ -1413,8 +1479,8 @@
 
     if (tokeq(tctx, "+"))
     {
-        if (nexttoken(ctx, 0, 1, 0, 0) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 1, 0, 0))
+            return 0;
         coissue = 1;
     } // if
 
@@ -1425,7 +1491,10 @@
     while (1)
     {
         if ( (strlen(opstr) + strlen(tctx->token)) >= (sizeof (opstr)-1) )
-            return fail(ctx, "Expected instruction");
+        {
+            fail(ctx, "Expected instruction");
+            return 0;
+        } // if
 
         char *ptr;
         for (ptr = tctx->token; *ptr != '\0'; ptr++)
@@ -1442,8 +1511,8 @@
 
         strcat(opstr, tctx->token);
 
-        if (nexttoken(ctx, 0, 0, 1, 1) == FAIL)
-            return FAIL;
+        if (!nexttoken(ctx, 0, 0, 1, 1))
+            return 0;
     } // while
 
     uint32 controls = 0;
@@ -1479,7 +1548,10 @@
     uint32 opcode = (uint32) i;
 
     if (!valid_opcode)
-        return failf(ctx, "Unknown instruction '%s'", opstr);
+    {
+        failf(ctx, "Unknown instruction '%s'", opstr);
+        return 0;
+    } // if
 
     // This might need to be IFC instead of IF.
     // !!! FIXME: compare opcode, not string
@@ -1502,7 +1574,7 @@
     else if (strcmp(instruction->opcode_string, "SETP") == 0)
     {
         if (!parse_condition(ctx, &controls))
-            return fail(ctx, "SETP requires a condition");
+            fail(ctx, "SETP requires a condition");
     } // else if
 
     instruction = &instructions[opcode];  // ...in case this changed.
@@ -1512,8 +1584,7 @@
     ctx->tokenbufpos = 0;
 
     const int tokcount = instruction->parse_args(ctx);
-    if (require_endline(ctx) == FAIL)
-        return FAIL;
+    require_endline(ctx);
 
     // insttoks bits are reserved and should be zero if < SM2.
     const uint32 insttoks = shader_version_atleast(ctx, 2, 0) ? tokcount-1 : 0;
@@ -1529,15 +1600,15 @@
     for (i = 0; i < (tokcount-1); i++)
         output_token(ctx, ctx->tokenbuf[i]);
 
-    return NOFAIL;
+    return 1;
 } // parse_instruction_token
 
 
-static int parse_version_token(Context *ctx)
+static void parse_version_token(Context *ctx)
 {
     TokenizerContext *tctx = &ctx->tctx;
-    if (nexttoken(ctx, 1, 1, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 1, 1, 0, 0))
+        return;
 
     uint32 shader_type = 0;
     if (tokeq(tctx, "vs"))
@@ -1553,75 +1624,75 @@
     else
     {
         // !!! FIXME: geometry shaders?
-        return fail(ctx, "Expected version string");
+        fail(ctx, "Expected version string");
     } // else
 
+    int bad_version = 0;
+
     uint32 major = 0;
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 0, 0, 0))
+        return;
     else if ( (!tokeq(tctx, "_")) && (!tokeq(tctx, ".")) )
-        return fail(ctx, "Expected version string");
-    else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+        bad_version = 1;
+    else if (!nexttoken(ctx, 0, 0, 0, 0))
+        return;
     else if (!ui32fromstr(tctx->token, &major))
-        return fail(ctx, "Expected version string");
+        bad_version = 1;
 
     uint32 minor = 0;
-    if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+    if (!nexttoken(ctx, 0, 0, 0, 0))
+        return;
     else if ( (!tokeq(tctx, "_")) && (!tokeq(tctx, ".")) )
-        return fail(ctx, "Expected version string");
-    else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL)
-        return FAIL;
+        bad_version = 1;
+    else if (!nexttoken(ctx, 0, 0, 0, 0))
+        return;
     else if (tokeq(tctx, "x"))
         minor = 1;
     else if (tokeq(tctx, "sw"))
         minor = 255;
     else if (!ui32fromstr(tctx->token, &minor))
-        return fail(ctx, "Expected version string");
+        bad_version = 1;
+
+    if (bad_version)
+        fail(ctx, "Expected version string");
+    else
+        require_endline(ctx);
 
     ctx->major_ver = major;
     ctx->minor_ver = minor;
 
-    if (require_endline(ctx) == FAIL)
-        return FAIL;
-
     ctx->version_token = (shader_type << 16) | (major << 8) | (minor << 0);
     output_token(ctx, ctx->version_token);
-    return NOFAIL;
 } // parse_version_token
 
 
-static int parse_phase_token(Context *ctx)
+static void parse_phase_token(Context *ctx)
 {
-    if (require_endline(ctx) == FAIL)
-        return FAIL;
+    require_endline(ctx);
     output_token(ctx, 0x0000FFFD); // phase token always 0x0000FFFD.
-    return NOFAIL;
 } // parse_phase_token
 
 
-static int parse_end_token(Context *ctx)
+static void parse_end_token(Context *ctx)
 {
-    if (require_endline(ctx) == FAIL)
-        return FAIL;
+    require_endline(ctx);
     // We don't emit the end token bits here, since it's valid for a shader
     //  to not specify an "end" string at all; it's implicit, in that case.
     // Instead, we make sure if we see "end" that it's the last thing we see.
-    if (nexttoken(ctx, 1, 1, 0, 1) != END_OF_STREAM)
-        return fail(ctx, "Content after END");
-    return NOFAIL;
+    nexttoken(ctx, 1, 1, 0, 1);
+    if (!ctx->eof)
+        fail(ctx, "Content after END");
 } // parse_end_token
 
 
-static int parse_token(Context *ctx)
+static void parse_token(Context *ctx)
 {
     TokenizerContext *tctx = &ctx->tctx;
     if (tokeq(tctx, "end"))
-        return parse_end_token(ctx);
+        parse_end_token(ctx);
     else if (tokeq(tctx, "phase"))
-        return parse_phase_token(ctx);
-    return parse_instruction_token(ctx);
+        parse_phase_token(ctx);
+    parse_instruction_token(ctx);
 } // parse_token
 
 
@@ -1647,14 +1718,26 @@
 } // build_context
 
 
+static void free_error_list(MOJOSHADER_free f, void *d, ErrorList *item)
+{
+    while (item != NULL)
+    {
+        ErrorList *next = item->next;
+        f((void *) item->error.error, d);
+        f((void *) item->error.filename, d);
+        f(item, d);
+        item = next;
+    } // while
+} // free_error_list
+
+
 static void destroy_context(Context *ctx)
 {
     if (ctx != NULL)
     {
         MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : internal_free);
         void *d = ctx->malloc_data;
-        if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str))
-            f((void *) ctx->failstr, d);
+        free_error_list(f, d, ctx->errors);
         if (ctx->output != NULL)
             f(ctx->output, d);
         if (ctx->token_to_source != NULL)
@@ -1666,6 +1749,31 @@
 } // destroy_context
 
 
+static MOJOSHADER_error *build_errors(Context *ctx)
+{
+    int total = 0;
+    MOJOSHADER_error *retval = (MOJOSHADER_error *)
+            Malloc(ctx, sizeof (MOJOSHADER_error) * ctx->error_count);
+    if (retval == NULL)
+        return NULL;
+
+    ErrorList *item = ctx->errors;
+    while (item != NULL)
+    {
+        ErrorList *next = item->next;
+        // reuse the string allocations
+        memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error));
+        Free(ctx, item);
+        item = next;
+        total++;
+    } // while
+    ctx->errors = NULL;
+
+    assert(total == ctx->error_count);
+    return retval;
+} // build_errors
+
+
 static const MOJOSHADER_parseData *build_failed_assembly(Context *ctx)
 {
     MOJOSHADER_parseData *retval = NULL;
@@ -1681,32 +1789,13 @@
     retval->free = (ctx->free == internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
 
-    // !!! FIXME: handle multiple errors.
-    retval->error_count = 1;
-    retval->errors = (MOJOSHADER_error *)
-                     Malloc(ctx, sizeof (MOJOSHADER_error));
-    if (retval->errors == NULL)
+    retval->error_count = ctx->error_count;
+    retval->errors = build_errors(ctx);
+    if ((retval->errors == NULL) && (ctx->error_count > 0))
     {
         Free(ctx, retval);
         return &out_of_mem_data;
     } // if
-    retval->errors->error = ctx->failstr;
-    retval->errors->filename = NULL;
-
-    ctx->failstr = NULL;  // don't let this get free()'d too soon.
-
-    switch (ctx->parse_phase)
-    {
-        case MOJOSHADER_PARSEPHASE_NOTSTARTED:
-            retval->errors->error_position = -2;
-            break;
-        case MOJOSHADER_PARSEPHASE_WORKING:
-            retval->errors->error_position = ctx->tctx.linenum;
-            break;
-        case MOJOSHADER_PARSEPHASE_DONE:
-            retval->errors->error_position = -1;
-            break;
-    } // switch
 
     return retval;
 } // build_failed_assembly
@@ -1884,8 +1973,7 @@
     for (i = 0; i < comment_count; i++)
         output_comment_string(ctx, comments[i]);
 
-    if (!isfail(ctx))
-        ctx->parse_phase = MOJOSHADER_PARSEPHASE_WORKING;
+    ctx->parse_phase = MOJOSHADER_PARSEPHASE_WORKING;
 } // output_comments
 
 
@@ -1897,6 +1985,7 @@
                             unsigned int symbol_count,
                             MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
 {
+    int failed = 0;
     MOJOSHADER_parseData *retval = NULL;
     Context *ctx = NULL;
 
@@ -1913,12 +2002,22 @@
     output_comments(ctx, comments, comment_count, symbols, symbol_count);
 
     // parse out the rest of the tokens after the version token...
-    while (nexttoken(ctx, 1, 1, 0, 1) == NOFAIL)
+    while (nexttoken(ctx, 1, 1, 0, 1))
+    {
+        if (isfail(ctx))
+        {
+            failed = 1;
+            ctx->isfail = 0;
+            skip_line(ctx);  // start fresh on next line.
+        } // if
         parse_token(ctx);
+    } // while
+
+    ctx->isfail = failed;
 
     output_token(ctx, 0x0000FFFF);   // end token always 0x0000FFFF.
 
-    if (isfail(ctx))
+    if (failed)
         retval = (MOJOSHADER_parseData *) build_failed_assembly(ctx);
     else
     {
--- a/mojoshader_internal.h	Tue Feb 03 04:10:50 2009 -0500
+++ b/mojoshader_internal.h	Tue Feb 03 04:14:00 2009 -0500
@@ -83,11 +83,6 @@
 
 #define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
 
-// Special-case return values from the parsing pipeline...
-#define FAIL (-1)
-#define NOFAIL (-2)
-#define END_OF_STREAM (-3)
-
 
 // Byteswap magic...
 
@@ -269,7 +264,14 @@
 } MOJOSHADER_parsePhase;
 
 extern MOJOSHADER_parseData out_of_mem_data;
-extern const char *out_of_mem_str;
+
+typedef struct ErrorList
+{
+    MOJOSHADER_error error;
+    struct ErrorList *next;
+} ErrorList;
+
+
 
 #endif  // _INCLUDE_MOJOSHADER_INTERNAL_H_