[template-glib] scope: add resolver to handle missing symbols



commit 20cf4a1f546b75f348483bfcd3fdf4ef837e5bb6
Author: Christian Hergert <chergert redhat com>
Date:   Sat May 14 13:09:47 2016 +0300

    scope: add resolver to handle missing symbols
    
    If we fail to locate a symbol, allow a resolver to generate the symbol.
    
    If successful, it will cache the symbol result in the top-most scope.
    We might want to cache the symbol in the scope that generated it, but
    it is not yet clear to me if that is the right idea. So I'll be a bit
    conservative and stash it in the scope closest to access.

 src/main.c       |   20 ++++++++++++++
 src/tmpl-scope.c |   77 ++++++++++++++++++++++++++++++++++++++++++------------
 src/tmpl-scope.h |   29 +++++++++++++-------
 3 files changed, 99 insertions(+), 27 deletions(-)
---
diff --git a/src/main.c b/src/main.c
index 76de746..d1cae25 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,6 +21,24 @@
 #include <stdlib.h>
 #include <tmpl-glib.h>
 
+static gboolean
+method_missing (TmplScope    *scope,
+                const gchar  *name,
+                TmplSymbol  **symbol,
+                gpointer      user_data)
+{
+  g_autofree gchar *str = g_strdup_printf ("missing symbol: %s", name);
+
+  g_assert (scope != NULL);
+  g_assert (name != NULL);
+  g_assert (symbol != NULL);
+
+  *symbol = tmpl_symbol_new ();
+  tmpl_symbol_assign_string (*symbol, str);
+
+  return TRUE;
+}
+
 gint
 main (gint   argc,
       gchar *argv[])
