From a41cb5cab9a89224620c895fa2530cd525f18aab Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 1 Nov 2010 02:20:37 -0400 Subject: [PATCH] Filled in some stubs to flesh out the AST API. --- mojoshader.h | 2 +- mojoshader_compiler.c | 292 +++++++++++++++++++++++++++++++++--------- 2 files changed, 233 insertions(+), 61 deletions(-) diff --git a/mojoshader.h b/mojoshader.h index cd8c4de3..66500cc3 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -1499,7 +1499,7 @@ typedef struct MOJOSHADER_astData /* * The actual syntax tree. You are responsible for walking it yourself. * CompilationUnits are always the top of the tree (functions, typedefs, - * global variables, etc). + * global variables, etc). Will be NULL on error. */ const MOJOSHADER_astNode *ast; diff --git a/mojoshader_compiler.c b/mojoshader_compiler.c index ee8f2900..0f5a1104 100644 --- a/mojoshader_compiler.c +++ b/mojoshader_compiler.c @@ -94,6 +94,7 @@ typedef struct Context SymbolMap usertypes; SymbolMap variables; MOJOSHADER_astNode *ast; // Abstract Syntax Tree + const char *source_profile; // These are entries allocated in the strcache, so these pointers are // valid from shortly after we create the cache until we destroy it @@ -126,7 +127,6 @@ typedef struct Context static inline void out_of_memory(Context *ctx) { - if (!ctx->out_of_memory) printf("out of memory\n"); // !!! FIXME: placeholder. ctx->isfail = ctx->out_of_memory = 1; } // out_of_memory @@ -152,13 +152,71 @@ static inline void Free(Context *ctx, void *ptr) ctx->free(ptr, ctx->malloc_data); } // Free -static void fail(Context *ctx, const char *str) +static void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3); +static void failf(Context *ctx, const char *fmt, ...) +{ + const char *fname = ctx->sourcefile; + const unsigned int error_position = ctx->sourceline; + + ctx->isfail = 1; + + const int MAX_ERROR_COUNT = 128; + if (ctx->error_count == (MAX_ERROR_COUNT-1)) + fmt = "Too many errors, not reporting any more."; + else if (ctx->error_count >= MAX_ERROR_COUNT) + return; + + 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); + vsnprintf(failstr, len + 1, fmt, ap); // rebuild it. + va_end(ap); + + error->error.error = failstr; + error->error.filename = fname ? StrDup(ctx, fname) : NULL; + error->error.error_position = error_position; + error->next = NULL; + + ErrorList *prev = NULL; + ErrorList *item = ctx->errors; + while (item != NULL) + { + prev = item; + item = item->next; + } // while + + if (prev == NULL) + ctx->errors = error; + else + prev->next = error; + + ctx->error_count++; + } // else +} // failf + +static inline void fail(Context *ctx, const char *reason) { - // !!! FIXME: placeholder. - (void) ctx; - printf("%s:%u: %s\n", ctx->sourcefile, ctx->sourceline, str); + failf(ctx, "%s", reason); } // fail +static inline int isfail(const Context *ctx) +{ + return ctx->isfail; +} // isfail + static void symbolmap_nuke(const void *k, const void *v, void *d) {/*no-op*/} @@ -2302,58 +2360,122 @@ static void parse_source(Context *ctx, const char *filename, } // parse_source -#if 0 -/* !!! FIXME: most of these ints should be unsigned. */ -typedef struct MOJOSHADER_astData +static MOJOSHADER_astData MOJOSHADER_out_of_mem_ast_data = { + 1, &MOJOSHADER_out_of_mem_error, 0, 0, 0, 0, 0 +}; + +// !!! FIXME: cut and paste from assembler. +static MOJOSHADER_error *build_errors(Context *ctx) { - /* - * The number of elements pointed to by (errors). - */ - int error_count; + int total = 0; + MOJOSHADER_error *retval = (MOJOSHADER_error *) + Malloc(ctx, sizeof (MOJOSHADER_error) * ctx->error_count); + if (retval == NULL) + return NULL; - /* - * (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. - * Note that this will only produce errors for syntax problems. Most of - * the things we expect a compiler to produce errors for--incompatible - * types, unknown identifiers, etc--are not checked at all during - * initial generation of the syntax tree...bogus programs that would - * fail to compile will pass here without error, if they are syntactically - * correct! - */ - MOJOSHADER_error *errors; - - /* - * The name of the source profile used to parse the shader. Will be NULL - * on error. - */ - const char *source_profile; + 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; - /* - * The actual syntax tree. You are responsible for walking it yourself. - * CompilationUnits are always the top of the tree (functions, typedefs, - * global variables, etc). - */ - const MOJOSHADER_astNode *ast; + assert(total == ctx->error_count); + return retval; +} // build_errors - /* - * This is the malloc implementation you passed to MOJOSHADER_parse(). - */ - MOJOSHADER_malloc malloc; - /* - * This is the free implementation you passed to MOJOSHADER_parse(). - */ - MOJOSHADER_free free; +// !!! FIXME: cut and paste from assembler. +static const MOJOSHADER_astData *build_failed_ast(Context *ctx) +{ + assert(isfail(ctx)); - /* - * This is the pointer you passed as opaque data for your allocator. - */ - void *malloc_data; -} MOJOSHADER_astData; -#endif + if (ctx->out_of_memory) + return &MOJOSHADER_out_of_mem_ast_data; + + MOJOSHADER_astData *retval = NULL; + retval = (MOJOSHADER_astData *) Malloc(ctx, sizeof (MOJOSHADER_astData)); + if (retval == NULL) + return &MOJOSHADER_out_of_mem_ast_data; + + memset(retval, '\0', sizeof (MOJOSHADER_astData)); + retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc; + retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free; + retval->malloc_data = ctx->malloc_data; + + retval->error_count = ctx->error_count; + retval->errors = build_errors(ctx); + if ((retval->errors == NULL) && (ctx->error_count > 0)) + { + Free(ctx, retval); + return &MOJOSHADER_out_of_mem_ast_data; + } // if + + return retval; +} // build_failed_ast + + +static const MOJOSHADER_astData *build_astdata(Context *ctx) +{ + MOJOSHADER_astData *retval = NULL; + + if (ctx->out_of_memory) + return &MOJOSHADER_out_of_mem_ast_data; + + retval = (MOJOSHADER_astData *) Malloc(ctx, sizeof (MOJOSHADER_astData)); + if (retval == NULL) + return &MOJOSHADER_out_of_mem_ast_data; + memset(retval, '\0', sizeof (MOJOSHADER_astData)); + + if (!isfail(ctx)) + { + retval->source_profile = ctx->source_profile; + ctx->source_profile = NULL; // don't free this with the context, now. + retval->ast = ctx->ast; + ctx->ast = NULL; // don't free this with the context, now. + } // if + + retval->error_count = ctx->error_count; + retval->errors = build_errors(ctx); + retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc; + retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free; + retval->malloc_data = ctx->malloc_data; + + return retval; +} // build_astdata + +static void choose_src_profile(Context *ctx, const char *srcprofile) +{ + #define TEST_PROFILE(x) do { \ + if (strcmp(srcprofile, x) == 0) { \ + ctx->source_profile = x; \ + return; \ + } \ + } while (0) + + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_VS_1_1); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_VS_2_0); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_VS_3_0); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_1); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_2); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_3); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_1_4); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_2_0); + TEST_PROFILE(MOJOSHADER_SRC_PROFILE_HLSL_PS_3_0); + + #undef TEST_PROFILE + + fail(ctx, "Unknown profile"); +} // choose_src_profile + + +// API entry point... // !!! FIXME: move this (and a lot of other things) to mojoshader_ast.c. const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile, @@ -2366,22 +2488,72 @@ const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile, MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) { - Context *ctx = build_context(m, f, d); - if (!ctx) - return NULL; // !!! FIXME: report error. + const MOJOSHADER_astData *retval = NULL; + Context *ctx = NULL; - parse_source(ctx, filename, source, sourcelen, defs, define_count, - include_open, include_close); + if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) ) + return &MOJOSHADER_out_of_mem_ast_data; // supply both or neither. - destroy_context(ctx); + ctx = build_context(m, f, d); + if (ctx == NULL) + return &MOJOSHADER_out_of_mem_ast_data; - // !!! FIXME: report success/error. - return NULL; -} // MOJOSHADER_ast + choose_src_profile(ctx, srcprofile); + + if (!isfail(ctx)) + { + parse_source(ctx, filename, source, sourcelen, defs, define_count, + include_open, include_close); + } // if + if (isfail(ctx)) + retval = (MOJOSHADER_astData *) build_failed_ast(ctx); + else + retval = build_astdata(ctx); -void MOJOSHADER_freeAstData(const MOJOSHADER_astData *data) + destroy_context(ctx); + return retval; +} // MOJOSHADER_parseAst + + +void MOJOSHADER_freeAstData(const MOJOSHADER_astData *_data) { + MOJOSHADER_astData *data = (MOJOSHADER_astData *) _data; + if ((data == NULL) || (data == &MOJOSHADER_out_of_mem_ast_data)) + return; // no-op. + + MOJOSHADER_free f = (data->free == NULL) ? MOJOSHADER_internal_free : data->free; + void *d = data->malloc_data; + int i; + + // we don't f(data->source_profile), because that's internal static data. + + // check for NULL in case of dumb free() impl. + 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->ast != NULL) + { + // !!! FIXME: make this not require a Context. + Context ctx; + memset(&ctx, '\0', sizeof (Context)); + ctx.malloc = data->malloc; + ctx.free = f; + ctx.malloc_data = d; + delete_compilation_unit(&ctx, + (MOJOSHADER_astCompilationUnit *) &data->ast->compunit); + } // if + + f(data, d); } // MOJOSHADER_freeAstData