Bug 555960 - Nested structs and unions (generation portion)
authorColin Walters <walters@src.gnome.org>
Wed, 4 Feb 2009 00:48:24 +0000 (00:48 +0000)
committerColin Walters <walters@src.gnome.org>
Wed, 4 Feb 2009 00:48:24 +0000 (00:48 +0000)
Patch from Andreas Rottmann <a.rottmann@gmx.at>.

* tests/scanner/utility.h (UtilityTaggedValue): Make the union
member anonymous.
(UtilityByte): New union typedef with an unnamed struct in it.

* giscanner/transformer.py (Transformer._create_struct): Create
unnamed structs for symbols with a None ident.
(Transformer._create_union): Likewise.

* giscanner/girwriter.py (GIRWriter._write_record): Allow name
being None.
(GIRWriter._write_union): Likewise.

* girepository/girparser.c (start_struct): Allow a NULL name for
non-toplevel structs.
(start_union): Likewise.

* tests/scanner/utility.h (UtilityTaggedValue): New struct
typedef, which has a nested union member.
* tests/scanner/utility-expected.gir: Adapted.

* giscanner/transformer.py (Transformer._create_member): Create
struct/union members if appropriate.
(Transformer._create_struct, Transformer._create_union): Allow for
structs/unions without a C type.

* giscanner/glibtransformer.py (GLibTransformer._resolve_field):
We don't need to resolve non-typef'd
(GLibTransformer._resolve_field): Add cases for non-typedef'd
struct/union "fields".

* giscanner/girwriter.py (GIRWriter._write_record): Allow for
records without a C type.
(GIRWriter._write_field): structs and unions may appear in places
where fields do.

svn path=/trunk/; revision=1082

ChangeLog
girepository/girparser.c
giscanner/girwriter.py
giscanner/glibtransformer.py
giscanner/transformer.py
tests/repository/Makefile.am
tests/scanner/utility-1.0-expected.gir
tests/scanner/utility-1.0-expected.tgir
tests/scanner/utility.h

index bf51572..97d6b3b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,41 @@
 2009-02-03  Andreas Rottmann  <a.rottmann@gmx.at>
+
+       * tests/scanner/utility.h (UtilityTaggedValue): Make the union
+       member anonymous.
+       (UtilityByte): New union typedef with an unnamed struct in it.
+
+       * giscanner/transformer.py (Transformer._create_struct): Create
+       unnamed structs for symbols with a None ident.
+       (Transformer._create_union): Likewise.
+
+       * giscanner/girwriter.py (GIRWriter._write_record): Allow name
+       being None.
+       (GIRWriter._write_union): Likewise.
+
+       * girepository/girparser.c (start_struct): Allow a NULL name for
+       non-toplevel structs.
+       (start_union): Likewise.
+
+       * tests/scanner/utility.h (UtilityTaggedValue): New struct
+       typedef, which has a nested union member.
+       * tests/scanner/utility-expected.gir: Adapted.
+
+       * giscanner/transformer.py (Transformer._create_member): Create
+       struct/union members if appropriate.
+       (Transformer._create_struct, Transformer._create_union): Allow for
+       structs/unions without a C type.
+
+       * giscanner/glibtransformer.py (GLibTransformer._resolve_field):
+       We don't need to resolve non-typef'd
+       (GLibTransformer._resolve_field): Add cases for non-typedef'd
+       struct/union "fields".
+
+       * giscanner/girwriter.py (GIRWriter._write_record): Allow for
+       records without a C type.
+       (GIRWriter._write_field): structs and unions may appear in places
+       where fields do.
+
+2009-02-03  Andreas Rottmann  <a.rottmann@gmx.at>
        
        * girepository/girparser.c (ParseContext): Removed member
        current_node, added node_stack instead.
