[gtk+/wip/otte/shader: 93/175] gskslcompiler: Add support for adding defines



commit af9aaf631b9f74cd676f70f636eb0b5c82dbdf28
Author: Benjamin Otte <otte redhat com>
Date:   Sun Sep 24 03:34:31 2017 +0200

    gskslcompiler: Add support for adding defines
    
    gtk-glsl implements this via the usual -D and -U options.
    
    Preprocessor directives aren't implemented yet, but defines are replaced
    in the source code already.

 gsk/gskslcompiler.c         |  130 ++++++++++++++++++++-
 gsk/gskslcompiler.h         |    9 ++
 gsk/gskslcompilerprivate.h  |   30 +++++
 gsk/gsksldefine.c           |  132 ++++++++++++++++++++
 gsk/gsksldefineprivate.h    |   51 ++++++++
 gsk/gskslpreprocessor.c     |  143 +++++++++++++++-------
 gsk/gsksltokenizer.c        |  277 +++++++++++++++++++++++++++++++++++++++++++
 gsk/gsksltokenizerprivate.h |    5 +-
 gsk/meson.build             |    1 +
 gtk/glsl.c                  |   44 +++++++-
 10 files changed, 773 insertions(+), 49 deletions(-)
---
diff --git a/gsk/gskslcompiler.c b/gsk/gskslcompiler.c
index 66ad1da..a97f277 100644
--- a/gsk/gskslcompiler.c
+++ b/gsk/gskslcompiler.c
@@ -18,13 +18,17 @@
 
 #include "config.h"
 
-#include "gskslcompiler.h"
+#include "gskslcompilerprivate.h"
 
+#include "gsksldefineprivate.h"
 #include "gskslpreprocessorprivate.h"
 #include "gskslprogramprivate.h"
+#include "gsksltokenizerprivate.h"
 
 struct _GskSlCompiler {
   GObject parent_instance;
+
+  GHashTable *defines;
 };
 
 G_DEFINE_TYPE (GskSlCompiler, gsk_sl_compiler, G_TYPE_OBJECT)
@@ -32,7 +36,9 @@ G_DEFINE_TYPE (GskSlCompiler, gsk_sl_compiler, G_TYPE_OBJECT)
 static void
 gsk_sl_compiler_dispose (GObject *object)
 {
-  //GskSlCompiler *compiler = GSK_SL_COMPILER (object);
+  GskSlCompiler *compiler = GSK_SL_COMPILER (object);
+
+  g_hash_table_destroy (compiler->defines);
 
   G_OBJECT_CLASS (gsk_sl_compiler_parent_class)->dispose (object);
 }
@@ -48,6 +54,8 @@ gsk_sl_compiler_class_init (GskSlCompilerClass *klass)
 static void
 gsk_sl_compiler_init (GskSlCompiler *compiler)
 {
+  compiler->defines = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                             NULL, (GDestroyNotify) gsk_sl_define_unref);
 }
 
 GskSlCompiler *
@@ -56,6 +64,124 @@ gsk_sl_compiler_new (void)
   return g_object_new (GSK_TYPE_SL_COMPILER, NULL);
 }
 
