diff --git a/Makefile.am b/Makefile.am index 514f0506..bf1d7bfa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -243,6 +243,7 @@ XFAIL_TESTS = \ tests/hlsl-vector-indexing.shader_test \ tests/hlsl-vector-indexing-uniform.shader_test \ tests/math.shader_test \ + tests/preproc-if.shader_test \ tests/preproc-ifdef.shader_test \ tests/preproc-if-expr.shader_test \ tests/preproc-invalid.shader_test \ diff --git a/libs/vkd3d-shader/preproc.h b/libs/vkd3d-shader/preproc.h index effcd995..7f77d278 100644 --- a/libs/vkd3d-shader/preproc.h +++ b/libs/vkd3d-shader/preproc.h @@ -22,6 +22,7 @@ #define __VKD3D_SHADER_PREPROC_H #include "vkd3d_shader_private.h" +#include "rbtree.h" struct preproc_if_state { @@ -33,6 +34,12 @@ struct preproc_if_state bool seen_else; }; +struct preproc_macro +{ + struct rb_entry entry; + char *name; +}; + struct preproc_ctx { void *scanner; @@ -44,6 +51,8 @@ struct preproc_ctx struct preproc_if_state *if_stack; size_t if_count, if_stack_size; + struct rb_tree macros; + int current_directive; bool last_was_newline; @@ -52,6 +61,7 @@ struct preproc_ctx bool error; }; +void preproc_free_macro(struct preproc_macro *macro) DECLSPEC_HIDDEN; void preproc_warning(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, enum vkd3d_shader_error error, const char *format, ...) VKD3D_PRINTF_FUNC(4, 5) DECLSPEC_HIDDEN; diff --git a/libs/vkd3d-shader/preproc.l b/libs/vkd3d-shader/preproc.l index 47ad1f13..89c205f1 100644 --- a/libs/vkd3d-shader/preproc.l +++ b/libs/vkd3d-shader/preproc.l @@ -67,7 +67,7 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* <> {yy_pop_state(yyscanner);} . {} -{IDENTIFIER} {return T_TEXT;} +{IDENTIFIER} {return T_IDENTIFIER;} /* We have no use for floats, but shouldn't parse them as integers. */ @@ -100,6 +100,8 @@ IDENTIFIER [A-Za-z_][A-Za-z0-9_]* for (p = yytext + 1; strchr(" \t", *p); ++p) ; + if (!strcmp(p, "define")) + return T_DEFINE; if (!strcmp(p, "elif")) return T_ELIF; if (!strcmp(p, "else")) @@ -154,6 +156,7 @@ static int return_token(int token, YYSTYPE *lval, const char *text) { switch (token) { + case T_IDENTIFIER: case T_INTEGER: case T_TEXT: if (!(lval->string = vkd3d_strdup(text))) @@ -191,6 +194,7 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) { switch (token) { + case T_DEFINE: case T_ELIF: case T_ELSE: case T_ENDIF: @@ -208,8 +212,18 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) TRACE("Parsing token %d, line %d, in directive %d, string %s.\n", token, lloc->line, ctx->current_directive, debugstr_a(text)); - if (!ctx->current_directive && !preproc_is_writing(ctx)) - continue; + switch (ctx->current_directive) + { + case T_ELIF: + case T_ELSE: + case T_ENDIF: + case T_IF: + break; + + default: + if (!preproc_is_writing(ctx)) + continue; + } if (ctx->current_directive) return return_token(token, lval, text); @@ -218,6 +232,19 @@ int yylex(YYSTYPE *lval, YYLTYPE *lloc, yyscan_t scanner) } } +static int preproc_macro_compare(const void *key, const struct rb_entry *entry) +{ + const struct preproc_macro *macro = RB_ENTRY_VALUE(entry, struct preproc_macro, entry); + const char *name = key; + + return strcmp(name, macro->name); +} + +static void preproc_macro_rb_free(struct rb_entry *entry, void *ctx) +{ + preproc_free_macro(RB_ENTRY_VALUE(entry, struct preproc_macro, entry)); +} + int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_code *out, struct vkd3d_shader_message_context *message_context) { @@ -226,6 +253,7 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, void *output_code; vkd3d_string_buffer_init(&ctx.buffer); + rb_init(&ctx.macros, preproc_macro_compare); ctx.message_context = message_context; ctx.location.source_name = compile_info->source_name; ctx.location.line = 1; @@ -248,6 +276,7 @@ int preproc_lexer_parse(const struct vkd3d_shader_compile_info *compile_info, } vkd3d_free(ctx.if_stack); + rb_destroy(&ctx.macros, preproc_macro_rb_free, NULL); if (ctx.error) { diff --git a/libs/vkd3d-shader/preproc.y b/libs/vkd3d-shader/preproc.y index 647bb45d..45cad443 100644 --- a/libs/vkd3d-shader/preproc.y +++ b/libs/vkd3d-shader/preproc.y @@ -66,6 +66,43 @@ static void yyerror(const YYLTYPE *loc, void *scanner, struct preproc_ctx *ctx, preproc_error(ctx, loc, VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX, "%s", string); } +static struct preproc_macro *preproc_find_macro(struct preproc_ctx *ctx, const char *name) +{ + struct rb_entry *entry; + + if ((entry = rb_get(&ctx->macros, name))) + return RB_ENTRY_VALUE(entry, struct preproc_macro, entry); + return NULL; +} + +static bool preproc_add_macro(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, char *name) +{ + struct preproc_macro *macro; + int ret; + + if ((macro = preproc_find_macro(ctx, name))) + { + preproc_warning(ctx, loc, VKD3D_SHADER_WARNING_PP_ALREADY_DEFINED, "Redefinition of %s.", name); + rb_remove(&ctx->macros, ¯o->entry); + preproc_free_macro(macro); + } + + TRACE("Defining new macro %s.\n", debugstr_a(name)); + + if (!(macro = vkd3d_malloc(sizeof(*macro)))) + return false; + macro->name = name; + ret = rb_put(&ctx->macros, name, ¯o->entry); + assert(!ret); + return true; +} + +void preproc_free_macro(struct preproc_macro *macro) +{ + vkd3d_free(macro->name); + vkd3d_free(macro); +} + static bool preproc_was_writing(struct preproc_ctx *ctx) { if (ctx->if_count < 2) @@ -135,17 +172,20 @@ static uint32_t preproc_parse_integer(const char *s) uint32_t integer; } +%token T_IDENTIFIER %token T_INTEGER %token T_TEXT %token T_NEWLINE +%token T_DEFINE "#define" %token T_ELIF "#elif" %token T_ELSE "#else" %token T_ENDIF "#endif" %token T_IF "#if" %type expr +%type body_token %% @@ -156,8 +196,25 @@ shader_text vkd3d_string_buffer_printf(&ctx->buffer, "\n"); } +body_text + : %empty + | body_text body_token + { + vkd3d_free($2); + } + +body_token + : T_IDENTIFIER + | T_INTEGER + | T_TEXT + directive - : T_IF expr T_NEWLINE + : T_DEFINE T_IDENTIFIER body_text T_NEWLINE + { + if (!preproc_add_macro(ctx, &@$, $2)) + YYABORT; + } + | T_IF expr T_NEWLINE { if (!preproc_push_if(ctx, !!$2)) YYABORT; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index b89d20e2..1011be27 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -82,6 +82,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX = 4000, + VKD3D_SHADER_WARNING_PP_ALREADY_DEFINED = 4300, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE = 4301, VKD3D_SHADER_WARNING_PP_UNKNOWN_DIRECTIVE = 4303, VKD3D_SHADER_WARNING_PP_UNTERMINATED_IF = 4305, diff --git a/tests/hlsl_d3d12.c b/tests/hlsl_d3d12.c index 77a7ea1a..38c1ab2c 100644 --- a/tests/hlsl_d3d12.c +++ b/tests/hlsl_d3d12.c @@ -349,7 +349,7 @@ static void test_preprocess(void) for (i = 0; i < ARRAY_SIZE(tests); ++i) { - if (i == 43) + if (i == 6) continue; vkd3d_test_set_context("Source \"%s\"", tests[i].source); todo_if (i <= 4 || (i >= 9 && i <= 14))