Allow multiple errors from parsing.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 03 Feb 2009 00:31:34 -0500
changeset 536 5af65fe6e917
parent 535 dfe0e7965f1f
child 537 00a1deb5d3d0
Allow multiple errors from parsing.
assemble.c
finderrors.c
mojoshader.c
mojoshader.h
mojoshader_assembler.c
mojoshader_opengl.c
testoutput.c
testparse.c
--- a/assemble.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/assemble.c	Tue Feb 03 00:31:34 2009 -0500
@@ -24,8 +24,16 @@
     int retval = 0;
 
     pd = MOJOSHADER_assemble(buf, NULL, 0, NULL, 0, NULL, NULL, NULL);
-    if (pd->error != NULL)
-        printf("ERROR: (line %d) %s\n", pd->error_position, pd->error);
+    if (pd->error_count > 0)
+    {
+        int i;
+        for (i = 0; i < pd->error_count; i++)
+        {
+            printf("ERROR: (line %d) %s\n",
+                    pd->errors[i].error_position,
+                    pd->errors[i].error);
+        } // for
+    } // if
     else
     {
         if (pd->output != NULL)
--- a/finderrors.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/finderrors.c	Tue Feb 03 00:31:34 2009 -0500
@@ -85,9 +85,9 @@
 
         buf[rc] = '\0';  // make sure the source is null-terminated.
         a = MOJOSHADER_assemble((char *) buf, 0, 0, 0, 0, 0, 0, 0);
-        if (a->error)
+        if (a->error_count > 0)
         {
-            report("FAIL: %s (line %d) %s\n", fname, a->error_position, a->error);
+            report("FAIL: %s (line %d) %s\n", fname, a->errors[0].error_position, a->errors[0].error);
             return 1;
         } // if
 
--- a/mojoshader.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/mojoshader.c	Tue Feb 03 00:31:34 2009 -0500
@@ -108,6 +108,12 @@
     struct ConstantsList *next;
 } ConstantsList;
 