+static void
+gsk_sl_compiler_add_define_error_func (GskSlTokenizer        *parser,
+                                       gboolean               fatal,
+                                       const GskCodeLocation *location,
+                                       const GskSlToken      *token,
+                                       const GError          *error,
+                                       gpointer               user_data)
+{
+  GError **real_error = user_data;
+
+  if (!fatal)
+    return;
+
+  if (*real_error)
+    return;
+
+  *real_error = g_error_copy (error);
+  g_prefix_error (real_error,
+                  "%3zu:%2zu: ",
+                  location->lines + 1,
+                  location->line_bytes);
+}
+
+gboolean
+gsk_sl_compiler_add_define (GskSlCompiler  *compiler,
+                            const char     *name,
+                            const char     *definition,
+                            GError        **error)
+{
+  GskSlTokenizer *tokenizer;
+  GskSlDefine *define;
+  GskCodeLocation location;
+  GskSlToken token = { 0, };
+  GError *real_error = NULL;
+  GBytes *bytes;
+
+  g_return_val_if_fail (GSK_IS_SL_COMPILER (compiler), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (!gsk_sl_string_is_valid_identifier (name))
+    {
+      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "Define name \"%s\" is not a valid identifier", 
name);
+      return FALSE;
+    }
+
+  if (definition == NULL)
+    definition = "1";
+
+  define = gsk_sl_define_new (name, NULL);
+  bytes = g_bytes_new_static (definition, strlen (definition));
+  tokenizer = gsk_sl_tokenizer_new (bytes,
+                                    gsk_sl_compiler_add_define_error_func,
+                                    &real_error,
+                                    NULL);
+
+  while (TRUE)
+    {
+      do
+        {
+          gsk_sl_token_clear (&token);
+          location = *gsk_sl_tokenizer_get_location (tokenizer);
+          gsk_sl_tokenizer_read_token (tokenizer, &token);
+        }
+      while (gsk_sl_token_is_skipped (&token));
+
+      if (gsk_sl_token_is (&token, GSK_SL_TOKEN_EOF))
+        break;
+
+      gsk_sl_define_add_token (define, &location, &token);
+    }
+
+  gsk_sl_token_clear (&token);
+  gsk_sl_tokenizer_unref (tokenizer);
+  g_bytes_unref (bytes);
+
+  if (real_error == NULL)
+    {
+      g_hash_table_replace (compiler->defines, (gpointer) gsk_sl_define_get_name (define), define);
+      return TRUE;
+    }
+  else
+    {
+      gsk_sl_define_unref (define);
+      g_propagate_error (error, real_error);
+      return FALSE;
+    }
+}
+
+void
+gsk_sl_compiler_remove_define (GskSlCompiler *compiler,
+                               const char    *name)
+{
+  g_return_if_fail (GSK_IS_SL_COMPILER (compiler));
+  g_return_if_fail (name != NULL);
+
+  g_hash_table_remove (compiler->defines, name);
+}
+
+GHashTable *
+gsk_sl_compiler_copy_defines (GskSlCompiler *compiler)
+{
+  GHashTable *copy;
+  GHashTableIter iter;
+  gpointer key, value;
+
+  copy = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                NULL, (GDestroyNotify) gsk_sl_define_unref);
+
+  g_hash_table_iter_init (&iter, compiler->defines);
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      g_hash_table_replace (copy, key, gsk_sl_define_ref (value));
+    }
+
+  return copy;
+}
+
 GskSlProgram *
 gsk_sl_compiler_compile (GskSlCompiler *compiler,
                          GBytes        *source)
diff --git a/gsk/gskslcompiler.h b/gsk/gskslcompiler.h
index 910897d..94c2763 100644
--- a/gsk/gskslcompiler.h
+++ b/gsk/gskslcompiler.h
@@ -35,6 +35,15 @@ GDK_AVAILABLE_IN_3_92
 GskSlCompiler *         gsk_sl_compiler_new                     (void);
 
 GDK_AVAILABLE_IN_3_92
+gboolean                gsk_sl_compiler_add_define              (GskSlCompiler       *compiler,
+                                                                 const char          *name,
+                                                                 const char          *definition,
+                                                                 GError             **error);
+GDK_AVAILABLE_IN_3_92
+void                    gsk_sl_compiler_remove_define           (GskSlCompiler       *compiler,
+                                                                 const char          *name);
+
+GDK_AVAILABLE_IN_3_92
 GskSlProgram *          gsk_sl_compiler_compile                 (GskSlCompiler       *compiler,
                                                                  GBytes              *source);
 
