summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gir/Everything-1.0-expected.gir26
-rw-r--r--gir/everything.c31
-rw-r--r--gir/everything.h2
-rw-r--r--giscanner/annotationparser.py63
-rw-r--r--giscanner/ast.py2
5 files changed, 109 insertions, 15 deletions
diff --git a/gir/Everything-1.0-expected.gir b/gir/Everything-1.0-expected.gir
index 2a96e2bb..669bef1e 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 e01a3a93..02a174c2 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 59a7a106..2af75aca 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 8c901a25..bb74cdc3 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 b0db6e23..0d1b9f3b 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):