+typedef struct ErrorList
+{
+    MOJOSHADER_error error;
+    struct ErrorList *next;
+} ErrorList;
+
 typedef struct VariableList
 {
     MOJOSHADER_uniformType type;
@@ -158,6 +164,8 @@
 // Context...this is state that changes as we parse through a shader...
 struct Context
 {
+    int isfail;
+    int out_of_memory;
     MOJOSHADER_malloc malloc;
     MOJOSHADER_free free;
     void *malloc_data;
@@ -183,7 +191,6 @@
     const char *shader_type_str;
     const char *endline;
     int endline_len;
-    const char *failstr;
     char scratch[SCRATCH_BUFFERS][SCRATCH_BUFFER_SIZE];
     int scratchidx;  // current scratch buffer.
     int profileid;
@@ -212,6 +219,8 @@
     int last_address_reg_component;
     RegisterList used_registers;
     RegisterList defined_registers;
+    int error_count;
+    ErrorList *errors;
     int constant_count;
     ConstantsList *constants;
     int uniform_count;
@@ -236,15 +245,15 @@
 
 // Convenience functions for allocators...
 
+static MOJOSHADER_error out_of_mem_error = { "Out of memory", NULL, -1 };
 MOJOSHADER_parseData out_of_mem_data = {
-    "Out of memory", -1, 0, 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0
+    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)
 {
-    if (ctx->failstr == NULL)
-        ctx->failstr = out_of_mem_str;  // fail() would call malloc().
+    ctx->isfail = ctx->out_of_memory = 1;
     return FAIL;
 } // out_of_memory
 
@@ -323,24 +332,13 @@
 
 static inline int isfail(const Context *ctx)
 {
-    return (ctx->failstr != NULL);
+    return ctx->isfail;
 } // isfail
 
 
 static inline char *get_scratch_buffer(Context *ctx)
 {
-    if ((ctx->scratchidx >= SCRATCH_BUFFERS) && !isfail(ctx))
-    {
-        // can't call fail() here, since it calls back into here.
-        const char *errstr = "BUG: overflowed scratch buffers";
-        char *failstr = (char *) Malloc(ctx, strlen(errstr) + 1);
-        if (failstr != NULL)
-        {
-            strcpy(failstr, errstr);
-            ctx->failstr = failstr;
-        } // if
-    } // if
-
+    assert(ctx->scratchidx < SCRATCH_BUFFERS);
     ctx->scratchidx = (ctx->scratchidx + 1) % SCRATCH_BUFFERS;
     return ctx->scratch[ctx->scratchidx];
 } // get_scratch_buffer
@@ -349,29 +347,71 @@
 static int failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3);
 static int failf(Context *ctx, const char *fmt, ...)
 {
-    if (ctx->failstr == NULL)  // don't change existing error.
-    {
-        char *scratch = get_scratch_buffer(ctx);
-        va_list ap;
-        va_start(ap, fmt);
-        const int len = vsnprintf(scratch, SCRATCH_BUFFER_SIZE, fmt, ap);
-        va_end(ap);
-
-        char *failstr = (char *) Malloc(ctx, len + 1);
-        if (failstr != NULL)
+    ctx->isfail = 1;
+    if (ctx->out_of_memory)
+        return FAIL;
+
+    int error_position = 0;
+    switch (ctx->parse_phase)
+    {
+        case MOJOSHADER_PARSEPHASE_NOTSTARTED:
+            error_position = -2;
+            break;
+        case MOJOSHADER_PARSEPHASE_WORKING:
+            error_position = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32);
+            break;
+        case MOJOSHADER_PARSEPHASE_DONE:
+            error_position = -1;
+            break;
+        default:
+            assert(0 && "Unexpected value");
+            return FAIL;
+    } // switch
+
+    ErrorList *error = (ErrorList *) Malloc(ctx, sizeof (ErrorList));
+    if (error == NULL)
+        return FAIL;
+
+    char *scratch = get_scratch_buffer(ctx);
+    va_list ap;
+    va_start(ap, fmt);
+    const int len = vsnprintf(scratch, SCRATCH_BUFFER_SIZE, fmt, ap);
+    va_end(ap);
+
+    char *failstr = (char *) Malloc(ctx, len + 1);
+    if (failstr == NULL)
+        Free(ctx, error);
+    else
+    {
+        // see comments about scratch buffer overflow in output_line().
+        if (len < SCRATCH_BUFFER_SIZE)
+            strcpy(failstr, scratch);  // copy it over.
+        else
         {
-            // see comments about scratch buffer overflow in output_line().
-            if (len < SCRATCH_BUFFER_SIZE)
-                strcpy(failstr, scratch);  // copy it over.
-            else
-            {
-                va_start(ap, fmt);
-                vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
-                va_end(ap);
-            } // else
-            ctx->failstr = failstr;
-        } // if
-    } // if
+            va_start(ap, fmt);
+            vsnprintf(failstr, len + 1, fmt, ap);  // rebuild it.
+            va_end(ap);
+        } // else
+
+        error->error.error = failstr;
+        error->error.filename = NULL;  // no filename at this level.
+        error->error.error_position = error_position;
+
+        ErrorList *prev = NULL;
+        error->next = ctx->errors;
+        while (error->next != NULL)
+        {
+            prev = error->next;
+            error->next = error->next->next;
+        } // while
+
+        if (prev != NULL)
+            prev->next = error;
+        else
+            ctx->errors = error;
+
+        ctx->error_count++;
+    } // else
 
     return FAIL;
 } // failf
@@ -6795,6 +6835,19 @@
 } // free_variable_list
 
 
+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)
@@ -6816,8 +6869,7 @@
         free_reglist(f, d, ctx->attributes.next);
         free_reglist(f, d, ctx->samplers.next);
         free_variable_list(f, d, ctx->variables);
