diff options
-rw-r--r-- | gir/Everything-1.0-expected.gir | 26 | ||||
-rw-r--r-- | gir/everything.c | 31 | ||||
-rw-r--r-- | gir/everything.h | 2 | ||||
-rw-r--r-- | giscanner/annotationparser.py | 63 | ||||
-rw-r--r-- | 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 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): |