tuxji commented on a change in pull request #488:
URL: https://github.com/apache/incubator-daffodil/pull/488#discussion_r574832636



##########
File path: 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -162,10 +322,31 @@ class CodeGeneratorState {
   def addComplexTypeStatements(child: ElementBase): Unit = {
     val C = localName(child)
     val e = child.name
+    val hasChoice = structs.top.initChoiceStatements.nonEmpty

Review comment:
       By a sequence with an anonymous choice, do you mean a complex type which 
looks like this?
   
   ```xml
     <xs:complexType name="NestedUnionType">
       <xs:sequence>
         <xs:element name="tag" type="idl:int32"/>
         <xs:choice dfdl:choiceDispatchKey="{xs:string(./tag)}">
           <xs:element name="foo" type="idl:FooType" dfdl:choiceBranchKey="1 
2"/>
           <xs:element name="bar" type="idl:BarType" dfdl:choiceBranchKey="3 
4"/>
         </xs:choice>
       </xs:sequence>
     </xs:complexType>
   ```
   
   I tried generating the C code and found out that the generator didn't 
recognize the anonymous choice was present - it generated a struct with all 
members like below,
   
   ```c
   typedef struct NestedUnion
   {
       InfosetBase _base;
       int32_t     tag;
       foo foo;
       bar bar;
   } NestedUnion;
   ```
   
   I could make the generator look for the anonymous choice and generate a 
struct with a nested anonymous union like this,
   
   ```c
   typedef struct NestedUnion
   {
       InfosetBase _base;
       int32_t     tag;
       size_t      _choice; // choice of which union field to use
       union
       {
           foo foo;
           bar bar;
       };
   } NestedUnion;
   ```
   
   The _choice member field serves an important role in the ERD structure.  
Since we have a _choice member field, we have a corresponding entry for it in 
childrenERDs and it has its own ERD structure.  When we walk that ERD and see 
it has a CHOICE type code, that tells us it's time to call the initChoice 
function to initialize the **union** child's ERD structure so it points to the 
ERD of the right alternative element.  The _choice element always should 
immediately precede the union element in the ERD structure, so if we inline it 
into the parent C struct due to an anonymous choice, we also should make sure 
it has a unique name (_choice1 instead of _choice) in case we need a _choice2 
for another anonymous choice following the first choice.  It might be possible 
to remove the _choice member field to save space, but we would need to move the 
CHOICE type code to the union child's ERD.  That ERD would no longer point to 
the union child's choice of alternative element ERDs; we would need 
 a way to walk the union child's choice of alternative elements and ERDs 
everywhere that `instance->_choice` currently gets used.  However, if Mike has 
a better suggestion how to design the choice runtime structure (CRD), then we 
could dispense with _choice.
   
   Yes, a name collision issue would arise in sequences with multiple 
alternative elements with the same name.  However, Daffodil won't even allow 
such a schema to compile.  When I tried changing bar to foo in NestedUnionType 
in nested.dfdl.xsd, Daffodil reported two schema definition errors:
   
   ```text
   [error] Schema Definition Error: Error loading schema due to 
org.xml.sax.SAXParseException; systemId: file:.../nested.dfdl.xsd; lineNumber: 
70; columnNumber: 25; cos-element-consistent: Error for type 
'#AnonType_dataNestedUnionType'. Multiple elements with name 'foo', with 
different types, appear in the model group.
   [error] Schema Definition Error: Error loading schema due to 
org.xml.sax.SAXParseException; systemId: file:.../nested.dfdl.xsd; lineNumber: 
70; columnNumber: 25; cos-nonambig: foo and foo (or elements from their 
substitution group) violate "Unique Particle Attribution". During validation 
against this schema, ambiguity would be created for those two particles.
   ```
   
   When I changed the second foo to have the same FooType, the first error 
disappeared but the second error remained.
   

##########
File path: 
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ingress_xdcc_bw.tdml
##########
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<tdml:testSuite
+  defaultConfig="config-runtime2"
+  defaultImplementations="daffodil daffodil-runtime2"
+  defaultRoundTrip="none"
+  description="TDML tests for ingress_xdcc_bw"
+  xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/";
+  xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData";>
+
+  <tdml:defineConfig name="config-runtime1">
+    <daf:tunables>
+      <daf:tdmlImplementation>daffodil</daf:tdmlImplementation>
+    </daf:tunables>
+  </tdml:defineConfig>
+
+  <tdml:defineConfig name="config-runtime2">
+    <daf:tunables>
+      <daf:tdmlImplementation>daffodil-runtime2</daf:tdmlImplementation>
+    </daf:tunables>
+  </tdml:defineConfig>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 111"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_111"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_111.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_111.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 111"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_111"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_111.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_111.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 112"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_112"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_112.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_112.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 112"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_112"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_112.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_112.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 113"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_113"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_113.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_113.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 113"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_113"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_113.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_113.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 114"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_114"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_114.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_114.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 114"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_114"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_114.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_114.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 115"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_115"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_115.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_115.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 115"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_115"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_115.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_115.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    description="ingress_xdcc_bw parse test 116"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_parse_116"
+    root="GapsPDU">
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_116.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_116.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    description="ingress_xdcc_bw unparse test 116"
+    model="ingress_xdcc_bw.dfdl.xsd"
+    name="ingress_xdcc_bw_unparse_116"
+    root="GapsPDU">
+    <tdml:infoset>
+      <tdml:dfdlInfoset 
type="file">ingress_xdcc_bw_unparse_116.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart 
type="file">ingress_xdcc_bw_parse_116.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+</tdml:testSuite>

Review comment:
       I can move the schemas and test cases for collaborators' use cases which 
just use runtime2 for code generation (not developing or testing runtime2 
itself) to some other place such as 
daffodil-test/src/test/{resources,scala}/org/apache/daffodil/runtime2.  It 
might be better to do it in another pull request that only moves things around, 
not changes things.  I want to keep those schemas and test cases to ensure 
their use cases keep working as expected.  Let me know if a different directory 
name or place sounds better.  I would like to see us start running runtime2 on 
as many of daffodil's own tests as possible too, which may make those 
collaborators' schemas and test cases unnecessary to keep around in the long 
term.

##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -38,6 +33,8 @@ typedef struct VisitEventHandler  VisitEventHandler;
 typedef void (*ERDInitSelf)(InfosetBase *infoNode);
 typedef void (*ERDParseSelf)(InfosetBase *infoNode, PState *pstate);
 typedef void (*ERDUnparseSelf)(const InfosetBase *infoNode, UState *ustate);
+typedef bool (*ERDInitChoice)(const InfosetBase *infoNode,

Review comment:
       Actually, the loop inside infoset.c's walkInfosetNode function calls 
initChoice to update the ERD structure to point to a choice of alternative 
child ERDs when walking a choice group's node, so initChoice must modify the 
following element's ERD structure (`childNode->erd`) as well as the infoset's 
CRD structure (`instance->_choice`).  Nevertheless, I've changed the type name 
ERDInitChoice to InitChoiceRD and added a comment to infoset.c to clarify 
initChoice's role.
   
   ```c
       case CHOICE:
           // Point next ERD to choice of alternative elements' ERDs
           if (!infoNode->erd->initChoice(infoNode, rootElement()))
               error_msg =
   ```
   

##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -81,34 +79,35 @@ typedef struct ElementRuntimeData
     const NamedQName    namedQName;
     const enum TypeCode typeCode;
     const size_t        numChildren;
-    const ptrdiff_t *   offsets;
+    const size_t *      offsets;
     const ERD **        childrenERDs;
 
     const ERDInitSelf    initSelf;
     const ERDParseSelf   parseSelf;
     const ERDUnparseSelf unparseSelf;
+    const ERDInitChoice  initChoice;
 } ERD;
 
-// InfosetBase - representation of an infoset element
+// InfosetBase - metadata of an infoset element
 
 typedef struct InfosetBase
 {
     const ERD *erd;
 } InfosetBase;
 
-// PState - parser state while parsing input
+// PState - mutable state while parsing data
 
 typedef struct PState
 {
-    FILE *      stream;    // input to read from
+    FILE *      stream;    // input to read data from
     const char *error_msg; // to stop if an error happens
 } PState;
 
-// UState - unparser state while unparsing infoset
+// UState - mutable state while unparsing infoset
 
 typedef struct UState
 {
-    FILE *      stream;    // output to write to
+    FILE *      stream;    // output to write infoset to

Review comment:
       Yes, "output to write data to" makes more sense, changed.
   

##########
File path: 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
     qnameInit
   }
 
+  /**
+   * We want to convert a choiceDispatchKey expression into C struct dot
+   * notation (rootElement->[subElement.field]) which will access the C
+   * struct field containing the choiceDispatchKey's runtime value.
+   *
+   * We make some assumptions to make generating the dot notation easier:
+   * - the expression starts with '{xs:string( and ends with )}'
+   * - the expression returns the value of a previous element without
+   *   changing the value in any way (except converting it to xs:string)
+   * - both the expression and the C code use only local names (for now...)
+   * - we can map the context node's path to a Unix-like slash path
+   * - all dpath operations look like Unix-like relative paths (../tag)
+   * - we can normalize the new path and convert it to C struct dot notation
+   * - we can store the accessed value in an int64_t local variable safely
+   */
+  private def choiceDispatchField(context: ElementBase): String = {
+    // We want to use SchemaComponent.scPath but it's private so duplicate it 
here (for now...)
+    def scPath(sc: SchemaComponent): Seq[SchemaComponent] = 
sc.optLexicalParent.map { scPath }.getOrElse(Nil) :+ sc
+    val localNames = scPath(context).map {
+      case er: AbstractElementRef => er.refQName.local
+      case e: ElementBase => e.namedQName.local
+      case ed: GlobalElementDecl => ed.namedQName.local
+      case _ => ""
+    }
+    val absoluteSlashPath = localNames.mkString("/")
+    val dispatchSlashPath = context.complexType.modelGroup match {
+      case choice: Choice if choice.isDirectDispatch =>
+        val expr = choice.choiceDispatchKeyEv.expr.toBriefXML()
+        val before = "'{xs:string("
+        val after = ")}'"
+        val relativePath = if (expr.startsWith(before) && expr.endsWith(after))
+          expr.substring(before.length, expr.length - after.length) else expr
+        val normalizedURI = new URI(absoluteSlashPath + "/" + 
relativePath).normalize
+        normalizedURI.getPath.substring(1)
+      case _ => ""
+    }
+    // Strip namespace prefixes since C code uses only local names (for now...)
+    val localDispatchSlashPath = dispatchSlashPath.replaceAll("/[^:]+:", "/")
+    val res = localDispatchSlashPath.replace('/', '.')
+    res
+  }

Review comment:
       Yes, I would like to see us add code generate support to the DPath 
compiler.  I agree this code feels fragile and we probably will need the DPath 
expression compiler once people start using dispatch expressions that don't 
match the supported format so far.
   

##########
File path: daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
##########
@@ -62,6 +63,9 @@ static const struct argp parse_argp = {
     parse_handler,  // function to get these CLI options
     parse_args_doc, // short usage documentation
     parse_doc,      // long help documentation
+    0,              // children

Review comment:
       I've replaced those comments with longer comments but programmers still 
will have to study argp_help.c in the argp library's source code and read the 
GNU gettext library's documentation before they can use those parameters.
   
   ```c
       0,  // child argps parsed after this argp
       0,  // function to replace help messages
       0   // domain name for translation lookup
   ```

##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
##########
@@ -159,6 +159,13 @@ walkInfosetNode(const VisitEventHandler *handler, const 
InfosetBase *infoNode)
             error_msg =
                 handler->visitNumberElem(handler, childERD, numLocation);
             break;
+        case CHOICE:
+            if (!infoNode->erd->initChoice(infoNode, rootElement()))

Review comment:
       NestedUnion.c in this change set has an example initChoice function but 
GitHub might have hidden its diff in your window at the time you did your 
search.  We call initChoice to update the following union child element's ERD 
structure (`childNode->erd`) as well as the infoset's CRD structure 
(`instance->_choice`) whenever we walk the infoset in walkInfosetNode 
(infoset.c), data_parseSelf (NestedUnion.c), or data_unparseSelf 
(NestedUnion.c).  Here's the corresponding code in the example initChoice 
(childNode points to the union child following the _choice field):
   
   ```c
       if (instance->_choice != NO_CHOICE)
       {
           const size_t choice = instance->_choice + 1; // skip the _choice 
field
           const size_t offset = instance->_base.erd->offsets[choice];
           const ERD *  childERD = instance->_base.erd->childrenERDs[choice];
           InfosetBase *childNode = (InfosetBase *)((const char *)instance + 
offset);
           childNode->erd = childERD;
           return true;
       }
   ```
   
   You're right, I've been assuming that the entire infonode is a single choice 
since I didn't consider anonymous choices needing to be inlined into the parent 
C struct.  The _choice field has its entry in childrenERDs and its own ERD 
structure naming it, so we can be more flexible instead of hardcoding 
`instance->_choice` in CodeGeneratorState.scala.  If you can give me a DFDL 
schema with multiple choice groups, I'll make that improvement in a future pull 
request.
   

##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -81,34 +79,35 @@ typedef struct ElementRuntimeData
     const NamedQName    namedQName;
     const enum TypeCode typeCode;
     const size_t        numChildren;
-    const ptrdiff_t *   offsets;
+    const size_t *      offsets;

Review comment:
       Those thoughts motivated me to change offsets to size_t:
     1) Offsets are always positive deltas from the infoset's base to following 
fields
     2) If I use size_t instead of ptrdiff_t, the delta can be twice as large 
and never becomes negative due to overflow
     3) Some C functions we call receive or return size_t rather than ptrdiff_t
   

