diff --git a/docs/reference/gsk/meson.build b/docs/reference/gsk/meson.build index 725a754ffd..668d13eac5 100644 --- a/docs/reference/gsk/meson.build +++ b/docs/reference/gsk/meson.build @@ -5,6 +5,7 @@ private_headers = [ 'gskgldriverprivate.h', 'gskglprofilerprivate.h', 'gskglrendererprivate.h', + 'gskpixelshaderprivate.h', 'gskprivate.h', 'gskprofilerprivate.h', 'gskrendererprivate.h', diff --git a/gsk/gsk.h b/gsk/gsk.h index e330600a06..7b2743c998 100644 --- a/gsk/gsk.h +++ b/gsk/gsk.h @@ -21,6 +21,7 @@ #define __GSK_H_INSIDE__ #include +#include #include #include #include diff --git a/gsk/gskpixelshader.c b/gsk/gskpixelshader.c new file mode 100644 index 0000000000..cec22c0546 --- /dev/null +++ b/gsk/gskpixelshader.c @@ -0,0 +1,184 @@ +/* GSK - The GTK Scene Kit + * + * Copyright 2017 © Benjamin Otte + * + * 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 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, see . + */ + +/** + * SECTION:GskPixelShader + * @Title: GskPixelShader + * @Short_description: A pixel shader + * + * #GskPixelShader is the object used to create pixel shaders. The language + * used is GLSL with a few extensions. + * + * #GskPixelShader is an immutable object: That means you cannot change + * anything about it other than increasing the reference count via + * g_object_ref(). + */ + +#include "config.h" + +#include "gskpixelshaderprivate.h" + +#include "gskdebugprivate.h" + +#include "gdk/gdkinternals.h" + +/** + * GskPixelShader: + * + * The `GskPixelShader` structure contains only private data. + * + * Since: 3.90 + */ + +enum { + PROP_0, + PROP_N_TEXTURES, + + N_PROPS +}; + +static GParamSpec *properties[N_PROPS]; + +G_DEFINE_TYPE (GskPixelShader, gsk_pixel_shader, G_TYPE_OBJECT) + +static void +gsk_pixel_shader_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) + { + /* GskPixelShader *self = GSK_PIXEL_SHADER (gobject); */ + + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +gsk_pixel_shader_get_property (GObject *gobject, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GskPixelShader *self = GSK_PIXEL_SHADER (gobject); + + switch (prop_id) + { + case PROP_N_TEXTURES: + g_value_set_uint (value, self->n_textures); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +gsk_pixel_shader_dispose (GObject *object) +{ + GskPixelShader *self = GSK_PIXEL_SHADER (object); + + gsk_sl_node_unref (self->program); + + G_OBJECT_CLASS (gsk_pixel_shader_parent_class)->dispose (object); +} + +static void +gsk_pixel_shader_class_init (GskPixelShaderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = gsk_pixel_shader_set_property; + gobject_class->get_property = gsk_pixel_shader_get_property; + gobject_class->dispose = gsk_pixel_shader_dispose; + + /** + * GskPixelShader:n-textures: + * + * The number of input textures to the shader. + * + * Since: 3.92 + */ + properties[PROP_N_TEXTURES] = + g_param_spec_uint ("n-textures", + "n textures", + "The number of input textures", + 0, + G_MAXUINT, + 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (gobject_class, N_PROPS, properties); +} + +static void +gsk_pixel_shader_init (GskPixelShader *self) +{ +} + +GskPixelShader * +gsk_pixel_shader_new_for_data (GBytes *source, + GskShaderErrorFunc error_func, + gpointer error_data) +{ + GskPixelShader *shader; + GskSlNode *program; + + g_return_val_if_fail (source != NULL, NULL); + + program = gsk_sl_node_new_program (source, NULL); + if (program == NULL) + return NULL; + + shader = g_object_new (GSK_TYPE_PIXEL_SHADER, NULL); + + shader->program = program; + + return shader; +} + +void +gsk_pixel_shader_print (GskPixelShader *shader, + GString *string) +{ + g_return_if_fail (GSK_IS_PIXEL_SHADER (shader)); + g_return_if_fail (string != NULL); + + gsk_sl_node_print (shader->program, string); +} + +char * +gsk_pixel_shader_to_string (GskPixelShader *shader) +{ + GString *string; + + g_return_val_if_fail (GSK_IS_PIXEL_SHADER (shader), NULL); + + string = g_string_new (NULL); + + gsk_pixel_shader_print (shader, string); + + return g_string_free (string, FALSE); +} + diff --git a/gsk/gskpixelshader.h b/gsk/gskpixelshader.h new file mode 100644 index 0000000000..1506d79693 --- /dev/null +++ b/gsk/gskpixelshader.h @@ -0,0 +1,62 @@ +/* GSK - The GTK Scene Kit + * + * Copyright 2017 © Benjamin Otte + * + * 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 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, see . + */ + +#ifndef __GSK_PIXEL_SHADER_H__ +#define __GSK_PIXEL_SHADER_H__ + +#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GSK_TYPE_PIXEL_SHADER (gsk_pixel_shader_get_type ()) + +#define GSK_PIXEL_SHADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_PIXEL_SHADER, GskPixelShader)) +#define GSK_IS_PIXEL_SHADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_PIXEL_SHADER)) + +typedef struct _GskPixelShaderClass GskPixelShaderClass; + +typedef void (* GskShaderErrorFunc) (GskPixelShader *shader, + gboolean fatal, + const GskCodeLocation *location, + const GError *error, + gpointer user_data); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPixelShader, g_object_unref) + + +GDK_AVAILABLE_IN_3_92 +GType gsk_pixel_shader_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_3_92 +GskPixelShader * gsk_pixel_shader_new_for_data (GBytes *source, + GskShaderErrorFunc error_func, + gpointer error_data); + +GDK_AVAILABLE_IN_3_92 +void gsk_pixel_shader_print (GskPixelShader *shader, + GString *string); +GDK_AVAILABLE_IN_3_92 +char * gsk_pixel_shader_to_string (GskPixelShader *shader); + +G_END_DECLS + +#endif /* __GSK_PIXEL_SHADER_H__ */ diff --git a/gsk/gskpixelshaderprivate.h b/gsk/gskpixelshaderprivate.h new file mode 100644 index 0000000000..a10ba06150 --- /dev/null +++ b/gsk/gskpixelshaderprivate.h @@ -0,0 +1,29 @@ +#ifndef __GSK_PIXEL_SHADER_PRIVATE_H__ +#define __GSK_PIXEL_SHADER_PRIVATE_H__ + +#include "gskpixelshader.h" + +#include "gskslnodeprivate.h" + +G_BEGIN_DECLS + +#define GSK_PIXEL_SHADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_TEXTURE, GskPixelShaderClass)) +#define GSK_IS_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_TEXTURE)) +#define GSK_PIXEL_SHADER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_TEXTURE, GskPixelShaderClass)) + +struct _GskPixelShader +{ + GObject parent_instance; + + GskSlNode *program; + + guint n_textures; +}; + +struct _GskPixelShaderClass { + GObjectClass parent_class; +}; + +G_END_DECLS + +#endif /* __GSK_PIXEL_SHADER_PRIVATE_H__ */ diff --git a/gsk/gskslnode.c b/gsk/gskslnode.c new file mode 100644 index 0000000000..2ecc6c4553 --- /dev/null +++ b/gsk/gskslnode.c @@ -0,0 +1,285 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#include "config.h" + +#include "gskslnodeprivate.h" + +#include "gskslpreprocessorprivate.h" +#include "gsksltypeprivate.h" + +#include + +static GskSlNode * +gsk_sl_node_alloc (const GskSlNodeClass *klass, + gsize size) +{ + GskSlNode *node; + + node = g_slice_alloc0 (size); + + node->class = klass; + node->ref_count = 1; + + return node; +} +#define gsk_sl_node_new(_name, _klass) ((_name *) gsk_sl_node_alloc ((_klass), sizeof (_name))) + +/* PROGRAM */ + +typedef struct _GskSlNodeProgram GskSlNodeProgram; + +struct _GskSlNodeProgram { + GskSlNode parent; + + GSList *declarations; + GSList *functions; +}; + +static void +gsk_sl_node_program_free (GskSlNode *node) +{ + GskSlNodeProgram *program = (GskSlNodeProgram *) node; + + g_slist_free (program->declarations); + g_slist_free (program->functions); + + g_slice_free (GskSlNodeProgram, program); +} + +static void +gsk_sl_node_program_print (GskSlNode *node, + GString *string) +{ + GskSlNodeProgram *program = (GskSlNodeProgram *) node; + GSList *l; + + for (l = program->declarations; l; l = l->next) + gsk_sl_node_print (l->data, string); + + for (l = program->functions; l; l = l->next) + gsk_sl_node_print (l->data, string); +} + +static const GskSlNodeClass GSK_SL_NODE_PROGRAM = { + gsk_sl_node_program_free, + gsk_sl_node_program_print +}; + +/* FUNCTION */ + +typedef struct _GskSlNodeFunction GskSlNodeFunction; + +struct _GskSlNodeFunction { + GskSlNode parent; + + GskSlType *return_type; + char *name; +}; + +static void +gsk_sl_node_function_free (GskSlNode *node) +{ + GskSlNodeFunction *function = (GskSlNodeFunction *) node; + + if (function->return_type) + gsk_sl_type_unref (function->return_type); + g_free (function->name); + + g_slice_free (GskSlNodeFunction, function); +} + +static void +gsk_sl_node_function_print (GskSlNode *node, + GString *string) +{ + GskSlNodeFunction *function = (GskSlNodeFunction *) node; + + gsk_sl_type_print (function->return_type, string); + g_string_append (string, "\n"); + + g_string_append (string, function->name); + g_string_append (string, " ("); + g_string_append (string, ")\n"); + + g_string_append (string, "{\n"); + g_string_append (string, "}\n"); +} + +static const GskSlNodeClass GSK_SL_NODE_FUNCTION = { + gsk_sl_node_function_free, + gsk_sl_node_function_print +}; + +/* API */ + +static GskSlNodeFunction * +gsk_sl_node_parse_function_prototype (GskSlNodeProgram *program, + GskSlPreprocessor *stream) +{ + GskSlType *type; + GskSlNodeFunction *function; + const GskSlToken *token; + + type = gsk_sl_type_new_parse (stream); + if (type == NULL) + return NULL; + + token = gsk_sl_preprocessor_get (stream); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_IDENTIFIER)) + { + gsk_sl_preprocessor_error (stream, "Expected a function name"); + gsk_sl_type_unref (type); + return NULL; + } + + function = gsk_sl_node_new (GskSlNodeFunction, &GSK_SL_NODE_FUNCTION); + function->return_type = type; + function->name = g_strdup (token->str); + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + + token = gsk_sl_preprocessor_get (stream); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_PAREN)) + { + gsk_sl_preprocessor_error (stream, "Expected an openening \"(\""); + gsk_sl_node_unref ((GskSlNode *) function); + return NULL; + } + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + + token = gsk_sl_preprocessor_get (stream); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_PAREN)) + { + gsk_sl_preprocessor_error (stream, "Expected a closing \")\""); + gsk_sl_node_unref ((GskSlNode *) function); + return NULL; + } + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + + return function; +} + +static gboolean +gsk_sl_node_parse_function_definition (GskSlNodeProgram *program, + GskSlPreprocessor *stream) +{ + GskSlNodeFunction *function; + const GskSlToken *token; + + function = gsk_sl_node_parse_function_prototype (program, stream); + if (function == NULL) + return FALSE; + + token = gsk_sl_preprocessor_get (stream); + if (gsk_sl_token_is (token, GSK_SL_TOKEN_SEMICOLON)) + { + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + program->functions = g_slist_prepend (program->functions, function); + return TRUE; + } + + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_LEFT_BRACE)) + { + gsk_sl_preprocessor_error (stream, "Expected an opening \"{\""); + gsk_sl_node_unref ((GskSlNode *) function); + return FALSE; + } + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + + token = gsk_sl_preprocessor_get (stream); + if (!gsk_sl_token_is (token, GSK_SL_TOKEN_RIGHT_BRACE)) + { + gsk_sl_preprocessor_error (stream, "Expected a closing \"}\""); + gsk_sl_node_unref ((GskSlNode *) function); + return FALSE; + } + gsk_sl_preprocessor_consume (stream, (GskSlNode *) function); + + program->functions = g_slist_prepend (program->functions, function); + + return TRUE; +} + +static gboolean +gsk_sl_node_parse_program (GskSlNodeProgram *program, + GskSlPreprocessor *stream) +{ + const GskSlToken *token; + gboolean result = TRUE; + + for (token = gsk_sl_preprocessor_get (stream); + !gsk_sl_token_is (token, GSK_SL_TOKEN_EOF); + token = gsk_sl_preprocessor_get (stream)) + { + if (!gsk_sl_node_parse_function_definition (program, stream)) + { + gsk_sl_preprocessor_consume (stream, (GskSlNode *) program); + result = FALSE; + } + } + + return result; +} + + +GskSlNode * +gsk_sl_node_new_program (GBytes *source, + GError **error) +{ + GskSlPreprocessor *stream; + GskSlNodeProgram *program; + + program = gsk_sl_node_new (GskSlNodeProgram, &GSK_SL_NODE_PROGRAM); + stream = gsk_sl_preprocessor_new (source); + + gsk_sl_node_parse_program (program, stream); + + gsk_sl_preprocessor_unref (stream); + + return (GskSlNode *) program; +} + +GskSlNode * +gsk_sl_node_ref (GskSlNode *node) +{ + g_return_val_if_fail (node != NULL, NULL); + + node->ref_count += 1; + + return node; +} + +void +gsk_sl_node_unref (GskSlNode *node) +{ + if (node == NULL) + return; + + node->ref_count -= 1; + if (node->ref_count > 0) + return; + + node->class->free (node); +} + +void +gsk_sl_node_print (GskSlNode *node, + GString *string) +{ + node->class->print (node, string); +} diff --git a/gsk/gskslnodeprivate.h b/gsk/gskslnodeprivate.h new file mode 100644 index 0000000000..776f59c876 --- /dev/null +++ b/gsk/gskslnodeprivate.h @@ -0,0 +1,52 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#ifndef __GSK_SL_NODE_PRIVATE_H__ +#define __GSK_SL_NODE_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _GskSlNode GskSlNode; +typedef struct _GskSlNodeClass GskSlNodeClass; + +struct _GskSlNode { + const GskSlNodeClass *class; + guint ref_count; +}; + +struct _GskSlNodeClass { + void (* free) (GskSlNode *node); + + void (* print) (GskSlNode *node, + GString *string); +}; + +GskSlNode * gsk_sl_node_new_program (GBytes *source, + GError **error); + +GskSlNode * gsk_sl_node_ref (GskSlNode *node); +void gsk_sl_node_unref (GskSlNode *node); + +void gsk_sl_node_print (GskSlNode *node, + GString *string); + +G_END_DECLS + +#endif /* __GSK_SL_NODE_PRIVATE_H__ */ diff --git a/gsk/gskslpreprocessor.c b/gsk/gskslpreprocessor.c new file mode 100644 index 0000000000..abd07a397d --- /dev/null +++ b/gsk/gskslpreprocessor.c @@ -0,0 +1,162 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#include "config.h" + +#include "gskslpreprocessorprivate.h" + +struct _GskSlPreprocessor +{ + int ref_count; + + GskSlTokenizer *tokenizer; + GskCodeLocation location; + GskSlToken token; +}; + +/* API */ + +static void +gsk_sl_preprocessor_error_func (GskSlTokenizer *parser, + gboolean fatal, + const GskCodeLocation *location, + const GskSlToken *token, + const GError *error, + gpointer user_data) +{ + g_printerr ("%3zu:%2zu: error: %3u: %s: %s\n", + location->lines + 1, location->line_bytes, + token->type, gsk_sl_token_to_string (token), + error->message); +} + +GskSlPreprocessor * +gsk_sl_preprocessor_new (GBytes *source) +{ + GskSlPreprocessor *preproc; + + preproc = g_slice_new0 (GskSlPreprocessor); + + preproc->ref_count = 1; + preproc->tokenizer = gsk_sl_tokenizer_new (source, + gsk_sl_preprocessor_error_func, + preproc, + NULL); + + return preproc; +} + +GskSlPreprocessor * +gsk_sl_preprocessor_ref (GskSlPreprocessor *preproc) +{ + g_return_val_if_fail (preproc != NULL, NULL); + + preproc->ref_count += 1; + + return preproc; +} + +void +gsk_sl_preprocessor_unref (GskSlPreprocessor *preproc) +{ + if (preproc == NULL) + return; + + preproc->ref_count -= 1; + if (preproc->ref_count > 0) + return; + + gsk_sl_tokenizer_unref (preproc->tokenizer); + gsk_sl_token_clear (&preproc->token); + + g_slice_free (GskSlPreprocessor, preproc); +} + +static gboolean +gsk_sl_token_is_skipped (const GskSlToken *token) +{ + return gsk_sl_token_is (token, GSK_SL_TOKEN_ERROR) + || gsk_sl_token_is (token, GSK_SL_TOKEN_WHITESPACE) + || gsk_sl_token_is (token, GSK_SL_TOKEN_COMMENT) + || gsk_sl_token_is (token, GSK_SL_TOKEN_SINGLE_LINE_COMMENT); +} + +static void +gsk_sl_token_ensure (GskSlPreprocessor *preproc) +{ + if (!gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_EOF)) + return; + + do + { + preproc->location = *gsk_sl_tokenizer_get_location (preproc->tokenizer); + gsk_sl_tokenizer_read_token (preproc->tokenizer, &preproc->token); + } + while (gsk_sl_token_is_skipped (&preproc->token)); +} + +const GskSlToken * +gsk_sl_preprocessor_get (GskSlPreprocessor *preproc) +{ + gsk_sl_token_ensure (preproc); + + return &preproc->token; +} + +const GskCodeLocation * +gsk_sl_preprocessor_get_location (GskSlPreprocessor *preproc) +{ + gsk_sl_token_ensure (preproc); + + return &preproc->location; +} + +void +gsk_sl_preprocessor_consume (GskSlPreprocessor *preproc, + GskSlNode *consumer) +{ + gsk_sl_token_ensure (preproc); + + gsk_sl_token_clear (&preproc->token); +} + +void +gsk_sl_preprocessor_error (GskSlPreprocessor *preproc, + const char *format, + ...) +{ + GError *error; + va_list args; + + va_start (args, format); + error = g_error_new_valist (G_FILE_ERROR, + G_FILE_ERROR_FAILED, + format, + args); + va_end (args); + + gsk_sl_token_ensure (preproc); + gsk_sl_preprocessor_error_func (preproc->tokenizer, + TRUE, + &preproc->location, + &preproc->token, + error, + NULL); + + g_error_free (error); +} diff --git a/gsk/gskslpreprocessorprivate.h b/gsk/gskslpreprocessorprivate.h new file mode 100644 index 0000000000..58e5888ee0 --- /dev/null +++ b/gsk/gskslpreprocessorprivate.h @@ -0,0 +1,47 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#ifndef __GSK_SL_PREPROCESSOR_PRIVATE_H__ +#define __GSK_SL_PREPROCESSOR_PRIVATE_H__ + +#include + +#include "gskslnodeprivate.h" +#include "gsksltokenizerprivate.h" + +G_BEGIN_DECLS + +typedef struct _GskSlPreprocessor GskSlPreprocessor; + +GskSlPreprocessor * gsk_sl_preprocessor_new (GBytes *source); + +GskSlPreprocessor * gsk_sl_preprocessor_ref (GskSlPreprocessor *preproc); +void gsk_sl_preprocessor_unref (GskSlPreprocessor *preproc); + +const GskSlToken * gsk_sl_preprocessor_get (GskSlPreprocessor *preproc); +const GskCodeLocation * gsk_sl_preprocessor_get_location (GskSlPreprocessor *preproc); +void gsk_sl_preprocessor_consume (GskSlPreprocessor *preproc, + GskSlNode *consumer); + +void gsk_sl_preprocessor_error (GskSlPreprocessor *preproc, + const char *format, + ...) G_GNUC_PRINTF(2, 3); + +G_END_DECLS + +#endif /* __GSK_SL_PREPROCESSOR_PRIVATE_H__ */ diff --git a/gsk/gsksltokenizer.c b/gsk/gsksltokenizer.c new file mode 100644 index 0000000000..3f2388141e --- /dev/null +++ b/gsk/gsksltokenizer.c @@ -0,0 +1,1838 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Benjamin Otte + * + * 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 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, see . + */ + +#include "config.h" + +#include "gsksltokenizerprivate.h" + +#include "gskpixelshader.h" + +#include +#include + +typedef struct _GskSlTokenReader GskSlTokenReader; + +struct _GskSlTokenReader { + const char * data; + const char * end; + + GskCodeLocation position; +}; + +struct _GskSlTokenizer +{ + gint ref_count; + GBytes *bytes; + GskSlTokenizerErrorFunc error_func; + gpointer user_data; + GDestroyNotify user_destroy; + + GskSlTokenReader reader; +}; + +static void +gsk_code_location_init (GskCodeLocation *location) +{ + memset (location, 0, sizeof (GskCodeLocation)); +} + +static void +gsk_code_location_init_copy (GskCodeLocation *location, + const GskCodeLocation *source) +{ + *location = *source; +} + +static void +gsk_code_location_advance (GskCodeLocation *location, + gsize bytes, + gsize chars) +{ + location->bytes += bytes; + location->chars += chars; + location->line_bytes += bytes; + location->line_chars += chars; +} + +static void +gsk_code_location_advance_newline (GskCodeLocation *location, + gsize n_chars) +{ + gsk_code_location_advance (location, n_chars, n_chars); + + location->lines++; + location->line_bytes = 0; + location->line_chars = 0; +} + +void +gsk_sl_token_clear (GskSlToken *token) +{ + switch (token->type) + { + case GSK_SL_TOKEN_IDENTIFIER: + g_free (token->str); + break; + + case GSK_SL_TOKEN_EOF: + case GSK_SL_TOKEN_ERROR: + case GSK_SL_TOKEN_WHITESPACE: + case GSK_SL_TOKEN_COMMENT: + case GSK_SL_TOKEN_SINGLE_LINE_COMMENT: + /* real tokens */ + case GSK_SL_TOKEN_CONST: + case GSK_SL_TOKEN_BOOL: + case GSK_SL_TOKEN_FLOAT: + case GSK_SL_TOKEN_DOUBLE: + case GSK_SL_TOKEN_INT: + case GSK_SL_TOKEN_UINT: + case GSK_SL_TOKEN_BREAK: + case GSK_SL_TOKEN_CONTINUE: + case GSK_SL_TOKEN_DO: + case GSK_SL_TOKEN_ELSE: + case GSK_SL_TOKEN_FOR: + case GSK_SL_TOKEN_IF: + case GSK_SL_TOKEN_DISCARD: + case GSK_SL_TOKEN_RETURN: + case GSK_SL_TOKEN_SWITCH: + case GSK_SL_TOKEN_CASE: + case GSK_SL_TOKEN_DEFAULT: + case GSK_SL_TOKEN_SUBROUTINE: + case GSK_SL_TOKEN_BVEC2: + case GSK_SL_TOKEN_BVEC3: + case GSK_SL_TOKEN_BVEC4: + case GSK_SL_TOKEN_IVEC2: + case GSK_SL_TOKEN_IVEC3: + case GSK_SL_TOKEN_IVEC4: + case GSK_SL_TOKEN_UVEC2: + case GSK_SL_TOKEN_UVEC3: + case GSK_SL_TOKEN_UVEC4: + case GSK_SL_TOKEN_VEC2: + case GSK_SL_TOKEN_VEC3: + case GSK_SL_TOKEN_VEC4: + case GSK_SL_TOKEN_MAT2: + case GSK_SL_TOKEN_MAT3: + case GSK_SL_TOKEN_MAT4: + case GSK_SL_TOKEN_CENTROID: + case GSK_SL_TOKEN_IN: + case GSK_SL_TOKEN_OUT: + case GSK_SL_TOKEN_INOUT: + case GSK_SL_TOKEN_UNIFORM: + case GSK_SL_TOKEN_PATCH: + case GSK_SL_TOKEN_SAMPLE: + case GSK_SL_TOKEN_BUFFER: + case GSK_SL_TOKEN_SHARED: + case GSK_SL_TOKEN_COHERENT: + case GSK_SL_TOKEN_VOLATILE: + case GSK_SL_TOKEN_RESTRICT: + case GSK_SL_TOKEN_READONLY: + case GSK_SL_TOKEN_WRITEONLY: + case GSK_SL_TOKEN_DVEC2: + case GSK_SL_TOKEN_DVEC3: + case GSK_SL_TOKEN_DVEC4: + case GSK_SL_TOKEN_DMAT2: + case GSK_SL_TOKEN_DMAT3: + case GSK_SL_TOKEN_DMAT4: + case GSK_SL_TOKEN_NOPERSPECTIVE: + case GSK_SL_TOKEN_FLAT: + case GSK_SL_TOKEN_SMOOTH: + case GSK_SL_TOKEN_LAYOUT: + case GSK_SL_TOKEN_MAT2X2: + case GSK_SL_TOKEN_MAT2X3: + case GSK_SL_TOKEN_MAT2X4: + case GSK_SL_TOKEN_MAT3X2: + case GSK_SL_TOKEN_MAT3X3: + case GSK_SL_TOKEN_MAT3X4: + case GSK_SL_TOKEN_MAT4X2: + case GSK_SL_TOKEN_MAT4X3: + case GSK_SL_TOKEN_MAT4X4: + case GSK_SL_TOKEN_DMAT2X2: + case GSK_SL_TOKEN_DMAT2X3: + case GSK_SL_TOKEN_DMAT2X4: + case GSK_SL_TOKEN_DMAT3X2: + case GSK_SL_TOKEN_DMAT3X3: + case GSK_SL_TOKEN_DMAT3X4: + case GSK_SL_TOKEN_DMAT4X2: + case GSK_SL_TOKEN_DMAT4X3: + case GSK_SL_TOKEN_DMAT4X4: + case GSK_SL_TOKEN_ATOMIC_UINT: + case GSK_SL_TOKEN_SAMPLER1D: + case GSK_SL_TOKEN_SAMPLER2D: + case GSK_SL_TOKEN_SAMPLER3D: + case GSK_SL_TOKEN_SAMPLERCUBE: + case GSK_SL_TOKEN_SAMPLER1DSHADOW: + case GSK_SL_TOKEN_SAMPLER2DSHADOW: + case GSK_SL_TOKEN_SAMPLERCUBESHADOW: + case GSK_SL_TOKEN_SAMPLER1DARRAY: + case GSK_SL_TOKEN_SAMPLER2DARRAY: + case GSK_SL_TOKEN_SAMPLER1DARRAYSHADOW: + case GSK_SL_TOKEN_SAMPLER2DARRAYSHADOW: + case GSK_SL_TOKEN_ISAMPLER1D: + case GSK_SL_TOKEN_ISAMPLER2D: + case GSK_SL_TOKEN_ISAMPLER3D: + case GSK_SL_TOKEN_ISAMPLERCUBE: + case GSK_SL_TOKEN_ISAMPLER1DARRAY: + case GSK_SL_TOKEN_ISAMPLER2DARRAY: + case GSK_SL_TOKEN_USAMPLER1D: + case GSK_SL_TOKEN_USAMPLER2D: + case GSK_SL_TOKEN_USAMPLER3D: + case GSK_SL_TOKEN_USAMPLERCUBE: + case GSK_SL_TOKEN_USAMPLER1DARRAY: + case GSK_SL_TOKEN_USAMPLER2DARRAY: + case GSK_SL_TOKEN_SAMPLER2DRECT: + case GSK_SL_TOKEN_SAMPLER2DRECTSHADOW: + case GSK_SL_TOKEN_ISAMPLER2DRECT: + case GSK_SL_TOKEN_USAMPLER2DRECT: + case GSK_SL_TOKEN_SAMPLERBUFFER: + case GSK_SL_TOKEN_ISAMPLERBUFFER: + case GSK_SL_TOKEN_USAMPLERBUFFER: + case GSK_SL_TOKEN_SAMPLERCUBEARRAY: + case GSK_SL_TOKEN_SAMPLERCUBEARRAYSHADOW: + case GSK_SL_TOKEN_ISAMPLERCUBEARRAY: + case GSK_SL_TOKEN_USAMPLERCUBEARRAY: + case GSK_SL_TOKEN_SAMPLER2DMS: + case GSK_SL_TOKEN_ISAMPLER2DMS: + case GSK_SL_TOKEN_USAMPLER2DMS: + case GSK_SL_TOKEN_SAMPLER2DMSARRAY: + case GSK_SL_TOKEN_ISAMPLER2DMSARRAY: + case GSK_SL_TOKEN_USAMPLER2DMSARRAY: + case GSK_SL_TOKEN_IMAGE1D: + case GSK_SL_TOKEN_IIMAGE1D: + case GSK_SL_TOKEN_UIMAGE1D: + case GSK_SL_TOKEN_IMAGE2D: + case GSK_SL_TOKEN_IIMAGE2D: + case GSK_SL_TOKEN_UIMAGE2D: + case GSK_SL_TOKEN_IMAGE3D: + case GSK_SL_TOKEN_IIMAGE3D: + case GSK_SL_TOKEN_UIMAGE3D: + case GSK_SL_TOKEN_IMAGE2DRECT: + case GSK_SL_TOKEN_IIMAGE2DRECT: + case GSK_SL_TOKEN_UIMAGE2DRECT: + case GSK_SL_TOKEN_IMAGECUBE: + case GSK_SL_TOKEN_IIMAGECUBE: + case GSK_SL_TOKEN_UIMAGECUBE: + case GSK_SL_TOKEN_IMAGEBUFFER: + case GSK_SL_TOKEN_IIMAGEBUFFER: + case GSK_SL_TOKEN_UIMAGEBUFFER: + case GSK_SL_TOKEN_IMAGE1DARRAY: + case GSK_SL_TOKEN_IIMAGE1DARRAY: + case GSK_SL_TOKEN_UIMAGE1DARRAY: + case GSK_SL_TOKEN_IMAGE2DARRAY: + case GSK_SL_TOKEN_IIMAGE2DARRAY: + case GSK_SL_TOKEN_UIMAGE2DARRAY: + case GSK_SL_TOKEN_IMAGECUBEARRAY: + case GSK_SL_TOKEN_IIMAGECUBEARRAY: + case GSK_SL_TOKEN_UIMAGECUBEARRAY: + case GSK_SL_TOKEN_IMAGE2DMS: + case GSK_SL_TOKEN_IIMAGE2DMS: + case GSK_SL_TOKEN_UIMAGE2DMS: + case GSK_SL_TOKEN_IMAGE2DMSARRAY: + case GSK_SL_TOKEN_IIMAGE2DMSARRAY: + case GSK_SL_TOKEN_UIMAGE2DMSARRAY: + case GSK_SL_TOKEN_STRUCT: + case GSK_SL_TOKEN_VOID: + case GSK_SL_TOKEN_WHILE: + case GSK_SL_TOKEN_FLOATCONSTANT: + case GSK_SL_TOKEN_DOUBLECONSTANT: + case GSK_SL_TOKEN_INTCONSTANT: + case GSK_SL_TOKEN_UINTCONSTANT: + case GSK_SL_TOKEN_BOOLCONSTANT: + case GSK_SL_TOKEN_LEFT_OP: + case GSK_SL_TOKEN_RIGHT_OP: + case GSK_SL_TOKEN_INC_OP: + case GSK_SL_TOKEN_DEC_OP: + case GSK_SL_TOKEN_LE_OP: + case GSK_SL_TOKEN_GE_OP: + case GSK_SL_TOKEN_EQ_OP: + case GSK_SL_TOKEN_NE_OP: + case GSK_SL_TOKEN_AND_OP: + case GSK_SL_TOKEN_OR_OP: + case GSK_SL_TOKEN_XOR_OP: + case GSK_SL_TOKEN_MUL_ASSIGN: + case GSK_SL_TOKEN_DIV_ASSIGN: + case GSK_SL_TOKEN_ADD_ASSIGN: + case GSK_SL_TOKEN_MOD_ASSIGN: + case GSK_SL_TOKEN_LEFT_ASSIGN: + case GSK_SL_TOKEN_RIGHT_ASSIGN: + case GSK_SL_TOKEN_AND_ASSIGN: + case GSK_SL_TOKEN_XOR_ASSIGN: + case GSK_SL_TOKEN_OR_ASSIGN: + case GSK_SL_TOKEN_SUB_ASSIGN: + case GSK_SL_TOKEN_LEFT_PAREN: + case GSK_SL_TOKEN_RIGHT_PAREN: + case GSK_SL_TOKEN_LEFT_BRACKET: + case GSK_SL_TOKEN_RIGHT_BRACKET: + case GSK_SL_TOKEN_LEFT_BRACE: + case GSK_SL_TOKEN_RIGHT_BRACE: + case GSK_SL_TOKEN_DOT: + case GSK_SL_TOKEN_COMMA: + case GSK_SL_TOKEN_COLON: + case GSK_SL_TOKEN_EQUAL: + case GSK_SL_TOKEN_SEMICOLON: + case GSK_SL_TOKEN_BANG: + case GSK_SL_TOKEN_DASH: + case GSK_SL_TOKEN_TILDE: + case GSK_SL_TOKEN_PLUS: + case GSK_SL_TOKEN_STAR: + case GSK_SL_TOKEN_SLASH: + case GSK_SL_TOKEN_PERCENT: + case GSK_SL_TOKEN_LEFT_ANGLE: + case GSK_SL_TOKEN_RIGHT_ANGLE: + case GSK_SL_TOKEN_VERTICAL_BAR: + case GSK_SL_TOKEN_CARET: + case GSK_SL_TOKEN_AMPERSAND: + case GSK_SL_TOKEN_QUESTION: + case GSK_SL_TOKEN_INVARIANT: + case GSK_SL_TOKEN_PRECISE: + case GSK_SL_TOKEN_HIGH_PRECISION: + case GSK_SL_TOKEN_MEDIUM_PRECISION: + case GSK_SL_TOKEN_LOW_PRECISION: + case GSK_SL_TOKEN_PRECISION: + break; + + default: + g_assert_not_reached (); + break; + } + + token->type = GSK_SL_TOKEN_EOF; +} + +static const char *keywords[] = { + [GSK_SL_TOKEN_CONST] = "const", + [GSK_SL_TOKEN_BOOL] = "bool", + [GSK_SL_TOKEN_FLOAT] = "float", + [GSK_SL_TOKEN_DOUBLE] = "double", + [GSK_SL_TOKEN_INT] = "int", + [GSK_SL_TOKEN_UINT] = "uint", + [GSK_SL_TOKEN_BREAK] = "break", + [GSK_SL_TOKEN_CONTINUE] = "continue", + [GSK_SL_TOKEN_DO] = "do", + [GSK_SL_TOKEN_ELSE] = "else", + [GSK_SL_TOKEN_FOR] = "for", + [GSK_SL_TOKEN_IF] = "if", + [GSK_SL_TOKEN_DISCARD] = "discard", + [GSK_SL_TOKEN_RETURN] = "return", + [GSK_SL_TOKEN_SWITCH] = "switch", + [GSK_SL_TOKEN_CASE] = "case", + [GSK_SL_TOKEN_DEFAULT] = "default", + [GSK_SL_TOKEN_SUBROUTINE] = "subroutine", + [GSK_SL_TOKEN_BVEC2] = "bvec2", + [GSK_SL_TOKEN_BVEC3] = "bvec3", + [GSK_SL_TOKEN_BVEC4] = "bvec4", + [GSK_SL_TOKEN_IVEC2] = "ivec2", + [GSK_SL_TOKEN_IVEC3] = "ivec3", + [GSK_SL_TOKEN_IVEC4] = "ivec4", + [GSK_SL_TOKEN_UVEC2] = "uvec2", + [GSK_SL_TOKEN_UVEC3] = "uvec3", + [GSK_SL_TOKEN_UVEC4] = "uvec4", + [GSK_SL_TOKEN_VEC2] = "vec2", + [GSK_SL_TOKEN_VEC3] = "vec3", + [GSK_SL_TOKEN_VEC4] = "vec4", + [GSK_SL_TOKEN_MAT2] = "mat2", + [GSK_SL_TOKEN_MAT3] = "mat3", + [GSK_SL_TOKEN_MAT4] = "mat4", + [GSK_SL_TOKEN_CENTROID] = "centroid", + [GSK_SL_TOKEN_IN] = "in", + [GSK_SL_TOKEN_OUT] = "out", + [GSK_SL_TOKEN_INOUT] = "inout", + [GSK_SL_TOKEN_UNIFORM] = "uniform", + [GSK_SL_TOKEN_PATCH] = "patch", + [GSK_SL_TOKEN_SAMPLE] = "sample", + [GSK_SL_TOKEN_BUFFER] = "buffer", + [GSK_SL_TOKEN_SHARED] = "shared", + [GSK_SL_TOKEN_COHERENT] = "coherent", + [GSK_SL_TOKEN_VOLATILE] = "volatile", + [GSK_SL_TOKEN_RESTRICT] = "restrict", + [GSK_SL_TOKEN_READONLY] = "readonly", + [GSK_SL_TOKEN_WRITEONLY] = "writeonly", + [GSK_SL_TOKEN_DVEC2] = "dvec2", + [GSK_SL_TOKEN_DVEC3] = "dvec3", + [GSK_SL_TOKEN_DVEC4] = "dvec4", + [GSK_SL_TOKEN_DMAT2] = "dmat2", + [GSK_SL_TOKEN_DMAT3] = "dmat3", + [GSK_SL_TOKEN_DMAT4] = "dmat4", + [GSK_SL_TOKEN_NOPERSPECTIVE] = "noperspective", + [GSK_SL_TOKEN_FLAT] = "flat", + [GSK_SL_TOKEN_SMOOTH] = "smooth", + [GSK_SL_TOKEN_LAYOUT] = "layout", + [GSK_SL_TOKEN_MAT2X2] = "mat2x2", + [GSK_SL_TOKEN_MAT2X3] = "mat2x3", + [GSK_SL_TOKEN_MAT2X4] = "mat2x4", + [GSK_SL_TOKEN_MAT3X2] = "mat3x2", + [GSK_SL_TOKEN_MAT3X3] = "mat3x3", + [GSK_SL_TOKEN_MAT3X4] = "mat3x4", + [GSK_SL_TOKEN_MAT4X2] = "mat4x2", + [GSK_SL_TOKEN_MAT4X3] = "mat4x3", + [GSK_SL_TOKEN_MAT4X4] = "mat4x4", + [GSK_SL_TOKEN_DMAT2X2] = "dmat2x2", + [GSK_SL_TOKEN_DMAT2X3] = "dmat2x3", + [GSK_SL_TOKEN_DMAT2X4] = "dmat2x4", + [GSK_SL_TOKEN_DMAT3X2] = "dmat3x2", + [GSK_SL_TOKEN_DMAT3X3] = "dmat3x3", + [GSK_SL_TOKEN_DMAT3X4] = "dmat3x4", + [GSK_SL_TOKEN_DMAT4X2] = "dmat4x2", + [GSK_SL_TOKEN_DMAT4X3] = "dmat4x3", + [GSK_SL_TOKEN_DMAT4X4] = "dmat4x4", + [GSK_SL_TOKEN_ATOMIC_UINT] = "atomic_uint", + [GSK_SL_TOKEN_SAMPLER1D] = "sampler1D", + [GSK_SL_TOKEN_SAMPLER2D] = "sampler2D", + [GSK_SL_TOKEN_SAMPLER3D] = "sampler3D", + [GSK_SL_TOKEN_SAMPLERCUBE] = "samplerCube", + [GSK_SL_TOKEN_SAMPLER1DSHADOW] = "sampler1DShadow", + [GSK_SL_TOKEN_SAMPLER2DSHADOW] = "sampler2DShadow", + [GSK_SL_TOKEN_SAMPLERCUBESHADOW] = "samplerCubeShadow", + [GSK_SL_TOKEN_SAMPLER1DARRAY] = "sampler1DArray", + [GSK_SL_TOKEN_SAMPLER2DARRAY] = "sampler2DArray", + [GSK_SL_TOKEN_SAMPLER1DARRAYSHADOW] = "sampler1DArrayShadow", + [GSK_SL_TOKEN_SAMPLER2DARRAYSHADOW] = "sampler2DArrayShadow", + [GSK_SL_TOKEN_ISAMPLER1D] = "isampler1D", + [GSK_SL_TOKEN_ISAMPLER2D] = "isampler2D", + [GSK_SL_TOKEN_ISAMPLER3D] = "isampler3D", + [GSK_SL_TOKEN_ISAMPLERCUBE] = "isamplerCube", + [GSK_SL_TOKEN_ISAMPLER1DARRAY] = "isampler1DArray", + [GSK_SL_TOKEN_ISAMPLER2DARRAY] = "isampler2DArray", + [GSK_SL_TOKEN_USAMPLER1D] = "usampler1D", + [GSK_SL_TOKEN_USAMPLER2D] = "usampler2D", + [GSK_SL_TOKEN_USAMPLER3D] = "usampler3D", + [GSK_SL_TOKEN_USAMPLERCUBE] = "usamplerCube", + [GSK_SL_TOKEN_USAMPLER1DARRAY] = "usampler1DArray", + [GSK_SL_TOKEN_USAMPLER2DARRAY] = "usampler2DArray", + [GSK_SL_TOKEN_SAMPLER2DRECT] = "sampler2DRect", + [GSK_SL_TOKEN_SAMPLER2DRECTSHADOW] = "sampler2DRectShadow", + [GSK_SL_TOKEN_ISAMPLER2DRECT] = "isampler2DRect", + [GSK_SL_TOKEN_USAMPLER2DRECT] = "usampler2DRect", + [GSK_SL_TOKEN_SAMPLERBUFFER] = "samplerBuffer", + [GSK_SL_TOKEN_ISAMPLERBUFFER] = "isamplerBuffer", + [GSK_SL_TOKEN_USAMPLERBUFFER] = "usamplerBuffer", + [GSK_SL_TOKEN_SAMPLERCUBEARRAY] = "samplerCubeArray", + [GSK_SL_TOKEN_SAMPLERCUBEARRAYSHADOW] = "samplerCubeArrayShadow", + [GSK_SL_TOKEN_ISAMPLERCUBEARRAY] = "isamplerCubeArray", + [GSK_SL_TOKEN_USAMPLERCUBEARRAY] = "usamplerCubeArray", + [GSK_SL_TOKEN_SAMPLER2DMS] = "sampler2DMS", + [GSK_SL_TOKEN_ISAMPLER2DMS] = "isampler2DMS", + [GSK_SL_TOKEN_USAMPLER2DMS] = "usampler2DMS", + [GSK_SL_TOKEN_SAMPLER2DMSARRAY] = "sampler2DMSArray", + [GSK_SL_TOKEN_ISAMPLER2DMSARRAY] = "isampler2DMSArray", + [GSK_SL_TOKEN_USAMPLER2DMSARRAY] = "usampler2DMSArray", + [GSK_SL_TOKEN_IMAGE1D] = "image1D", + [GSK_SL_TOKEN_IIMAGE1D] = "iimage1D", + [GSK_SL_TOKEN_UIMAGE1D] = "uimage1D", + [GSK_SL_TOKEN_IMAGE2D] = "image2D", + [GSK_SL_TOKEN_IIMAGE2D] = "iimage2D", + [GSK_SL_TOKEN_UIMAGE2D] = "uimage2D", + [GSK_SL_TOKEN_IMAGE3D] = "image3D", + [GSK_SL_TOKEN_IIMAGE3D] = "iimage3D", + [GSK_SL_TOKEN_UIMAGE3D] = "uimage3D", + [GSK_SL_TOKEN_IMAGE2DRECT] = "image2DRect", + [GSK_SL_TOKEN_IIMAGE2DRECT] = "iimage2DRect", + [GSK_SL_TOKEN_UIMAGE2DRECT] = "uimage2DRect", + [GSK_SL_TOKEN_IMAGECUBE] = "imageCube", + [GSK_SL_TOKEN_IIMAGECUBE] = "iimageCube", + [GSK_SL_TOKEN_UIMAGECUBE] = "uimageCube", + [GSK_SL_TOKEN_IMAGEBUFFER] = "imageBuffer", + [GSK_SL_TOKEN_IIMAGEBUFFER] = "iimageBuffer", + [GSK_SL_TOKEN_UIMAGEBUFFER] = "uimageBuffer", + [GSK_SL_TOKEN_IMAGE1DARRAY] = "image1DArray", + [GSK_SL_TOKEN_IIMAGE1DARRAY] = "iimage1DArray", + [GSK_SL_TOKEN_UIMAGE1DARRAY] = "uimage1DArray", + [GSK_SL_TOKEN_IMAGE2DARRAY] = "image2DArray", + [GSK_SL_TOKEN_IIMAGE2DARRAY] = "iimage2DArray", + [GSK_SL_TOKEN_UIMAGE2DARRAY] = "uimage2DArray", + [GSK_SL_TOKEN_IMAGECUBEARRAY] = "imageCubeArray", + [GSK_SL_TOKEN_IIMAGECUBEARRAY] = "iimageCubeArray", + [GSK_SL_TOKEN_UIMAGECUBEARRAY] = "uimageCubeArray", + [GSK_SL_TOKEN_IMAGE2DMS] = "image2DMS", + [GSK_SL_TOKEN_IIMAGE2DMS] = "iimage2DMS", + [GSK_SL_TOKEN_UIMAGE2DMS] = "uimage2DMS", + [GSK_SL_TOKEN_IMAGE2DMSARRAY] = "image2DMSArray", + [GSK_SL_TOKEN_IIMAGE2DMSARRAY] = "iimage2DMSArray", + [GSK_SL_TOKEN_UIMAGE2DMSARRAY] = "uimage2DMSArray", + [GSK_SL_TOKEN_STRUCT] = "struct", + [GSK_SL_TOKEN_VOID] = "void", + [GSK_SL_TOKEN_WHILE] = "while", + [GSK_SL_TOKEN_INVARIANT] = "invariant", + [GSK_SL_TOKEN_PRECISE] = "precise", + [GSK_SL_TOKEN_HIGH_PRECISION] = "highp", + [GSK_SL_TOKEN_MEDIUM_PRECISION] = "mediump", + [GSK_SL_TOKEN_LOW_PRECISION] = "lowp", + [GSK_SL_TOKEN_PRECISION] = "precision" +}; + +void +gsk_sl_token_print (const GskSlToken *token, + GString *string) +{ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + switch (token->type) + { + case GSK_SL_TOKEN_EOF: + case GSK_SL_TOKEN_ERROR: + case GSK_SL_TOKEN_COMMENT: + case GSK_SL_TOKEN_SINGLE_LINE_COMMENT: + break; + + case GSK_SL_TOKEN_WHITESPACE: + g_string_append (string, " "); + break; + + case GSK_SL_TOKEN_FLOAT: + case GSK_SL_TOKEN_DOUBLE: + case GSK_SL_TOKEN_CONST: + case GSK_SL_TOKEN_BOOL: + case GSK_SL_TOKEN_INT: + case GSK_SL_TOKEN_UINT: + case GSK_SL_TOKEN_BREAK: + case GSK_SL_TOKEN_CONTINUE: + case GSK_SL_TOKEN_DO: + case GSK_SL_TOKEN_ELSE: + case GSK_SL_TOKEN_FOR: + case GSK_SL_TOKEN_IF: + case GSK_SL_TOKEN_DISCARD: + case GSK_SL_TOKEN_RETURN: + case GSK_SL_TOKEN_SWITCH: + case GSK_SL_TOKEN_CASE: + case GSK_SL_TOKEN_DEFAULT: + case GSK_SL_TOKEN_SUBROUTINE: + case GSK_SL_TOKEN_BVEC2: + case GSK_SL_TOKEN_BVEC3: + case GSK_SL_TOKEN_BVEC4: + case GSK_SL_TOKEN_IVEC2: + case GSK_SL_TOKEN_IVEC3: + case GSK_SL_TOKEN_IVEC4: + case GSK_SL_TOKEN_UVEC2: + case GSK_SL_TOKEN_UVEC3: + case GSK_SL_TOKEN_UVEC4: + case GSK_SL_TOKEN_VEC2: + case GSK_SL_TOKEN_VEC3: + case GSK_SL_TOKEN_VEC4: + case GSK_SL_TOKEN_MAT2: + case GSK_SL_TOKEN_MAT3: + case GSK_SL_TOKEN_MAT4: + case GSK_SL_TOKEN_CENTROID: + case GSK_SL_TOKEN_IN: + case GSK_SL_TOKEN_OUT: + case GSK_SL_TOKEN_INOUT: + case GSK_SL_TOKEN_UNIFORM: + case GSK_SL_TOKEN_PATCH: + case GSK_SL_TOKEN_SAMPLE: + case GSK_SL_TOKEN_BUFFER: + case GSK_SL_TOKEN_SHARED: + case GSK_SL_TOKEN_COHERENT: + case GSK_SL_TOKEN_VOLATILE: + case GSK_SL_TOKEN_RESTRICT: + case GSK_SL_TOKEN_READONLY: + case GSK_SL_TOKEN_WRITEONLY: + case GSK_SL_TOKEN_DVEC2: + case GSK_SL_TOKEN_DVEC3: + case GSK_SL_TOKEN_DVEC4: + case GSK_SL_TOKEN_DMAT2: + case GSK_SL_TOKEN_DMAT3: + case GSK_SL_TOKEN_DMAT4: + case GSK_SL_TOKEN_NOPERSPECTIVE: + case GSK_SL_TOKEN_FLAT: + case GSK_SL_TOKEN_SMOOTH: + case GSK_SL_TOKEN_LAYOUT: + case GSK_SL_TOKEN_MAT2X2: + case GSK_SL_TOKEN_MAT2X3: + case GSK_SL_TOKEN_MAT2X4: + case GSK_SL_TOKEN_MAT3X2: + case GSK_SL_TOKEN_MAT3X3: + case GSK_SL_TOKEN_MAT3X4: + case GSK_SL_TOKEN_MAT4X2: + case GSK_SL_TOKEN_MAT4X3: + case GSK_SL_TOKEN_MAT4X4: + case GSK_SL_TOKEN_DMAT2X2: + case GSK_SL_TOKEN_DMAT2X3: + case GSK_SL_TOKEN_DMAT2X4: + case GSK_SL_TOKEN_DMAT3X2: + case GSK_SL_TOKEN_DMAT3X3: + case GSK_SL_TOKEN_DMAT3X4: + case GSK_SL_TOKEN_DMAT4X2: + case GSK_SL_TOKEN_DMAT4X3: + case GSK_SL_TOKEN_DMAT4X4: + case GSK_SL_TOKEN_ATOMIC_UINT: + case GSK_SL_TOKEN_SAMPLER1D: + case GSK_SL_TOKEN_SAMPLER2D: + case GSK_SL_TOKEN_SAMPLER3D: + case GSK_SL_TOKEN_SAMPLERCUBE: + case GSK_SL_TOKEN_SAMPLER1DSHADOW: + case GSK_SL_TOKEN_SAMPLER2DSHADOW: + case GSK_SL_TOKEN_SAMPLERCUBESHADOW: + case GSK_SL_TOKEN_SAMPLER1DARRAY: + case GSK_SL_TOKEN_SAMPLER2DARRAY: + case GSK_SL_TOKEN_SAMPLER1DARRAYSHADOW: + case GSK_SL_TOKEN_SAMPLER2DARRAYSHADOW: + case GSK_SL_TOKEN_ISAMPLER1D: + case GSK_SL_TOKEN_ISAMPLER2D: + case GSK_SL_TOKEN_ISAMPLER3D: + case GSK_SL_TOKEN_ISAMPLERCUBE: + case GSK_SL_TOKEN_ISAMPLER1DARRAY: + case GSK_SL_TOKEN_ISAMPLER2DARRAY: + case GSK_SL_TOKEN_USAMPLER1D: + case GSK_SL_TOKEN_USAMPLER2D: + case GSK_SL_TOKEN_USAMPLER3D: + case GSK_SL_TOKEN_USAMPLERCUBE: + case GSK_SL_TOKEN_USAMPLER1DARRAY: + case GSK_SL_TOKEN_USAMPLER2DARRAY: + case GSK_SL_TOKEN_SAMPLER2DRECT: + case GSK_SL_TOKEN_SAMPLER2DRECTSHADOW: + case GSK_SL_TOKEN_ISAMPLER2DRECT: + case GSK_SL_TOKEN_USAMPLER2DRECT: + case GSK_SL_TOKEN_SAMPLERBUFFER: + case GSK_SL_TOKEN_ISAMPLERBUFFER: + case GSK_SL_TOKEN_USAMPLERBUFFER: + case GSK_SL_TOKEN_SAMPLERCUBEARRAY: + case GSK_SL_TOKEN_SAMPLERCUBEARRAYSHADOW: + case GSK_SL_TOKEN_ISAMPLERCUBEARRAY: + case GSK_SL_TOKEN_USAMPLERCUBEARRAY: + case GSK_SL_TOKEN_SAMPLER2DMS: + case GSK_SL_TOKEN_ISAMPLER2DMS: + case GSK_SL_TOKEN_USAMPLER2DMS: + case GSK_SL_TOKEN_SAMPLER2DMSARRAY: + case GSK_SL_TOKEN_ISAMPLER2DMSARRAY: + case GSK_SL_TOKEN_USAMPLER2DMSARRAY: + case GSK_SL_TOKEN_IMAGE1D: + case GSK_SL_TOKEN_IIMAGE1D: + case GSK_SL_TOKEN_UIMAGE1D: + case GSK_SL_TOKEN_IMAGE2D: + case GSK_SL_TOKEN_IIMAGE2D: + case GSK_SL_TOKEN_UIMAGE2D: + case GSK_SL_TOKEN_IMAGE3D: + case GSK_SL_TOKEN_IIMAGE3D: + case GSK_SL_TOKEN_UIMAGE3D: + case GSK_SL_TOKEN_IMAGE2DRECT: + case GSK_SL_TOKEN_IIMAGE2DRECT: + case GSK_SL_TOKEN_UIMAGE2DRECT: + case GSK_SL_TOKEN_IMAGECUBE: + case GSK_SL_TOKEN_IIMAGECUBE: + case GSK_SL_TOKEN_UIMAGECUBE: + case GSK_SL_TOKEN_IMAGEBUFFER: + case GSK_SL_TOKEN_IIMAGEBUFFER: + case GSK_SL_TOKEN_UIMAGEBUFFER: + case GSK_SL_TOKEN_IMAGE1DARRAY: + case GSK_SL_TOKEN_IIMAGE1DARRAY: + case GSK_SL_TOKEN_UIMAGE1DARRAY: + case GSK_SL_TOKEN_IMAGE2DARRAY: + case GSK_SL_TOKEN_IIMAGE2DARRAY: + case GSK_SL_TOKEN_UIMAGE2DARRAY: + case GSK_SL_TOKEN_IMAGECUBEARRAY: + case GSK_SL_TOKEN_IIMAGECUBEARRAY: + case GSK_SL_TOKEN_UIMAGECUBEARRAY: + case GSK_SL_TOKEN_IMAGE2DMS: + case GSK_SL_TOKEN_IIMAGE2DMS: + case GSK_SL_TOKEN_UIMAGE2DMS: + case GSK_SL_TOKEN_IMAGE2DMSARRAY: + case GSK_SL_TOKEN_IIMAGE2DMSARRAY: + case GSK_SL_TOKEN_UIMAGE2DMSARRAY: + case GSK_SL_TOKEN_STRUCT: + case GSK_SL_TOKEN_VOID: + case GSK_SL_TOKEN_WHILE: + case GSK_SL_TOKEN_INVARIANT: + case GSK_SL_TOKEN_PRECISE: + case GSK_SL_TOKEN_HIGH_PRECISION: + case GSK_SL_TOKEN_MEDIUM_PRECISION: + case GSK_SL_TOKEN_LOW_PRECISION: + case GSK_SL_TOKEN_PRECISION: + g_assert (keywords[token->type] != NULL); + g_string_append (string, keywords[token->type]); + break; + + case GSK_SL_TOKEN_IDENTIFIER: + g_string_append (string, token->str); + break; + + case GSK_SL_TOKEN_FLOATCONSTANT: + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, token->f); + g_string_append (string, buf); + if (strchr (buf, '.') == NULL) + g_string_append (string, ".0"); + g_string_append (string, "f"); + break; + + case GSK_SL_TOKEN_DOUBLECONSTANT: + g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, token->d); + g_string_append (string, buf); + if (strchr (buf, '.') == NULL) + g_string_append (string, ".0"); + break; + + case GSK_SL_TOKEN_INTCONSTANT: + g_string_append_printf (string, "%" G_GINT32_FORMAT, token->i32); + break; + + case GSK_SL_TOKEN_UINTCONSTANT: + g_string_append_printf (string, "%" G_GUINT32_FORMAT"u", token->u32); + break; + + case GSK_SL_TOKEN_BOOLCONSTANT: + g_string_append (string, token->b ? "true" : "false"); + break; + + case GSK_SL_TOKEN_LEFT_OP: + g_string_append (string, "<<"); + break; + + case GSK_SL_TOKEN_RIGHT_OP: + g_string_append (string, ">>"); + break; + + case GSK_SL_TOKEN_INC_OP: + g_string_append (string, "++"); + break; + + case GSK_SL_TOKEN_DEC_OP: + g_string_append (string, "--"); + break; + + case GSK_SL_TOKEN_LE_OP: + g_string_append (string, "<="); + break; + + case GSK_SL_TOKEN_GE_OP: + g_string_append (string, ">="); + break; + + case GSK_SL_TOKEN_EQ_OP: + g_string_append (string, "=="); + break; + + case GSK_SL_TOKEN_NE_OP: + g_string_append (string, "!="); + break; + + case GSK_SL_TOKEN_AND_OP: + g_string_append (string, "&&"); + break; + + case GSK_SL_TOKEN_OR_OP: + g_string_append (string, "||"); + break; + + case GSK_SL_TOKEN_XOR_OP: + g_string_append (string, "^^"); + break; + + case GSK_SL_TOKEN_MUL_ASSIGN: + g_string_append (string, "*="); + break; + + case GSK_SL_TOKEN_DIV_ASSIGN: + g_string_append (string, "/="); + break; + + case GSK_SL_TOKEN_ADD_ASSIGN: + g_string_append (string, "+="); + break; + + case GSK_SL_TOKEN_MOD_ASSIGN: + g_string_append (string, "%="); + break; + + case GSK_SL_TOKEN_LEFT_ASSIGN: + g_string_append (string, "<<="); + break; + + case GSK_SL_TOKEN_RIGHT_ASSIGN: + g_string_append (string, ">>="); + break; + + case GSK_SL_TOKEN_AND_ASSIGN: + g_string_append (string, "&="); + break; + + case GSK_SL_TOKEN_XOR_ASSIGN: + g_string_append (string, "^="); + break; + + case GSK_SL_TOKEN_OR_ASSIGN: + g_string_append (string, "|="); + break; + + case GSK_SL_TOKEN_SUB_ASSIGN: + g_string_append (string, "-="); + break; + + case GSK_SL_TOKEN_LEFT_PAREN: + g_string_append_c (string, '('); + break; + + case GSK_SL_TOKEN_RIGHT_PAREN: + g_string_append_c (string, ')'); + break; + + case GSK_SL_TOKEN_LEFT_BRACKET: + g_string_append_c (string, '['); + break; + + case GSK_SL_TOKEN_RIGHT_BRACKET: + g_string_append_c (string, ']'); + break; + + case GSK_SL_TOKEN_LEFT_BRACE: + g_string_append_c (string, '{'); + break; + + case GSK_SL_TOKEN_RIGHT_BRACE: + g_string_append_c (string, '}'); + break; + + case GSK_SL_TOKEN_DOT: + g_string_append_c (string, '.'); + break; + + case GSK_SL_TOKEN_COMMA: + g_string_append_c (string, ','); + break; + + case GSK_SL_TOKEN_COLON: + g_string_append_c (string, ':'); + break; + + case GSK_SL_TOKEN_EQUAL: + g_string_append_c (string, '='); + break; + + case GSK_SL_TOKEN_SEMICOLON: + g_string_append_c (string, ';'); + break; + + case GSK_SL_TOKEN_BANG: + g_string_append_c (string, '!'); + break; + + case GSK_SL_TOKEN_DASH: + g_string_append_c (string, '-'); + break; + + case GSK_SL_TOKEN_TILDE: + g_string_append_c (string, '~'); + break; + + case GSK_SL_TOKEN_PLUS: + g_string_append_c (string, '+'); + break; + + case GSK_SL_TOKEN_STAR: + g_string_append_c (string, '*'); + break; + + case GSK_SL_TOKEN_SLASH: + g_string_append_c (string, '/'); + break; + + case GSK_SL_TOKEN_PERCENT: + g_string_append_c (string, '%'); + break; + + case GSK_SL_TOKEN_LEFT_ANGLE: + g_string_append_c (string, '<'); + break; + + case GSK_SL_TOKEN_RIGHT_ANGLE: + g_string_append_c (string, '>'); + break; + + case GSK_SL_TOKEN_VERTICAL_BAR: + g_string_append_c (string, '|'); + break; + + case GSK_SL_TOKEN_CARET: + g_string_append_c (string, '^'); + break; + + case GSK_SL_TOKEN_AMPERSAND: + g_string_append_c (string, '&'); + break; + + case GSK_SL_TOKEN_QUESTION: + g_string_append_c (string, '?'); + break; + + default: + g_assert_not_reached (); + break; + } +} + +char * +gsk_sl_token_to_string (const GskSlToken *token) +{ + GString *string; + + string = g_string_new (NULL); + gsk_sl_token_print (token, string); + return g_string_free (string, FALSE); +} + +static void +gsk_sl_token_init (GskSlToken *token, + GskSlTokenType type) +{ + token->type = type; +} + +static void +gsk_sl_token_init_float (GskSlToken *token, + GskSlTokenType type, + double d) +{ + gsk_sl_token_init (token, type); + + if (type == GSK_SL_TOKEN_FLOATCONSTANT) + { + token->f = d; + } + else if (type == GSK_SL_TOKEN_DOUBLECONSTANT) + { + token->d = d; + } + else + { + g_assert_not_reached (); + } +} + +static void +gsk_sl_token_init_number (GskSlToken *token, + GskSlTokenType type, + guint64 number) +{ + gsk_sl_token_init (token, type); + + if (type == GSK_SL_TOKEN_INTCONSTANT) + { + token->i32 = number; + } + else if (type == GSK_SL_TOKEN_UINTCONSTANT) + { + token->u32 = number; + } + else + { + g_assert_not_reached (); + } +} + +static void +gsk_sl_token_reader_init (GskSlTokenReader *reader, + GBytes *bytes) +{ + reader->data = g_bytes_get_data (bytes, NULL); + reader->end = reader->data + g_bytes_get_size (bytes); + + gsk_code_location_init (&reader->position); +} + +static void +gsk_sl_token_reader_init_copy (GskSlTokenReader *reader, + const GskSlTokenReader *source) +{ + *reader = *source; +} + +static inline gsize +gsk_sl_token_reader_remaining (const GskSlTokenReader *reader) +{ + return reader->end - reader->data; +} + +static inline gboolean +is_newline (char c) +{ + return c == '\n' + || c == '\r'; +} + +static gboolean +is_identifier_start (char c) +{ + return g_ascii_isalpha (c) + || c == '_'; +} + +static gboolean +is_identifier (char c) +{ + return is_identifier_start (c) + || g_ascii_isdigit (c); +} + +static inline gboolean +is_whitespace (char c) +{ + return c == ' ' + || c == '\t' + || c == '\f' + || c == '\n' + || c == '\r'; +} + +static inline gsize +gsk_sl_token_reader_forward (GskSlTokenReader *reader, + gsize n) +{ + gsize i, len; + + i = 0; + len = reader->end - reader->data; + + for (; n; n--) + { + /* Skip '\' + newline */ + if (len - i > 1 && + reader->data[i] == '\\' && + is_newline (reader->data[i + 1])) + { + i += 2; + if (len - i > 0 && + is_newline (reader->data[i]) && + reader->data[i] != reader->data[i - 1]) + i++; + } + + if (i >= len) + return i; + + i++; + } + + return i; +} + +static inline char +gsk_sl_token_reader_get (GskSlTokenReader *reader, + gsize n) +{ + gsize offset; + + offset = gsk_sl_token_reader_forward (reader, n); + + if (offset >= reader->end - reader->data) + return 0; + + return reader->data[offset]; +} + +static inline void +gsk_sl_token_reader_consume (GskSlTokenReader *reader, + gsize n) +{ + gsize i, offset; + + offset = gsk_sl_token_reader_forward (reader, n); + + for (i = 0; i < offset; i++) + { + if (!is_newline (reader->data[i])) + { + gsk_code_location_advance (&reader->position, 1, 1); + } + else + { + if (reader->end - reader->data > i + 2 && + is_newline (reader->data[i + 1]) && + reader->data[i] != reader->data[i + 1]) + gsk_code_location_advance_newline (&reader->position, 2); + else + gsk_code_location_advance_newline (&reader->position, 1); + } + } + + reader->data += offset; +} + +GskSlTokenizer * +gsk_sl_tokenizer_new (GBytes *bytes, + GskSlTokenizerErrorFunc func, + gpointer user_data, + GDestroyNotify user_destroy) +{ + GskSlTokenizer *tokenizer; + + tokenizer = g_slice_new0 (GskSlTokenizer); + tokenizer->ref_count = 1; + tokenizer->bytes = g_bytes_ref (bytes); + tokenizer->error_func = func; + tokenizer->user_data = user_data; + tokenizer->user_destroy = user_destroy; + + gsk_sl_token_reader_init (&tokenizer->reader, bytes); + + return tokenizer; +} + +GskSlTokenizer * +gsk_sl_tokenizer_ref (GskSlTokenizer *tokenizer) +{ + tokenizer->ref_count++; + + return tokenizer; +} + +void +gsk_sl_tokenizer_unref (GskSlTokenizer *tokenizer) +{ + tokenizer->ref_count--; + if (tokenizer->ref_count > 0) + return; + + if (tokenizer->user_destroy) + tokenizer->user_destroy (tokenizer->user_data); + + g_bytes_unref (tokenizer->bytes); + g_slice_free (GskSlTokenizer, tokenizer); +} + +const GskCodeLocation * +gsk_sl_tokenizer_get_location (GskSlTokenizer *tokenizer) +{ + return &tokenizer->reader.position; +} + +static void +set_parse_error (GError **error, + const char *format, + ...) G_GNUC_PRINTF(2, 3); +static void +set_parse_error (GError **error, + const char *format, + ...) +{ + va_list args; + + if (error == NULL) + return; + + g_assert (*error == NULL); + + va_start (args, format); + *error = g_error_new_valist (G_FILE_ERROR, + G_FILE_ERROR_FAILED, + format, + args); + va_end (args); +} + +static void +gsk_sl_tokenizer_emit_error (GskSlTokenizer *tokenizer, + const GskCodeLocation *location, + const GskSlToken *token, + const GError *error) +{ + if (tokenizer->error_func) + tokenizer->error_func (tokenizer, TRUE, location, token, error, tokenizer->user_data); + else + g_warning ("Unhandled GLSL error: %zu:%zu: %s", location->lines + 1, location->line_chars + 1, error->message); +} + +static void +gsk_sl_token_reader_read_multi_line_comment (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + gsk_sl_token_reader_consume (reader, 2); + + while (reader->data < reader->end) + { + if (gsk_sl_token_reader_get (reader, 0) == '*' && + gsk_sl_token_reader_get (reader, 1) == '/') + { + gsk_sl_token_reader_consume (reader, 2); + gsk_sl_token_init (token, GSK_SL_TOKEN_COMMENT); + return; + } + gsk_sl_token_reader_consume (reader, 1); + } + + gsk_sl_token_init (token, GSK_SL_TOKEN_COMMENT); + set_parse_error (error, "Unterminated comment at end of document."); +} + +static void +gsk_sl_token_reader_read_single_line_comment (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + char c; + + gsk_sl_token_reader_consume (reader, 2); + + for (c = gsk_sl_token_reader_get (reader, 0); + c != 0 && !is_newline (c); + c = gsk_sl_token_reader_get (reader, 0)) + { + gsk_sl_token_reader_consume (reader, 1); + } + + gsk_sl_token_init (token, GSK_SL_TOKEN_SINGLE_LINE_COMMENT); +} + +static void +gsk_sl_token_reader_read_whitespace (GskSlTokenReader *reader, + GskSlToken *token) +{ + do { + gsk_sl_token_reader_consume (reader, 1); + } while (is_whitespace (gsk_sl_token_reader_get (reader, 0))); + + gsk_sl_token_init (token, GSK_SL_TOKEN_WHITESPACE); +} + +static gboolean +gsk_sl_token_reader_read_float_number (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + int exponent_sign = 0; + guint64 integer = 0; + gint64 fractional = 0, fractional_length = 1, exponent = 0; + gboolean is_int = TRUE, overflow = FALSE; + char c; + guint i; + + for (i = 0; ; i++) + { + c = gsk_sl_token_reader_get (reader, i); + + if (!g_ascii_isdigit (c)) + break; + if (integer > G_MAXUINT64 / 10) + overflow = TRUE; + + integer = 10 * integer + g_ascii_digit_value (c); + } + + if (c == '.') + { + is_int = FALSE; + + for (i = i + 1; ; i++) + { + c = gsk_sl_token_reader_get (reader, i); + + if (!g_ascii_isdigit (c)) + break; + + if (fractional_length < G_MAXINT64 / 10) + { + fractional = 10 * fractional + g_ascii_digit_value (c); + fractional_length *= 10; + } + } + } + + if (c == 'e' || c == 'E') + { + is_int = FALSE; + + c = gsk_sl_token_reader_get (reader, i + 1); + + if (c == '-') + { + exponent_sign = -1; + c = gsk_sl_token_reader_get (reader, i + 2); + } + else if (c == '+') + { + exponent_sign = 1; + c = gsk_sl_token_reader_get (reader, i + 2); + } + + if (g_ascii_isdigit (c)) + { + if (exponent_sign == 0) + { + i++; + exponent_sign = 1; + } + else + { + i += 2; + } + + for (i = 0; ; i++) + { + c = gsk_sl_token_reader_get (reader, i); + + if (!g_ascii_isdigit (c)) + break; + + exponent = 10 * exponent + g_ascii_digit_value (c); + } + } + else + { + c = gsk_sl_token_reader_get (reader, i); + } + } + + gsk_sl_token_reader_consume (reader, i); + + if (is_int) + { + if (integer > G_MAXUINT32) + overflow = TRUE; + + if (c == 'U' || c == 'u') + { + gsk_sl_token_reader_consume (reader, 1); + gsk_sl_token_init_number (token, GSK_SL_TOKEN_UINTCONSTANT, integer); + } + else + { + gsk_sl_token_init_number (token, GSK_SL_TOKEN_INTCONSTANT, integer); + } + + if (overflow) + { + set_parse_error (error, "Overflow in integer constant"); + return FALSE; + } + + return TRUE; + } + else + { + double d = (integer + ((double) fractional / fractional_length)) * pow (10, exponent_sign * exponent); + + if (c == 'f' || c == 'F') + { + gsk_sl_token_reader_consume (reader, 1); + gsk_sl_token_init_float (token, GSK_SL_TOKEN_FLOATCONSTANT, d); + } + else if ((c == 'l' && gsk_sl_token_reader_get (reader, 1) == 'f') || + (c == 'L' && gsk_sl_token_reader_get (reader, 1) == 'F')) + { + gsk_sl_token_reader_consume (reader, 2); + gsk_sl_token_init_float (token, GSK_SL_TOKEN_DOUBLECONSTANT, d); + } + else + { + gsk_sl_token_init_float (token, GSK_SL_TOKEN_FLOATCONSTANT, d); + } + + if (overflow) + { + set_parse_error (error, "Overflow in floating point constant"); + return FALSE; + } + + return TRUE; + } +} + +static gboolean +gsk_sl_token_reader_read_hex_number (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + char c; + guint64 result = 0; + gboolean overflow = FALSE; + + for (c = gsk_sl_token_reader_get (reader, 0); + g_ascii_isxdigit (c); + c = gsk_sl_token_reader_get (reader, 0)) + { + if (result > G_MAXUINT32 / 16) + overflow = TRUE; + + result = result * 16 + g_ascii_xdigit_value (c); + gsk_sl_token_reader_consume (reader, 1); + } + + if (c == 'U' || c == 'u') + { + gsk_sl_token_reader_consume (reader, 1); + gsk_sl_token_init_number (token, GSK_SL_TOKEN_UINTCONSTANT, result); + } + else + { + gsk_sl_token_init_number (token, GSK_SL_TOKEN_INTCONSTANT, result); + } + + if (overflow) + { + set_parse_error (error, "Overflow in integer constant"); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsk_sl_token_reader_read_octal_number (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + char c; + guint i; + guint64 result = 0; + gboolean overflow = FALSE; + + for (i = 0; ; i++) + { + c = gsk_sl_token_reader_get (reader, i); + if (c < '0' || c > '7') + break; + + if (result > G_MAXUINT32 / 8) + overflow = TRUE; + + result = result * 8 + g_ascii_digit_value (c); + } + + if (c == 'U' || c == 'u') + { + gsk_sl_token_reader_consume (reader, i + 1); + gsk_sl_token_init_number (token, GSK_SL_TOKEN_UINTCONSTANT, result); + } + else if (c == '.' || c == 'e' || c == 'f' || c == 'E' || c == 'F') + { + return gsk_sl_token_reader_read_float_number (reader, token, error); + } + else + { + gsk_sl_token_reader_consume (reader, i); + gsk_sl_token_init_number (token, GSK_SL_TOKEN_INTCONSTANT, result); + } + + if (overflow) + { + set_parse_error (error, "Overflow in octal constant"); + return FALSE; + } + + return TRUE; +} + +static gboolean +gsk_sl_token_reader_read_number (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + if (gsk_sl_token_reader_get (reader, 0) == '0') + { + char c = gsk_sl_token_reader_get (reader, 1); + if (c == 'x' || c == 'X') + { + if (!g_ascii_isxdigit (gsk_sl_token_reader_get (reader, 3))) + { + gsk_sl_token_init_number (token, GSK_SL_TOKEN_INTCONSTANT, 0); + gsk_sl_token_reader_consume (reader, 1); + return TRUE; + } + gsk_sl_token_reader_consume (reader, 2); + return gsk_sl_token_reader_read_hex_number (reader, token, error); + } + else + { + return gsk_sl_token_reader_read_octal_number (reader, token, error); + } + } + + return gsk_sl_token_reader_read_float_number (reader, token, error); +} + +static gboolean +gsk_sl_token_reader_read_identifier (GskSlTokenReader *reader, + GskSlToken *token, + GError **error) +{ + GString *string; + guint i; + char *ident; + char c; + + string = g_string_new (""); + + for (c = gsk_sl_token_reader_get (reader, 0); + is_identifier (c); + c = gsk_sl_token_reader_get (reader, 0)) + { + g_string_append_c (string, c); + gsk_sl_token_reader_consume (reader, 1); + } + + ident = g_string_free (string, FALSE); + + if (g_str_equal (ident, "true")) + { + gsk_sl_token_init (token, GSK_SL_TOKEN_BOOLCONSTANT); + token->b = TRUE; + return TRUE; + } + else if (g_str_equal (ident, "false")) + { + gsk_sl_token_init (token, GSK_SL_TOKEN_BOOLCONSTANT); + token->b = FALSE; + return TRUE; + } + + for (i = 0; i < G_N_ELEMENTS (keywords); i++) + { + if (keywords[i] == NULL) + continue; + + if (g_str_equal (ident, keywords[i])) + { + gsk_sl_token_init (token, i); + return TRUE; + } + } + + gsk_sl_token_init (token, GSK_SL_TOKEN_IDENTIFIER); + token->str = ident; + + return TRUE; +} + +void +gsk_sl_tokenizer_read_token (GskSlTokenizer *tokenizer, + GskSlToken *token) +{ + GskSlTokenReader reader; + GError *error = NULL; + char c; + + gsk_sl_token_reader_init_copy (&reader, &tokenizer->reader); + c = gsk_sl_token_reader_get (&reader, 0); + + switch (c) + { + case 0: + gsk_sl_token_init (token, GSK_SL_TOKEN_EOF); + break; + + case '\n': + case '\r': + case '\t': + case '\f': + case ' ': + gsk_sl_token_reader_read_whitespace (&reader, token); + break; + + case '/': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '/': + gsk_sl_token_reader_read_single_line_comment (&reader, token, &error); + break; + case '*': + gsk_sl_token_reader_read_multi_line_comment (&reader, token, &error); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_DIV_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_SLASH); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '<': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '<': + if (gsk_sl_token_reader_get (&reader, 2) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_ASSIGN); + gsk_sl_token_reader_consume (&reader, 3); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_OP); + gsk_sl_token_reader_consume (&reader, 2); + } + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_LE_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_ANGLE); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + + case '>': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '>': + if (gsk_sl_token_reader_get (&reader, 2) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_ASSIGN); + gsk_sl_token_reader_consume (&reader, 3); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_OP); + gsk_sl_token_reader_consume (&reader, 2); + } + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_GE_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_ANGLE); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '+': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '+': + gsk_sl_token_init (token, GSK_SL_TOKEN_INC_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_ADD_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_PLUS); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '-': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '-': + gsk_sl_token_init (token, GSK_SL_TOKEN_DEC_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_SUB_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_DASH); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '=': + if (gsk_sl_token_reader_get (&reader, 1) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_EQ_OP); + gsk_sl_token_reader_consume (&reader, 2); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_EQUAL); + gsk_sl_token_reader_consume (&reader, 1); + } + break; + + case '!': + if (gsk_sl_token_reader_get (&reader, 1) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_NE_OP); + gsk_sl_token_reader_consume (&reader, 2); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_BANG); + gsk_sl_token_reader_consume (&reader, 1); + } + break; + + case '&': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '&': + gsk_sl_token_init (token, GSK_SL_TOKEN_AND_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_AND_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_AMPERSAND); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '|': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '|': + gsk_sl_token_init (token, GSK_SL_TOKEN_OR_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_OR_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_VERTICAL_BAR); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '^': + switch (gsk_sl_token_reader_get (&reader, 1)) + { + case '^': + gsk_sl_token_init (token, GSK_SL_TOKEN_XOR_OP); + gsk_sl_token_reader_consume (&reader, 2); + break; + case '=': + gsk_sl_token_init (token, GSK_SL_TOKEN_XOR_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + break; + default: + gsk_sl_token_init (token, GSK_SL_TOKEN_CARET); + gsk_sl_token_reader_consume (&reader, 1); + break; + } + break; + + case '*': + if (gsk_sl_token_reader_get (&reader, 1) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_MUL_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_STAR); + gsk_sl_token_reader_consume (&reader, 1); + } + break; + + case '%': + if (gsk_sl_token_reader_get (&reader, 1) == '=') + { + gsk_sl_token_init (token, GSK_SL_TOKEN_MOD_ASSIGN); + gsk_sl_token_reader_consume (&reader, 2); + } + else + { + gsk_sl_token_init (token, GSK_SL_TOKEN_PERCENT); + gsk_sl_token_reader_consume (&reader, 1); + } + break; + + case '(': + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_PAREN); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case ')': + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_PAREN); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '[': + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_BRACKET); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case ']': + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_BRACKET); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '{': + gsk_sl_token_init (token, GSK_SL_TOKEN_LEFT_BRACE); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '}': + gsk_sl_token_init (token, GSK_SL_TOKEN_RIGHT_BRACE); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '.': + gsk_sl_token_init (token, GSK_SL_TOKEN_DOT); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case ':': + gsk_sl_token_init (token, GSK_SL_TOKEN_COLON); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case ';': + gsk_sl_token_init (token, GSK_SL_TOKEN_SEMICOLON); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '~': + gsk_sl_token_init (token, GSK_SL_TOKEN_TILDE); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case '?': + gsk_sl_token_init (token, GSK_SL_TOKEN_QUESTION); + gsk_sl_token_reader_consume (&reader, 1); + break; + + case ',': + gsk_sl_token_init (token, GSK_SL_TOKEN_COMMA); + gsk_sl_token_reader_consume (&reader, 1); + break; + + default: + if (g_ascii_isdigit (c)) + { + gsk_sl_token_reader_read_number (&reader, token, &error); + } + else if (is_identifier_start (c)) + { + gsk_sl_token_reader_read_identifier (&reader, token, &error); + } + else + { + set_parse_error (&error, "Unknown character 0x%X", gsk_sl_token_reader_get (&reader, 0)); + gsk_sl_token_init (token, GSK_SL_TOKEN_ERROR); + gsk_sl_token_reader_consume (&reader, 1); + } + break; + } + + if (error != NULL) + { + GskCodeLocation error_location; + + gsk_code_location_init_copy (&error_location, &reader.position); + gsk_sl_token_reader_init_copy (&tokenizer->reader, &reader); + gsk_sl_tokenizer_emit_error (tokenizer, &error_location, token, error); + g_error_free (error); + } + else + { + gsk_sl_token_reader_init_copy (&tokenizer->reader, &reader); + } +} + diff --git a/gsk/gsksltokenizerprivate.h b/gsk/gsksltokenizerprivate.h new file mode 100644 index 0000000000..ea769fce41 --- /dev/null +++ b/gsk/gsksltokenizerprivate.h @@ -0,0 +1,293 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Benjamin Otte + * + * 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 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, see . + */ + +#ifndef __GSK_SL_TOKENIZER_PRIVATE_H__ +#define __GSK_SL_TOKENIZER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +typedef enum { + GSK_SL_TOKEN_EOF = 0, + GSK_SL_TOKEN_ERROR, + GSK_SL_TOKEN_WHITESPACE, + GSK_SL_TOKEN_COMMENT, + GSK_SL_TOKEN_SINGLE_LINE_COMMENT, + /* real tokens */ + GSK_SL_TOKEN_CONST, + GSK_SL_TOKEN_BOOL, + GSK_SL_TOKEN_FLOAT, + GSK_SL_TOKEN_DOUBLE, + GSK_SL_TOKEN_INT, + GSK_SL_TOKEN_UINT, + GSK_SL_TOKEN_BREAK, + GSK_SL_TOKEN_CONTINUE, + GSK_SL_TOKEN_DO, + GSK_SL_TOKEN_ELSE, + GSK_SL_TOKEN_FOR, + GSK_SL_TOKEN_IF, + GSK_SL_TOKEN_DISCARD, + GSK_SL_TOKEN_RETURN, + GSK_SL_TOKEN_SWITCH, + GSK_SL_TOKEN_CASE, + GSK_SL_TOKEN_DEFAULT, + GSK_SL_TOKEN_SUBROUTINE, + GSK_SL_TOKEN_BVEC2, + GSK_SL_TOKEN_BVEC3, + GSK_SL_TOKEN_BVEC4, + GSK_SL_TOKEN_IVEC2, + GSK_SL_TOKEN_IVEC3, + GSK_SL_TOKEN_IVEC4, + GSK_SL_TOKEN_UVEC2, + GSK_SL_TOKEN_UVEC3, + GSK_SL_TOKEN_UVEC4, + GSK_SL_TOKEN_VEC2, + GSK_SL_TOKEN_VEC3, + GSK_SL_TOKEN_VEC4, + GSK_SL_TOKEN_MAT2, + GSK_SL_TOKEN_MAT3, + GSK_SL_TOKEN_MAT4, + GSK_SL_TOKEN_CENTROID, + GSK_SL_TOKEN_IN, + GSK_SL_TOKEN_OUT, + GSK_SL_TOKEN_INOUT, + GSK_SL_TOKEN_UNIFORM, + GSK_SL_TOKEN_PATCH, + GSK_SL_TOKEN_SAMPLE, + GSK_SL_TOKEN_BUFFER, + GSK_SL_TOKEN_SHARED, + GSK_SL_TOKEN_COHERENT, + GSK_SL_TOKEN_VOLATILE, + GSK_SL_TOKEN_RESTRICT, + GSK_SL_TOKEN_READONLY, + GSK_SL_TOKEN_WRITEONLY, + GSK_SL_TOKEN_DVEC2, + GSK_SL_TOKEN_DVEC3, + GSK_SL_TOKEN_DVEC4, + GSK_SL_TOKEN_DMAT2, + GSK_SL_TOKEN_DMAT3, + GSK_SL_TOKEN_DMAT4, + GSK_SL_TOKEN_NOPERSPECTIVE, + GSK_SL_TOKEN_FLAT, + GSK_SL_TOKEN_SMOOTH, + GSK_SL_TOKEN_LAYOUT, + GSK_SL_TOKEN_MAT2X2, + GSK_SL_TOKEN_MAT2X3, + GSK_SL_TOKEN_MAT2X4, + GSK_SL_TOKEN_MAT3X2, + GSK_SL_TOKEN_MAT3X3, + GSK_SL_TOKEN_MAT3X4, + GSK_SL_TOKEN_MAT4X2, + GSK_SL_TOKEN_MAT4X3, + GSK_SL_TOKEN_MAT4X4, + GSK_SL_TOKEN_DMAT2X2, + GSK_SL_TOKEN_DMAT2X3, + GSK_SL_TOKEN_DMAT2X4, + GSK_SL_TOKEN_DMAT3X2, + GSK_SL_TOKEN_DMAT3X3, + GSK_SL_TOKEN_DMAT3X4, + GSK_SL_TOKEN_DMAT4X2, + GSK_SL_TOKEN_DMAT4X3, + GSK_SL_TOKEN_DMAT4X4, + GSK_SL_TOKEN_ATOMIC_UINT, + GSK_SL_TOKEN_SAMPLER1D, + GSK_SL_TOKEN_SAMPLER2D, + GSK_SL_TOKEN_SAMPLER3D, + GSK_SL_TOKEN_SAMPLERCUBE, + GSK_SL_TOKEN_SAMPLER1DSHADOW, + GSK_SL_TOKEN_SAMPLER2DSHADOW, + GSK_SL_TOKEN_SAMPLERCUBESHADOW, + GSK_SL_TOKEN_SAMPLER1DARRAY, + GSK_SL_TOKEN_SAMPLER2DARRAY, + GSK_SL_TOKEN_SAMPLER1DARRAYSHADOW, + GSK_SL_TOKEN_SAMPLER2DARRAYSHADOW, + GSK_SL_TOKEN_ISAMPLER1D, + GSK_SL_TOKEN_ISAMPLER2D, + GSK_SL_TOKEN_ISAMPLER3D, + GSK_SL_TOKEN_ISAMPLERCUBE, + GSK_SL_TOKEN_ISAMPLER1DARRAY, + GSK_SL_TOKEN_ISAMPLER2DARRAY, + GSK_SL_TOKEN_USAMPLER1D, + GSK_SL_TOKEN_USAMPLER2D, + GSK_SL_TOKEN_USAMPLER3D, + GSK_SL_TOKEN_USAMPLERCUBE, + GSK_SL_TOKEN_USAMPLER1DARRAY, + GSK_SL_TOKEN_USAMPLER2DARRAY, + GSK_SL_TOKEN_SAMPLER2DRECT, + GSK_SL_TOKEN_SAMPLER2DRECTSHADOW, + GSK_SL_TOKEN_ISAMPLER2DRECT, + GSK_SL_TOKEN_USAMPLER2DRECT, + GSK_SL_TOKEN_SAMPLERBUFFER, + GSK_SL_TOKEN_ISAMPLERBUFFER, + GSK_SL_TOKEN_USAMPLERBUFFER, + GSK_SL_TOKEN_SAMPLERCUBEARRAY, + GSK_SL_TOKEN_SAMPLERCUBEARRAYSHADOW, + GSK_SL_TOKEN_ISAMPLERCUBEARRAY, + GSK_SL_TOKEN_USAMPLERCUBEARRAY, + GSK_SL_TOKEN_SAMPLER2DMS, + GSK_SL_TOKEN_ISAMPLER2DMS, + GSK_SL_TOKEN_USAMPLER2DMS, + GSK_SL_TOKEN_SAMPLER2DMSARRAY, + GSK_SL_TOKEN_ISAMPLER2DMSARRAY, + GSK_SL_TOKEN_USAMPLER2DMSARRAY, + GSK_SL_TOKEN_IMAGE1D, + GSK_SL_TOKEN_IIMAGE1D, + GSK_SL_TOKEN_UIMAGE1D, + GSK_SL_TOKEN_IMAGE2D, + GSK_SL_TOKEN_IIMAGE2D, + GSK_SL_TOKEN_UIMAGE2D, + GSK_SL_TOKEN_IMAGE3D, + GSK_SL_TOKEN_IIMAGE3D, + GSK_SL_TOKEN_UIMAGE3D, + GSK_SL_TOKEN_IMAGE2DRECT, + GSK_SL_TOKEN_IIMAGE2DRECT, + GSK_SL_TOKEN_UIMAGE2DRECT, + GSK_SL_TOKEN_IMAGECUBE, + GSK_SL_TOKEN_IIMAGECUBE, + GSK_SL_TOKEN_UIMAGECUBE, + GSK_SL_TOKEN_IMAGEBUFFER, + GSK_SL_TOKEN_IIMAGEBUFFER, + GSK_SL_TOKEN_UIMAGEBUFFER, + GSK_SL_TOKEN_IMAGE1DARRAY, + GSK_SL_TOKEN_IIMAGE1DARRAY, + GSK_SL_TOKEN_UIMAGE1DARRAY, + GSK_SL_TOKEN_IMAGE2DARRAY, + GSK_SL_TOKEN_IIMAGE2DARRAY, + GSK_SL_TOKEN_UIMAGE2DARRAY, + GSK_SL_TOKEN_IMAGECUBEARRAY, + GSK_SL_TOKEN_IIMAGECUBEARRAY, + GSK_SL_TOKEN_UIMAGECUBEARRAY, + GSK_SL_TOKEN_IMAGE2DMS, + GSK_SL_TOKEN_IIMAGE2DMS, + GSK_SL_TOKEN_UIMAGE2DMS, + GSK_SL_TOKEN_IMAGE2DMSARRAY, + GSK_SL_TOKEN_IIMAGE2DMSARRAY, + GSK_SL_TOKEN_UIMAGE2DMSARRAY, + GSK_SL_TOKEN_STRUCT, + GSK_SL_TOKEN_VOID, + GSK_SL_TOKEN_WHILE, + GSK_SL_TOKEN_IDENTIFIER, + GSK_SL_TOKEN_FLOATCONSTANT, + GSK_SL_TOKEN_DOUBLECONSTANT, + GSK_SL_TOKEN_INTCONSTANT, + GSK_SL_TOKEN_UINTCONSTANT, + GSK_SL_TOKEN_BOOLCONSTANT, + GSK_SL_TOKEN_LEFT_OP, + GSK_SL_TOKEN_RIGHT_OP, + GSK_SL_TOKEN_INC_OP, + GSK_SL_TOKEN_DEC_OP, + GSK_SL_TOKEN_LE_OP, + GSK_SL_TOKEN_GE_OP, + GSK_SL_TOKEN_EQ_OP, + GSK_SL_TOKEN_NE_OP, + GSK_SL_TOKEN_AND_OP, + GSK_SL_TOKEN_OR_OP, + GSK_SL_TOKEN_XOR_OP, + GSK_SL_TOKEN_MUL_ASSIGN, + GSK_SL_TOKEN_DIV_ASSIGN, + GSK_SL_TOKEN_ADD_ASSIGN, + GSK_SL_TOKEN_MOD_ASSIGN, + GSK_SL_TOKEN_LEFT_ASSIGN, + GSK_SL_TOKEN_RIGHT_ASSIGN, + GSK_SL_TOKEN_AND_ASSIGN, + GSK_SL_TOKEN_XOR_ASSIGN, + GSK_SL_TOKEN_OR_ASSIGN, + GSK_SL_TOKEN_SUB_ASSIGN, + GSK_SL_TOKEN_LEFT_PAREN, + GSK_SL_TOKEN_RIGHT_PAREN, + GSK_SL_TOKEN_LEFT_BRACKET, + GSK_SL_TOKEN_RIGHT_BRACKET, + GSK_SL_TOKEN_LEFT_BRACE, + GSK_SL_TOKEN_RIGHT_BRACE, + GSK_SL_TOKEN_DOT, + GSK_SL_TOKEN_COMMA, + GSK_SL_TOKEN_COLON, + GSK_SL_TOKEN_EQUAL, + GSK_SL_TOKEN_SEMICOLON, + GSK_SL_TOKEN_BANG, + GSK_SL_TOKEN_DASH, + GSK_SL_TOKEN_TILDE, + GSK_SL_TOKEN_PLUS, + GSK_SL_TOKEN_STAR, + GSK_SL_TOKEN_SLASH, + GSK_SL_TOKEN_PERCENT, + GSK_SL_TOKEN_LEFT_ANGLE, + GSK_SL_TOKEN_RIGHT_ANGLE, + GSK_SL_TOKEN_VERTICAL_BAR, + GSK_SL_TOKEN_CARET, + GSK_SL_TOKEN_AMPERSAND, + GSK_SL_TOKEN_QUESTION, + GSK_SL_TOKEN_INVARIANT, + GSK_SL_TOKEN_PRECISE, + GSK_SL_TOKEN_HIGH_PRECISION, + GSK_SL_TOKEN_MEDIUM_PRECISION, + GSK_SL_TOKEN_LOW_PRECISION, + GSK_SL_TOKEN_PRECISION +} GskSlTokenType; + +typedef struct _GskSlToken GskSlToken; +typedef struct _GskSlTokenizer GskSlTokenizer; + +typedef void (* GskSlTokenizerErrorFunc) (GskSlTokenizer *parser, + gboolean fatal, + const GskCodeLocation *location, + const GskSlToken *token, + const GError *error, + gpointer user_data); + +struct _GskSlToken { + GskSlTokenType type; + union { + gint32 i32; + guint32 u32; + float f; + double d; + gboolean b; + char *str; + }; +}; + +void gsk_sl_token_clear (GskSlToken *token); + +gboolean gsk_sl_token_is_finite (const GskSlToken *token); +#define gsk_sl_token_is(token, _type) ((token)->type == (_type)) +gboolean gsk_sl_token_is_ident (const GskSlToken *token, + const char *ident); +gboolean gsk_sl_token_is_function (const GskSlToken *token, + const char *ident); + +void gsk_sl_token_print (const GskSlToken *token, + GString *string); +char * gsk_sl_token_to_string (const GskSlToken *token); + +GskSlTokenizer * gsk_sl_tokenizer_new (GBytes *bytes, + GskSlTokenizerErrorFunc func, + gpointer user_data, + GDestroyNotify user_destroy); + +GskSlTokenizer * gsk_sl_tokenizer_ref (GskSlTokenizer *tokenizer); +void gsk_sl_tokenizer_unref (GskSlTokenizer *tokenizer); + +const GskCodeLocation * gsk_sl_tokenizer_get_location (GskSlTokenizer *tokenizer); + +void gsk_sl_tokenizer_read_token (GskSlTokenizer *tokenizer, + GskSlToken *token); + +G_END_DECLS + +#endif /* __GSK_SL_TOKENIZER_PRIVATE_H__ */ diff --git a/gsk/gsksltype.c b/gsk/gsksltype.c new file mode 100644 index 0000000000..8958a412d9 --- /dev/null +++ b/gsk/gsksltype.c @@ -0,0 +1,161 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#include "config.h" + +#include "gsksltypeprivate.h" + +#include + +struct _GskSlType +{ + int ref_count; + + GskSlBuiltinType builtin; +}; + +static GskSlType +builtin_types[GSK_SL_N_BUILTIN_TYPES] = { + [GSK_SL_VOID] = { 1, GSK_SL_VOID }, + [GSK_SL_FLOAT] = { 1, GSK_SL_FLOAT }, + [GSK_SL_DOUBLE] = { 1, GSK_SL_DOUBLE }, + [GSK_SL_INT] = { 1, GSK_SL_INT }, + [GSK_SL_UINT] = { 1, GSK_SL_UINT }, + [GSK_SL_BOOL] = { 1, GSK_SL_BOOL }, + [GSK_SL_VEC2] = { 1, GSK_SL_VEC2 }, + [GSK_SL_VEC3] = { 1, GSK_SL_VEC3 }, + [GSK_SL_VEC4] = { 1, GSK_SL_VEC4 } +}; + +GskSlType * +gsk_sl_type_new_parse (GskSlPreprocessor *stream) +{ + GskSlBuiltinType builtin; + const GskSlToken *token; + + token = gsk_sl_preprocessor_get (stream); + + switch ((guint) token->type) + { + case GSK_SL_TOKEN_VOID: + builtin = GSK_SL_VOID; + break; + case GSK_SL_TOKEN_FLOAT: + builtin = GSK_SL_FLOAT; + break; + case GSK_SL_TOKEN_DOUBLE: + builtin = GSK_SL_DOUBLE; + break; + case GSK_SL_TOKEN_INT: + builtin = GSK_SL_INT; + break; + case GSK_SL_TOKEN_UINT: + builtin = GSK_SL_UINT; + break; + case GSK_SL_TOKEN_BOOL: + builtin = GSK_SL_BOOL; + break; + case GSK_SL_TOKEN_VEC2: + builtin = GSK_SL_VEC2; + break; + case GSK_SL_TOKEN_VEC3: + builtin = GSK_SL_VEC3; + break; + case GSK_SL_TOKEN_VEC4: + builtin = GSK_SL_VEC4; + break; + default: + gsk_sl_preprocessor_error (stream, "Expected type specifier"); + return NULL; + } + + gsk_sl_preprocessor_consume (stream, NULL); + return gsk_sl_type_ref (gsk_sl_type_get_builtin (builtin)); +} + +GskSlType * +gsk_sl_type_get_builtin (GskSlBuiltinType builtin) +{ + g_assert (builtin < GSK_SL_N_BUILTIN_TYPES); + + return &builtin_types[builtin]; +} + +GskSlType * +gsk_sl_type_ref (GskSlType *type) +{ + g_return_val_if_fail (type != NULL, NULL); + + type->ref_count += 1; + + return type; +} + +void +gsk_sl_type_unref (GskSlType *type) +{ + if (type == NULL) + return; + + type->ref_count -= 1; + if (type->ref_count > 0) + return; + + g_assert_not_reached (); +} + +void +gsk_sl_type_print (const GskSlType *type, + GString *string) +{ + switch (type->builtin) + { + case GSK_SL_VOID: + g_string_append (string, "void"); + break; + case GSK_SL_FLOAT: + g_string_append (string, "float"); + break; + case GSK_SL_DOUBLE: + g_string_append (string, "double"); + break; + case GSK_SL_INT: + g_string_append (string, "int"); + break; + case GSK_SL_UINT: + g_string_append (string, "uint"); + break; + case GSK_SL_BOOL: + g_string_append (string, "bool"); + break; + case GSK_SL_VEC2: + g_string_append (string, "vec2"); + break; + case GSK_SL_VEC3: + g_string_append (string, "vec3"); + break; + case GSK_SL_VEC4: + g_string_append (string, "vec4"); + break; + /* add more above */ + case GSK_SL_N_BUILTIN_TYPES: + default: + g_assert_not_reached (); + break; + } +} diff --git a/gsk/gsksltypeprivate.h b/gsk/gsksltypeprivate.h new file mode 100644 index 0000000000..25f10c528f --- /dev/null +++ b/gsk/gsksltypeprivate.h @@ -0,0 +1,55 @@ +/* GTK - The GIMP Toolkit + * + * Copyright © 2017 Benjamin Otte + * + * 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 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, see . + */ + +#ifndef __GSK_SL_TYPE_PRIVATE_H__ +#define __GSK_SL_TYPE_PRIVATE_H__ + +#include + +#include "gskslpreprocessorprivate.h" + +G_BEGIN_DECLS + +typedef enum { + GSK_SL_VOID, + GSK_SL_FLOAT, + GSK_SL_DOUBLE, + GSK_SL_INT, + GSK_SL_UINT, + GSK_SL_BOOL, + GSK_SL_VEC2, + GSK_SL_VEC3, + GSK_SL_VEC4, + /* add more above */ + GSK_SL_N_BUILTIN_TYPES +} GskSlBuiltinType; + +typedef struct _GskSlType GskSlType; + +GskSlType * gsk_sl_type_new_parse (GskSlPreprocessor *stream); +GskSlType * gsk_sl_type_get_builtin (GskSlBuiltinType builtin); + +GskSlType * gsk_sl_type_ref (GskSlType *type); +void gsk_sl_type_unref (GskSlType *type); + +void gsk_sl_type_print (const GskSlType *type, + GString *string); + +G_END_DECLS + +#endif /* __GSK_SL_TYPE_PRIVATE_H__ */ diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h index 32fab4cf49..ff958ab5c3 100644 --- a/gsk/gsktypes.h +++ b/gsk/gsktypes.h @@ -26,7 +26,18 @@ #include #include +typedef struct _GskCodeLocation GskCodeLocation; +typedef struct _GskPixelShader GskPixelShader; typedef struct _GskRenderer GskRenderer; typedef struct _GskTexture GskTexture; +struct _GskCodeLocation +{ + gsize bytes; + gsize chars; + gsize lines; + gsize line_bytes; + gsize line_chars; +}; + #endif /* __GSK_TYPES_H__ */ diff --git a/gsk/meson.build b/gsk/meson.build index a86e1566b6..d9ecbefb1c 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -14,6 +14,7 @@ gsk_private_source_shaders = [ ] gsk_public_sources = files([ + 'gskpixelshader.c', 'gskrenderer.c', 'gskrendernode.c', 'gskrendernodeimpl.c', @@ -31,10 +32,15 @@ gsk_private_sources = files([ 'gskprivate.c', 'gskprofiler.c', 'gskshaderbuilder.c', + 'gskslnode.c', + 'gskslpreprocessor.c', + 'gsksltokenizer.c', + 'gsksltype.c' ]) gsk_public_headers = files([ 'gskenums.h', + 'gskpixelshader.h', 'gskrenderer.h', 'gskrendernode.h', 'gskroundedrect.h', diff --git a/gtk/inspector/init.c b/gtk/inspector/init.c index f3d5738e6d..b509906d78 100644 --- a/gtk/inspector/init.c +++ b/gtk/inspector/init.c @@ -60,6 +60,8 @@ gtk_inspector_init (void) g_type_ensure (G_TYPE_LIST_STORE); + g_type_ensure (GSK_TYPE_PIXEL_SHADER); + g_type_ensure (GTK_TYPE_CELL_RENDERER_GRAPH); g_type_ensure (GTK_TYPE_GRAPH_DATA); g_type_ensure (GTK_TYPE_INSPECTOR_ACTIONS); diff --git a/testsuite/gsksl/meson.build b/testsuite/gsksl/meson.build new file mode 100644 index 0000000000..6c206c4fa6 --- /dev/null +++ b/testsuite/gsksl/meson.build @@ -0,0 +1,3 @@ + +test_parser = executable('test-parser', 'test-parser.c', dependencies: libgtk_dep) +test('gsksl/parser', test_parser) diff --git a/testsuite/gsksl/test-parser.c b/testsuite/gsksl/test-parser.c new file mode 100644 index 0000000000..d35c063249 --- /dev/null +++ b/testsuite/gsksl/test-parser.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2017 Red Hat Inc. + * + * Author: + * Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +#include "config.h" + +#include +#include +#include +#include + +#ifdef G_OS_WIN32 +# include +#endif + +static char * +test_get_reference_file (const char *glsl_file) +{ + GString *file = g_string_new (NULL); + + if (g_str_has_suffix (glsl_file, ".glsl")) + g_string_append_len (file, glsl_file, strlen (glsl_file) - 4); + else + g_string_append (file, glsl_file); + + g_string_append (file, ".ref.glsl"); + + if (!g_file_test (file->str, G_FILE_TEST_EXISTS)) + { + g_string_free (file, TRUE); + return g_strdup (glsl_file); + } + + return g_string_free (file, FALSE); +} + +static char * +test_get_errors_file (const char *glsl_file) +{ + GString *file = g_string_new (NULL); + + if (g_str_has_suffix (glsl_file, ".glsl")) + g_string_append_len (file, glsl_file, strlen (glsl_file) - 4); + else + g_string_append (file, glsl_file); + + g_string_append (file, ".errors"); + + if (!g_file_test (file->str, G_FILE_TEST_EXISTS)) + { + g_string_free (file, TRUE); + return NULL; + } + + return g_string_free (file, FALSE); +} + +static char * +diff_with_file (const char *file1, + char *text, + gssize len, + GError **error) +{ + const char *command[] = { "diff", "-u", file1, NULL, NULL }; + char *diff, *tmpfile; + int fd; + + diff = NULL; + + if (len < 0) + len = strlen (text); + + /* write the text buffer to a temporary file */ + fd = g_file_open_tmp (NULL, &tmpfile, error); + if (fd < 0) + return NULL; + + if (write (fd, text, len) != (int) len) + { + close (fd); + g_set_error (error, + G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Could not write data to temporary file '%s'", tmpfile); + goto done; + } + close (fd); + command[3] = tmpfile; + + /* run diff command */ + g_spawn_sync (NULL, + (char **) command, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, + &diff, + NULL, NULL, + error); + +done: + g_unlink (tmpfile); + g_free (tmpfile); + + return diff; +} + +#if 0 +static void +append_error_value (GString *string, + GType enum_type, + guint value) +{ + GEnumClass *enum_class; + GEnumValue *enum_value; + + enum_class = g_type_class_ref (enum_type); + enum_value = g_enum_get_value (enum_class, value); + + g_string_append (string, enum_value->value_name); + + g_type_class_unref (enum_class); +} +#endif + +static void +parsing_error_cb (GskPixelShader *shader, + gboolean fatal, + const GskCodeLocation *location, + const GError *error, + gpointer user_data) +{ + GString *errors = user_data; + + if (fatal) + g_test_fail (); + + g_string_append_printf (errors, + "%zu:%zu: %s: ", + location->lines, location->line_chars, + fatal ? "ERROR" : "warning"); + + g_string_append_printf (errors, + "%s\n", + error->message); +} + +static void +parse_glsl_file (GFile *file, gboolean generate) +{ + GskPixelShader *shader; + GBytes *bytes; + char *glsl, *diff; + char *glsl_file, *reference_file, *errors_file; + gsize length; + GString *errors; + GError *error = NULL; + + glsl_file = g_file_get_path (file); + errors = g_string_new (""); + + g_file_load_contents (file, NULL, + &glsl, &length, + NULL, &error); + g_assert_no_error (error); + bytes = g_bytes_new_take (glsl, length); + + shader = gsk_pixel_shader_new_for_data (bytes, + parsing_error_cb, + errors); + + glsl = gsk_pixel_shader_to_string (shader); + + if (generate) + { + g_print ("%s", glsl); + goto out; + } + + reference_file = test_get_reference_file (glsl_file); + + diff = diff_with_file (reference_file, glsl, -1, &error); + g_assert_no_error (error); + + if (diff && diff[0]) + { + g_test_message ("Resulting CSS doesn't match reference:\n%s", diff); + g_test_fail (); + } + g_free (reference_file); + + errors_file = test_get_errors_file (glsl_file); + + if (errors_file) + { + diff = diff_with_file (errors_file, errors->str, errors->len, &error); + g_assert_no_error (error); + + if (diff && diff[0]) + { + g_test_message ("Errors don't match expected errors:\n%s", diff); + g_test_fail (); + } + } + else if (errors->str[0]) + { + g_test_message ("Unexpected errors:\n%s", errors->str); + g_test_fail (); + } + + g_object_unref (shader); + g_free (errors_file); + g_string_free (errors, TRUE); + + g_free (diff); + +out: + g_free (glsl_file); + g_free (glsl); +} + +static void +test_glsl_file (GFile *file) +{ + parse_glsl_file (file, FALSE); +} + +static void +add_test_for_file (GFile *file) +{ + char *path; + + path = g_file_get_path (file); + + g_test_add_vtable (path, + 0, + g_object_ref (file), + NULL, + (GTestFixtureFunc) test_glsl_file, + (GTestFixtureFunc) g_object_unref); + + g_free (path); +} + +static int +compare_files (gconstpointer a, gconstpointer b) +{ + GFile *file1 = G_FILE (a); + GFile *file2 = G_FILE (b); + char *path1, *path2; + int result; + + path1 = g_file_get_path (file1); + path2 = g_file_get_path (file2); + + result = strcmp (path1, path2); + + g_free (path1); + g_free (path2); + + return result; +} + +static void +add_tests_for_files_in_directory (GFile *dir) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + GList *files; + GError *error = NULL; + + enumerator = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error); + g_assert_no_error (error); + files = NULL; + + while ((info = g_file_enumerator_next_file (enumerator, NULL, &error))) + { + const char *filename; + + filename = g_file_info_get_name (info); + + if (!g_str_has_suffix (filename, ".glsl") || + g_str_has_suffix (filename, ".out.glsl") || + g_str_has_suffix (filename, ".ref.glsl")) + { + g_object_unref (info); + continue; + } + + files = g_list_prepend (files, g_file_get_child (dir, filename)); + + g_object_unref (info); + } + + g_assert_no_error (error); + g_object_unref (enumerator); + + files = g_list_sort (files, compare_files); + g_list_foreach (files, (GFunc) add_test_for_file, NULL); + g_list_free_full (files, g_object_unref); +} + +int +main (int argc, char **argv) +{ + gtk_test_init (&argc, &argv); + + if (argc < 2) + { + const char *basedir; + GFile *dir; + + basedir = g_test_get_dir (G_TEST_DIST); + dir = g_file_new_for_path (basedir); + add_tests_for_files_in_directory (dir); + + g_object_unref (dir); + } + else if (strcmp (argv[1], "--generate") == 0) + { + if (argc >= 3) + { + GFile *file = g_file_new_for_commandline_arg (argv[2]); + + parse_glsl_file (file, TRUE); + + g_object_unref (file); + } + } + else + { + guint i; + + for (i = 1; i < argc; i++) + { + GFile *file = g_file_new_for_commandline_arg (argv[i]); + + add_test_for_file (file); + + g_object_unref (file); + } + } + + return g_test_run (); +} + diff --git a/testsuite/meson.build b/testsuite/meson.build index f966e769da..c6375502ab 100644 --- a/testsuite/meson.build +++ b/testsuite/meson.build @@ -4,6 +4,7 @@ installed_test_datadir = join_paths(gtk_datadir, 'installed-tests', 'gtk-4.0') subdir('gdk') subdir('gsk') +subdir('gsksl') subdir('gtk') subdir('css') subdir('a11y')