-        if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str))
-            f((void *) ctx->failstr, d);
+        free_error_list(f, d, ctx->errors);
         f(ctx, d);
     } // if
 } // destroy_context
@@ -7043,6 +7095,31 @@
 } // build_samplers
 
 
+static MOJOSHADER_error *build_errors(Context *ctx)
+{
+    int total = 0;
+    MOJOSHADER_error *retval;
+    retval = (MOJOSHADER_error *) Malloc(ctx, sizeof (MOJOSHADER_error));
+    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 MOJOSHADER_attribute *build_attributes(Context *ctx, int *_count)
 {
     int count = 0;
@@ -7123,9 +7200,13 @@
     MOJOSHADER_attribute *attributes = NULL;
     MOJOSHADER_sampler *samplers = NULL;
     MOJOSHADER_swizzle *swizzles = NULL;
+    MOJOSHADER_error *errors = NULL;
     MOJOSHADER_parseData *retval = NULL;
     int attribute_count = 0;
 
+    if (ctx->out_of_memory)
+        return &out_of_mem_data;
+
     retval = (MOJOSHADER_parseData*) Malloc(ctx, sizeof(MOJOSHADER_parseData));
     if (retval == NULL)
         return &out_of_mem_data;
@@ -7148,6 +7229,9 @@
         samplers = build_samplers(ctx);
 
     if (!isfail(ctx))
+        errors = build_errors(ctx);
+
+    if (!isfail(ctx))
     {
         if (ctx->swizzles_count > 0)
         {
@@ -7188,25 +7272,20 @@
             Free(ctx, samplers);
         } // if
 
-        switch (ctx->parse_phase)
+        if (ctx->out_of_memory)
         {
-            case MOJOSHADER_PARSEPHASE_NOTSTARTED:
-                retval->error_position = -2;
-                break;
-            case MOJOSHADER_PARSEPHASE_WORKING:
-                retval->error_position = (ctx->tokens - ctx->orig_tokens) * sizeof (uint32);
-                break;
-            case MOJOSHADER_PARSEPHASE_DONE:
-                retval->error_position = -1;
-                break;
-        } // switch
-
-        retval->error = ctx->failstr;  // we recycle.  :)
-        ctx->failstr = NULL;  // don't let this get free()'d too soon.
+            for (i = 0; i < ctx->sampler_count; i++)
+            {
+                Free(ctx, (void *) errors[i].filename);
+                Free(ctx, (void *) errors[i].error);
+            } // for
+            Free(ctx, ctx->errors);
+            Free(ctx, retval);
+            return &out_of_mem_data;
+        } // if
     } // if
     else
     {
-        retval->error_position = -3;
         retval->profile = ctx->profile->name;
         retval->output = output;
         retval->output_len = ctx->output_len;
@@ -7226,6 +7305,8 @@
         retval->swizzles = swizzles;
     } // else
 
+    retval->error_count = ctx->error_count;
+    retval->errors = errors;
     retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
@@ -7473,6 +7554,18 @@
     if (data->swizzles != NULL)
         f((void *) data->swizzles, d);
 
+    if (data->errors != NULL)
+    {
+        for (i = 0; i < data->error_count; i++)
+        {
+            if (data->errors[i].error != NULL)
+                f((void *) data->errors[i].error, d);
+            if (data->errors[i].filename != NULL)
+                f((void *) data->errors[i].filename, d);
+        } // for
+        f((void *) data->errors, d);
+    } // if
+
     if (data->uniforms != NULL)
     {
         for (i = 0; i < data->uniform_count; i++)
@@ -7515,9 +7608,6 @@
         f((void *) data->symbols, d);
     } // if
 
-    if ((data->error != NULL) && (data->error != out_of_mem_str))
-        f((void *) data->error, d);
-
     f(data, d);
 } // MOJOSHADER_freeParseData
 