diff --git a/gsk/gskslcompilerprivate.h b/gsk/gskslcompilerprivate.h
new file mode 100644
index 0000000..184502f
--- /dev/null
+++ b/gsk/gskslcompilerprivate.h
@@ -0,0 +1,30 @@
+/* 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_COMPILER_PRIVATE_H__
+#define __GSK_SL_COMPILER_PRIVATE_H__
+
+#include "gsk/gskslcompiler.h"
+
+G_BEGIN_DECLS
+
+GHashTable *            gsk_sl_compiler_copy_defines            (GskSlCompiler          *compiler);
+
+G_END_DECLS
+
+#endif /* __GSK_SL_COMPILER_PRIVATE_H__ */
diff --git a/gsk/gsksldefine.c b/gsk/gsksldefine.c
new file mode 100644
index 0000000..c3ad56b
--- /dev/null
+++ b/gsk/gsksldefine.c
@@ -0,0 +1,132 @@
+/* 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 "gsksldefineprivate.h"
+
+typedef struct _GskSlDefineToken GskSlDefineToken;
+
+struct _GskSlDefineToken {
+  GskCodeLocation location;
+  GskSlToken token;
+};
+
+struct _GskSlDefine {
+  int ref_count;
+
+  char *name;
+  GFile *source_file;
+
+  GArray *tokens;
+};
+
+GskSlDefine *
+gsk_sl_define_new (const char *name,
+                   GFile      *source_file)
+{
+  GskSlDefine *result;
+
+  result = g_slice_new0 (GskSlDefine);
+
+  result->ref_count = 1;
+  result->name = g_strdup (name);
+  if (source_file)
+    result->source_file = g_object_ref (source_file);
+  result->tokens = g_array_new (FALSE, FALSE, sizeof (GskSlDefineToken));
+
+  return result;
+}
+
+GskSlDefine *
+gsk_sl_define_ref (GskSlDefine *define)
+{
+  g_return_val_if_fail (define != NULL, NULL);
+
+  define->ref_count += 1;
+
+  return define;
+}
+
+void
+gsk_sl_define_unref (GskSlDefine *define)
+{
+  if (define == NULL)
+    return;
+
+  define->ref_count -= 1;
+  if (define->ref_count > 0)
+    return;
+
+  g_array_free (define->tokens, TRUE);
+
+  if (define->source_file)
+    g_object_unref (define->source_file);
+  g_free (define->name);
+
+  g_slice_free (GskSlDefine, define);
+}
+
+const char *
+gsk_sl_define_get_name (GskSlDefine *define)
+{
+  return define->name;
+}
+
+GFile *
+gsk_sl_define_get_source_file (GskSlDefine *define)
+{
+  return define->source_file;
+}
+
+guint
+gsk_sl_define_get_n_tokens (GskSlDefine *define)
+{
+  return define->tokens->len;
+}
+
+void
+gsk_sl_define_get_token (GskSlDefine     *define,
+                         guint            i,
+                         GskCodeLocation *location,
+                         GskSlToken      *token)
+{
+  GskSlDefineToken *dt;
+  
+  dt = &g_array_index (define->tokens, GskSlDefineToken, i);
+
+  if (location)
+    *location = dt->location;
+  if (token)
+    gsk_sl_token_copy (token, &dt->token);
+}
+
+void
+gsk_sl_define_add_token (GskSlDefine           *define,
+                         const GskCodeLocation *location,
+                         const GskSlToken      *token)
+{
+  GskSlDefineToken dt;
+
+  dt.location = *location;
+  gsk_sl_token_copy (&dt.token, token);
+
+  g_array_append_val (define->tokens, dt);
+}
+
+
diff --git a/gsk/gsksldefineprivate.h b/gsk/gsksldefineprivate.h
new file mode 100644
index 0000000..92ac0db
--- /dev/null
+++ b/gsk/gsksldefineprivate.h
@@ -0,0 +1,51 @@
+/* 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_DEFINE_PRIVATE_H__
+#define __GSK_SL_DEFINE_PRIVATE_H__
+
+#include <glib.h>
+
+#include "gsksltokenizerprivate.h"
+#include "gsksltypesprivate.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GskSlDefine GskSlDefine;
+
+GskSlDefine *           gsk_sl_define_new                         (const char            *name,
+                                                                   GFile                 *source_file);
+
+GskSlDefine *           gsk_sl_define_ref                         (GskSlDefine           *define);
+void                    gsk_sl_define_unref                       (GskSlDefine           *define);
+
+const char *            gsk_sl_define_get_name                    (GskSlDefine           *define);
+GFile *                 gsk_sl_define_get_source_file             (GskSlDefine           *define);
+guint                   gsk_sl_define_get_n_tokens                (GskSlDefine           *define);
+void                    gsk_sl_define_get_token                   (GskSlDefine           *define,
+                                                                   guint                  i,
+                                                                   GskCodeLocation       *location,
+                                                                   GskSlToken            *token);
+
+void                    gsk_sl_define_add_token                   (GskSlDefine           *define,
+                                                                   const GskCodeLocation *location,
+                                                                   const GskSlToken      *token);
+
+G_END_DECLS
+
+#endif /* __GSK_SL_DEFINE_PRIVATE_H__ */
diff --git a/gsk/gskslpreprocessor.c b/gsk/gskslpreprocessor.c
index eec8d0b..efa154f 100644
--- a/gsk/gskslpreprocessor.c
+++ b/gsk/gskslpreprocessor.c
@@ -20,16 +20,25 @@
 
 #include "gskslpreprocessorprivate.h"
 
