2020-12-07 10:56:30 -08:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
2020-12-15 15:13:20 -08:00
|
|
|
#define PREPROC_YYLTYPE struct vkd3d_shader_location
|
|
|
|
|
2020-12-07 10:56:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
%code provides
|
|
|
|
{
|
|
|
|
|
|
|
|
int preproc_yylex(PREPROC_YYSTYPE *yylval_param, PREPROC_YYLTYPE *yylloc_param, void *scanner);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
%code
|
|
|
|
{
|
|
|
|
|
2020-12-15 15:13:20 -08:00
|
|
|
#define YYLLOC_DEFAULT(cur, rhs, n) (cur) = YYRHSLOC(rhs, !!n)
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-12-21 12:37:06 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-12-07 10:56:30 -08:00
|
|
|
static void yyerror(const YYLTYPE *loc, void *scanner, struct preproc_ctx *ctx, const char *string)
|
|
|
|
{
|
2020-12-15 15:13:20 -08:00
|
|
|
preproc_error(ctx, loc, VKD3D_SHADER_ERROR_PP_INVALID_SYNTAX, "%s", string);
|
2020-12-07 10:56:30 -08:00
|
|
|
}
|
|
|
|
|
2021-01-07 09:48:06 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-12-21 12:37:06 -08:00
|
|
|
static bool preproc_was_writing(struct preproc_ctx *ctx)
|
|
|
|
{
|
|
|
|
if (ctx->if_count < 2)
|
|
|
|
return true;
|
|
|
|
return ctx->if_stack[ctx->if_count - 2].current_true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool preproc_push_if(struct preproc_ctx *ctx, bool condition)
|
|
|
|
{
|
|
|
|
struct preproc_if_state *state;
|
|
|
|
|
|
|
|
if (!vkd3d_array_reserve((void **)&ctx->if_stack, &ctx->if_stack_size, ctx->if_count + 1, sizeof(*ctx->if_stack)))
|
|
|
|
return false;
|
|
|
|
state = &ctx->if_stack[ctx->if_count++];
|
|
|
|
state->current_true = condition && preproc_was_writing(ctx);
|
2020-12-15 15:13:22 -08:00
|
|
|
state->seen_true = condition;
|
|
|
|
state->seen_else = false;
|
2020-12-21 12:37:06 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int char_to_int(char c)
|
|
|
|
{
|
|
|
|
if ('0' <= c && c <= '9')
|
|
|
|
return c - '0';
|
|
|
|
if ('A' <= c && c <= 'F')
|
|
|
|
return c - 'A' + 10;
|
|
|
|
if ('a' <= c && c <= 'f')
|
|
|
|
return c - 'a' + 10;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t preproc_parse_integer(const char *s)
|
|
|
|
{
|
|
|
|
uint32_t base = 10, ret = 0;
|
|
|
|
int digit;
|
|
|
|
|
|
|
|
if (*s == '0')
|
|
|
|
{
|
|
|
|
base = 8;
|
|
|
|
++s;
|
|
|
|
if (*s == 'x' || *s == 'X')
|
|
|
|
{
|
|
|
|
base = 16;
|
|
|
|
++s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((digit = char_to_int(*s++)) >= 0)
|
|
|
|
ret = ret * base + (uint32_t)digit;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-07 10:56:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
%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}
|
|
|
|
|
2020-12-21 12:37:06 -08:00
|
|
|
%union
|
|
|
|
{
|
|
|
|
char *string;
|
|
|
|
uint32_t integer;
|
|
|
|
}
|
|
|
|
|
2021-01-07 09:48:06 -08:00
|
|
|
%token <string> T_IDENTIFIER
|
2020-12-21 12:37:06 -08:00
|
|
|
%token <string> T_INTEGER
|
|
|
|
%token <string> T_TEXT
|
|
|
|
|
|
|
|
%token T_NEWLINE
|
|
|
|
|
2021-01-07 09:48:06 -08:00
|
|
|
%token T_DEFINE "#define"
|
2020-12-15 15:13:23 -08:00
|
|
|
%token T_ELIF "#elif"
|
2020-12-15 15:13:22 -08:00
|
|
|
%token T_ELSE "#else"
|
2020-12-21 12:37:06 -08:00
|
|
|
%token T_ENDIF "#endif"
|
|
|
|
%token T_IF "#if"
|
2021-01-07 09:48:07 -08:00
|
|
|
%token T_IFDEF "#ifdef"
|
2020-12-21 12:37:06 -08:00
|
|
|
|
|
|
|
%type <integer> expr
|
2021-01-07 09:48:06 -08:00
|
|
|
%type <string> body_token
|
2020-12-07 10:56:30 -08:00
|
|
|
|
|
|
|
%%
|
|
|
|
|
|
|
|
shader_text
|
|
|
|
: %empty
|
2020-12-21 12:37:06 -08:00
|
|
|
| shader_text directive
|
|
|
|
{
|
|
|
|
vkd3d_string_buffer_printf(&ctx->buffer, "\n");
|
|
|
|
}
|
|
|
|
|
2021-01-07 09:48:06 -08:00
|
|
|
body_text
|
|
|
|
: %empty
|
|
|
|
| body_text body_token
|
|
|
|
{
|
|
|
|
vkd3d_free($2);
|
|
|
|
}
|
|
|
|
|
|
|
|
body_token
|
|
|
|
: T_IDENTIFIER
|
|
|
|
| T_INTEGER
|
|
|
|
| T_TEXT
|
|
|
|
|
2020-12-21 12:37:06 -08:00
|
|
|
directive
|
2021-01-07 09:48:06 -08:00
|
|
|
: T_DEFINE T_IDENTIFIER body_text T_NEWLINE
|
|
|
|
{
|
|
|
|
if (!preproc_add_macro(ctx, &@$, $2))
|
|
|
|
YYABORT;
|
|
|
|
}
|
|
|
|
| T_IF expr T_NEWLINE
|
2020-12-21 12:37:06 -08:00
|
|
|
{
|
|
|
|
if (!preproc_push_if(ctx, !!$2))
|
|
|
|
YYABORT;
|
|
|
|
}
|
2021-01-07 09:48:07 -08:00
|
|
|
| T_IFDEF T_IDENTIFIER T_NEWLINE
|
|
|
|
{
|
|
|
|
preproc_push_if(ctx, !!preproc_find_macro(ctx, $2));
|
|
|
|
vkd3d_free($2);
|
|
|
|
}
|
2020-12-15 15:13:23 -08:00
|
|
|
| T_ELIF expr T_NEWLINE
|
|
|
|
{
|
|
|
|
if (ctx->if_count)
|
|
|
|
{
|
|
|
|
struct preproc_if_state *state = &ctx->if_stack[ctx->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.");
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 15:13:22 -08:00
|
|
|
| T_ELSE T_NEWLINE
|
|
|
|
{
|
|
|
|
if (ctx->if_count)
|
|
|
|
{
|
|
|
|
struct preproc_if_state *state = &ctx->if_stack[ctx->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.");
|
|
|
|
}
|
|
|
|
}
|
2020-12-21 12:37:06 -08:00
|
|
|
| T_ENDIF T_NEWLINE
|
|
|
|
{
|
|
|
|
if (ctx->if_count)
|
|
|
|
--ctx->if_count;
|
|
|
|
else
|
|
|
|
preproc_warning(ctx, &@$, VKD3D_SHADER_WARNING_PP_INVALID_DIRECTIVE,
|
|
|
|
"Ignoring #endif without prior #if.");
|
|
|
|
}
|
|
|
|
|
|
|
|
expr
|
|
|
|
: T_INTEGER
|
|
|
|
{
|
|
|
|
$$ = preproc_parse_integer($1);
|
|
|
|
vkd3d_free($1);
|
|
|
|
}
|