[gtk+/wip/otte/shader: 1/156] gsk: Add a skeleton for a GLSL parser
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/otte/shader: 1/156] gsk: Add a skeleton for a GLSL parser
- Date: Sun, 22 Oct 2017 03:16:07 +0000 (UTC)
commit f9a25c0b629e5abcf17b8b26df2a723079ddff23
Author: Benjamin Otte <otte redhat com>
Date: Thu Sep 14 04:16:40 2017 +0200
gsk: Add a skeleton for a GLSL parser
docs/reference/gsk/meson.build | 1 +
gsk/gsk.h | 1 +
gsk/gskpixelshader.c | 184 ++++
gsk/gskpixelshader.h | 62 ++
gsk/gskpixelshaderprivate.h | 29 +
gsk/gskslnode.c | 285 +++++++
gsk/gskslnodeprivate.h | 52 ++
gsk/gskslpreprocessor.c | 162 ++++
gsk/gskslpreprocessorprivate.h | 47 +
gsk/gsksltokenizer.c | 1838 ++++++++++++++++++++++++++++++++++++++++
gsk/gsksltokenizerprivate.h | 293 +++++++
gsk/gsksltype.c | 161 ++++
gsk/gsksltypeprivate.h | 55 ++
gsk/gsktypes.h | 11 +
gsk/meson.build | 6 +
gtk/inspector/init.c | 2 +
testsuite/gsksl/meson.build | 3 +
testsuite/gsksl/test-parser.c | 360 ++++++++
testsuite/meson.build | 1 +
19 files changed, 3553 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/gsk/meson.build b/docs/reference/gsk/meson.build
index 725a754..668d13e 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 e330600..7b2743c 100644
--- a/gsk/gsk.h
+++ b/gsk/gsk.h
@@ -21,6 +21,7 @@
#define __GSK_H_INSIDE__
#include <gsk/gskenums.h>
+#include <gsk/gskpixelshader.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>
#include <gsk/gskroundedrect.h>
diff --git a/gsk/gskpixelshader.c b/gsk/gskpixelshader.c
new file mode 100644
index 0000000..cec22c0
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * 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 0000000..1506d79
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_PIXEL_SHADER_H__
+#define __GSK_PIXEL_SHADER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+
+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 0000000..a10ba06
--- /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 0000000..2ecc6c4
--- /dev/null
+++ b/gsk/gskslnode.c
@@ -0,0 +1,285 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gskslnodeprivate.h"
+
+#include "gskslpreprocessorprivate.h"
+#include "gsksltypeprivate.h"
+
+#include <string.h>
+
+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 0000000..776f59c
--- /dev/null
+++ b/gsk/gskslnodeprivate.h
@@ -0,0 +1,52 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_SL_NODE_PRIVATE_H__
+#define __GSK_SL_NODE_PRIVATE_H__
+
+#include <glib.h>
+
+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 0000000..abd07a3
--- /dev/null
+++ b/gsk/gskslpreprocessor.c
@@ -0,0 +1,162 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 0000000..58e5888
--- /dev/null
+++ b/gsk/gskslpreprocessorprivate.h
@@ -0,0 +1,47 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_SL_PREPROCESSOR_PRIVATE_H__
+#define __GSK_SL_PREPROCESSOR_PRIVATE_H__
+
+#include <glib.h>
+
+#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 0000000..3f23881
--- /dev/null
+++ b/gsk/gsksltokenizer.c
@@ -0,0 +1,1838 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gsksltokenizerprivate.h"
+
+#include "gskpixelshader.h"
+
+#include <math.h>
+#include <string.h>
+
+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 0000000..ea769fc
--- /dev/null
+++ b/gsk/gsksltokenizerprivate.h
@@ -0,0 +1,293 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2011 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_SL_TOKENIZER_PRIVATE_H__
+#define __GSK_SL_TOKENIZER_PRIVATE_H__
+
+#include <gsktypes.h>
+
+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 0000000..8958a41
--- /dev/null
+++ b/gsk/gsksltype.c
@@ -0,0 +1,161 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gsksltypeprivate.h"
+
+#include <string.h>
+
+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 0000000..25f10c5
--- /dev/null
+++ b/gsk/gsksltypeprivate.h
@@ -0,0 +1,55 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright © 2017 Benjamin Otte <otte gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_SL_TYPE_PRIVATE_H__
+#define __GSK_SL_TYPE_PRIVATE_H__
+
+#include <glib.h>
+
+#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 32fab4c..ff958ab 100644
--- a/gsk/gsktypes.h
+++ b/gsk/gsktypes.h
@@ -26,7 +26,18 @@
#include <gdk/gdk.h>
#include <gsk/gskenums.h>
+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 a86e156..d9ecbef 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 f3d5738..b509906 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 0000000..6c206c4
--- /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 0000000..d35c063
--- /dev/null
+++ b/testsuite/gsksl/test-parser.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 Red Hat Inc.
+ *
+ * Author:
+ * Benjamin Otte <otte redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gstdio.h>
+#include <gsk/gsk.h>
+#include <gtk/gtk.h>
+
+#ifdef G_OS_WIN32
+# include <io.h>
+#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 fa283dd..25bb498 100644
--- a/testsuite/meson.build
+++ b/testsuite/meson.build
@@ -13,6 +13,7 @@ installed_test_env = [
subdir('gdk')
subdir('gsk')
+subdir('gsksl')
subdir('gtk')
subdir('css')
subdir('a11y')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]