+#include "gskslcompilerprivate.h"
+#include "gsksldefineprivate.h"
 #include "gsksltokenizerprivate.h"
 
+typedef struct _GskSlPpToken GskSlPpToken;
+
+struct _GskSlPpToken {
+  GskCodeLocation location;
+  GskSlToken token;
+};
+
 struct _GskSlPreprocessor
 {
   int ref_count;
 
   GskSlCompiler *compiler;
   GskSlTokenizer *tokenizer;
-  GskCodeLocation location;
-  GskSlToken token;
+  GArray *tokens;
+  GHashTable *defines;
 };
 
 /* API */
@@ -48,6 +57,14 @@ gsk_sl_preprocessor_error_func (GskSlTokenizer        *parser,
               error->message);
 }
 
+static void
+gsk_sl_preprocessor_clear_token (gpointer data)
+{
+  GskSlPpToken *pp = data;
+
+  gsk_sl_token_clear (&pp->token);
+}
+
 GskSlPreprocessor *
 gsk_sl_preprocessor_new (GskSlCompiler *compiler,
                          GBytes        *source)
@@ -62,6 +79,9 @@ gsk_sl_preprocessor_new (GskSlCompiler *compiler,
                                             gsk_sl_preprocessor_error_func,
                                             preproc,
                                             NULL);
+  preproc->tokens = g_array_new (FALSE, FALSE, sizeof (GskSlPpToken));
+  g_array_set_clear_func (preproc->tokens, gsk_sl_preprocessor_clear_token);
+  preproc->defines = gsk_sl_compiler_copy_defines (compiler);
 
   return preproc;
 }
@@ -86,35 +106,29 @@ gsk_sl_preprocessor_unref (GskSlPreprocessor *preproc)
   if (preproc->ref_count > 0)
     return;
 
+  g_hash_table_destroy (preproc->defines);
   gsk_sl_tokenizer_unref (preproc->tokenizer);
-  gsk_sl_token_clear (&preproc->token);
   g_object_unref (preproc->compiler);
+  g_array_free (preproc->tokens, TRUE);
 
   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_NEWLINE)
-      || 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 gboolean
-gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc)
+gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc,
+                                GskSlPpToken      *pp)
 {
   gboolean was_newline;
   
+  pp->token = (GskSlToken) { 0, };
+
   do 
     {
-      preproc->location = *gsk_sl_tokenizer_get_location (preproc->tokenizer);
-      was_newline = gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_NEWLINE);
-      gsk_sl_tokenizer_read_token (preproc->tokenizer, &preproc->token);
+      pp->location = *gsk_sl_tokenizer_get_location (preproc->tokenizer);
+      was_newline = gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_NEWLINE);
+      gsk_sl_tokenizer_read_token (preproc->tokenizer, &pp->token);
     }
-  while (gsk_sl_token_is_skipped (&preproc->token));
+  while (gsk_sl_token_is_skipped (&pp->token));
 
   return was_newline;
 }
@@ -122,17 +136,23 @@ gsk_sl_preprocessor_next_token (GskSlPreprocessor *preproc)
 static void
 gsk_sl_preprocessor_handle_preprocessor_directive (GskSlPreprocessor *preproc)
 {
-  gboolean was_newline = gsk_sl_preprocessor_next_token (preproc);
+  GskSlPpToken pp;
+
+  gboolean was_newline = gsk_sl_preprocessor_next_token (preproc, &pp);
 
   /* empty # line */
   if (was_newline)
-    return;
+    {
+      gsk_sl_preprocessor_clear_token (&pp);
+      return;
+    }
 
-  if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_IDENTIFIER))
+  if (gsk_sl_token_is (&pp.token, GSK_SL_TOKEN_IDENTIFIER))
     {
-      if (g_str_equal (preproc->token.str, "define"))
+      if (g_str_equal (pp.token.str, "define"))
         {
           gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #define.");
+          gsk_sl_preprocessor_clear_token (&pp);
         }
 #if 0
       else if (g_str_equal (preproc->token.str, "else"))
@@ -165,49 +185,89 @@ gsk_sl_preprocessor_handle_preprocessor_directive (GskSlPreprocessor *preproc)
       else if (g_str_equal (preproc->token.str, "pragma"))
         {
         }
+      else if (g_str_equal (preproc->token.str, "undef"))
+        {
+        }
       else if (g_str_equal (preproc->token.str, "version"))
         {
         }
 #endif
       else
         {
-          gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #%s.", preproc->token.str);
+          gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #%s.", pp.token.str);
+          gsk_sl_preprocessor_clear_token (&pp);
         }
     }