index 46c16e5..3e1f9df 100644 (file)
@@ -2112,7 +2112,10 @@ start_struct (GMarkupParseContext *context,
              GError             **error)
 {
   if (strcmp (element_name, "record") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
@@ -2127,7 +2130,7 @@ start_struct (GMarkupParseContext *context,
       gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values);
       gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values);
 
-      if (name == NULL)
+      if (name == NULL && ctx->node_stack == NULL)
        {
          MISSING_ATTRIBUTE (context, error, element_name, "name");
          return FALSE;
@@ -2145,7 +2148,7 @@ start_struct (GMarkupParseContext *context,
 
       struct_ = (GIrNodeStruct *) g_ir_node_new (G_IR_NODE_STRUCT);
       
-      ((GIrNode *)struct_)->name = g_strdup (name);
+      ((GIrNode *)struct_)->name = g_strdup (name ? name : "");
       if (deprecated)
        struct_->deprecated = TRUE;
       else
@@ -2156,9 +2159,10 @@ start_struct (GMarkupParseContext *context,
 
       struct_->gtype_name = g_strdup (gtype_name);
       struct_->gtype_init = g_strdup (gtype_init);
-      
-      ctx->current_module->entries = 
-       g_list_append (ctx->current_module->entries, struct_);
+
+      if (ctx->node_stack == NULL)
+        ctx->current_module->entries = 
+          g_list_append (ctx->current_module->entries, struct_);
       push_node (ctx, (GIrNode *)struct_);
       
       state_switch (ctx, STATE_STRUCT);
@@ -2176,8 +2180,11 @@ start_union (GMarkupParseContext *context,
             ParseContext       *ctx,
             GError             **error)
 {
-  if (strcmp (element_name, "union") == 0 && 
-      ctx->state == STATE_NAMESPACE)
+  if (strcmp (element_name, "union") == 0 &&
+      (ctx->state == STATE_NAMESPACE ||
+       ctx->state == STATE_UNION ||
+       ctx->state == STATE_STRUCT ||
+       ctx->state == STATE_CLASS))
     {
       const gchar *name;
       const gchar *deprecated;
@@ -2189,7 +2196,7 @@ start_union (GMarkupParseContext *context,
       typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
       typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
       
-      if (name == NULL)
+      if (name == NULL && ctx->node_stack == NULL)
        MISSING_ATTRIBUTE (context, error, element_name, "name");
       else
        {
@@ -2197,7 +2204,7 @@ start_union (GMarkupParseContext *context,
 
          union_ = (GIrNodeUnion *) g_ir_node_new (G_IR_NODE_UNION);
          
-         ((GIrNode *)union_)->name = g_strdup (name);
+         ((GIrNode *)union_)->name = g_strdup (name ? name : "");
          union_->gtype_name = g_strdup (typename);
          union_->gtype_init = g_strdup (typeinit);
          if (deprecated)
@@ -2205,8 +2212,9 @@ start_union (GMarkupParseContext *context,
          else
            union_->deprecated = FALSE;
 
-         ctx->current_module->entries = 
-           g_list_append (ctx->current_module->entries, union_);
+          if (ctx->node_stack == NULL)
+            ctx->current_module->entries = 
+              g_list_append (ctx->current_module->entries, union_);
          push_node (ctx, (GIrNode *)union_);
          
          state_switch (ctx, STATE_UNION);
@@ -2685,6 +2693,41 @@ require_one_of_end_elements (GMarkupParseContext *context,
 }
 
 static gboolean
+state_switch_end_struct_or_union (GMarkupParseContext *context,
+                                  ParseContext *ctx,
+                                  const gchar *element_name,
+                                  GError **error)
+{
+  pop_node (ctx);
+  if (ctx->node_stack == NULL)
+    {
+      state_switch (ctx, STATE_NAMESPACE);
+    }
+  else 
+    {
+      if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT)
+        state_switch (ctx, STATE_STRUCT);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION)
+        state_switch (ctx, STATE_UNION);
+      else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT)
+        state_switch (ctx, STATE_CLASS);
+      else
+        {
+          int line_number, char_number;
+          g_markup_parse_context_get_position (context, &line_number, &char_number);
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_INVALID_CONTENT,
+                       "Unexpected end tag '%s' on line %d char %d",
+                       element_name,
+                       line_number, char_number);
+          return FALSE;
+        }
+    }
+  return TRUE;
+}
+
+static gboolean
 require_end_element (GMarkupParseContext *context,
                     ParseContext        *ctx,
                     const char          *expected_name,
@@ -2897,8 +2940,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_STRUCT:
       if (require_end_element (context, ctx, "record", element_name, error))
        {
-         pop_node (ctx);
-         state_switch (ctx, STATE_NAMESPACE);
+         state_switch_end_struct_or_union (context, ctx, element_name, error);
        }
       break;
 
@@ -2914,8 +2956,7 @@ end_element_handler (GMarkupParseContext *context,
     case STATE_UNION:
       if (require_end_element (context, ctx, "union", element_name, error))
        {
-         pop_node (ctx);
-         state_switch (ctx, STATE_NAMESPACE);
+         state_switch_end_struct_or_union (context, ctx, element_name, error);
        }
       break;
     case STATE_IMPLEMENTS:
index 51a208d..cd19e98 100644 (file)
@@ -366,8 +366,11 @@ and/or use gtk-doc annotations. ''')
                 ('glib:get-type', boxed.get_type)]
 
     def _write_record(self, record):
-        attrs = [('name', record.name),
-                 ('c:type', record.symbol)]
+        attrs = []
+        if record.name is not None:
+            attrs.append(('name', record.name))
+        if record.symbol is not None: # the record might be anonymous
+            attrs.append(('c:type', record.symbol))
         if record.disguised:
             attrs.append(('disguised', '1'))
         if record.doc:
@@ -386,8 +389,11 @@ and/or use gtk-doc annotations. ''')
                 self._write_method(method)
 
     def _write_union(self, union):
-        attrs = [('name', union.name),
-                 ('c:type', union.symbol)]
+        attrs = []
+        if union.name is not None:
+            attrs.append(('name', union.name))
+        if union.symbol is not None: # the union might be anonymous
+            attrs.append(('c:type', union.symbol))
         if union.doc:
             attrs.append(('doc', union.doc))
         self._append_version(union, attrs)
@@ -410,19 +416,22 @@ and/or use gtk-doc annotations. ''')
 
         if isinstance(field, Callback):
             self._write_callback(field)
-            return
-
-        attrs = [('name', field.name)]
-        # Fields are assumed to be read-only
-        # (see also girparser.c and generate.c)
-        if not field.readable:
-            attrs.append(('readable', '0'))
-        if field.writable:
-            attrs.append(('writable', '1'))
-        if field.bits:
-            attrs.append(('bits', str(field.bits)))
-        with self.tagcontext('field', attrs):
-            self._write_type(field.type)
+        elif isinstance(field, Struct):
+            self._write_record(field)
+        elif isinstance(field, Union):
+            self._write_union(field)
+        else:
+            attrs = [('name', field.name)]
+            # Fields are assumed to be read-only
+            # (see also girparser.c and generate.c)
+            if not field.readable:
+                attrs.append(('readable', '0'))
+            if field.writable:
+                attrs.append(('writable', '1'))
+            if field.bits:
+                attrs.append(('bits', str(field.bits)))
+            with self.tagcontext('field', attrs):
+                self._write_type(field.type)
 
     def _write_signal(self, signal):
         attrs = [('name', signal.name)]
index 51076a2..749b652 100644 (file)
@@ -870,8 +870,12 @@ class GLibTransformer(object):
     def _resolve_field(self, field):
         if isinstance(field, Callback):
             self._resolve_function(field)
-            return
-        field.type = self._resolve_param_type(field.type)
+        elif isinstance(field, Record): # non-typedef'd struct
+            self._resolve_record(field)
+        elif isinstance(field, Union): # non-typedef'd union
+            self._resolve_union(field)
+        else:
+            field.type = self._resolve_param_type(field.type)
 
     def _resolve_alias(self, alias):
         alias.target = self._resolve_type_name(alias.target, alias.target)
index 1d781c6..f6e89ce 100644 (file)
@@ -307,6 +307,10 @@ class Transformer(object):
         if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
             node = self._create_callback(symbol)
+        elif source_type.type == CTYPE_STRUCT and source_type.name is None:
+            node = self._create_struct(symbol, anonymous=True)
+        elif source_type.type == CTYPE_UNION and source_type.name is None:
+            node = self._create_union(symbol, anonymous=True)
         else:
             # Special handling for fields; we don't have annotations on them
             # to apply later, yet.
@@ -478,8 +482,14 @@ class Transformer(object):
         self._typedefs_ns[callback.name] = callback
         return callback
 
-    def _create_compound(self, klass, symbol):
-        compound = self._typedefs_ns.get(symbol.ident, None)
+    def _create_compound(self, klass, symbol, anonymous):
+        if symbol.ident is None:
+            # the compound is an anonymous member of another union or a struct
+            assert anonymous
+            compound = klass(None, None)
+        else:
+            compound = self._typedefs_ns.get(symbol.ident, None)
+
         if compound is None:
             # This is a bit of a hack; really we should try
             # to resolve through the typedefs to find the real
@@ -500,11 +510,11 @@ class Transformer(object):
 
         return compound
 
-    def _create_struct(self, symbol):
-        return self._create_compound(Struct, symbol)
+    def _create_struct(self, symbol, anonymous=False):
+        return self._create_compound(Struct, symbol, anonymous)
 
-    def _create_union(self, symbol):
-        return self._create_compound(Union, symbol)
+    def _create_union(self, symbol, anonymous=False):
+        return self._create_compound(Union, symbol, anonymous)
 
     def _create_callback(self, symbol):
         parameters = self._create_parameters(symbol.base_type.base_type)
index 1dd5c4c..e21cff4 100644 (file)
@@ -13,4 +13,5 @@ gitestthrows_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
 gitestthrows_LDADD = $(top_builddir)/girepository/libgirepository-1.0.la $(GIREPO_LIBS)
 
 TESTS = gitestrepo gitestthrows
-TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG)
+TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG) \
+   XDG_DATA_DIRS="$(top_srcdir)/gir:$(XDG_DATA_DIRS)"
index 81c1a6b..bb5a5e4 100644 (file)
@@ -46,6 +46,35 @@ and/or use gtk-doc annotations.  -->
         <type name="GObject.ObjectClass" c:type="GObjectClass"/>
       </field>
     </record>
+    <record name="TaggedValue" c:type="UtilityTaggedValue">
+      <field name="tag" writable="1">
+        <type name="int" c:type="int"/>
+      </field>
+      <union>
+        <field name="v_pointer" writable="1">
+          <type name="any" c:type="gpointer"/>
+        </field>
+        <field name="v_real" writable="1">
+          <type name="double" c:type="double"/>
+        </field>
+        <field name="v_integer" writable="1">
+          <type name="long" c:type="long"/>
+        </field>
+      </union>
+    </record>
+    <union name="Byte" c:type="UtilityByte">
+      <field name="value" writable="1">
+        <type name="uint8" c:type="guint8"/>
+      </field>
+      <record>
+        <field name="first_nibble" writable="1" bits="4">
+          <type name="uint8" c:type="guint8"/>
+        </field>
+        <field name="second_nibble" writable="1" bits="4">
+          <type name="uint8" c:type="guint8"/>
+        </field>
+      </record>
+    </union>
     <callback name="FileFunc" c:type="UtilityFileFunc">
       <return-value transfer-ownership="none">
         <type name="none" c:type="void"/>
index 26e6d19..7a2364e 100644 (file)
         <type name="GObject.ObjectClass"/>
       </field>
     </record>
+    <record name="TaggedValue">
+      <field name="tag" writable="1">
+        <type name="int"/>
+      </field>
+      <!-- FIXME: anonymous union member missing -->
+    </record>
+    <union name="Byte">
+      <field name="value" writable="1">
+        <type name="uint8"/>
+      </field>
+      <!-- FIXME: anonymous struct member missing -->
+    </union>
     <callback name="FileFunc">
       <return-value transfer-ownership="none">
         <type name="none"/>
index a744f03..b493a67 100644 (file)
@@ -23,6 +23,27 @@ struct _UtilityObjectClass
 /* This one is similar to Pango.Glyph */
 typedef guint32 UtilityGlyph;
 
+typedef struct
+{
+  int tag;
+  union
+  {
+    gpointer v_pointer;
+    double v_real;
+    long v_integer;
+  };
+} UtilityTaggedValue;
+
+typedef union
+{
+  guint8 value;
+  struct
+  {
+    guint8 first_nibble : 4;
+    guint8 second_nibble : 4;
+  };
+} UtilityByte;
+
 typedef void (*UtilityFileFunc)(const char *path, gpointer user_data);
 
 GType                 utility_object_get_type          (void) G_GNUC_CONST;