[gobject-introspection] Bug 581685: Parse parameterized types (using <>) in annotations.



commit c5dedb9cf43110990ef788e468cf23aef0cf83fb
Author: C. Scott Ananian <cscott litl com>
Date:   Fri Jun 12 10:57:38 2009 -0400

    Bug 581685: Parse parameterized types (using <>) in annotations.
    
    You can now specify a nested parameterized type in annotations as
    (for example):
      @param: (type GLib.HashTable<utf8,GLib.HashTable<utf,utf>>)
    or
      @param: (element-type utf8 GLib.HashTable<utf,utf>)
    
    New test functions for the Everything typelib show how it works.

 gir/Everything-1.0-expected.gir |   26 ++++++++++++++++
 gir/everything.c                |   31 +++++++++++++++++++
 gir/everything.h                |    2 +
 giscanner/annotationparser.py   |   63 ++++++++++++++++++++++++++++++--------
 giscanner/ast.py                |    2 +-
 5 files changed, 109 insertions(+), 15 deletions(-)
---
diff --git a/gir/Everything-1.0-expected.gir b/gir/Everything-1.0-expected.gir
index 2a96e2b..669bef1 100644
--- a/gir/Everything-1.0-expected.gir
+++ b/gir/Everything-1.0-expected.gir
@@ -705,6 +705,32 @@ call and can be released on return.">
         </parameter>
       </parameters>
     </function>
+    <function name="test_ghash_nested_everything_return"
+              c:identifier="test_ghash_nested_everything_return"
+              doc="Specify nested parameterized types directly with the (type ) annotation.">
+      <return-value transfer-ownership="full">
+        <type name="GLib.HashTable" c:type="GHashTable*">
+          <type name="utf8"/>
+          <type name="GLib.HashTable">
+            <type name="utf8"/>
+            <type name="utf8"/>
+          </type>
+        </type>
+      </return-value>
+    </function>
+    <function name="test_ghash_nested_everything_return2"
+              c:identifier="test_ghash_nested_everything_return2"
+              doc="element-type annotation.">
+      <return-value transfer-ownership="full">
+        <type name="GLib.HashTable" c:type="GHashTable*">
+          <type name="utf8"/>
+          <type name="GLib.HashTable">
+            <type name="utf8"/>
+            <type name="utf8"/>
+          </type>
+        </type>
+      </return-value>
+    </function>
     <function name="test_ghash_nothing_in"
               c:identifier="test_ghash_nothing_in">
       <return-value transfer-ownership="none">
diff --git a/gir/everything.c b/gir/everything.c
index e01a3a9..02a174c 100644
--- a/gir/everything.c
+++ b/gir/everything.c
@@ -905,6 +905,37 @@ void test_ghash_everything_in (GHashTable *in)
   g_hash_table_destroy (in);
 }
 
+/* Nested collection types */
+
+/**
+ * test_ghash_nested_everything_return:
+ * Specify nested parameterized types directly with the (type ) annotation.
+ *
+ * Return value: (type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) (transfer full):
+ */
+GHashTable *
+test_ghash_nested_everything_return (void)
+{
+  GHashTable *hash;
+  hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                               (void (*) (gpointer)) g_hash_table_destroy);
+  g_hash_table_insert(hash, g_strdup("wibble"), test_table_ghash_new_full());
+  return hash;
+}
+
+/**
+ * test_ghash_nested_everything_return2:
+ * Another way of specifying nested parameterized types: using the
+ * element-type annotation.
+ *
+ * Return value: (element-type utf8 GLib.HashTable<utf8,utf8>) (transfer full):
+ */
+GHashTable *
+test_ghash_nested_everything_return2 (void)
+{
+  return test_ghash_nested_everything_return();
+}
+
 /************************************************************************/
 
 /* error? */
diff --git a/gir/everything.h b/gir/everything.h
index 59a7a10..2af75ac 100644
--- a/gir/everything.h
+++ b/gir/everything.h
@@ -90,6 +90,8 @@ void test_ghash_nothing_in2 (GHashTable *in);
 void test_ghash_container_in (GHashTable *in);
 void test_ghash_everything_in (GHashTable *in);
 void test_ghash_free (GHashTable *in);
+GHashTable *test_ghash_nested_everything_return (void);
+GHashTable *test_ghash_nested_everything_return2 (void);
 
 /* error? */
 
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 8c901a2..bb74cdc 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -20,6 +20,7 @@
 
 # AnnotationParser - parses gtk-doc annotations
 
+import re
 import sys
 
 from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
@@ -424,7 +425,6 @@ class AnnotationApplier(object):
         # We're only attempting to name the signal parameters if
         # the number of parameter tags (@foo) is the same or greater
         # than the number of signal parameters