@@ -47,6 +65,8 @@ main (gint   argc,
   file = g_file_new_for_commandline_arg (argv [1]);
   scope = tmpl_scope_new ();
 
+  tmpl_scope_set_resolver (scope, method_missing, NULL, NULL);
+
   if (!tmpl_template_parse_file (tmpl, file, NULL, &error))
     {
       g_printerr ("ERROR: %s\n", error->message);
diff --git a/src/tmpl-scope.c b/src/tmpl-scope.c
index f1c892a..a4a0792 100644
--- a/src/tmpl-scope.c
+++ b/src/tmpl-scope.c
@@ -21,9 +21,12 @@
 
 struct _TmplScope
 {
-  volatile gint  ref_count;
-  TmplScope     *parent;
-  GHashTable    *symbols;
+  volatile gint      ref_count;
+  TmplScope         *parent;
+  GHashTable        *symbols;
+  TmplScopeResolver  resolver;
+  gpointer           resolver_data;
+  GDestroyNotify     resolver_destroy;
 };
 
 G_DEFINE_BOXED_TYPE (TmplScope, tmpl_scope, tmpl_scope_ref, tmpl_scope_unref)
@@ -47,6 +50,10 @@ tmpl_scope_unref (TmplScope *self)
 
   if (g_atomic_int_dec_and_test (&self->ref_count))
     {
+      if (self->resolver_destroy)
+        g_clear_pointer (&self->resolver_data, self->resolver_destroy);
+      self->resolver = NULL;
+      self->resolver_destroy = NULL;
       g_clear_pointer (&self->symbols, g_hash_table_unref);
       g_clear_pointer (&self->parent, tmpl_scope_unref);
       g_slice_free (TmplScope, self);
@@ -99,6 +106,7 @@ tmpl_scope_get_full (TmplScope   *self,
                      gboolean     create)
 {
   TmplSymbol *symbol = NULL;
+  TmplScope *parent;
 
   g_return_val_if_fail (self != NULL, NULL);
 
@@ -110,17 +118,22 @@ tmpl_scope_get_full (TmplScope   *self,
     }
 
   /* Try to locate the symbol in a parent scope */
-  if (symbol == NULL)
+  for (parent = self->parent; parent != NULL; parent = parent->parent)
     {
-      TmplScope *parent;
+      if (parent->symbols != NULL)
+        {
+          if ((symbol = g_hash_table_lookup (parent->symbols, name)))
+            return symbol;
+        }
+    }
 
-      for (parent = self->parent; parent != NULL; parent = parent->parent)
+  /* Call our resolver helper to locate the symbol */
+  for (parent = self; parent != NULL; parent = parent->parent)
+    {
+      if (parent->resolver)
         {
-          if (parent->symbols != NULL)
-            {
-              if ((symbol = g_hash_table_lookup (parent->symbols, name)))
-                return symbol;
-            }
+          if (parent->resolver (parent, name, &symbol, parent->resolver_data) && symbol)
+            goto save_symbol;
         }
     }
 
@@ -128,15 +141,20 @@ tmpl_scope_get_full (TmplScope   *self,
     {
       /* Define the symbol in this scope */
       symbol = tmpl_symbol_new ();
-      if (self->symbols == NULL)
-        self->symbols = g_hash_table_new_full (g_str_hash,
-                                               g_str_equal,
-                                               g_free,
-                                               (GDestroyNotify)tmpl_symbol_unref);
-      g_hash_table_insert (self->symbols, g_strdup (name), symbol);
+      goto save_symbol;
     }
 
   return symbol;
+
+save_symbol:
+  if (self->symbols == NULL)
+    self->symbols = g_hash_table_new_full (g_str_hash,
+                                           g_str_equal,
+                                           g_free,
+                                           (GDestroyNotify)tmpl_symbol_unref);
+  g_hash_table_insert (self->symbols, g_strdup (name), symbol);
+
+  return symbol;
 }
 
 /**
@@ -166,3 +184,28 @@ tmpl_scope_peek (TmplScope   *self,
 {
   return tmpl_scope_get_full (self, name, FALSE);
 }
+
+void
+tmpl_scope_set_resolver (TmplScope         *self,
+                         TmplScopeResolver  resolver,
+                         gpointer           user_data,
+                         GDestroyNotify     destroy)
+{
+  g_return_if_fail (self != NULL);
+
+  if (resolver != self->resolver ||
+      user_data != self->resolver_data ||
+      destroy != self->resolver_destroy)
+    {
+      if (self->resolver && self->resolver_destroy && self->resolver_data)
+        {
+          g_clear_pointer (&self->resolver_data, self->resolver_destroy);
+          self->resolver_destroy = NULL;
+          self->resolver = NULL;
+        }
+
+      self->resolver = resolver;
+      self->resolver_data = user_data;
+      self->resolver_destroy = destroy;
+    }
+}
diff --git a/src/tmpl-scope.h b/src/tmpl-scope.h
index 60efd9a..12e1871 100644
--- a/src/tmpl-scope.h
+++ b/src/tmpl-scope.h
@@ -27,17 +27,26 @@
 
 G_BEGIN_DECLS
 
+typedef gboolean (*TmplScopeResolver) (TmplScope    *scope,
+                                       const gchar  *name,
+                                       TmplSymbol  **symbol,
+                                       gpointer      user_data);
+
 TmplScope  *tmpl_scope_new             (void);
-TmplScope  *tmpl_scope_new_with_parent (TmplScope   *parent);
-TmplScope  *tmpl_scope_ref             (TmplScope   *self);
-void        tmpl_scope_unref           (TmplScope   *self);
-TmplSymbol *tmpl_scope_peek            (TmplScope   *self,
-                                        const gchar *name);
-TmplSymbol *tmpl_scope_get             (TmplScope   *self,
-                                        const gchar *name);
-void        tmpl_scope_set             (TmplScope   *self,
-                                        const gchar *name,
-                                        TmplSymbol  *symbol);
+TmplScope  *tmpl_scope_new_with_parent (TmplScope         *parent);
+TmplScope  *tmpl_scope_ref             (TmplScope         *self);
+void        tmpl_scope_unref           (TmplScope         *self);
+TmplSymbol *tmpl_scope_peek            (TmplScope         *self,
+                                        const gchar       *name);
+TmplSymbol *tmpl_scope_get             (TmplScope         *self,
+                                        const gchar       *name);
+void        tmpl_scope_set             (TmplScope         *self,
+                                        const gchar       *name,
+                                        TmplSymbol        *symbol);
+void        tmpl_scope_set_resolver    (TmplScope         *self,
+                                        TmplScopeResolver  resolver,
+                                        gpointer           user_data,
+                                        GDestroyNotify     destroy);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (TmplScope, tmpl_scope_unref)
 


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