##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
+#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
+#include <math.h>       // for NAN
+#include <stdbool.h>    // for bool, false, true
+#include <stddef.h>     // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState 
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "tag", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "_choice", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    CHOICE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "a", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "b", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "c", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+    (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+    &a_FooType_ERD,
+    &b_FooType_ERD,
+    &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "foo", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    foo_offsets, // offsets
+    foo_childrenERDs, // childrenERDs
+    (ERDInitSelf)&foo_initSelf, // initSelf
+    (ERDParseSelf)&foo_parseSelf, // parseSelf
+    (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "x", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "y", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "z", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+    (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+    &x_BarType_ERD,
+    &y_BarType_ERD,
+    &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "bar", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    bar_offsets, // offsets
+    bar_childrenERDs, // childrenERDs
+    (ERDInitSelf)&bar_initSelf, // initSelf
+    (ERDParseSelf)&bar_parseSelf, // parseSelf
+    (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+    (const char *)&data_compute_offsets._choice - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.foo - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.bar - (const char 
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+    &_choice_data_NestedUnionType_ERD,
+    &foo_data_NestedUnionType_ERD,
+    &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "data", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    data_offsets, // offsets
+    data_childrenERDs, // childrenERDs

Review comment:
       You're looking at data_childrenERDs which has 3 (not 2) child ERDs in 
it.  The 1st child ERD is for the _choice field preceding the choice group and 
the 2nd & 3rd child ERDs are for each of the 2 alternative elements in data's 
choice group.  I made the assumption that a choice group's ERD would have no 
other child ERDs in it, but we can have more child elements preceding / 
following the _choice field and union field if we inline anonymous choice 
groups into the parent group.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -660,10 +559,10 @@ littleEndian_unparseSelf(const littleEndian *instance, 
UState *ustate)
 static void
 ex_nums_initSelf(ex_nums *instance)
 {
+    instance->_base.erd = &ex_nums_ERD;
     array_initSelf(&instance->array);
     bigEndian_initSelf(&instance->bigEndian);
     littleEndian_initSelf(&instance->littleEndian);
-    instance->_base.erd = &ex_nums__ERD;
 }
 

Review comment:
       Test cases don't use those src/main/resources/examples files.  I keep 
them close to the rest of the main C files because they serve as examples of 
what the code generator will create as well as scaffolding for further changes 
to be made.  For example, I manually edited ex_nums.h and ex_nums.c to include 
arrays, figured out what the C code for parsing and unparsing arrays should 
look like, and finally made corresponding changes to the code generator so I 
could replace ex_nums.[ch] with the generator's automatically generated code.  
I also included NestedUnion.[ch] in the pull request to 1) show everyone what 
generated code for choice groups looks like, and 2) let us make changes to that 
code manually in the future if necessary.
   
   Saying handCrafted isn't quite the right name.  Every example is generated 
by running "daffodil generate c" on a corresponding DFDL schema in the test 
resources.  Sometimes I hand craft changes to those examples, but I end up 
replacing them with the generated code in my pull request to let the examples 
show what the generated code really looks like.  I do have to rename the files 
(including the #include <generated_code.h> header) and add the Apache copyright 
each time I replace the files.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -269,186 +211,137 @@ static const ptrdiff_t bigEndian_offsets[10] = {
 };
 
 static const ERD *bigEndian_childrenERDs[10] = {
-    &be_double_bigEndian_ex_nums__ERD,
-    &be_float_bigEndian_ex_nums__ERD,
-    &be_uint64_bigEndian_ex_nums__ERD,
-    &be_uint32_bigEndian_ex_nums__ERD,
-    &be_uint16_bigEndian_ex_nums__ERD,
-    &be_uint8_bigEndian_ex_nums__ERD,
-    &be_int64_bigEndian_ex_nums__ERD,
-    &be_int32_bigEndian_ex_nums__ERD,
-    &be_int16_bigEndian_ex_nums__ERD,
-    &be_int8_bigEndian_ex_nums__ERD
-};
-
-static const ERD bigEndian_ex_nums__ERD = {
+    &be_double_bigEndian_ex_nums_ERD,
+    &be_float_bigEndian_ex_nums_ERD,
+    &be_uint64_bigEndian_ex_nums_ERD,
+    &be_uint32_bigEndian_ex_nums_ERD,
+    &be_uint16_bigEndian_ex_nums_ERD,
+    &be_uint8_bigEndian_ex_nums_ERD,
+    &be_int64_bigEndian_ex_nums_ERD,
+    &be_int32_bigEndian_ex_nums_ERD,
+    &be_int16_bigEndian_ex_nums_ERD,
+    &be_int8_bigEndian_ex_nums_ERD
+};
+
+static const ERD bigEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "bigEndian", // namedQName.local
         NULL, // namedQName.ns
     },
-    COMPLEX,                         // typeCode
-    10,                               // numChildren
-    bigEndian_offsets,                      // offsets
-    bigEndian_childrenERDs,                 // childrenERDs
-    (ERDInitSelf)&bigEndian_initSelf,       // initSelf
-    (ERDParseSelf)&bigEndian_parseSelf,     // parseSelf
+    COMPLEX, // typeCode
+    10, // numChildren
+    bigEndian_offsets, // offsets
+    bigEndian_childrenERDs, // childrenERDs
+    (ERDInitSelf)&bigEndian_initSelf, // initSelf
+    (ERDParseSelf)&bigEndian_parseSelf, // parseSelf
     (ERDUnparseSelf)&bigEndian_unparseSelf, // unparseSelf
+    NULL // initChoice
 };
 
-static const ERD le_uint64_littleEndian_ex_nums__ERD = {
+static const ERD le_uint64_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_uint64", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_UINT64, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_uint32_littleEndian_ex_nums__ERD = {
+static const ERD le_uint32_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_uint32", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_UINT32, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_uint16_littleEndian_ex_nums__ERD = {
+static const ERD le_uint16_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_uint16", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_UINT16, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_uint8_littleEndian_ex_nums__ERD = {
+static const ERD le_uint8_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_uint8", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_UINT8, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_int64_littleEndian_ex_nums__ERD = {
+static const ERD le_int64_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_int64", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_INT64, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_int32_littleEndian_ex_nums__ERD = {
+static const ERD le_int32_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_int32", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_INT32, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_int16_littleEndian_ex_nums__ERD = {
+static const ERD le_int16_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_int16", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_INT16, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_int8_littleEndian_ex_nums__ERD = {
+static const ERD le_int8_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_int8", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_INT8, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_float_littleEndian_ex_nums__ERD = {
+static const ERD le_float_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_float", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_FLOAT, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-static const ERD le_double_littleEndian_ex_nums__ERD = {
+static const ERD le_double_littleEndian_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "le_double", // namedQName.local
         NULL, // namedQName.ns
     },
     PRIMITIVE_DOUBLE, // typeCode
-    0,               // numChildren
-    NULL,            // offsets
-    NULL,            // childrenERDs
-    NULL,            // initSelf
-    NULL,            // parseSelf
-    NULL,            // unparseSelf
+    0, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 static const littleEndian littleEndian_compute_offsets;
 
-static const ptrdiff_t littleEndian_offsets[10] = {
+static const size_t littleEndian_offsets[10] = {

Review comment:
       Are you saying that we could merge some offsets tables such as 
bigEndian_offsets and littleEndian_offsets together to save space since they 
have identical contents?  The DFDL schema has separate bigEndian and 
littleEndian elements so the C code generator generates everything for them 
separately.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -80,182 +71,133 @@ static const ptrdiff_t array_offsets[6] = {
 };
 
 static const ERD *array_childrenERDs[6] = {
-    &be_int16_array_ex_nums__ERD,
-    &be_int16_array_ex_nums__ERD,
-    &be_int16_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD
+    &be_int16_array_ex_nums_ERD,
+    &be_int16_array_ex_nums_ERD,
+    &be_int16_array_ex_nums_ERD,
+    &be_float_array_ex_nums_ERD,
+    &be_float_array_ex_nums_ERD,
+    &be_float_array_ex_nums_ERD
 };
 
-static const ERD array_ex_nums__ERD = {
+static const ERD array_ex_nums_ERD = {
     {
         NULL, // namedQName.prefix
         "array", // namedQName.local
         NULL, // namedQName.ns
     },
-    COMPLEX,                         // typeCode
-    6,                               // numChildren
-    array_offsets,                      // offsets
-    array_childrenERDs,                 // childrenERDs
-    (ERDInitSelf)&array_initSelf,       // initSelf
-    (ERDParseSelf)&array_parseSelf,     // parseSelf
+    COMPLEX, // typeCode
+    6, // numChildren

Review comment:
       Agreed, do you want me to make those changes in a future pull request or 
can you?  I would love to get some help :).
   

##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -80,182 +71,133 @@ static const ptrdiff_t array_offsets[6] = {
 };
 
 static const ERD *array_childrenERDs[6] = {
-    &be_int16_array_ex_nums__ERD,
-    &be_int16_array_ex_nums__ERD,
-    &be_int16_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD,
-    &be_float_array_ex_nums__ERD
+    &be_int16_array_ex_nums_ERD,

Review comment:
       I tried to handle arrays differently than scalars when looping over 
child elements in infoset.c's walkInfosetNode function.  However, I realized if 
I expanded arrays inline within childrenERDs, the loop wouldn't need any 
special case treatment of arrays.  Remember that the C language has less 
self-reflection capability than Java or Scala which makes it difficult to find 
the actual size of arrays using the `sizeof()` operator (arrays lose their 
compile time size information at runtime).  If we find a place to put an 
explicit array size somewhere in the ERD structure, I agree we could avoid 
expanding arrays inline.  I don't expect our collaborators to use very large 
arrays in their wire protocol packets, but once we start supporting other use 
cases that require very large arrays, we'll need to change the ERD structure.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
+#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
+#include <math.h>       // for NAN
+#include <stdbool.h>    // for bool, false, true
+#include <stddef.h>     // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState 
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "tag", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "_choice", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    CHOICE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "a", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "b", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "c", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+    (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+    &a_FooType_ERD,
+    &b_FooType_ERD,
+    &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "foo", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    foo_offsets, // offsets
+    foo_childrenERDs, // childrenERDs
+    (ERDInitSelf)&foo_initSelf, // initSelf
+    (ERDParseSelf)&foo_parseSelf, // parseSelf
+    (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "x", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "y", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "z", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+    (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+    &x_BarType_ERD,
+    &y_BarType_ERD,
+    &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "bar", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    bar_offsets, // offsets
+    bar_childrenERDs, // childrenERDs
+    (ERDInitSelf)&bar_initSelf, // initSelf
+    (ERDParseSelf)&bar_parseSelf, // parseSelf
+    (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+    (const char *)&data_compute_offsets._choice - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.foo - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.bar - (const char 
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+    &_choice_data_NestedUnionType_ERD,
+    &foo_data_NestedUnionType_ERD,
+    &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "data", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    data_offsets, // offsets
+    data_childrenERDs, // childrenERDs
+    (ERDInitSelf)&data_initSelf, // initSelf
+    (ERDParseSelf)&data_parseSelf, // parseSelf
+    (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+    (ERDInitChoice)&data_initChoice // initChoice

Review comment:
       The only ERD which has a non-null initChoice is a choice group which 
must choose a single child element from a set of alternative child elements.  
Is that your understanding, or are you thinking that the ERD for a _choice 
field would have a non-null initSelf/initChoice?  Right now the ERD for a 
_choice field is a CHOICE type with only a name defined for it and we always 
assign the NO_CHOICE constant to it when initializing it in its parent's 
initSelf:
   
   ```c
   static const ERD _choice_data_NestedUnionType_ERD = {
       {
           NULL, // namedQName.prefix
           "_choice", // namedQName.local
           NULL, // namedQName.ns
       },
       CHOICE, // typeCode
       0, NULL, NULL, NULL, NULL, NULL, NULL
   };
   ```
   
   It would be nice if we could merge initSelf and initChoice, but I see a 
couple of problems.  One problem is that initSelf and initChoice take different 
parameters.  The C code we generate from the choiceDispatchKey expression needs 
to start from the root element's base in order to return the value of the 
correct dispatch key field.  The second and more serious problem is that 
initSelf overwrites *every* C struct member field with "uninitialized" constant 
values.  We don't ever want to call initSelf again after initializing an 
infoset root instance, especially not when we are reading data into the 
infoset, walking the infoset, or writing data from the infoset.  An initSelf 
for a choice group calls initSelf on each of the alternative elements to set 
their fields to uninitialized constant values, while initChoice changes only 
the value in the _choice member field and the ERD pointer value in the 
following union child's _base.erd member field.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
+#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
+#include <math.h>       // for NAN
+#include <stdbool.h>    // for bool, false, true
+#include <stddef.h>     // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState 
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "tag", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "_choice", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    CHOICE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "a", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "b", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "c", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+    (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+    &a_FooType_ERD,
+    &b_FooType_ERD,
+    &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "foo", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    foo_offsets, // offsets
+    foo_childrenERDs, // childrenERDs
+    (ERDInitSelf)&foo_initSelf, // initSelf
+    (ERDParseSelf)&foo_parseSelf, // parseSelf
+    (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "x", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "y", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "z", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+    (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+    &x_BarType_ERD,
+    &y_BarType_ERD,
+    &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "bar", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    bar_offsets, // offsets
+    bar_childrenERDs, // childrenERDs
+    (ERDInitSelf)&bar_initSelf, // initSelf
+    (ERDParseSelf)&bar_parseSelf, // parseSelf
+    (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+    (const char *)&data_compute_offsets._choice - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.foo - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.bar - (const char 
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+    &_choice_data_NestedUnionType_ERD,
+    &foo_data_NestedUnionType_ERD,
+    &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "data", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    data_offsets, // offsets
+    data_childrenERDs, // childrenERDs
+    (ERDInitSelf)&data_initSelf, // initSelf
+    (ERDParseSelf)&data_parseSelf, // parseSelf
+    (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+    (ERDInitChoice)&data_initChoice // initChoice
+};
+
+static const NestedUnion NestedUnion_compute_offsets;
+
+static const size_t NestedUnion_offsets[2] = {
+    (const char *)&NestedUnion_compute_offsets.tag - (const char 
*)&NestedUnion_compute_offsets,
+    (const char *)&NestedUnion_compute_offsets.data - (const char 
*)&NestedUnion_compute_offsets
+};
+
+static const ERD *NestedUnion_childrenERDs[2] = {
+    &tag_NestedUnionType_ERD,
+    &data_NestedUnionType_ERD
+};
+
+static const ERD NestedUnion_ERD = {
+    {
+        "idl", // namedQName.prefix
+        "NestedUnion", // namedQName.local
+        "urn:idl:1.0", // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    NestedUnion_offsets, // offsets
+    NestedUnion_childrenERDs, // childrenERDs
+    (ERDInitSelf)&NestedUnion_initSelf, // initSelf
+    (ERDParseSelf)&NestedUnion_parseSelf, // parseSelf
+    (ERDUnparseSelf)&NestedUnion_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+// Return a root element to be used for parsing or unparsing
+
+InfosetBase *
+rootElement(void)
+{
+    static bool initialized;
+    static NestedUnion root;
+    if (!initialized)
+    {
+        NestedUnion_initSelf(&root);
+        initialized = true;
+    }
+    return &root._base;
+}
+
+// Methods to initialize, parse, and unparse infoset nodes
+
+static void
+foo_initSelf(foo *instance)
+{
+    instance->_base.erd = &foo_data_NestedUnionType_ERD;
+    instance->a = 0xCCCCCCCC;
+    instance->b = 0xCCCCCCCC;
+    instance->c = 0xCCCCCCCC;
+}
+
+static void
+foo_parseSelf(foo *instance, PState *pstate)
+{
+    parse_be_int32(&instance->a, pstate);
+    parse_be_int32(&instance->b, pstate);
+    parse_be_int32(&instance->c, pstate);
+}
+
+static void
+foo_unparseSelf(const foo *instance, UState *ustate)
+{
+    unparse_be_int32(instance->a, ustate);
+    unparse_be_int32(instance->b, ustate);
+    unparse_be_int32(instance->c, ustate);
+}
+
+static void
+bar_initSelf(bar *instance)
+{
+    instance->_base.erd = &bar_data_NestedUnionType_ERD;
+    instance->x = NAN;
+    instance->y = NAN;
+    instance->z = NAN;
+}
+
+static void
+bar_parseSelf(bar *instance, PState *pstate)
+{
+    parse_be_double(&instance->x, pstate);
+    parse_be_double(&instance->y, pstate);
+    parse_be_double(&instance->z, pstate);
+}
+
+static void
+bar_unparseSelf(const bar *instance, UState *ustate)
+{
+    unparse_be_double(instance->x, ustate);
+    unparse_be_double(instance->y, ustate);
+    unparse_be_double(instance->z, ustate);
+}
+
+static void
+data_initSelf(data *instance)
+{
+    instance->_base.erd = &data_NestedUnionType_ERD;
+    instance->_choice = NO_CHOICE;
+    foo_initSelf(&instance->foo);
+    bar_initSelf(&instance->bar);
+}
+
+static bool
+data_initChoice(data *instance, const NestedUnion *rootElement)
+{
+    int64_t key = rootElement->tag;
+    switch (key)
+    {
+    case 1:
+    case 2:
+        instance->_choice = 0;
+        break;
+    case 3:
+    case 4:
+        instance->_choice = 1;
+        break;
+    default:
+        instance->_choice = NO_CHOICE;
+        break;
+    }
+
+    if (instance->_choice != NO_CHOICE)
+    {
+        const size_t choice = instance->_choice + 1; // skip the _choice field
+        const size_t offset = instance->_base.erd->offsets[choice];

Review comment:
       You're looking at the initChoice function's code following a dispatch 
key switch statement.  The preceding switch statement has just computed the 
choice value used to index into the offsets & ERDs of the choice group's 
children and the code you're now looking at is about to update the ERD pointer 
value in the union child's _base.erd member field.
   

##########
File path: 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
     qnameInit
   }
 
+  /**
+   * We want to convert a choiceDispatchKey expression into C struct dot
+   * notation (rootElement->[subElement.field]) which will access the C
+   * struct field containing the choiceDispatchKey's runtime value.
+   *
+   * We make some assumptions to make generating the dot notation easier:
+   * - the expression starts with '{xs:string( and ends with )}'
+   * - the expression returns the value of a previous element without
+   *   changing the value in any way (except converting it to xs:string)
+   * - both the expression and the C code use only local names (for now...)
+   * - we can map the context node's path to a Unix-like slash path
+   * - all dpath operations look like Unix-like relative paths (../tag)
+   * - we can normalize the new path and convert it to C struct dot notation
+   * - we can store the accessed value in an int64_t local variable safely
+   */
+  private def choiceDispatchField(context: ElementBase): String = {
+    // We want to use SchemaComponent.scPath but it's private so duplicate it 
here (for now...)
+    def scPath(sc: SchemaComponent): Seq[SchemaComponent] = 
sc.optLexicalParent.map { scPath }.getOrElse(Nil) :+ sc
+    val localNames = scPath(context).map {
+      case er: AbstractElementRef => er.refQName.local
+      case e: ElementBase => e.namedQName.local
+      case ed: GlobalElementDecl => ed.namedQName.local
+      case _ => ""
+    }
+    val absoluteSlashPath = localNames.mkString("/")
+    val dispatchSlashPath = context.complexType.modelGroup match {
+      case choice: Choice if choice.isDirectDispatch =>
+        val expr = choice.choiceDispatchKeyEv.expr.toBriefXML()
+        val before = "'{xs:string("
+        val after = ")}'"
+        val relativePath = if (expr.startsWith(before) && expr.endsWith(after))
+          expr.substring(before.length, expr.length - after.length) else expr
+        val normalizedURI = new URI(absoluteSlashPath + "/" + 
relativePath).normalize
+        normalizedURI.getPath.substring(1)
+      case _ => ""
+    }
+    // Strip namespace prefixes since C code uses only local names (for now...)
+    val localDispatchSlashPath = dispatchSlashPath.replaceAll("/[^:]+:", "/")
+    val res = localDispatchSlashPath.replace('/', '.')
+    res
+  }
+
+  def addBeforeSwitchStatements(context: ElementBase): Unit = {
+    val erd = erdName(context)
+    val initStatement = s"    instance->_base.erd = &$erd;"
+
+    structs.top.initStatements += initStatement
+
+    val dispatchField = choiceDispatchField(context)
+    if (dispatchField.nonEmpty) {
+      val C = localName(context)
+      val declaration =
+        s"""    size_t      _choice; // choice of which union field to use
+           |    union
+           |    {""".stripMargin
+      val erdDef =
+        s"""static const ERD _choice_$erd = {
+           |    {
+           |        NULL, // namedQName.prefix
+           |        "_choice", // namedQName.local
+           |        NULL, // namedQName.ns
+           |    },
+           |    CHOICE, // typeCode
+           |    0, NULL, NULL, NULL, NULL, NULL, NULL
+           |};
+           |""".stripMargin
+      val offsetComputation = s"    (const char 
*)&${C}_compute_offsets._choice - (const char *)&${C}_compute_offsets"
+      val erdComputation = s"    &_choice_$erd"
+      val initStatement = s"    instance->_choice = NO_CHOICE;"
+      val initChoiceStatement =
+        s"""    int64_t key = rootElement->$dispatchField;
+           |    switch (key)
+           |    {""".stripMargin
+      val parseStatement =
+        s"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+           |    switch (instance->_choice)
+           |    {""".stripMargin
+      val unparseStatement =
+        s"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+           |    switch (instance->_choice)
+           |    {""".stripMargin
+
+      erds += erdDef
+      structs.top.declarations += declaration
+      structs.top.offsetComputations += offsetComputation
+      structs.top.erdComputations += erdComputation
+      structs.top.initStatements += initStatement
+      structs.top.initChoiceStatements += initChoiceStatement
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+  }
+
+  def addAfterSwitchStatements(): Unit = {
+    if (structs.top.initChoiceStatements.nonEmpty) {
+      val declaration = s"    };"
+      val initChoiceStatement =
+        s"""    default:
+           |        instance->_choice = NO_CHOICE;
+           |        break;
+           |    }
+           |
+           |    if (instance->_choice != NO_CHOICE)
+           |    {
+           |        const size_t choice = instance->_choice + 1; // skip the 
_choice field
+           |        const size_t offset = instance->_base.erd->offsets[choice];
+           |        const ERD *  childERD = 
instance->_base.erd->childrenERDs[choice];
+           |        InfosetBase *childNode = (InfosetBase *)((const char 
*)instance + offset);
+           |        childNode->erd = childERD;
+           |        return true;
+           |    }
+           |    else
+           |    {
+           |        return false;
+           |    }""".stripMargin
+      val parseStatement =
+        s"""    default:
+           |        pstate->error_msg = "node's _choice field was not 
initialized during parsing";

Review comment:
       This place is where we report a processing error at runtime.  It's not 
where we report an error that comes from users making mistakes writing schemas, 
although it's possible that a failure of the implementation could allow some 
errors to result in processing errors that should have been reported as schema 
definition errors sooner.
   

##########
File path: 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
     qnameInit
   }
 
+  /**
+   * We want to convert a choiceDispatchKey expression into C struct dot
+   * notation (rootElement->[subElement.field]) which will access the C
+   * struct field containing the choiceDispatchKey's runtime value.
+   *

Review comment:
       Yes, let's modify the DPath expression compiler to generate C code in a 
future pull request.
   

##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
+#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
+#include <math.h>       // for NAN
+#include <stdbool.h>    // for bool, false, true
+#include <stddef.h>     // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState 
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "tag", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "_choice", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    CHOICE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "a", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "b", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "c", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+    (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+    (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+    &a_FooType_ERD,
+    &b_FooType_ERD,
+    &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "foo", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    foo_offsets, // offsets
+    foo_childrenERDs, // childrenERDs
+    (ERDInitSelf)&foo_initSelf, // initSelf
+    (ERDParseSelf)&foo_parseSelf, // parseSelf
+    (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "x", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "y", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "z", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+    (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+    (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+    &x_BarType_ERD,
+    &y_BarType_ERD,
+    &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "bar", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    3, // numChildren
+    bar_offsets, // offsets
+    bar_childrenERDs, // childrenERDs
+    (ERDInitSelf)&bar_initSelf, // initSelf
+    (ERDParseSelf)&bar_parseSelf, // parseSelf
+    (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+    (const char *)&data_compute_offsets._choice - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.foo - (const char 
*)&data_compute_offsets,
+    (const char *)&data_compute_offsets.bar - (const char 
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+    &_choice_data_NestedUnionType_ERD,
+    &foo_data_NestedUnionType_ERD,
+    &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "data", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    data_offsets, // offsets
+    data_childrenERDs, // childrenERDs
+    (ERDInitSelf)&data_initSelf, // initSelf
+    (ERDParseSelf)&data_parseSelf, // parseSelf
+    (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+    (ERDInitChoice)&data_initChoice // initChoice
+};
+
+static const NestedUnion NestedUnion_compute_offsets;
+
+static const size_t NestedUnion_offsets[2] = {
+    (const char *)&NestedUnion_compute_offsets.tag - (const char 
*)&NestedUnion_compute_offsets,
+    (const char *)&NestedUnion_compute_offsets.data - (const char 
*)&NestedUnion_compute_offsets
+};
+
+static const ERD *NestedUnion_childrenERDs[2] = {
+    &tag_NestedUnionType_ERD,
+    &data_NestedUnionType_ERD
+};
+
+static const ERD NestedUnion_ERD = {
+    {
+        "idl", // namedQName.prefix
+        "NestedUnion", // namedQName.local
+        "urn:idl:1.0", // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    2, // numChildren
+    NestedUnion_offsets, // offsets
+    NestedUnion_childrenERDs, // childrenERDs
+    (ERDInitSelf)&NestedUnion_initSelf, // initSelf
+    (ERDParseSelf)&NestedUnion_parseSelf, // parseSelf
+    (ERDUnparseSelf)&NestedUnion_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+// Return a root element to be used for parsing or unparsing
+
+InfosetBase *
+rootElement(void)
+{
+    static bool initialized;
+    static NestedUnion root;
+    if (!initialized)
+    {
+        NestedUnion_initSelf(&root);
+        initialized = true;
+    }
+    return &root._base;
+}
+
+// Methods to initialize, parse, and unparse infoset nodes
+
+static void
+foo_initSelf(foo *instance)
+{
+    instance->_base.erd = &foo_data_NestedUnionType_ERD;
+    instance->a = 0xCCCCCCCC;
+    instance->b = 0xCCCCCCCC;
+    instance->c = 0xCCCCCCCC;
+}
+
+static void
+foo_parseSelf(foo *instance, PState *pstate)
+{
+    parse_be_int32(&instance->a, pstate);
+    parse_be_int32(&instance->b, pstate);
+    parse_be_int32(&instance->c, pstate);
+}
+
+static void
+foo_unparseSelf(const foo *instance, UState *ustate)
+{
+    unparse_be_int32(instance->a, ustate);
+    unparse_be_int32(instance->b, ustate);
+    unparse_be_int32(instance->c, ustate);
+}
+
+static void
+bar_initSelf(bar *instance)
+{
+    instance->_base.erd = &bar_data_NestedUnionType_ERD;
+    instance->x = NAN;
+    instance->y = NAN;
+    instance->z = NAN;
+}
+
+static void
+bar_parseSelf(bar *instance, PState *pstate)
+{
+    parse_be_double(&instance->x, pstate);
+    parse_be_double(&instance->y, pstate);
+    parse_be_double(&instance->z, pstate);
+}
+
+static void
+bar_unparseSelf(const bar *instance, UState *ustate)
+{
+    unparse_be_double(instance->x, ustate);
+    unparse_be_double(instance->y, ustate);
+    unparse_be_double(instance->z, ustate);
+}
+
+static void
+data_initSelf(data *instance)
+{
+    instance->_base.erd = &data_NestedUnionType_ERD;
+    instance->_choice = NO_CHOICE;
+    foo_initSelf(&instance->foo);
+    bar_initSelf(&instance->bar);
+}
+
+static bool
+data_initChoice(data *instance, const NestedUnion *rootElement)
+{
+    int64_t key = rootElement->tag;
+    switch (key)
+    {
+    case 1:
+    case 2:
+        instance->_choice = 0;
+        break;
+    case 3:
+    case 4:
+        instance->_choice = 1;
+        break;
+    default:
+        instance->_choice = NO_CHOICE;
+        break;
+    }
+
+    if (instance->_choice != NO_CHOICE)
+    {
+        const size_t choice = instance->_choice + 1; // skip the _choice field
+        const size_t offset = instance->_base.erd->offsets[choice];
+        const ERD *  childERD = instance->_base.erd->childrenERDs[choice];
+        InfosetBase *childNode = (InfosetBase *)((const char *)instance + 
offset);
+        childNode->erd = childERD;
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+static void
+data_parseSelf(data *instance, PState *pstate)
+{
+    instance->_base.erd->initChoice(&instance->_base, rootElement());
+    switch (instance->_choice)
+    {
+    case 0:
+        foo_parseSelf(&instance->foo, pstate);
+        break;
+    case 1:
+        bar_parseSelf(&instance->bar, pstate);
+        break;
+    default:
+        pstate->error_msg = "node's _choice field was not initialized during 
parsing";

Review comment:
       Actually, there's a way to cause a processing error during parse.  Note 
that a person using the DFDL schema could store a value in the dispatch key 
field which has no valid mapping to any branch key of the choice group's 
alternative child elements.  Daffodil's runtime1 reports a processing error 
when this happens in a binary data file, "ChoiceDispatchNoMatch: Parse Error: 
Choice dispatch key (0) failed to match any of the branch keys".  Note 
Daffodil's runtime1 unparses an infoset containing an invalid dispatch key 
value without reporting a processing error (it doesn't check whether the 
mapping is valid).  However, I think it's fine for Daffodil's runtime2 to 
perform more strict checking during parse, unparse, and walk.  I've revised 
each error message to be more clear what the problem is as well:
   
   ```c
       pstate->error_msg =
           "Parse error: no match between choice dispatch key and any branch 
key";
   ```
   

##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -568,6 +467,7 @@ array_unparseSelf(const array *instance, UState *ustate)
 static void
 bigEndian_initSelf(bigEndian *instance)

Review comment:
       I wanted two separate elements in the TestRuntime2 DFDL schema where all 
the integers and floating point numbers were either bigEndian or littleEndian 
to ensure more complete test coverage of the generated C code.  I explicitly 
named these elements "bigEndian" and "littleEndian" but the names could have 
been "Bar" and "Foo" instead.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to