-  else if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_ELSE))
-    {
-      gsk_sl_preprocessor_error (preproc, "Unknown preprocessor directive #else.");
-    }
   else
     {
       gsk_sl_preprocessor_error (preproc, "Missing identifier for preprocessor directive.");
+      gsk_sl_preprocessor_clear_token (&pp);
+    }
+  
+  while (!gsk_sl_preprocessor_next_token (preproc, &pp))
+    gsk_sl_preprocessor_clear_token (&pp);
+}
+
+static void
+gsk_sl_preprocessor_append_token (GskSlPreprocessor *preproc,
+                                  GskSlPpToken      *pp,
+                                  GSList            *used_defines)
+{
+  if (gsk_sl_token_is (&pp->token, GSK_SL_TOKEN_IDENTIFIER))
+    {
+      GskSlDefine *define;
+      char *ident = pp->token.str;
+
+      define = g_hash_table_lookup (preproc->defines, ident);
+      if (define &&
+          !g_slist_find (used_defines, define))
+        {
+          GSList new_defines = { define, used_defines };
+          GskSlPpToken dpp;
+          guint i;
+
+          for (i = 0; i < gsk_sl_define_get_n_tokens (define); i++)
+            {
+              gsk_sl_define_get_token (define, i, &dpp.location, &dpp.token);
+              gsk_sl_preprocessor_append_token (preproc, &dpp, &new_defines);
+            }
+
+          gsk_sl_preprocessor_clear_token (pp);
+          return;
+        }
+
+      gsk_sl_token_init_from_identifier (&pp->token, ident);
+      g_free (ident);
     }
 
-  while (!gsk_sl_preprocessor_next_token (preproc));
+  g_array_append_val (preproc->tokens, *pp);
 }
 
 static void
 gsk_sl_preprocessor_ensure (GskSlPreprocessor *preproc)
 {
+  GskSlPpToken pp;
   gboolean was_newline = FALSE;
 
-  if (!gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_EOF))
+  if (preproc->tokens->len > 0)
     return;
 
-  was_newline = gsk_sl_preprocessor_next_token (preproc);
+  was_newline = gsk_sl_preprocessor_next_token (preproc, &pp);
 
   while (TRUE)
     {
-      if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_HASH))
+      if (gsk_sl_token_is (&pp.token, GSK_SL_TOKEN_HASH))
         {
           if (!was_newline &&
-              preproc->location.bytes != 0)
+              pp.location.bytes != 0)
             {
               gsk_sl_preprocessor_error (preproc, "Unexpected \"#\" - preprocessor directives must be at 
start of line.");
-              was_newline = gsk_sl_preprocessor_next_token (preproc);
+              gsk_sl_preprocessor_clear_token (&pp);
+              was_newline = gsk_sl_preprocessor_next_token (preproc, &pp);
             }
           else
             {
+              gsk_sl_preprocessor_clear_token (&pp);
               gsk_sl_preprocessor_handle_preprocessor_directive (preproc);
               was_newline = TRUE;
             }
@@ -218,12 +278,7 @@ gsk_sl_preprocessor_ensure (GskSlPreprocessor *preproc)
         }
     }
 
-  if (gsk_sl_token_is (&preproc->token, GSK_SL_TOKEN_IDENTIFIER))
-    {
-      char *ident = preproc->token.str;
-      gsk_sl_token_init_from_identifier (&preproc->token, ident);
-      g_free (ident);
-    }
+  gsk_sl_preprocessor_append_token (preproc, &pp, NULL);
 }
 
 const GskSlToken *