-        resolve = self._transformer.resolve_param_type
         if block and len(block.tags) > len(signal.parameters):
             names = block.tags.items()
         else:
@@ -436,7 +436,7 @@ class AnnotationApplier(object):
                 options = getattr(tag, 'options', {})
                 param_type = options.get(OPT_TYPE)
                 if param_type:
-                    param.type.name = resolve(param_type.one())
+                    param.type = self._resolve(param_type.one(), param.type)
             else:
                 tag = None
             self._parse_param(signal, param, tag)
@@ -523,8 +523,7 @@ class AnnotationApplier(object):
             node.allow_none = True
         param_type = options.get(OPT_TYPE)
         if param_type:
-            resolve = self._transformer.resolve_param_type
-            node.type.name = resolve(param_type.one())
+            node.type = self._resolve(param_type.one(), node.type)
 
         assert node.transfer is not None
         if tag is not None and tag.comment is not None:
@@ -583,12 +582,12 @@ class AnnotationApplier(object):
 
         element_type = options.get(OPT_ELEMENT_TYPE)
         if element_type is not None:
-            element_type_name = element_type.one()
+            element_type_node = self._resolve(element_type.one())
         else:
-            element_type_name = node.type.name
+            element_type_node = Type(node.type.name) # erase ctype
 
         container_type = Array(node.type.ctype,
-                               element_type_name)
+                               element_type_node)
         container_type.is_const = node.type.is_const
         if OPT_ARRAY_ZERO_TERMINATED in array_values:
             container_type.zeroterminated = array_values.get(
@@ -604,29 +603,65 @@ class AnnotationApplier(object):
                 node.type.name == 'utf8' and
                 self._guess_direction(node) == PARAM_DIRECTION_IN):
                 # FIXME: unsigned char/guchar should be uint8
-                container_type.element_type = 'int8'
+                container_type.element_type = Type('int8')
         container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
         return container_type
 
+    def _resolve(self, type_str, orig_node=None):
+        def grab_one(type_str, resolver, top_combiner, combiner):
+            """Return a complete type, and the trailing string part after it.
+            Use resolver() on each identifier, and combiner() on the parts of
+            each complete type. (top_combiner is used on the top-most type.)"""
+            bits = re.split(r'([,<>])', type_str, 1)
+            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+            args = [resolver(first)]
+            if sep == '<':
+                while sep != '>':
+                    next, rest = grab_one(rest, resolver, combiner, combiner)
+                    args.append(next)
+                    sep, rest = rest[0], rest[1:]
+            else:
+                rest = sep + rest
+            return top_combiner(*args), rest
+        def resolver(ident):
+            return self._transformer.resolve_param_type(Type(ident))
+        def combiner(base, *rest):
+            if not rest:
+                return base
+            if base.name in ['GLib.List', 'GLib.SList'] and len(rest)==1:
+                return List(base.name, base.ctype, *rest)
+            if base.name in ['GLib.HashTable'] and len(rest)==2:
+                return Map(base.name, base.ctype, *rest)
+            print "WARNING: throwing away type parameters:", type_str
+            return base
+        def top_combiner(base, *rest):
+            """For the top most type, recycle orig_node if possible."""
+            if orig_node is not None:
+                orig_node.name = base.name
+                base = orig_node # preserve other properties of orig_node
+            return combiner(base, *rest)
+
+        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+        if rest:
+            print "WARNING: throwing away trailing part of type:", type_str
+        return result
+
     def _parse_element_type(self, parent, node, options):
         element_type_opt = options.get(OPT_ELEMENT_TYPE)
         element_type = element_type_opt.flat()
         if node.type.name in ['GLib.List', 'GLib.SList']:
             assert len(element_type) == 1
-            etype = Type(element_type[0])
             container_type = List(
                 node.type.name,
                 node.type.ctype,
-                self._transformer.resolve_param_type(etype))
+                self._resolve(element_type[0]))
         elif node.type.name in ['GLib.HashTable']:
             assert len(element_type) == 2
-            key_type = Type(element_type[0])
-            value_type = Type(element_type[1])
             container_type = Map(
                 node.type.name,
                 node.type.ctype,
-                self._transformer.resolve_param_type(key_type),
-                self._transformer.resolve_param_type(value_type))
+                self._resolve(element_type[0]),
+                self._resolve(element_type[1]))
         else:
             print 'FIXME: unhandled element-type container:', node
         return container_type
diff --git a/giscanner/ast.py b/giscanner/ast.py
index b0db6e2..0d1b9f3 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -300,7 +300,7 @@ class Map(Type):
         self.value_type = value_type
 
     def __repr__(self):
-        return 'Map(%r <%r,%r.)' % (self.name, self.key_type, self.value_type)
+        return 'Map(%r <%r,%r>)' % (self.name, self.key_type, self.value_type)
 
 
 class Alias(Node):



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