--- a/mojoshader.h	Mon Feb 02 22:56:13 2009 -0500
+++ b/mojoshader.h	Tue Feb 03 00:31:34 2009 -0500
@@ -319,11 +319,7 @@
 } MOJOSHADER_symbol;
 
 
-/*
- * Structure used to return data from parsing of a shader...
- */
-/* !!! FIXME: most of these ints should be unsigned. */
-typedef struct MOJOSHADER_parseData
+typedef struct MOJOSHADER_error
 {
     /*
      * Human-readable error, if there is one. Will be NULL if there was no
@@ -333,6 +329,11 @@
     const char *error;
 
     /*
+     * Filename where error happened.
+     */
+    const char *filename;
+
+    /*
      * Position of error, if there is one. Will be -3 if there was no
      *  error, -2 if there was an error before processing started, and
      *  -1 if there was an error during final processing. If >= 0,
@@ -341,6 +342,25 @@
      *  a line number in the source code you supplied (starting at one).
      */
     int error_position;
+} MOJOSHADER_error;
+
+/*
+ * Structure used to return data from parsing of a shader...
+ */
+/* !!! FIXME: most of these ints should be unsigned. */
+typedef struct MOJOSHADER_parseData
+{
+    /*
+     * The number of elements pointed to by (errors).
+     */
+    int error_count;
+
+    /*
+     * (error_count) elements of data that specify errors that were generated
+     *  by parsing this shader.
+     * This can be NULL if there were no errors or if (error_count) is zero.
+     */
+    MOJOSHADER_error *errors;
 
     /*
      * The name of the profile used to parse the shader. Will be NULL on error.
--- a/mojoshader_assembler.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/mojoshader_assembler.c	Tue Feb 03 00:31:34 2009 -0500
@@ -27,10 +27,15 @@
 } TokenizerContext;
 
 
-typedef struct Context Context;
+typedef struct SourcePos
+{
+    const char *filename;
+    uint32 line;
+} SourcePos;
+
 
 // Context...this is state that changes as we assemble a shader...
-struct Context
+typedef struct Context
 {
     MOJOSHADER_malloc malloc;
     MOJOSHADER_free free;
@@ -46,13 +51,13 @@
     int tokenbufpos;
     DestArgInfo dest_arg;
     uint32 *output;
-    uint32 *token_to_line;
+    SourcePos *token_to_source;
     uint8 *ctab;
     uint32 ctab_len;
     uint32 ctab_allocation;
     size_t output_len;
     size_t output_allocation;
-};
+} Context;
 
 
 // Convenience functions for allocators...
@@ -153,6 +158,13 @@
 } // ui32fromstr
 
 
+static inline void add_token_sourcepos(Context *ctx, const size_t idx)
+{
+    ctx->token_to_source[idx].line = ctx->tctx.linenum;
+    ctx->token_to_source[idx].filename = NULL;
+} // add_token_sourcepos
+
+
 static void output_token_noswap(Context *ctx, const uint32 token)
 {
     if (isfail(ctx))
@@ -172,19 +184,19 @@
         Free(ctx, ctx->output);
         ctx->output = (uint32 *) ptr;
 
-        ptr = Malloc(ctx, newsize * sizeof (uint32));
+        ptr = Malloc(ctx, newsize * sizeof (SourcePos));
         if (ptr == NULL)
             return;
         if (ctx->output_len > 0)
-            memcpy(ptr, ctx->token_to_line, ctx->output_len * sizeof (uint32));
-        Free(ctx, ctx->token_to_line);
-        ctx->token_to_line = (uint32 *) ptr;
+            memcpy(ptr, ctx->token_to_source, ctx->output_len * sizeof (SourcePos));
+        Free(ctx, ctx->token_to_source);
+        ctx->token_to_source = (SourcePos *) ptr;
 
         ctx->output_allocation = newsize;
     } // if
 
     ctx->output[ctx->output_len] = token;
-    ctx->token_to_line[ctx->output_len] = ctx->tctx.linenum;
+    add_token_sourcepos(ctx, ctx->output_len);
     ctx->output_len++;
 } // output_token_noswap
 
@@ -1645,8 +1657,8 @@
             f((void *) ctx->failstr, d);
         if (ctx->output != NULL)
             f(ctx->output, d);
-        if (ctx->token_to_line != NULL)
-            f(ctx->token_to_line, d);
+        if (ctx->token_to_source != NULL)
+            f(ctx->token_to_source, d);
         if (ctx->ctab != NULL)
             f(ctx->ctab, d);
         f(ctx, d);
@@ -1668,19 +1680,30 @@
     retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc;
     retval->free = (ctx->free == internal_free) ? NULL : ctx->free;
     retval->malloc_data = ctx->malloc_data;
-    retval->error = ctx->failstr;  // we recycle.  :)
+
+    // !!! FIXME: handle multiple errors.
+    retval->errors = (MOJOSHADER_error *)
+                     Malloc(ctx, sizeof (MOJOSHADER_error));
+    if (retval->errors == NULL)
+    {
+        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->error_position = -2;
+            retval->errors->error_position = -2;
             break;
         case MOJOSHADER_PARSEPHASE_WORKING:
-            retval->error_position = ctx->tctx.linenum;
+            retval->errors->error_position = ctx->tctx.linenum;
             break;
         case MOJOSHADER_PARSEPHASE_DONE:
-            retval->error_position = -1;
+            retval->errors->error_position = -1;
             break;
     } // switch
 
@@ -1910,6 +1933,8 @@
                                       ctx->output_len * sizeof (uint32),
                                       NULL, 0, m, f, d);
 
+// !!! FIXME: multiple errors.
+#if 0
         // on error, map the bytecode back to a line number.
         if (retval->error_position >= 0)
         {
@@ -1920,6 +1945,7 @@
             else
                 retval->error_position = -1;  // oh well.
         } // if
+#endif
     } // if
 
     destroy_context(ctx);
--- a/mojoshader_opengl.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/mojoshader_opengl.c	Tue Feb 03 00:31:34 2009 -0500
@@ -1067,9 +1067,9 @@
                                                       ctx->malloc_fn,
                                                       ctx->free_fn,
                                                       ctx->malloc_data);
-    if (pd->error != NULL)
+    if (pd->errors != NULL)
     {
-        set_error(pd->error);
+        set_error(pd->errors[0].error);
         goto compile_shader_fail;
     } // if
 
--- a/testoutput.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/testoutput.c	Tue Feb 03 00:31:34 2009 -0500
@@ -17,8 +17,16 @@
     int retval = 0;
 
     pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, NULL, NULL, NULL);
-    if (pd->error != NULL)
-        printf("ERROR: (position %d) %s\n", pd->error_position, pd->error);
+    if (pd->error_count > 0)
+    {
+        int i;
+        for (i = 0; i < pd->error_count; i++)
+        {
+            printf("ERROR: (line %d) %s\n",
+                    pd->errors[i].error_position,
+                    pd->errors[i].error);
+        } // for
+    } // if
     else
     {
         retval = 1;
--- a/testparse.c	Mon Feb 02 22:56:13 2009 -0500
+++ b/testparse.c	Tue Feb 03 00:31:34 2009 -0500
@@ -61,8 +61,16 @@
 
     pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, Malloc, Free, NULL);
     printf("PROFILE: %s\n", prof);
-    if (pd->error != NULL)
-        printf("ERROR: (position %d) %s\n", pd->error_position, pd->error);
+    if (pd->error_count > 0)
+    {
+        int i;
+        for (i = 0; i < pd->error_count; i++)
+        {
+            printf("ERROR: (position %d) %s\n",
+                    pd->errors[i].error_position,
+                    pd->errors[i].error);
+        } // for
+    } // if
     else
     {
         retval = 1;