@@ -231,7 +286,7 @@ gsk_sl_preprocessor_get (GskSlPreprocessor *preproc)
 {
   gsk_sl_preprocessor_ensure (preproc);
 
-  return &preproc->token;
+  return &g_array_index (preproc->tokens, GskSlPpToken, 0).token;
 }
 
 const GskCodeLocation *
@@ -239,7 +294,7 @@ gsk_sl_preprocessor_get_location (GskSlPreprocessor *preproc)
 {
   gsk_sl_preprocessor_ensure (preproc);
 
-  return &preproc->location;
+  return &g_array_index (preproc->tokens, GskSlPpToken, 0).location;
 }
 
 void
@@ -248,7 +303,7 @@ gsk_sl_preprocessor_consume (GskSlPreprocessor *preproc,
 {
   gsk_sl_preprocessor_ensure (preproc);
 
-  gsk_sl_token_clear (&preproc->token);
+  g_array_remove_index (preproc->tokens, 0);
 }
 
 void
@@ -269,8 +324,8 @@ gsk_sl_preprocessor_error (GskSlPreprocessor *preproc,
   gsk_sl_preprocessor_ensure (preproc);
   gsk_sl_preprocessor_error_func (preproc->tokenizer,
                                   TRUE,
-                                  &preproc->location,
-                                  &preproc->token,
+                                  gsk_sl_preprocessor_get_location (preproc),
+                                  gsk_sl_preprocessor_get (preproc),
                                   error,
                                   NULL);
 
diff --git a/gsk/gsksltokenizer.c b/gsk/gsksltokenizer.c
index 2f6867c..5306a6b 100644
--- a/gsk/gsksltokenizer.c
+++ b/gsk/gsksltokenizer.c
@@ -314,6 +314,256 @@ gsk_sl_token_clear (GskSlToken *token)
   token->type = GSK_SL_TOKEN_EOF;
 }
 
+void
+gsk_sl_token_copy (GskSlToken       *dest,
+                   const GskSlToken *src)
+{
+  dest->type = src->type;
+
+  switch (src->type)
+    {
+    case GSK_SL_TOKEN_IDENTIFIER:
+      dest->str = g_strdup (src->str);
+      break;
+
+    case GSK_SL_TOKEN_BOOLCONSTANT:
+      dest->b = src->b;
+      break;
+
+    case GSK_SL_TOKEN_FLOATCONSTANT:
+      dest->f = src->f;
+      break;
+
+    case GSK_SL_TOKEN_DOUBLECONSTANT:
+      dest->d = src->d;
+      break;
+
+    case GSK_SL_TOKEN_INTCONSTANT:
+      dest->i32 = src->i32;
+      break;
+
+    case GSK_SL_TOKEN_UINTCONSTANT:
+      dest->u32 = src->u32;
+      break;
+
+    case GSK_SL_TOKEN_EOF:
+    case GSK_SL_TOKEN_ERROR:
+    case GSK_SL_TOKEN_NEWLINE:
+    case GSK_SL_TOKEN_WHITESPACE:
+    case GSK_SL_TOKEN_COMMENT:
+    case GSK_SL_TOKEN_SINGLE_LINE_COMMENT:
+    case GSK_SL_TOKEN_CONST:
+    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_FLOAT:
+    case GSK_SL_TOKEN_DOUBLE:
+    case GSK_SL_TOKEN_INT:
+    case GSK_SL_TOKEN_UINT:
+    case GSK_SL_TOKEN_BOOL:
+    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_HASH:
+    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;
+    }
+}
+
 static const char *keywords[] = {
   [GSK_SL_TOKEN_CONST] = "const",
   [GSK_SL_TOKEN_BOOL] = "bool",
@@ -1484,6 +1734,23 @@ gsk_sl_token_reader_read_identifier (GskSlTokenReader  *reader,
   token->str = g_string_free (string, FALSE);
 }
 
+gboolean
+gsk_sl_string_is_valid_identifier (const char *ident)
+{
+  guint i;
+
+  if (!is_identifier_start (ident[0]))
+    return FALSE;
+
+  for (i = 1; ident[i]; i++)
+    {
+      if (!is_identifier (ident[i]))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
 void
 gsk_sl_token_init_from_identifier (GskSlToken *token,
                                    const char *ident)
@@ -1518,6 +1785,16 @@ gsk_sl_token_init_from_identifier (GskSlToken *token,
   token->str = g_strdup (ident);
 }
 
+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_NEWLINE)
+      || 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);
+}
+
 void
 gsk_sl_tokenizer_read_token (GskSlTokenizer *tokenizer,
                              GskSlToken     *token)
diff --git a/gsk/gsksltokenizerprivate.h b/gsk/gsksltokenizerprivate.h
index f39e64f..e968d31 100644
--- a/gsk/gsksltokenizerprivate.h
+++ b/gsk/gsksltokenizerprivate.h
@@ -265,11 +265,14 @@ struct _GskSlToken {
 };
 
 void                    gsk_sl_token_clear                      (GskSlToken            *token);
+void                    gsk_sl_token_copy                       (GskSlToken            *dest,
+                                                                 const GskSlToken      *src);
 
+gboolean                gsk_sl_string_is_valid_identifier       (const char            *ident);
 void                    gsk_sl_token_init_from_identifier       (GskSlToken            *token,
                                                                  const char            *ident);
 
-gboolean                gsk_sl_token_is_finite                  (const GskSlToken      *token);
+gboolean                gsk_sl_token_is_skipped                 (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);
diff --git a/gsk/meson.build b/gsk/meson.build
index 17b82cc..292b67f 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -34,6 +34,7 @@ gsk_private_sources = files([
   'gskprivate.c',
   'gskprofiler.c',
   'gskshaderbuilder.c',
+  'gsksldefine.c',
   'gskslfunction.c',
   'gskslnode.c',
   'gskslpreprocessor.c',
diff --git a/gtk/glsl.c b/gtk/glsl.c
index fcb9a56..c698521 100644
--- a/gtk/glsl.c
+++ b/gtk/glsl.c
@@ -132,6 +132,41 @@ usage (GOptionContext *ctx)
   exit (EXIT_FAILURE);
 }
 
+static gboolean
+define (const gchar *option_name,
+        const gchar *value,
+        gpointer data,
+        GError **error)
+{
+  GskSlCompiler *compiler = data;
+  char **tokens;
+  gboolean result;
+
+  tokens = g_strsplit (value, "=", 2);
+
+  result = gsk_sl_compiler_add_define (compiler,
+                                       tokens[0],
+                                       tokens[1],
+                                       error);
+
+  g_strfreev (tokens);
+
+  return result;
+}
+
+static gboolean
+undefine (const gchar *option_name,
+          const gchar *value,
+          gpointer data,
+          GError **error)
+{
+  GskSlCompiler *compiler = data;
+
+  gsk_sl_compiler_remove_define (compiler, value);
+
+  return TRUE;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -139,13 +174,16 @@ main (int argc, char *argv[])
   char **filenames = NULL;
   char *output_file = NULL;
   gboolean print = FALSE;
-  GskSlCompiler *compiler;
   const GOptionEntry entries[] = {
+    { "define", 'D', 0, G_OPTION_ARG_CALLBACK, define, "Add a preprocssor definition", "NAME[=VALUE]" },
+    { "undef", 'U', 0, G_OPTION_ARG_CALLBACK, undefine, "Cancel previous preprocessor definition", "NAME" },
     { "print", 'p', 0, G_OPTION_ARG_NONE, &print, "Print instead of compiling", NULL },
     { "output", 'o', 0, G_OPTION_ARG_FILENAME, &output_file, "Output filename", "FILE" },
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, "List of input files", "FILE 
[FILE...]" },
     { NULL, }
   };
+  GskSlCompiler *compiler;
+  GOptionGroup *group;
   GError *error = NULL;
   GOutputStream *output;
   gboolean success = TRUE;
@@ -157,7 +195,9 @@ main (int argc, char *argv[])
 
   compiler = gsk_sl_compiler_new ();
   ctx = g_option_context_new (NULL);
-  g_option_context_add_main_entries (ctx, entries, NULL);
+  group = g_option_group_new (NULL, NULL, NULL, g_object_ref (compiler), g_object_unref);
+  g_option_group_add_entries (group, entries);
+  g_option_context_set_main_group (ctx, group);
 
   if (!g_option_context_parse (ctx, &argc, &argv, &error))
     {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]