mirror of
				https://gitlab.winehq.org/wine/vkd3d.git
				synced 2025-09-12 18:50:22 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			795 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			795 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| /*
 | |
|  * HLSL preprocessor
 | |
|  *
 | |
|  * Copyright 2020 Zebediah Figura for CodeWeavers
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 | |
|  */
 | |
| 
 | |
| %code requires
 | |
| {
 | |
| 
 | |
| #include "vkd3d_shader_private.h"
 | |
| #include "preproc.h"
 | |
| #include <stdio.h>
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| #define PREPROC_YYLTYPE struct vkd3d_shader_location
 | |
| 
 | |
| struct parse_arg_names
 | |
| {
 | |
|     char **args;
 | |
|     size_t count;
 | |
| };
 | |
| 
 | |
| }
 | |
| 
 | |
| %code provides
 | |
| {
 | |
| 
 | |
| int preproc_yylex(PREPROC_YYSTYPE *yylval_param, PREPROC_YYLTYPE *yylloc_param, void *scanner);
 | |
| 
 | |
| }
 | |
| 
 | |
| %code
 | |
| {
 | |
| 
 | |
| #define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n)
 | |
| 
 | |
| #ifndef S_ISREG
 | |
| # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
 | |
| #endif
 | |
| 
 | |
| static void preproc_error(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
 | |
|         enum vkd3d_shader_error error, const char *format, ...)
 | |
| {
 | |
|     va_list args;
 | |
| 
 | |
|     va_start(args, format);
 | |
|     vkd3d_shader_verror(ctx->message_context, loc, error, format, args);
 | |
|     va_end(args);
 | |
|     ctx->error = true;
 | |
| }
 | |
| 
 | |
| void preproc_warning(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc,
 | |
|         enum vkd3d_shader_error error, const char *format, ...)
 | |
| {
 | |
|     va_list args;
 | |
| 
 | |
|     va_start(args, format);
 | |
|     vkd3d_shader_vwarning(ctx->message_context, loc, error, format, args);
 | |
|     va_end(args);
 | |
| }
 | |
| 
 | |
| static void yyerror(const YYLTYPE *loc, void *scanner, struct preproc_ctx *ctx, const char *string)
 | |
| {
 | |
|     preproc_error(ctx, loc, VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX, "%s", string);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| bool preproc_add_macro(struct preproc_ctx *ctx, const struct vkd3d_shader_location *loc, char *name, char **arg_names,
 | |
|         size_t arg_count, const struct vkd3d_shader_location *body_loc, struct vkd3d_string_buffer *body)
 | |
| {
 | |
|     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 with %zu arguments.\n", debugstr_a(name), arg_count);
 | |
| 
 | |
|     if (!(macro = vkd3d_malloc(sizeof(*macro))))
 | |
|         return false;
 | |
|     macro->name = name;
 | |
|     macro->arg_names = arg_names;
 | |
|     macro->arg_count = arg_count;
 | |
|     macro->body.text = *body;
 | |
|     macro->body.location = *body_loc;
 | |
|     ret = rb_put(&ctx->macros, name, ¯o->entry);
 | |
|     VKD3D_ASSERT(!ret);
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void preproc_free_macro(struct preproc_macro *macro)
 | |
| {
 | |
|     unsigned int i;
 | |
| 
 | |
|     vkd3d_free(macro->name);
 | |
|     for (i = 0; i < macro->arg_count; ++i)
 | |
|         vkd3d_free(macro->arg_names[i]);
 | |
|     vkd3d_free(macro->arg_names);
 | |
|     vkd3d_string_buffer_cleanup(¯o->body.text);
 | |
|     vkd3d_free(macro);
 | |
| }
 | |
| 
 | |
| static bool preproc_was_writing(struct preproc_ctx *ctx)
 | |
| {
 | |
|     const struct preproc_file *file = preproc_get_top_file(ctx);
 | |
| 
 | |
|     /* This applies across files, since we can't #include anyway if we weren't
 | |
|      * writing. */
 | |
|     if (file->if_count < 2)
 | |
|         return true;
 | |
|     return file->if_stack[file->if_count - 2].current_true;
 | |
| }
 | |
| 
 | |
| static bool preproc_push_if(struct preproc_ctx *ctx, bool condition)
 | |
| {
 | |
|     struct preproc_file *file = preproc_get_top_file(ctx);
 | |
|     struct preproc_if_state *state;
 | |
| 
 | |
|     if (!vkd3d_array_reserve((void **)&file->if_stack, &file->if_stack_size,
 | |
|             file->if_count + 1, sizeof(*file->if_stack)))
 | |
|         return false;
 | |
|     state = &file->if_stack[file->if_count++];
 | |
|     state->current_true = condition && preproc_was_writing(ctx);
 | |
|     state->seen_true = condition;
 | |
|     state->seen_else = false;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static int default_open_include(const char *filename, bool local,
 | |
|         const char *parent_data, void *context, struct vkd3d_shader_code *out)
 | |
| {
 | |
|     uint8_t *data, *new_data;
 | |
|     size_t size = 4096;
 | |
|     struct stat st;
 | |
|     size_t pos = 0;
 | |
|     size_t ret;
 | |
|     FILE *f;
 | |
| 
 | |
|     if (!(f = fopen(filename, "rb")))
 | |
|     {
 | |
|         ERR("Unable to open %s for reading.\n", debugstr_a(filename));
 | |
|         return VKD3D_ERROR;
 | |
|     }
 | |
| 
 | |
|     if (fstat(fileno(f), &st) == -1)
 | |
|     {
 | |
|         ERR("Could not stat file %s.\n", debugstr_a(filename));
 | |
|         fclose(f);
 | |
|         return VKD3D_ERROR;
 | |
|     }
 | |
| 
 | |
|     if (S_ISREG(st.st_mode))
 | |
|         size = st.st_size;
 | |
| 
 | |
|     if (!size)
 | |
|     {
 | |
|         fclose(f);
 | |
| 
 | |
|         out->code = NULL;
 | |
|         out->size = 0;
 | |
| 
 | |
|         return VKD3D_OK;
 | |
|     }
 | |
| 
 | |
|     if (!(data = vkd3d_malloc(size)))
 | |
|     {
 | |
|         fclose(f);
 | |
|         return VKD3D_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
| 
 | |
|     for (;;)
 | |
|     {
 | |
|         if (pos >= size)
 | |
|         {
 | |
|             if (size > SIZE_MAX / 2 || !(new_data = vkd3d_realloc(data, size * 2)))
 | |
|             {
 | |
|                 vkd3d_free(data);
 | |
|                 fclose(f);
 | |
|                 return VKD3D_ERROR_OUT_OF_MEMORY;
 | |
|             }
 | |
|             data = new_data;
 | |
|             size *= 2;
 | |
|         }
 | |
| 
 | |
|         if (!(ret = fread(&data[pos], 1, size - pos, f)))
 | |
|             break;
 | |
|         pos += ret;
 | |
|     }
 | |
| 
 | |
|     if (!feof(f))
 | |
|     {
 | |
|         vkd3d_free(data);
 | |
|         return VKD3D_ERROR;
 | |
|     }
 | |
| 
 | |
|     fclose(f);
 | |
| 
 | |
|     out->code = data;
 | |
|     out->size = pos;
 | |
| 
 | |
|     return VKD3D_OK;
 | |
| }
 | |
| 
 | |
| static void default_close_include(const struct vkd3d_shader_code *code, void *context)
 | |
| {
 | |
|     vkd3d_free((void *)code->code);
 | |
| }
 | |
| 
 | |
| void preproc_close_include(struct preproc_ctx *ctx, const struct vkd3d_shader_code *code)
 | |
| {
 | |
|     PFN_vkd3d_shader_close_include close_include = ctx->preprocess_info->pfn_close_include;
 | |
| 
 | |
|     if (!close_include)
 | |
|         close_include = default_close_include;
 | |
| 
 | |
|     close_include(code, ctx->preprocess_info->include_context);
 | |
| }
 | |
| 
 | |
| static const void *get_parent_data(struct preproc_ctx *ctx)
 | |
| {
 | |
|     if (ctx->file_count == 1)
 | |
|         return NULL;
 | |
|     return preproc_get_top_file(ctx)->code.code;
 | |
| }
 | |
| 
 | |
| static void free_parse_arg_names(struct parse_arg_names *args)
 | |
| {
 | |
|     unsigned int i;
 | |
| 
 | |
|     for (i = 0; i < args->count; ++i)
 | |
|         vkd3d_free(args->args[i]);
 | |
|     vkd3d_free(args->args);
 | |
| }
 | |
| 
 | |
| }
 | |
| 
 | |
| %define api.prefix {preproc_yy}
 | |
| %define api.pure full
 | |
| %define parse.error verbose
 | |
| %expect 0
 | |
| %locations
 | |
| %lex-param {yyscan_t scanner}
 | |
| %parse-param {void *scanner}
 | |
| %parse-param {struct preproc_ctx *ctx}
 | |
| 
 | |
| %union
 | |
| {
 | |
|     char *string;
 | |
|     const char *const_string;
 | |
|     uint32_t integer;
 | |
|     struct vkd3d_string_buffer string_buffer;
 | |
|     struct parse_arg_names arg_names;
 | |
| }
 | |
| 
 | |
| %token <string> T_HASHSTRING
 | |
| %token <string> T_IDENTIFIER
 | |
| %token <string> T_IDENTIFIER_PAREN
 | |
| %token <string> T_INTEGER
 | |
| %token <string> T_STRING
 | |
| %token <string> T_TEXT
 | |
| 
 | |
| %token T_NEWLINE
 | |
| 
 | |
| %token T_DEFINE "#define"
 | |
| %token T_ERROR "#error"
 | |
| %token T_ELIF "#elif"
 | |
| %token T_ELSE "#else"
 | |
| %token T_ENDIF "#endif"
 | |
| %token T_IF "#if"
 | |
| %token T_IFDEF "#ifdef"
 | |
| %token T_IFNDEF "#ifndef"
 | |
| %token T_INCLUDE "#include"
 | |
| %token T_LINE "#line"
 | |
| %token T_PRAGMA "#pragma"
 | |
| %token T_UNDEF "#undef"
 | |
| 
 | |
| %token T_CONCAT "##"
 | |
| 
 | |
| %token T_LE "<="
 | |
| %token T_GE ">="
 | |
| %token T_EQ "=="
 | |
| %token T_NE "!="
 | |
| %token T_AND "&&"
 | |
| %token T_OR "||"
 | |
| %token T_DEFINED "defined"
 | |
| 
 | |
| %type <integer> primary_expr
 | |
| %type <integer> unary_expr
 | |
| %type <integer> mul_expr
 | |
| %type <integer> add_expr
 | |
| %type <integer> ineq_expr
 | |
| %type <integer> eq_expr
 | |
| %type <integer> bitand_expr
 | |
| %type <integer> bitxor_expr
 | |
| %type <integer> bitor_expr
 | |
| %type <integer> logicand_expr
 | |
| %type <integer> logicor_expr
 | |
| %type <integer> expr
 | |
| %type <string> body_token
 | |
| %type <const_string> body_token_const
 | |
| %type <string_buffer> body_text
 | |
| %type <arg_names> identifier_list
 | |
| 
 | |
| %%
 | |
| 
 | |
| shader_text
 | |
|     : %empty
 | |
|     | shader_text directive
 | |
|         {
 | |
|             vkd3d_string_buffer_printf(&ctx->buffer, "\n");
 | |
|         }
 | |
| 
 | |
| identifier_list
 | |
|     : T_IDENTIFIER
 | |
|         {
 | |
|             if (!($$.args = vkd3d_malloc(sizeof(*$$.args))))
 | |
|                 YYABORT;
 | |
|             $$.args[0] = $1;
 | |
|             $$.count = 1;
 | |
|         }
 | |
|     | identifier_list ',' T_IDENTIFIER
 | |
|         {
 | |
|             char **new_array;
 | |
| 
 | |
|             if (!(new_array = vkd3d_realloc($1.args, ($1.count + 1) * sizeof(*$$.args))))
 | |
|             {
 | |
|                 free_parse_arg_names(&$1);
 | |
|                 YYABORT;
 | |
|             }
 | |
|             $$.args = new_array;
 | |
|             $$.count = $1.count + 1;
 | |
|             $$.args[$1.count] = $3;
 | |
|         }
 | |
| 
 | |
| body_text
 | |
|     : %empty
 | |
|         {
 | |
|             vkd3d_string_buffer_init(&$$);
 | |
|         }
 | |
|     | body_text body_token
 | |
|         {
 | |
|             if (vkd3d_string_buffer_printf(&$$, "%s ", $2) < 0)
 | |
|             {
 | |
|                 vkd3d_free($2);
 | |
|                 YYABORT;
 | |
|             }
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | body_text body_token_const
 | |
|         {
 | |
|             if (vkd3d_string_buffer_printf(&$$, "%s ", $2) < 0)
 | |
|                 YYABORT;
 | |
|         }
 | |
| 
 | |
| body_token
 | |
|     : T_HASHSTRING
 | |
|     | T_IDENTIFIER
 | |
|     | T_IDENTIFIER_PAREN
 | |
|     | T_INTEGER
 | |
|     | T_TEXT
 | |
| 
 | |
| body_token_const
 | |
|     : '('
 | |
|         {
 | |
|             $$ = "(";
 | |
|         }
 | |
|     | ')'
 | |
|         {
 | |
|             $$ = ")";
 | |
|         }
 | |
|     | '['
 | |
|         {
 | |
|             $$ = "[";
 | |
|         }
 | |
|     | ']'
 | |
|         {
 | |
|             $$ = "]";
 | |
|         }
 | |
|     | '{'
 | |
|         {
 | |
|             $$ = "{";
 | |
|         }
 | |
|     | '}'
 | |
|         {
 | |
|             $$ = "}";
 | |
|         }
 | |
|     | ','
 | |
|         {
 | |
|             $$ = ",";
 | |
|         }
 | |
|     | '+'
 | |
|         {
 | |
|             $$ = "+";
 | |
|         }
 | |
|     | '-'
 | |
|         {
 | |
|             $$ = "-";
 | |
|         }
 | |
|     | '!'
 | |
|         {
 | |
|             $$ = "!";
 | |
|         }
 | |
|     | '*'
 | |
|         {
 | |
|             $$ = "*";
 | |
|         }
 | |
|     | '/'
 | |
|         {
 | |
|             $$ = "/";
 | |
|         }
 | |
|     | '<'
 | |
|         {
 | |
|             $$ = "<";
 | |
|         }
 | |
|     | '>'
 | |
|         {
 | |
|             $$ = ">";
 | |
|         }
 | |
|     | '&'
 | |
|         {
 | |
|             $$ = "&";
 | |
|         }
 | |
|     | '|'
 | |
|         {
 | |
|             $$ = "|";
 | |
|         }
 | |
|     | '^'
 | |
|         {
 | |
|             $$ = "^";
 | |
|         }
 | |
|     | '?'
 | |
|         {
 | |
|             $$ = "?";
 | |
|         }
 | |
|     | ':'
 | |
|         {
 | |
|             $$ = ":";
 | |
|         }
 | |
|     | T_CONCAT
 | |
|         {
 | |
|             $$ = "##";
 | |
|         }
 | |
|     | T_LE
 | |
|         {
 | |
|             $$ = "<=";
 | |
|         }
 | |
|     | T_GE
 | |
|         {
 | |
|             $$ = ">=";
 | |
|         }
 | |
|     | T_EQ
 | |
|         {
 | |
|             $$ = "==";
 | |
|         }
 | |
|     | T_NE
 | |
|         {
 | |
|             $$ = "!=";
 | |
|         }
 | |
|     | T_AND
 | |
|         {
 | |
|             $$ = "&&";
 | |
|         }
 | |
|     | T_OR
 | |
|         {
 | |
|             $$ = "||";
 | |
|         }
 | |
|     | T_DEFINED
 | |
|         {
 | |
|             $$ = "defined";
 | |
|         }
 | |
| 
 | |
| directive
 | |
|     : T_DEFINE T_IDENTIFIER body_text T_NEWLINE
 | |
|         {
 | |
|             if (!preproc_add_macro(ctx, &@$, $2, NULL, 0, &@3, &$3))
 | |
|             {
 | |
|                 vkd3d_free($2);
 | |
|                 vkd3d_string_buffer_cleanup(&$3);
 | |
|                 YYABORT;
 | |
|             }
 | |
|         }
 | |
|     | T_DEFINE T_IDENTIFIER_PAREN '(' identifier_list ')' body_text T_NEWLINE
 | |
|         {
 | |
|             if (!preproc_add_macro(ctx, &@6, $2, $4.args, $4.count, &@6, &$6))
 | |
|             {
 | |
|                 vkd3d_free($2);
 | |
|                 free_parse_arg_names(&$4);
 | |
|                 vkd3d_string_buffer_cleanup(&$6);
 | |
|                 YYABORT;
 | |
|             }
 | |
|         }
 | |
|     | T_UNDEF T_IDENTIFIER T_NEWLINE
 | |
|         {
 | |
|             struct preproc_macro *macro;
 | |
| 
 | |
|             if ((macro = preproc_find_macro(ctx, $2)))
 | |
|             {
 | |
|                 TRACE("Removing macro definition %s.\n", debugstr_a($2));
 | |
|                 rb_remove(&ctx->macros, ¯o->entry);
 | |
|                 preproc_free_macro(macro);
 | |
|             }
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_IF expr T_NEWLINE
 | |
|         {
 | |
|             if (!preproc_push_if(ctx, !!$2))
 | |
|                 YYABORT;
 | |
|         }
 | |
|     | T_IFDEF T_IDENTIFIER T_NEWLINE
 | |
|         {
 | |
|             preproc_push_if(ctx, !!preproc_find_macro(ctx, $2));
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_IFNDEF T_IDENTIFIER T_NEWLINE
 | |
|         {
 | |
|             preproc_push_if(ctx, !preproc_find_macro(ctx, $2));
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_ELIF expr T_NEWLINE
 | |
|         {
 | |
|             const struct preproc_file *file = preproc_get_top_file(ctx);
 | |
| 
 | |
|             if (file->if_count)
 | |
|             {
 | |
|                 struct preproc_if_state *state = &file->if_stack[file->if_count - 1];
 | |
| 
 | |
|                 if (state->seen_else)
 | |
|                 {
 | |
|                     preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE, "Ignoring #elif after #else.");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     state->current_true = $2 && !state->seen_true && preproc_was_writing(ctx);
 | |
|                     state->seen_true = $2 || state->seen_true;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE,
 | |
|                         "Ignoring #elif without prior #if.");
 | |
|             }
 | |
|         }
 | |
|     | T_ELSE T_NEWLINE
 | |
|         {
 | |
|             const struct preproc_file *file = preproc_get_top_file(ctx);
 | |
| 
 | |
|             if (file->if_count)
 | |
|             {
 | |
|                 struct preproc_if_state *state = &file->if_stack[file->if_count - 1];
 | |
| 
 | |
|                 if (state->seen_else)
 | |
|                 {
 | |
|                     preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE, "Ignoring #else after #else.");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     state->current_true = !state->seen_true && preproc_was_writing(ctx);
 | |
|                     state->seen_else = true;
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE,
 | |
|                         "Ignoring #else without prior #if.");
 | |
|             }
 | |
|         }
 | |
|     | T_ENDIF T_NEWLINE
 | |
|         {
 | |
|             struct preproc_file *file = preproc_get_top_file(ctx);
 | |
| 
 | |
|             if (file->if_count)
 | |
|                 --file->if_count;
 | |
|             else
 | |
|                 preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE,
 | |
|                         "Ignoring #endif without prior #if.");
 | |
|         }
 | |
|     | T_ERROR T_NEWLINE
 | |
|         {
 | |
|             preproc_error(ctx, &@$, VKD3D_SHADER_ERROR_PP_ERROR_DIRECTIVE, "Error directive.");
 | |
|         }
 | |
|     | T_ERROR T_STRING T_NEWLINE
 | |
|         {
 | |
|             preproc_error(ctx, &@$, VKD3D_SHADER_ERROR_PP_ERROR_DIRECTIVE, "Error directive: %s", $2);
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_INCLUDE T_STRING T_NEWLINE
 | |
|         {
 | |
|             PFN_vkd3d_shader_open_include open_include = ctx->preprocess_info->pfn_open_include;
 | |
|             struct vkd3d_shader_code code;
 | |
|             char *filename;
 | |
|             int result;
 | |
| 
 | |
|             if (!(filename = vkd3d_malloc(strlen($2) - 1)))
 | |
|                 YYABORT;
 | |
| 
 | |
|             if (!open_include)
 | |
|                 open_include = default_open_include;
 | |
| 
 | |
|             memcpy(filename, $2 + 1, strlen($2) - 2);
 | |
|             filename[strlen($2) - 2] = 0;
 | |
| 
 | |
|             if (!(result = open_include(filename, $2[0] == '"', get_parent_data(ctx),
 | |
|                     ctx->preprocess_info->include_context, &code)))
 | |
|             {
 | |
|                 if (!preproc_push_include(ctx, filename, &code))
 | |
|                 {
 | |
|                     preproc_close_include(ctx, &code);
 | |
|                     vkd3d_free(filename);
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 preproc_error(ctx, &@$, VKD3D_SHADER_ERROR_PP_INCLUDE_FAILED, "Failed to open %s.", $2);
 | |
|                 vkd3d_free(filename);
 | |
|             }
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_LINE T_INTEGER T_NEWLINE
 | |
|         {
 | |
|             FIXME("#line directive.\n");
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_LINE T_INTEGER T_STRING T_NEWLINE
 | |
|         {
 | |
|             FIXME("#line directive.\n");
 | |
|             vkd3d_free($2);
 | |
|             vkd3d_free($3);
 | |
|         }
 | |
| 
 | |
| primary_expr
 | |
|     : T_INTEGER
 | |
|         {
 | |
|             $$ = vkd3d_parse_integer($1);
 | |
|             vkd3d_free($1);
 | |
|         }
 | |
|     | T_IDENTIFIER
 | |
|         {
 | |
|             $$ = 0;
 | |
|             vkd3d_free($1);
 | |
|         }
 | |
|     | T_DEFINED T_IDENTIFIER
 | |
|         {
 | |
|             $$ = !!preproc_find_macro(ctx, $2);
 | |
|             vkd3d_free($2);
 | |
|         }
 | |
|     | T_DEFINED '(' T_IDENTIFIER ')'
 | |
|         {
 | |
|             $$ = !!preproc_find_macro(ctx, $3);
 | |
|             vkd3d_free($3);
 | |
|         }
 | |
|     | '(' expr ')'
 | |
|         {
 | |
|             $$ = $2;
 | |
|         }
 | |
| 
 | |
| unary_expr
 | |
|     : primary_expr
 | |
|     | '+' unary_expr
 | |
|         {
 | |
|             $$ = $2;
 | |
|         }
 | |
|     | '-' unary_expr
 | |
|         {
 | |
|             $$ = -$2;
 | |
|         }
 | |
|     | '!' unary_expr
 | |
|         {
 | |
|             $$ = !$2;
 | |
|         }
 | |
| 
 | |
| mul_expr
 | |
|     : unary_expr
 | |
|     | mul_expr '*' unary_expr
 | |
|         {
 | |
|             $$ = $1 * $3;
 | |
|         }
 | |
|     | mul_expr '/' unary_expr
 | |
|         {
 | |
|             if (!$3)
 | |
|             {
 | |
|                 preproc_warning(ctx, &@3, VKD3D_SHADER_WARNING_PP_DIV_BY_ZERO, "Division by zero.");
 | |
|                 $3 = 1;
 | |
|             }
 | |
|             $$ = $1 / $3;
 | |
|         }
 | |
| 
 | |
| add_expr
 | |
|     : mul_expr
 | |
|     | add_expr '+' mul_expr
 | |
|         {
 | |
|             $$ = $1 + $3;
 | |
|         }
 | |
|     | add_expr '-' mul_expr
 | |
|         {
 | |
|             $$ = $1 - $3;
 | |
|         }
 | |
| 
 | |
| ineq_expr
 | |
|     : add_expr
 | |
|     | ineq_expr '<' add_expr
 | |
|         {
 | |
|             $$ = $1 < $3;
 | |
|         }
 | |
|     | ineq_expr '>' add_expr
 | |
|         {
 | |
|             $$ = $1 > $3;
 | |
|         }
 | |
|     | ineq_expr T_LE add_expr
 | |
|         {
 | |
|             $$ = $1 <= $3;
 | |
|         }
 | |
|     | ineq_expr T_GE add_expr
 | |
|         {
 | |
|             $$ = $1 >= $3;
 | |
|         }
 | |
| 
 | |
| eq_expr
 | |
|     : ineq_expr
 | |
|     | eq_expr T_EQ ineq_expr
 | |
|         {
 | |
|             $$ = $1 == $3;
 | |
|         }
 | |
|     | eq_expr T_NE ineq_expr
 | |
|         {
 | |
|             $$ = $1 != $3;
 | |
|         }
 | |
| 
 | |
| bitand_expr
 | |
|     : eq_expr
 | |
|     | bitand_expr '&' eq_expr
 | |
|         {
 | |
|             $$ = $1 & $3;
 | |
|         }
 | |
| 
 | |
| bitxor_expr
 | |
|     : bitand_expr
 | |
|     | bitxor_expr '^' bitand_expr
 | |
|         {
 | |
|             $$ = $1 ^ $3;
 | |
|         }
 | |
| 
 | |
| bitor_expr
 | |
|     : bitxor_expr
 | |
|     | bitor_expr '|' bitxor_expr
 | |
|         {
 | |
|             $$ = $1 | $3;
 | |
|         }
 | |
| 
 | |
| logicand_expr
 | |
|     : bitor_expr
 | |
|     | logicand_expr T_AND bitor_expr
 | |
|         {
 | |
|             $$ = $1 && $3;
 | |
|         }
 | |
| 
 | |
| logicor_expr
 | |
|     : logicand_expr
 | |
|     | logicor_expr T_OR logicand_expr
 | |
|         {
 | |
|             $$ = $1 || $3;
 | |
|         }
 | |
| 
 | |
| expr
 | |
|     : logicor_expr
 | |
|     | expr '?' logicor_expr ':' logicor_expr
 | |
|         {
 | |
|             $$ = $1 ? $3 : $5;
 | |
|         }
 |