You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ji...@apache.org on 2021/04/16 20:52:19 UTC

[daffodil] 01/01: Add C code generator and runtime2 backend

This is an automated email from the ASF dual-hosted git repository.

jinterrante pushed a commit to branch runtime2-2202
in repository https://gitbox.apache.org/repos/asf/daffodil.git

commit 32ea29a146c139c943b43397f33ee06c8ea42a93
Author: John Interrante <in...@research.ge.com>
AuthorDate: Sun Sep 20 11:42:14 2020 -0400

    Add C code generator and runtime2 backend
    
    Add a new runtime2 backend to Daffodil with a C code generator and
    libcli/libruntime libraries.  The C code generator implements a small
    but still functional subset of DFDL 1.0 for binary data:
    
    - binary boolean, integer, and real numbers
    - arrays of simple and complex elements
    - choice groups using dispatch/branch keys
    - validation of "fixed" attributes
    - padding of explicit length complex elements with fill bytes
    
    Also add a new CLI subcommand "daffodil generate" to call the code
    generator and modify the CLI subcommand "daffodil test" to support
    running TDML tests with daffodil-runtime2.
    
    We are documenting reviewer requested changes in [Runtime2
    ToDos](https://daffodil.apache.org/dev/design-notes/runtime2-todos/).
    We plan to address these changes while continuing to build out the C
    code generator to implement more of the "Runtime 2 Design" table in
    <https://cwiki.apache.org/confluence/display/DAFFODIL/WIP%3A+Daffodil+Runtime+2>.
    Build requirements and instructions are in the top-level README.md and
    BUILD.md files.
    
    DAFFODIL-2202
    ______________________________________________________________________
    
    Changelog for selected files:
    
    main.yml - Use msys2 shell on windows, install runtime2 build
    requirements, use explicit versions for actions and oses.
    
    sonarclound.yml - Install runtime2 build requirements, use explicit
    versions for actions and oses.
    
    README.md - Fix or disable markdownlint warnings, wrap paragraphs, add
    runtime2 build requirements, sort hyperlink tags alphabetically.
    
    build.sbt - Add runtime2 project, allow environment variables CC and
    AR to override cCompiler and ccArchiveCommand's default settings,
    ensure C compiler test-builds C files by doing a recursive glob on "c"
    and "examples", make tdmlProc and cli depend on runtime2 because they
    call it, and add runtime2 to daffodil-test's test classpath.
    
    bin.LICENSE - Add os-lib license.
    
    Main.scala - Add generate subcommand option.
    
    Compiler.scala - Make ProcessorFactory.forLanguage call runtime2 using
    Class.forName to avoid core dependency on runtime2.
    
    ElementBase.scala, ElementDeclMixin.scala, ElementRef.scala - Add DSOM
    getters for fixedAttr, hasFixedValue, fixedValueAsString & fixedValue,
    make IDEA recommended fixes.
    
    ElementBaseGrammarMixin.scala - Make IDEA recommended fixes.
    
    ElementDeclGrammarMixin.scala - Give Runtime2CodeGenerator access to
    RootGrammarMixin.documentElement.
    
    Grammar.scala - Give Runtime2CodeGenerator access to
    BinaryGram.children.
    
    GrammarTerm.scala - Allow subclasses to override Gram.context and
    change its type from SchemaComponent to ElementBase.
    
    Production.scala - Give Runtime2CodeGenerator access to Prod.guard.
    
    ElementCombinator.scala - Give Runtime2CodeGenerator access to
    ElementParseAndUnspecifiedLength.{context, eBeforeContext, eValue,
    eAfterValue, repTypeElementGram, subComb}, make IDEA recommended
    fixes.
    
    PrimitivesBinaryNumber.scala - Give Runtime2CodeGenerator access to
    BinaryIntegerKnownLength.{signed, lengthInBits}.
    
    SequenceCombinator.scala - Give Runtime2CodeGenerator access to
    OrderedSequence.sequenceChildren, make IDEA recommended fixes.
    
    tdml.xsd - Add daffodil-runtime2 to tdml:{defaultImplementations,
    implementations}.
    
    dafext.xsd - Add tdmlImplementation to tunables.
    
    DFDLParserUnparser.scala - Add ProcessorFactory.forLanguage, trait
    CodeGenerator, split half of DataProcessor into DataProcessorBase.
    
    DataProcessor.scala - Minor cleanups.
    
    TDMLRunner.scala - Create TDMLDFDLProcessorFactory or
    Runtime2TDMLDFDLProcessorFactory object depending on
    tdmlImplementation tunable and call cleanUp on parse/unparse results.
    
    TDMLDFDLProcessor.scala - Add TDMLResult.cleanUp abstract function.
    
    DaffodilTDMLDFDLProcessor.scala - Implement
    DaffodilTDMLParseResult.cleanUp and DaffodilTDMLUnparseResult.cleanUp.
    
    Runtime2TDMLDFDLProcessor.scala - Run TDML tests on runtime2 backend.
    Use cleanUp to remove all temporary files.
    
    choice-unparse2.tdml - Change model name from "c" to "c1" to avoid
    conflict with daffodil-runtime2's "c" resources directory.
    
    Dependencies.scala - Add os-lib dependency for writing C files.
    
    Rat.scala - Ignore runtime2 tests' binary data files.
    
    plugins.sbt - Add sbt-cc plugin so sbt can test-build C files.
    
    sonar-project.properties - Make scan ignore C files since SonarCloud
    CLI Docker action can't analyze them anyway.  Also prevent some scan
    warnings.
---
 .github/workflows/main.yml                         |   57 +-
 .github/workflows/sonarcloud.yml                   |   28 +-
 BUILD.md                                           |  114 +++
 README.md                                          |   98 +-
 build.sbt                                          |   41 +-
 daffodil-cli/bin.LICENSE                           |   31 +
 .../daffodil/generating/TestCLIGenerateC.scala     |  198 ++++
 .../src/main/scala/org/apache/daffodil/Main.scala  |  102 +-
 .../org/apache/daffodil/compiler/Compiler.scala    |   32 +-
 .../org/apache/daffodil/dsom/ElementBase.scala     |   82 +-
 .../apache/daffodil/dsom/ElementDeclMixin.scala    |    9 +
 .../org/apache/daffodil/dsom/ElementRef.scala      |    2 +
 .../daffodil/grammar/ElementBaseGrammarMixin.scala |    9 +-
 .../daffodil/grammar/ElementDeclGrammarMixin.scala |    5 +-
 .../org/apache/daffodil/grammar/Grammar.scala      |    6 +-
 .../org/apache/daffodil/grammar/GrammarTerm.scala  |    2 +-
 .../org/apache/daffodil/grammar/Production.scala   |    6 +-
 .../grammar/primitives/ElementCombinator.scala     |   39 +-
 .../primitives/PrimitivesBinaryNumber.scala        |    4 +-
 .../grammar/primitives/SequenceCombinator.scala    |   14 +-
 .../resources/org/apache/daffodil/xsd/tdml.xsd     |    1 +
 .../resources/org/apache/daffodil/xsd/dafext.xsd   |    8 +
 .../apache/daffodil/api/DFDLParserUnparser.scala   |   40 +-
 .../apache/daffodil/processors/DataProcessor.scala |   14 +-
 daffodil-runtime2/src/main/resources/.clang-format |   24 +
 daffodil-runtime2/src/main/resources/c/Makefile    |   55 ++
 .../src/main/resources/c/libcli/daffodil_argp.c    |  300 ++++++
 .../src/main/resources/c/libcli/daffodil_argp.h    |   46 +-
 .../src/main/resources/c/libcli/daffodil_main.c    |  146 +++
 .../src/main/resources/c/libcli/stack.c            |   87 ++
 .../src/main/resources/c/libcli/stack.h            |   62 ++
 .../src/main/resources/c/libcli/xml_reader.c       |  414 ++++++++
 .../src/main/resources/c/libcli/xml_reader.h       |   30 +-
 .../src/main/resources/c/libcli/xml_writer.c       |  204 ++++
 .../src/main/resources/c/libcli/xml_writer.h       |   28 +-
 .../src/main/resources/c/libruntime/errors.c       |  203 ++++
 .../src/main/resources/c/libruntime/errors.h       |  129 +++
 .../src/main/resources/c/libruntime/infoset.c      |  198 ++++
 .../src/main/resources/c/libruntime/infoset.h      |  120 +++
 .../src/main/resources/c/libruntime/parsers.c      |  167 ++++
 .../src/main/resources/c/libruntime/parsers.h      |   70 ++
 .../src/main/resources/c/libruntime/unparsers.c    |  151 +++
 .../src/main/resources/c/libruntime/unparsers.h    |   78 ++
 .../src/main/resources/examples/NestedUnion.c      |  442 +++++++++
 .../src/main/resources/examples/NestedUnion.h      |   62 ++
 .../src/main/resources/examples/ex_nums.c          | 1008 ++++++++++++++++++++
 .../src/main/resources/examples/ex_nums.h          |   96 ++
 .../apache/daffodil/runtime2/CodeGenerator.scala   |  190 ++++
 .../daffodil/runtime2/Runtime2CodeGenerator.scala  |   82 ++
 .../daffodil/runtime2/Runtime2DataProcessor.scala  |  207 ++++
 .../generators/BinaryAbstractCodeGenerator.scala   |   69 ++
 .../generators/BinaryBooleanCodeGenerator.scala    |   45 +
 .../generators/BinaryFloatCodeGenerator.scala      |   26 +-
 .../BinaryIntegerKnownLengthCodeGenerator.scala    |   42 +
 .../runtime2/generators/CodeGeneratorState.scala   |  635 ++++++++++++
 ...entParseAndUnspecifiedLengthCodeGenerator.scala |   59 ++
 .../generators/OrderedSequenceCodeGenerator.scala  |   24 +-
 .../runtime2/generators/SeqCompCodeGenerator.scala |   20 +-
 .../org/apache/daffodil/runtime2/ex_nums.dfdl.xsd  |  161 ++++
 .../org/apache/daffodil/runtime2/ex_nums.tdml      |  102 ++
 .../org/apache/daffodil/runtime2/ex_nums_parse.dat |  Bin 0 -> 159 bytes
 .../daffodil/runtime2/ex_nums_unparse_errors.xml   |   72 ++
 .../daffodil/runtime2/ex_nums_unparse_runtime1.xml |   72 ++
 .../daffodil/runtime2/ex_nums_unparse_runtime2.xml |   72 ++
 .../org/apache/daffodil/runtime2/nested.dfdl.xsd   |   84 ++
 .../org/apache/daffodil/runtime2/nested.tdml       |   94 ++
 .../daffodil/runtime2/nested_struct_parse.dat      |  Bin 0 -> 36 bytes
 .../daffodil/runtime2/nested_struct_unparse.xml    |   32 +
 .../daffodil/runtime2/nested_union_parse_2.dat     |  Bin 0 -> 16 bytes
 .../daffodil/runtime2/nested_union_parse_4.dat     |  Bin 0 -> 28 bytes
 .../daffodil/runtime2/nested_union_unparse_2.xml   |   28 +
 .../daffodil/runtime2/nested_union_unparse_4.xml   |   28 +
 .../daffodil/runtime2/TestCodeGenerator.scala      |  139 +++
 .../org/apache/daffodil/tdml/TDMLRunner.scala      |   52 +-
 .../tdml/processor/TDMLDFDLProcessor.scala         |    5 +-
 .../tdml/processor/DaffodilTDMLDFDLProcessor.scala |    5 +
 .../tdml/processor/Runtime2TDMLDFDLProcessor.scala |  237 +++++
 .../runtime2/ISRM_green_to_orange_60000.dfdl.xsd   |  107 +++
 .../runtime2/ISRM_green_to_orange_60000.tdml       |   89 ++
 .../ISRM_green_to_orange_60000_parse_0.dat         |  Bin 0 -> 212 bytes
 .../ISRM_green_to_orange_60000_parse_1.dat         |  Bin 0 -> 212 bytes
 .../ISRM_green_to_orange_60000_unparse_0.xml       |  195 ++++
 .../ISRM_green_to_orange_60000_unparse_1.xml       |   24 +
 .../runtime2/ISRM_orange_to_green_60002.dfdl.xsd   |   73 ++
 .../runtime2/ISRM_orange_to_green_60002.tdml       |   65 ++
 .../runtime2/ISRM_orange_to_green_60002_parse.dat  |  Bin 0 -> 80 bytes
 .../ISRM_orange_to_green_60002_unparse.xml         |  102 ++
 .../runtime2/MPU_green_to_orange_60004.dfdl.xsd    |   74 ++
 .../runtime2/MPU_green_to_orange_60004.tdml        |   65 ++
 .../runtime2/MPU_green_to_orange_60004_parse.dat   |  Bin 0 -> 81 bytes
 .../runtime2/MPU_green_to_orange_60004_unparse.xml |  103 ++
 .../runtime2/MPU_orange_to_green_60006.dfdl.xsd    |  163 ++++
 .../runtime2/MPU_orange_to_green_60006.tdml        |   89 ++
 .../runtime2/MPU_orange_to_green_60006_parse_0.dat |  Bin 0 -> 240 bytes
 .../runtime2/MPU_orange_to_green_60006_parse_1.dat |  Bin 0 -> 240 bytes
 .../MPU_orange_to_green_60006_unparse_0.xml        |  219 +++++
 .../MPU_orange_to_green_60006_unparse_1.xml        |   24 +
 .../daffodil/runtime2/aptina_settings_parse.dat    |  Bin 0 -> 21 bytes
 .../daffodil/runtime2/aptina_settings_unparse.xml  |   36 +
 .../daffodil/runtime2/camera_state_parse.dat       |  Bin 0 -> 11 bytes
 .../daffodil/runtime2/camera_state_unparse.xml     |   28 +
 .../org/apache/daffodil/runtime2/command_parse.dat |  Bin 0 -> 13 bytes
 .../apache/daffodil/runtime2/command_unparse.xml   |   30 +
 .../daffodil/runtime2/egress_xdcc_bw.dfdl.xsd      |  244 +++++
 .../apache/daffodil/runtime2/egress_xdcc_bw.tdml   |  300 ++++++
 .../daffodil/runtime2/egress_xdcc_bw_parse_11.dat  |  Bin 0 -> 36 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_12.dat  |  Bin 0 -> 28 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_13.dat  |  Bin 0 -> 120 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_14.dat  |  Bin 0 -> 28 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_15.dat  |  Bin 0 -> 52 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_16.dat  |  Bin 0 -> 28 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_17.dat  |  Bin 0 -> 56 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_18.dat  |  Bin 0 -> 28 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_19.dat  |  Bin 0 -> 744 bytes
 .../daffodil/runtime2/egress_xdcc_bw_parse_20.dat  |  Bin 0 -> 28 bytes
 .../runtime2/egress_xdcc_bw_unparse_11.xml         |   44 +
 .../runtime2/egress_xdcc_bw_unparse_12.xml         |   42 +
 .../runtime2/egress_xdcc_bw_unparse_13.xml         |  137 +++
 .../runtime2/egress_xdcc_bw_unparse_14.xml         |   42 +
 .../runtime2/egress_xdcc_bw_unparse_15.xml         |   45 +
 .../runtime2/egress_xdcc_bw_unparse_16.xml         |   42 +
 .../runtime2/egress_xdcc_bw_unparse_17.xml         |   73 ++
 .../runtime2/egress_xdcc_bw_unparse_18.xml         |   42 +
 .../runtime2/egress_xdcc_bw_unparse_19.xml         |  638 +++++++++++++
 .../runtime2/egress_xdcc_bw_unparse_20.xml         |   42 +
 .../daffodil/runtime2/ingress_xdcc_bw.dfdl.xsd     |  202 ++++
 .../apache/daffodil/runtime2/ingress_xdcc_bw.tdml  |  196 ++++
 .../runtime2/ingress_xdcc_bw_parse_111.dat         |  Bin 0 -> 36 bytes
 .../runtime2/ingress_xdcc_bw_parse_112.dat         |  Bin 0 -> 28 bytes
 .../runtime2/ingress_xdcc_bw_parse_113.dat         |  Bin 0 -> 120 bytes
 .../runtime2/ingress_xdcc_bw_parse_114.dat         |  Bin 0 -> 28 bytes
 .../runtime2/ingress_xdcc_bw_parse_115.dat         |  Bin 0 -> 1184 bytes
 .../runtime2/ingress_xdcc_bw_parse_116.dat         |  Bin 0 -> 28 bytes
 .../runtime2/ingress_xdcc_bw_unparse_111.xml       |   44 +
 .../runtime2/ingress_xdcc_bw_unparse_112.xml       |   42 +
 .../runtime2/ingress_xdcc_bw_unparse_113.xml       |  137 +++
 .../runtime2/ingress_xdcc_bw_unparse_114.xml       |   42 +
 .../runtime2/ingress_xdcc_bw_unparse_115.xml       |  859 +++++++++++++++++
 .../runtime2/ingress_xdcc_bw_unparse_116.xml       |   42 +
 .../org/apache/daffodil/runtime2/limits_parse.dat  |  Bin 0 -> 6 bytes
 .../apache/daffodil/runtime2/limits_unparse.xml    |   25 +
 .../daffodil/runtime2/orion-command.dfdl.xsd       |  132 +++
 .../apache/daffodil/runtime2/orion-command.tdml    |  161 ++++
 .../daffodil/runtime2/video_settings_parse.dat     |  Bin 0 -> 20 bytes
 .../daffodil/runtime2/video_settings_unparse.xml   |   37 +
 .../section15/choice_groups/choice-unparse2.tdml   |    8 +-
 .../daffodil/runtime2/TestEgressXdccBw.scala       |   54 ++
 .../org/apache/daffodil/runtime2/TestExNums.scala  |   39 +
 .../daffodil/runtime2/TestIngressXdccBw.scala      |   46 +
 .../runtime2/TestIsrmGreenToOrange60000.scala      |   38 +
 .../runtime2/TestIsrmOrangeToGreen60002.scala      |   26 +-
 .../runtime2/TestMpuGreenToOrange60004.scala       |   26 +-
 .../runtime2/TestMpuOrangeToGreen60006.scala       |   38 +
 .../org/apache/daffodil/runtime2/TestNested.scala  |   40 +
 .../daffodil/runtime2/TestOrionCommand.scala       |   44 +
 project/Dependencies.scala                         |    3 +-
 project/Rat.scala                                  |   33 +
 project/plugins.sbt                                |    2 +
 sonar-project.properties                           |   11 +-
 159 files changed, 12851 insertions(+), 297 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index e8d1b55..92930d3 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -25,30 +25,61 @@ jobs:
       matrix:
         java_version: [ 8, 11, 16 ]
         scala_version: [ 2.12.11 ]
-        os: [ 'ubuntu-latest', 'windows-latest' ]
-    env:
-      SBT: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }} coverage
-      SBTNOCOV: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }}
+        os: [ ubuntu-20.04, windows-2019 ]
+        include:
+          - os: ubuntu-20.04
+            shell: bash
+          - os: windows-2019
+            shell: msys2 {0}
 
     runs-on: ${{ matrix.os }}
     defaults:
       run:
-        shell: bash
-
+        shell: ${{ matrix.shell }}
+    env:
+      SBT: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }} coverage
+      SBTNOCOV: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }}
     steps:
 
       ############################################################
       # Setup
       ############################################################
 
-      - name: Checkout Repository
-        uses: actions/checkout@v2.0.0
+      - name: Install Dependencies (Linux)
+        if: runner.os == 'Linux'
+        run: sudo apt-get install -y libmxml-dev
+
+      - name: Install Dependencies (Windows)
+        if: runner.os == 'Windows'
+        uses: msys2/setup-msys2@v2
+        with:
+          install: gcc libargp-devel make pkgconf
+          path-type: inherit
 
-      - name: Install Java
-        uses: actions/setup-java@v1
+      - name: Check out mxml source (Windows)
+        if: runner.os == 'Windows'
+        uses: actions/checkout@v2.3.4
+        with:
+          repository: michaelrsweet/mxml
+          ref: v3.2
+          path: mxml
+
+      - name: Install mxml library (Windows)
+        if: runner.os == 'Windows'
+        run: |
+          cd mxml
+          ./configure --prefix=/usr --disable-shared --disable-threads
+          make
+          make install
+
+      - name: Setup Java
+        uses: actions/setup-java@v1.4.3
         with:
           java-version: ${{ matrix.java_version }}
 
+      - name: Check out Repository
+        uses: actions/checkout@v2.3.4
+
       ############################################################
       # Build & Package
       ############################################################
@@ -62,9 +93,9 @@ jobs:
       - name: Package Zip & Tar
         run: $SBTNOCOV daffodil-cli/Universal/packageBin daffodil-cli/Universal/packageZipTarball
 
-      - name: Package RPM
-        run: $SBTNOCOV daffodil-cli/Rpm/packageBin
+      - name: Package RPM (Linux)
         if: runner.os == 'Linux'
+        run: $SBTNOCOV daffodil-cli/Rpm/packageBin
 
       ############################################################
       # Test
@@ -83,4 +114,4 @@ jobs:
         run: $SBT coverageAggregate
 
       - name: Upload Coverage Report
-        uses: codecov/codecov-action@v1
+        uses: codecov/codecov-action@v1.3.1
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
index 0c5e651..d22007b 100644
--- a/.github/workflows/sonarcloud.yml
+++ b/.github/workflows/sonarcloud.yml
@@ -14,24 +14,34 @@
 # limitations under the License.
 
 name: SonarCloud Workflow
+
 on: [push]
+
 jobs:
   sonarCloudTrigger:
     if: github.repository == 'apache/daffodil'
-    name: SonarCloud Trigger
-    runs-on: ubuntu-latest
+    name: SonarCloud Scan, Scala ${{ matrix.scala_version }}, ${{ matrix.os }}
     strategy:
-      fail-fast: false
       matrix:
-        scala_version: [ '2.12.11']
+        scala_version: [ 2.12.11 ]
+        os: [ ubuntu-20.04 ]
+
+    runs-on: ${{ matrix.os }}
     steps:
 
       ############################################################
       # Setup
       ############################################################
 
-      - name: Checkout Repository
-        uses: actions/checkout@v2.0.0
+      - name: Install Dependencies (Linux)
+        if: runner.os == 'Linux'
+        run: sudo apt-get install -y libmxml-dev
+
+      - name: Check out Repository
+        uses: actions/checkout@v2.3.4
+        # Disabling shallow clone is recommended for improving reporting
+        with:
+          fetch-depth: 0
 
       ############################################################
       # Build & Scan
@@ -39,12 +49,12 @@ jobs:
 
       - name: Compile
         run: $SBT compile Test/compile IntegrationTest/compile
-        shell: bash
         env:
-          SBT: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }} coverage
+          SBT: sbt -J-Xms1024m -J-Xmx5120m -J-XX:ReservedCodeCacheSize=512m -J-XX:MaxMetaspaceSize=1024m ++${{ matrix.scala_version }}
 
       - name: SonarCloud Scan
-        uses: sonarsource/sonarcloud-github-action@master
+        uses: sonarsource/sonarcloud-github-action@v1.5
+        # Don't setup Java since its env vars will screw up sonarcloud action
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/BUILD.md b/BUILD.md
new file mode 100644
index 0000000..784e648
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,114 @@
+<!--
+  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.
+-->
+
+# Build Requirements
+
+Daffodil's build requirements include:
+
+* JDK 8 or higher
+* SBT 0.13.8 or higher
+* C compiler C99 or higher
+* Mini-XML Version 3.2 or higher
+
+You will need the Java Software Development Kit ([JDK]) and the Scala
+Build Tool ([SBT]) to build Daffodil, run all tests, create packages,
+and more.  You can install the latest [Java 11 LTS][JDK] version and
+the latest [SBT] version following their websites' instructions or
+install them using your operating system's package manager.
+
+Since Daffodil now has a C backend as well as a Scala backend, you
+will need a C compiler supporting the [C99] standard or later, the
+[Mini-XML] library, and possibly the GNU [argp] library if your
+system's C library doesn't include it already.  You can install either
+[gcc] or [clang] using your operating system's package manager.  If
+you can't install the [Mini-XML] library using your operating system's
+package manager, you'll have to build it from source.  We'll tell you
+how to do that on Windows but the commands should work on other
+operating systems too.
+
+You can set your environment variables "CC" and "AR" to the correct
+commands (or set them to `true` to disable C compilation altogether)
+if you don't want `sbt compile` to call your C compiler with `cc` and
+`ar` as the default commands.
+
+## Fedora 33
+
+You can use the `dnf` package manager to install most of the build
+requirements needed to build Daffodil:
+
+    sudo dnf install gcc git java-11-openjdk-devel make mxml-devel pkgconf
+
+However, Fedora has no sbt package in its default repositories.
+You'll have to install the latest [SBT] version following its
+website's instructions.
+
+Now you can build Daffodil from source and the sbt and daffodil
+commands you type will be able to call the C compiler.
+
+## Ubuntu 20.04
+
+You can use the `apt` package manager to install most of the build
+requirements needed to build Daffodil:
+
+    sudo apt install build-essential default-jdk git libmxml-dev
+
+However, Ubuntu has no sbt package in its default repositories.
+You'll have to install the latest [SBT] version following its
+website's instructions.
+
+Now you can build Daffodil from source and the sbt and daffodil
+commands you type will be able to call the C compiler.
+
+## Windows 10
+
+Install the latest [Java 11 LTS][JDK] version and the latest [SBT]
+version following their websites' instructions.
+
+Install [MSYS2] following its website's instructions and open a new
+"MSYS2 MSYS" window.  We'll need its collection of free programs and
+libraries.
+
+Install [gcc] and [libargp][argp] using MSYS2's `pacman` package
+manager:
+
+    pacman -S gcc git libargp-devel make pkgconf
+
+However, MSYS2 has no [libmxml-devel][Mini-XML] package so you'll have
+to build the [Mini-XML] library from source:
+
+    git clone -b v3.2 https://github.com/michaelrsweet/mxml.git
+    cd mxml
+    ./configure --prefix=/usr --disable-shared --disable-threads
+    make
+    make install
+
+Define an environment variable with the name `MSYS2_PATH_TYPE` and the
+value `inherit` using Windows' control panel for editing environment
+variables.
+
+Now when you open a new "MSYS2 MSYS" window from the Start Menu to
+build Daffodil from source, the sbt and daffodil commands you type
+will be able to call the C compiler.
+
+[C99]: https://en.wikipedia.org/wiki/C99
+[JDK]: https://adoptopenjdk.net/
+[Mini-XML]: https://www.msweet.org/mxml/
+[MSYS2]: https://www.msys2.org/
+[SBT]: https://www.scala-sbt.org/
+[argp]: https://packages.msys2.org/package/libargp-devel
+[clang]: https://clang.llvm.org/get_started.html
+[gcc]: https://linuxize.com/post/how-to-install-gcc-on-ubuntu-20-04/
diff --git a/README.md b/README.md
index d090999..ad7982f 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,9 @@
   limitations under the License.
 -->
 
+<!-- markdownlint-disable first-line-heading -->
+<!-- markdownlint-disable line-length -->
+<!-- markdownlint-disable no-inline-html -->
 [<img src="https://daffodil.apache.org/assets/themes/apache/img/apache-daffodil-logo.svg" height="85" align="left" alt="Apache Daffodil"/>][Website]
 [<img src="https://img.shields.io/github/workflow/status/apache/daffodil/Daffodil%20CI/master.svg" align="right"/>][GitHub Actions]
 <br clear="right" />
@@ -23,95 +26,88 @@
 [<img src="https://img.shields.io/maven-central/v/org.apache.daffodil/daffodil-core_2.12.svg?color=brightgreen&label=version" align="right"/>][Releases]
 <br clear="both" />
 
-Apache Daffodil is an open-source implementation of the [DFDL specification]
-that uses DFDL data descriptions to parse fixed format data into an infoset.
-This infoset is commonly converted into XML or JSON to enable the use of
-well-established XML or JSON technologies and libraries to consume, inspect,
-and manipulate fixed format data in existing solutions. Daffodil is also
-capable of serializing or "unparsing" data back to the original data format.
-The DFDL infoset can also be converted directly to/from the data structures
-carried by data processing frameworks so as to bypass any XML/JSON overheads.
+Apache Daffodil is an open-source implementation of the [DFDL
+specification] that uses DFDL data descriptions to parse fixed format
+data into an infoset.  This infoset is commonly converted into XML or
+JSON to enable the use of well-established XML or JSON technologies
+and libraries to consume, inspect, and manipulate fixed format data in
+existing solutions.  Daffodil is also capable of serializing or
+"unparsing" data back to the original data format.  The DFDL infoset
+can also be converted directly to/from the data structures carried by
+data processing frameworks so as to bypass any XML/JSON overheads.
 
-For more information about Daffodil, see https://daffodil.apache.org/.
+For more information about Daffodil, see <https://daffodil.apache.org/>.
 
 ## Build Requirements
 
 * JDK 8 or higher
 * SBT 0.13.8 or higher
+* C compiler C99 or higher
+* Mini-XML Version 3.2 or higher
+
+See [BUILD.md](BUILD.md) for more details.
 
 ## Getting Started
 
-[SBT] is the officially supported tool to build Daffodil, run all tests, create packages,
-and more. Below are some of the more common commands used for Daffodil development.
+[SBT] is the officially supported tool to build Daffodil.  Below are
+some of the more commonly used commands for Daffodil development.
 
 ### Compile
 
-```text
-$ sbt compile
-```
+Compile source code:
+
+    sbt compile
+
 ### Tests
 
-Run all unit tests:
+Run unit tests:
 
-```text
-$ sbt test 
-```
+    sbt test
 
-Run all command line interface tests:
+Run command line interface tests:
 
-```text
-$ sbt IntegrationTest/test
-```
+    sbt IntegrationTest/test
 
 ### Command Line Interface
 
-Create Linux and Windows shell scripts in `daffodil-cli/target/universal/stage/bin/`. See
-the [Command Line Interface] documentation for details on its usage:
+Build the command line interface (Linux and Windows shell scripts in
+`daffodil-cli/target/universal/stage/bin/`; see the [Command Line
+Interface] documentation for details on their usage):
 
-```btext
-$ sbt daffodil-cli/stage
-```
+    sbt daffodil-cli/stage
 
 ### License Check
 
-Generate an [Apache RAT] license check report located in ``target/rat.txt`` and error if
-any unapproved licenses are found:
+Run [Apache RAT] (license audit report in `target/rat.txt` and error
+if any unapproved licenses are found):
 
-```text
-$ sbt ratCheck
-```
+    sbt ratCheck
 
 ### Test Coverage Report
 
-Generate an [sbt-scoverage] test coverage report located in
-``target/scala-ver/scoverage-report/``:
+Run [sbt-scoverage] (report in `target/scala-ver/scoverage-report/`):
 
-```text
-$ sbt clean coverage test IntegrationTest/test
-$ sbt coverageAggregate
-```
+    sbt clean coverage test IntegrationTest/test
+    sbt coverageAggregate
 
 ## Getting Help
 
-For questions, we can be reached at the dev@daffodil.apache.org or
-users@daffodil.apache.org mailing lists. Bugs can be reported via the [Daffodil JIRA].
+You can ask questions on the dev@daffodil.apache.org or
+users@daffodil.apache.org mailing lists.  You can report bugs via the
+[Daffodil JIRA].
 
 ## License
 
 Apache Daffodil is licensed under the [Apache License, v2.0].
 
-
-
-
 [Apache License, v2.0]: https://www.apache.org/licenses/LICENSE-2.0
 [Apache RAT]: https://creadur.apache.org/rat/
-[CodeCov]: https://codecov.io/gh/apache/daffodil/
+[CodeCov]: https://app.codecov.io/gh/apache/daffodil
 [Command Line Interface]: https://daffodil.apache.org/cli/
-[Daffodil JIRA]: https://issues.apache.org/jira/projects/DAFFODIL
-[DFDL specification]: http://www.ogf.org/dfdl
-[Open Grid Forum]: http://www.ogf.org
-[Releases]: http://daffodil.apache.org/releases/
-[SBT]: http://www.scala-sbt.org
+[DFDL specification]: https://daffodil.apache.org/docs/dfdl/
+[Daffodil JIRA]: https://issues.apache.org/jira/projects/DAFFODIL/
 [Github Actions]: https://github.com/apache/daffodil/actions?query=branch%3Amaster+
-[Website]: https://daffodil.apache.org
-[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage
+[Releases]: http://daffodil.apache.org/releases/
+[SBT]: https://www.scala-sbt.org/
+[Website]: https://daffodil.apache.org/
+[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage/
diff --git a/build.sbt b/build.sbt
index 78e89e2..2b92167 100644
--- a/build.sbt
+++ b/build.sbt
@@ -15,6 +15,10 @@
  * limitations under the License.
  */
 
+import sbt.io.Path.flatRebase
+import sbtcc._
+import scala.collection.immutable.ListSet
+
 // Silence an errant sbt linter warning about unused sbt settings. For some
 // reason, the sbt linter thinks the below settings are set but not used, which
 // leads to a bunch of noisy warnings. But they clearly are used. Seems to be a
@@ -29,9 +33,9 @@ lazy val genManaged = taskKey[Unit]("Generate managed sources and resources")
 lazy val genProps = taskKey[Seq[File]]("Generate properties scala source")
 lazy val genSchemas = taskKey[Seq[File]]("Generated DFDL schemas")
 
-lazy val daffodil         = Project("daffodil", file(".")).configs(IntegrationTest)
+lazy val daffodil         = project.in(file(".")).configs(IntegrationTest)
                               .enablePlugins(JavaUnidocPlugin, ScalaUnidocPlugin)
-                              .aggregate(macroLib, propgen, lib, io, runtime1, runtime1Unparser, core, japi, sapi, tdmlLib, tdmlProc, cli, udf, schematron, test, testIBM1, tutorials, testStdLayout)
+                              .aggregate(macroLib, propgen, lib, io, runtime1, runtime1Unparser, runtime2, core, japi, sapi, tdmlLib, tdmlProc, cli, udf, schematron, test, testIBM1, tutorials, testStdLayout)
                               .settings(commonSettings, nopublish, ratSettings, unidocSettings)
 
 lazy val macroLib         = Project("daffodil-macro-lib", file("daffodil-macro-lib")).configs(IntegrationTest)
@@ -57,6 +61,31 @@ lazy val runtime1Unparser = Project("daffodil-runtime1-unparser", file("daffodil
                               .dependsOn(runtime1, lib % "test->test", runtime1 % "test->test")
                               .settings(commonSettings)
 
+val runtime2CFiles        = Library("libruntime2.a")
+lazy val runtime2         = Project("daffodil-runtime2", file("daffodil-runtime2")).configs(IntegrationTest)
+                              .enablePlugins(CcPlugin)
+                              .dependsOn(core, core % "test->test")
+                              .settings(commonSettings)
+                              .settings(
+                                Compile / cCompiler := sys.env.getOrElse("CC", "cc"),
+                                Compile / ccArchiveCommand := sys.env.getOrElse("AR", "ar"),
+                                Compile / ccTargets := ListSet(runtime2CFiles),
+                                Compile / cSources  := Map(
+                                  runtime2CFiles -> (
+                                    ((Compile / resourceDirectory).value / "c" ** GlobFilter("*.c")).get() ++
+                                    ((Compile / resourceDirectory).value / "examples" ** GlobFilter("*.c")).get()
+                                  )
+                                ),
+                                Compile / cIncludeDirectories := Map(
+                                  runtime2CFiles -> Seq(
+                                    (Compile / resourceDirectory).value / "c" / "libcli",
+                                    (Compile / resourceDirectory).value / "c" / "libruntime",
+                                    (Compile / resourceDirectory).value / "examples"
+                                  )
+                                ),
+                                Compile / cFlags := (Compile / cFlags).value.withDefaultValue(Seq("-Wall", "-Wextra"))
+                              )
+
 lazy val core             = Project("daffodil-core", file("daffodil-core")).configs(IntegrationTest)
                               .dependsOn(runtime1Unparser, udf, lib % "test->test", runtime1 % "test->test")
                               .settings(commonSettings)
@@ -74,11 +103,11 @@ lazy val tdmlLib             = Project("daffodil-tdml-lib", file("daffodil-tdml-
                               .settings(commonSettings)
 
 lazy val tdmlProc         = Project("daffodil-tdml-processor", file("daffodil-tdml-processor")).configs(IntegrationTest)
-                              .dependsOn(tdmlLib, core)
+                              .dependsOn(tdmlLib, runtime2, core)
                               .settings(commonSettings)
 
 lazy val cli              = Project("daffodil-cli", file("daffodil-cli")).configs(IntegrationTest)
-                              .dependsOn(tdmlProc, sapi, japi, schematron % Runtime, udf % "it->test") // causes sapi/japi to be pulled in to the helper zip/tar
+                              .dependsOn(tdmlProc, runtime2, sapi, japi, schematron % Runtime, udf % "it->test") // causes runtime2/sapi/japi to be pulled into the helper zip/tar
                               .settings(commonSettings, nopublish)
                               .settings(libraryDependencies ++= Dependencies.cli)
 
@@ -92,7 +121,7 @@ lazy val schematron       = Project("daffodil-schematron", file("daffodil-schema
                               .configs(IntegrationTest)
 
 lazy val test             = Project("daffodil-test", file("daffodil-test")).configs(IntegrationTest)
-                              .dependsOn(tdmlProc, udf % "test->test")
+                              .dependsOn(tdmlProc, runtime2 % "test->test", udf % "test->test")
                               .settings(commonSettings, nopublish)
                               //
                               // Uncomment the following line to run these tests 
@@ -131,7 +160,7 @@ lazy val commonSettings = Seq(
     "-Xfatal-warnings",
     "-Xxml:-coalescing",
     "-Xfuture"
-),
+  ),
   // add scalac options that are version specific
   scalacOptions ++= scalacCrossOptions(scalaVersion.value),
   // Workaround issue that some options are valid for javac, not javadoc.
diff --git a/daffodil-cli/bin.LICENSE b/daffodil-cli/bin.LICENSE
index d75313f..740ac7f 100644
--- a/daffodil-cli/bin.LICENSE
+++ b/daffodil-cli/bin.LICENSE
@@ -1445,3 +1445,34 @@ is subject to the terms and conditions of the following licenses.
     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     THE SOFTWARE.
+
+  This product bundles libraries from 'os-lib', including the following files:
+    - lib/com.lihaoyi.geany_<VERSION>.jar
+    - lib/com.lihaoyi.os-lib_<VERSION>.jar
+  These files are available under the MIT license:
+
+    License
+    =======
+
+
+    The MIT License (MIT)
+
+    Copyright (c) 2019 Li Haoyi (haoyi.sg@gmail.com)
+
+    Permission is hereby granted, free of charge, to any person obtaining a copy
+    of this software and associated documentation files (the "Software"), to deal
+    in the Software without restriction, including without limitation the rights
+    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
diff --git a/daffodil-cli/src/it/scala/org/apache/daffodil/generating/TestCLIGenerateC.scala b/daffodil-cli/src/it/scala/org/apache/daffodil/generating/TestCLIGenerateC.scala
new file mode 100644
index 0000000..33ed867
--- /dev/null
+++ b/daffodil-cli/src/it/scala/org/apache/daffodil/generating/TestCLIGenerateC.scala
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.generating
+
+import net.sf.expectit.matcher.Matchers.contains
+import net.sf.expectit.matcher.Matchers.eof
+import org.apache.daffodil.CLI.Util
+import org.junit.After
+import org.junit.Test
+
+/**
+ * Checks that we can run the "daffodil generate c" subcommand with
+ * various options and get expected outputs.
+ */
+class TestCLIGenerateC {
+
+  val daffodil: String = Util.binPath
+  lazy val schemaFile: String = if (Util.isWindows) Util.cmdConvert(sf) else sf
+  val sf: String = Util.daffodilPath("daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.dfdl.xsd")
+  // Ensure all tests remove tempDir after creating it
+  val tempDir: os.Path = os.temp.dir()
+
+  @After def after(): Unit = {
+    os.remove.all(tempDir)
+  }
+
+  @Test def test_CLI_Generate_schema(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("")
+    try {
+      shell.sendLine(generateCmd)
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+
+    assert(os.exists(tempDir/"c"/"libruntime"/"generated_code.c"))
+  }
+
+  @Test def test_CLI_Generate_noC_error(): Unit = {
+    val generateCmd = s"$daffodil generate -s $schemaFile $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("Unknown option 's'"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_otherThanC_error(): Unit = {
+    val generateCmd = s"$daffodil generate vhld -s $schemaFile $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("Unknown option 's'"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_noSchema_error(): Unit = {
+    val generateCmd = s"$daffodil generate c $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("Required option 'schema' not found"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_twoSchema_error(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile -s $schemaFile $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("you should provide exactly one argument"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_verbose(): Unit = {
+    val generateCmd = s"$daffodil -v generate c -s $schemaFile $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("[info] Time (compiling)"))
+      shell.expect(contains("[info] Time (generating)"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+
+    assert(os.exists(tempDir/"c"/"libruntime"/"generated_code.c"))
+  }
+
+  @Test def test_CLI_Generate_root(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile -r {http://example.com}ex_nums $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("")
+    try {
+      shell.sendLine(generateCmd)
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+
+    assert(os.exists(tempDir/"c"/"libruntime"/"generated_code.c"))
+  }
+
+  @Test def test_CLI_Generate_root_error(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile -r {ex}ex_nums $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("Schema Definition Error"))
+      shell.expect(contains("No global element found for {ex}ex_nums"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_namespaceNoRoot_error(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile -r {http://example.com} $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("", expectErr = true)
+    try {
+      shell.sendLine(generateCmd)
+      shell.expect(contains("Invalid syntax for extended QName"))
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+  }
+
+  @Test def test_CLI_Generate_tunable(): Unit = {
+    val generateCmd = s"$daffodil generate c -s $schemaFile -T parseUnparsePolicy=parseOnly $tempDir"
+    val exitCmd = "exit"
+
+    val shell = Util.start("")
+    try {
+      shell.sendLine(generateCmd)
+      shell.sendLine(exitCmd)
+      shell.expect(eof())
+    } finally {
+      shell.close()
+    }
+
+    assert(os.exists(tempDir/"c"/"libruntime"/"generated_code.c"))
+  }
+}
diff --git a/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala b/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
index a94672b..1696ff5 100644
--- a/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
+++ b/daffodil-cli/src/main/scala/org/apache/daffodil/Main.scala
@@ -502,11 +502,47 @@ class CLIConf(arguments: Array[String]) extends scallop.ScallopConf(arguments)
     val info = tally(descr = "increment test result information output level, one level for each -i")
   }
 
+  // Generate Subcommand Options
+  object generate extends scallop.Subcommand("generate") {
+    descr("generate <language> code from a DFDL schema")
+
+    banner("""|Usage: daffodil [GLOBAL_OPTS] generate <language> [SUBCOMMAND_OPTS]
+              |""".stripMargin)
+    shortSubcommandsHelp()
+    footer("""|
+              |Run 'daffodil generate <language> --help' for subcommand specific options""".stripMargin)
+
+    object c extends scallop.Subcommand("c") {
+      banner("""|Usage: daffodil generate c -s <schema> [-r [{namespace}]<root>]
+                |                           [-c <file>] [outputDir]
+                |
+                |Generate C code from a DFDL schema to parse or unparse data
+                |
+                |Generate Options:""".stripMargin)
+
+      descr("generate C code from a DFDL schema")
+      helpWidth(76)
+
+      val language = "c"
+      val schema = opt[URI]("schema", required = true, argName = "file", descr = "the annotated DFDL schema to use to generate source code.")
+      val rootNS = opt[RefQName]("root", argName = "node", descr = "the root element of the XML file to use.  An optional namespace may be provided. This needs to be one of the top-level elements of the DFDL schema defined with --schema. Requires --schema. If not supplied uses the first element of the first schema")
+      val tunables = props[String]('T', keyName = "tunable", valueName = "value", descr = "daffodil tunable to be used when compiling schema.")
+      val config = opt[File](short = 'c', argName = "file", descr = "path to file containing configuration items.")
+      val outputDir = trailArg[String](required = false, descr = "output directory in which to generate source code. If not specified, uses current directory.")
+
+      requireOne(schema) // --schema must be provided
+      validateFileIsFile(config) // --config must be a file that exists
+    }
+    addSubcommand(c)
+    requireSubcommand()
+  }
+
   addSubcommand(parse)
   addSubcommand(performance)
   addSubcommand(unparse)
   addSubcommand(save)
   addSubcommand(test)
+  addSubcommand(generate)
 
   mutuallyExclusive(trace, debug) // cannot provide both --trace and --debug
   requireSubcommand()
@@ -654,7 +690,7 @@ object Main extends Logging {
 
   def createProcessorFromSchema(schema: URI, rootNS: Option[RefQName], path: Option[String],
     tunables: Map[String, String],
-    mode: ValidationMode.Type) = {
+    mode: ValidationMode.Type): Option[DFDL.DataProcessor] = {
     val compiler = {
       val c = Compiler().withTunables(tunables)
       rootNS match {
@@ -685,6 +721,31 @@ object Main extends Logging {
     pf
   }
 
+  def createGeneratorFromSchema(schema: URI, rootNS: Option[RefQName], tunables: Map[String, String],
+                                language: String): Option[DFDL.CodeGenerator] = {
+    val compiler = {
+      val c = Compiler().withTunables(tunables)
+      rootNS match {
+        case None => c
+        case Some(RefQName(_, root, ns)) => c.withDistinguishedRootNode(root, ns.toStringOrNullIfNoNS)
+      }
+    }
+
+    val schemaSource = URISchemaSource(schema)
+    val cg = Timer.getResult("compiling", {
+      val processorFactory = compiler.compileSource(schemaSource)
+      if (!processorFactory.isError) {
+        val generator = processorFactory.forLanguage(language)
+        displayDiagnostics(generator)
+        Some(generator)
+      } else {
+        displayDiagnostics(processorFactory)
+        None
+      }
+    })
+    cg
+  }
+
   // write blobs to $PWD/daffodil-blobs/*.bin
   val blobDir = Paths.get(System.getProperty("user.dir"), "daffodil-blobs")
   val blobSuffix = ".bin"
@@ -1379,11 +1440,42 @@ object Main extends Logging {
         0
       }
 
-      case _ => {
-        // This should never happen, this is caught by validation
-        Assert.impossible()
-        // 1
+      case Some(conf.generate) => {
+        conf.subcommands match {
+          case List(conf.generate, conf.generate.c) => {
+            val generateOpts = conf.generate.c
+
+            // Read any config file and any tunables given as arguments
+            val cfgFileNode = generateOpts.config.toOption match {
+              case None => None
+              case Some(pathToConfig) => Some(this.loadConfigurationFile(pathToConfig))
+            }
+            val tunables = retrieveTunables(generateOpts.tunables, cfgFileNode)
+
+            // Create a CodeGenerator from the DFDL schema
+            val generator = createGeneratorFromSchema(generateOpts.schema(), generateOpts.rootNS.toOption,
+              tunables, generateOpts.language)
+
+            // Ask the CodeGenerator to generate source code from the DFDL schema
+            val rootNS = generateOpts.rootNS.toOption
+            val outputDir = generateOpts.outputDir.toOption.getOrElse(".")
+            val rc = generator match {
+              case Some(generator) => {
+                Timer.getResult("generating", generator.generateCode(rootNS, outputDir))
+                displayDiagnostics(generator)
+                if (generator.isError) 1 else 0
+              }
+              case None => 1
+            }
+            rc
+          }
+          // Required to avoid "match may not be exhaustive", but should never happen
+          case _ => Assert.impossible()
+        }
       }
+
+      // Required to avoid "match may not be exhaustive", but should never happen
+      case _ => Assert.impossible()
     }
 
     ret
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala b/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
index 3c74654..714ac12 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/compiler/Compiler.scala
@@ -26,6 +26,7 @@ import java.util.zip.GZIPInputStream
 import java.util.zip.ZipException
 
 import scala.collection.immutable.Queue
+import scala.util.Try
 import scala.xml.Node
 import org.apache.daffodil.api.DFDL
 import org.apache.daffodil.api.DaffodilSchemaSource
@@ -108,6 +109,27 @@ final class ProcessorFactory private(
 
   override def onPath(xpath: String) = sset.onPath(xpath)
 
+  override def forLanguage(language: String): DFDL.CodeGenerator = {
+    Assert.usage(!isError)
+
+    // Do a poor man's pluggable code generator implementation - we can replace
+    // it after we observe how the validator SPI evolves and wait for our
+    // requirements to become clearer
+    val className = language match {
+      case "c" => "org.apache.daffodil.runtime2.CodeGenerator"
+      case _ => s"code generator; source language $language is not supported"
+    }
+    import scala.language.existentials // Needed to make next line compile
+    val clazz = Try(Class.forName(className))
+    val constructor = clazz.map { _.getDeclaredConstructor(sset.root.getClass) }
+    val tryInstance = constructor.map { _.newInstance(sset.root).asInstanceOf[DFDL.CodeGenerator] }
+    val codeGenerator = tryInstance.recover {
+      case ex => throw new InvalidParserException("Error creating " + className, ex)
+    }.get
+
+    codeGenerator
+  }
+
   override def isError = sset.isError
 
   @deprecated("Use arguments to Compiler.compileSource or compileFile.", "2.6.0")
@@ -163,7 +185,7 @@ class Compiler private (var validateDFDLSchemas: Boolean,
   }
 
   @deprecated("Use constructor argument.", "2.6.0")
-  def setValidateDFDLSchemas(value: Boolean) = validateDFDLSchemas = value
+  def setValidateDFDLSchemas(value: Boolean): Unit = validateDFDLSchemas = value
 
   def withValidateDFDLSchemas(value: Boolean) = copy(validateDFDLSchemas = value)
 
@@ -279,10 +301,10 @@ class Compiler private (var validateDFDLSchemas: Boolean,
       //
       case cnf: ClassNotFoundException => {
         val cpString =
-          if (Misc.classPath.length == 0) " empty."
+          if (Misc.classPath.isEmpty) " empty."
           else ":\n" + Misc.classPath.mkString("\n\t")
         val fmtMsg = "%s\nThe class may not exist in this Java JVM version (%s), or it is missing from the classpath which is%s"
-        val msg = fmtMsg.format(cnf.getMessage(), scala.util.Properties.javaVersion, cpString)
+        val msg = fmtMsg.format(cnf.getMessage, scala.util.Properties.javaVersion, cpString)
         throw new InvalidParserException(msg, cnf)
       }
     }
@@ -329,10 +351,10 @@ class Compiler private (var validateDFDLSchemas: Boolean,
     val err = pf.isError
     val diags = pf.getDiagnostics // might be warnings even if not isError
     if (err) {
-      Assert.invariant(diags.length > 0)
+      Assert.invariant(diags.nonEmpty)
       log(LogLevel.Compile, "Compilation (ProcessorFactory) produced %d errors/warnings.", diags.length)
     } else {
-      if (diags.length > 0) {
+      if (diags.nonEmpty) {
         log(LogLevel.Compile, "Compilation (ProcessorFactory) produced %d warnings.", diags.length)
       } else {
         log(LogLevel.Compile, "ProcessorFactory completed with no errors.")
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index e1435d9..3d05999 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -32,10 +32,11 @@ import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.dpath.InvalidPrimitiveDataException
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.api.WarnID
-import java.lang.{Integer => JInt}
+import java.lang.{ Integer => JInt }
 
 import org.apache.daffodil.dsom.walker.ElementBaseView
 import org.apache.daffodil.infoset.DataValue
+import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
 import org.apache.daffodil.infoset.DataValue.DataValuePrimitiveOrUseNilForDefaultOrNull
 
 /**
@@ -122,8 +123,6 @@ trait ElementBase
    */
   def typeDef: TypeBase
 
-  private lazy val optRepValueSet = optSimpleType.flatMap(_.optRepValueSet)
-
   /**
    * The DPathElementInfo objects referenced within an IVC
    * that calls dfdl:contentLength( thingy )
@@ -319,15 +318,16 @@ trait ElementBase
   }.value
 
   /**
-   * Is either None, Some(primTypeValue) or Some(UseNilForDefault), which is a
-   * singleton object indicating that the item is nillable, and useNilForDefault was true.
+   * Is either DataValue[AnyRef], DataValue.NoValue, or DataValue.UseNilForDefault,
+   * which is a singleton object indicating that the item is nillable and
+   * useNilForDefault was true.
    *
    * The value will always be of the matching primitive types for the element, and
    * directly usable as the value of a simple-type element.
    *
-   * When a value is used, it is created from the XSD default or fixed attribute of the
+   * When a value is used, it is created from the XSD default attribute of the
    * element declaration, and that string cannot contain DFDL entities of any kind,
-   * nor any PUA-remapped characters. This insures the default/fixed value can still be
+   * nor any PUA-remapped characters. This insures the default value can still be
    * used for ordinary XML-schema validation outside of Daffodil/DFDL.
    */
   final lazy val defaultValue: DataValuePrimitiveOrUseNilForDefaultOrNull = {
@@ -354,6 +354,38 @@ trait ElementBase
     } else DataValue.NoValue
   }
 
+  /**
+   * Is either DataValue[AnyRef] or DataValue.NoValue.
+   *
+   * The value will always be of the matching primitive types for the element, and
+   * directly usable as the value of a simple-type element.
+   *
+   * When a value is used, it is created from the XSD fixed attribute of the
+   * element declaration, and that string cannot contain DFDL entities of any kind,
+   * nor any PUA-remapped characters. This insures the fixed value can still be
+   * used for ordinary XML-schema validation outside of Daffodil/DFDL.
+   */
+  final lazy val fixedValue: DataValuePrimitiveNullable = {
+    if (hasFixedValue && (isScalar || isArrayWithAtLeastOneRequiredArrayElement)) {
+      val dv = {
+        //
+        // Note: no remapping PUA chars or otherwise messing with the text of the fixed value
+        // because this must be a regular XSD fixed value so that Xerces validation
+        // will work.
+        //
+        val str = fixedValueAsString
+        val value = try {
+          primType.fromXMLString(str)
+        } catch {
+          case ipd: InvalidPrimitiveDataException =>
+            SDE("Invalid fixed value: %s", ipd.getMessage)
+        }
+        value
+      }
+      dv
+    } else DataValue.NoValue
+  }
+
   lazy val unparserInfosetElementDefaultingBehavior: UnparserInfo.InfosetEventBehavior = {
     import UnparserInfo._
     if (!isRepresented) MustExist
@@ -441,12 +473,12 @@ trait ElementBase
   }
 
   final lazy val isParentUnorderedSequence: Boolean = {
-    optLexicalParent.map { lp =>
+    optLexicalParent.exists { lp =>
       lp match {
         case s: SequenceTermBase if !s.isOrdered => true
         case _ => false
       }
-    }.getOrElse(false)
+    }
   }
 
   private def getImplicitAlignmentInBits(thePrimType: PrimType, theRepresentation: Representation): Int = {
@@ -610,7 +642,7 @@ trait ElementBase
     }
   }
 
-  final def hasFixedLengthOf(n: Int) = {
+  final def hasFixedLengthOf(n: Int): Boolean = {
     // FIXME: needs to work in lengthUnits. If length units is bytes/bits
     // and encoding is variable-width charset, what should this return?
     // (Perhaps should be usage error?)
@@ -793,16 +825,16 @@ trait ElementBase
 
   import org.apache.daffodil.dsom.FacetTypes._
 
-  private lazy val hasPattern: Boolean = typeDef.optRestriction.map { _.hasPattern }.getOrElse(false)
-  private lazy val hasEnumeration: Boolean = typeDef.optRestriction.map { _.hasEnumeration }.getOrElse(false)
-  protected lazy val hasMinLength = typeDef.optRestriction.map { _.hasMinLength }.getOrElse(false)
-  protected lazy val hasMaxLength = typeDef.optRestriction.map { _.hasMaxLength }.getOrElse(false)
-  private lazy val hasMinInclusive = typeDef.optRestriction.map { _.hasMinInclusive }.getOrElse(false)
-  private lazy val hasMaxInclusive = typeDef.optRestriction.map { _.hasMaxInclusive }.getOrElse(false)
-  private lazy val hasMinExclusive = typeDef.optRestriction.map { _.hasMinExclusive }.getOrElse(false)
-  private lazy val hasMaxExclusive = typeDef.optRestriction.map { _.hasMaxExclusive }.getOrElse(false)
-  private lazy val hasTotalDigits = typeDef.optRestriction.map { _.hasTotalDigits }.getOrElse(false)
-  private lazy val hasFractionDigits = typeDef.optRestriction.map { _.hasFractionDigits }.getOrElse(false)
+  private lazy val hasPattern: Boolean = typeDef.optRestriction.exists(_.hasPattern)
+  private lazy val hasEnumeration: Boolean = typeDef.optRestriction.exists(_.hasEnumeration)
+  protected lazy val hasMinLength = typeDef.optRestriction.exists(_.hasMinLength)
+  protected lazy val hasMaxLength = typeDef.optRestriction.exists(_.hasMaxLength)
+  private lazy val hasMinInclusive = typeDef.optRestriction.exists(_.hasMinInclusive)
+  private lazy val hasMaxInclusive = typeDef.optRestriction.exists(_.hasMaxInclusive)
+  private lazy val hasMinExclusive = typeDef.optRestriction.exists(_.hasMinExclusive)
+  private lazy val hasMaxExclusive = typeDef.optRestriction.exists(_.hasMaxExclusive)
+  private lazy val hasTotalDigits = typeDef.optRestriction.exists(_.hasTotalDigits)
+  private lazy val hasFractionDigits = typeDef.optRestriction.exists(_.hasFractionDigits)
 
   final lazy val patternValues: Seq[FacetValueR] = {
     Assert.invariant(hasPattern)
@@ -843,7 +875,7 @@ trait ElementBase
         // we convert to the number type.
         //
         // This means we cannot check and SDE here on incorrect simple type.
-        return (zeroBD, unbBD)
+        (zeroBD, unbBD)
       }
       case st: SimpleTypeDefBase if st.optRepTypeElement.isDefined => (st.optRepTypeElement.get.minLength, st.optRepTypeElement.get.maxLength)
       case st: SimpleTypeDefBase if st.optRestriction.isDefined => {
@@ -993,6 +1025,12 @@ trait ElementBase
   def hasDefaultValue: Boolean
 
   /**
+   * Does the element have a fixed value?
+   */
+  def fixedValueAsString: String
+  def hasFixedValue: Boolean
+
+  /**
    * We require that there be a concept of empty if we're going to be able to default something
    * and we are going to require that we can tell this statically. I.e., we're not going to defer this to runtime
    * just in case the delimiters are being determined at runtime.
@@ -1072,8 +1110,6 @@ trait ElementBase
     }
   }
 
-  private def gcd(a: Int, b: Int): Int = if (b == 0) a else gcd(b, a % b)
-
   /**
    * Changed to a warning - DFDL WG decided to make this check optional, but it
    * is still useful as a warning.
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementDeclMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementDeclMixin.scala
index 40598d0..99da373 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementDeclMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementDeclMixin.scala
@@ -54,6 +54,8 @@ trait ElementDeclMixin
 
   final def hasDefaultValue: Boolean = defaultAttr.isDefined
 
+  final def hasFixedValue: Boolean = fixedAttr.isDefined
+
   override final def simpleType: SimpleTypeBase = optSimpleType.get
 
   override final def complexType: ComplexTypeBase = optComplexType.get
@@ -141,6 +143,8 @@ trait ElementDeclMixin
 
   final lazy val defaultAttr = xml.attribute("default")
 
+  final lazy val fixedAttr = xml.attribute("fixed")
+
   final lazy val defaultValueAsString = {
     Assert.usage(hasDefaultValue)
     val dv = defaultAttr.get.text
@@ -151,6 +155,11 @@ trait ElementDeclMixin
     dv
   }
 
+  final lazy val fixedValueAsString = {
+    Assert.usage(hasFixedValue)
+    fixedAttr.get.text
+  }
+
   final lazy val isNillable = (xml \ "@nillable").text == "true"
 }
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
index 735f300..d709869 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
@@ -43,7 +43,9 @@ abstract class AbstractElementRef(
 
   def complexType: ComplexTypeBase = this.referencedElement.complexType
   def defaultValueAsString: String = this.referencedElement.defaultValueAsString
+  def fixedValueAsString: String = this.referencedElement.fixedValueAsString
   def hasDefaultValue: Boolean = this.referencedElement.hasDefaultValue
+  def hasFixedValue: Boolean = this.referencedElement.hasFixedValue
   def isComplexType: Boolean = this.referencedElement.isComplexType
   def isNillable: Boolean = this.referencedElement.isNillable
   def isSimpleType: Boolean = this.referencedElement.isSimpleType
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
index 14be1d7..61da856 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
@@ -18,14 +18,11 @@
 package org.apache.daffodil.grammar
 
 import java.lang.{ Long => JLong }
-import scala.Boolean
-import scala.Long
 import org.apache.daffodil.api.WarnID
 import org.apache.daffodil.dpath.NodeInfo
 import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.dsom.PrefixLengthQuasiElementDecl
 import org.apache.daffodil.dsom.ElementBase
-import org.apache.daffodil.dsom.ExpressionCompilers
 import org.apache.daffodil.dsom.InitiatedTerminatedMixin
 import org.apache.daffodil.dsom.TunableLimitExceededError
 import org.apache.daffodil.exceptions.Assert
@@ -1016,7 +1013,7 @@ trait ElementBaseGrammarMixin
     }
 
   private def withDelimiterStack(body: => Gram) = {
-    if (hasDelimiters || immediatelyEnclosingModelGroup.map(_.hasDelimiters).getOrElse(false)) DelimiterStackCombinatorElement(this, body)
+    if (hasDelimiters || immediatelyEnclosingModelGroup.exists(_.hasDelimiters)) DelimiterStackCombinatorElement(this, body)
     else body
   }
 
@@ -1185,7 +1182,7 @@ trait ElementBaseGrammarMixin
     val exprNamespaces = exprProp.location.namespaces
     val qn = GlobalQName(Some("daf"), "outputValueCalc", XMLUtils.dafintURI)
     val expr = ExpressionCompilers.AnyRef.compileExpression(
-      qn, primType, exprText, exprNamespaces, dpathCompileInfo, false, self, dpathCompileInfo)
+      qn, primType, exprText, exprNamespaces, dpathCompileInfo, isEvaluatedAbove = false, self, dpathCompileInfo)
     expr
   }
 
@@ -1299,7 +1296,7 @@ trait ElementBaseGrammarMixin
       val len = optLengthConstant.get
       val maxLengthLong = maxLength.longValueExact
       val minLengthLong = minLength.longValueExact
-      def warn(m: String, value: Long) = SDW(WarnID.FacetExplicitLengthOutOfRange, "Explicit dfdl:length of %s is out of range for facet %sLength='%s'.", len, m, value)
+      def warn(m: String, value: Long): Unit = SDW(WarnID.FacetExplicitLengthOutOfRange, "Explicit dfdl:length of %s is out of range for facet %sLength='%s'.", len, m, value)
       if (maxLengthLong != -1 && len > maxLengthLong) warn("max", maxLengthLong)
       Assert.invariant(minLengthLong >= 0)
       if (minLengthLong > 0 && len < minLengthLong) warn("min", minLengthLong)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
index f0bc051..c294f68 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
@@ -17,8 +17,8 @@
 
 package org.apache.daffodil.grammar
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
 import org.apache.daffodil.dsom.Root
+import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
 
 trait RootGrammarMixin
   extends LocalElementGrammarMixin // can be repeating if not root
@@ -29,6 +29,5 @@ trait RootGrammarMixin
     UnicodeByteOrderMark(this) ~ documentElement
   }
 
-  private def documentElement = enclosedElement
-
+  def documentElement = enclosedElement
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
index 4941037..9b75e5f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
@@ -41,7 +41,7 @@ abstract class BinaryGram(context: SchemaComponent, childrenArg: Seq[Gram]) exte
   protected def open: String
   protected def close: String
 
-  protected final lazy val children = {
+  lazy val children = {
     val c = childrenArg
     c
   }
@@ -87,7 +87,7 @@ class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends Bi
 
   final override lazy val parser = {
     if (parserChildren.isEmpty) new NadaParser(context.runtimeData)
-    else if (parserChildren.length == 1) parserChildren(0)
+    else if (parserChildren.length == 1) parserChildren.head
     else new SeqCompParser(context.runtimeData, parserChildren.toVector)
   }
 
@@ -105,7 +105,7 @@ class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends Bi
 
   final override lazy val unparser = {
     if (unparserChildren.isEmpty) new NadaUnparser(context.runtimeData)
-    else if (unparserChildren.length == 1) unparserChildren(0)
+    else if (unparserChildren.length == 1) unparserChildren.head
     else new SeqCompUnparser(context.runtimeData, unparserChildren.toVector)
   }
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
index db042a9..cc464bc 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
@@ -65,7 +65,7 @@ abstract class Gram(contextArg: SchemaComponent)
 
   val forWhat: ParserOrUnparser = BothParserAndUnparser
 
-  final val context: SchemaComponent = contextArg
+  val context: SchemaComponent = contextArg
 
   def term = context.asInstanceOf[Term]
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Production.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Production.scala
index 40a4580..d10ef4e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Production.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Production.scala
@@ -41,7 +41,11 @@ import org.apache.daffodil.util.LogLevel
  * Prod objects are not required. They essentially provide some useful debug capability
  * because a grammar term object will display as it's name, not as some anonymous object.
  */
-final class Prod(nameArg: String, val sc: SchemaComponent, guard: Boolean, gramArg: => Gram, override val forWhat: ParserOrUnparser)
+final class Prod(nameArg: String,
+                 val sc: SchemaComponent,
+                 val guard: Boolean,
+                 gramArg: => Gram,
+                 override val forWhat: ParserOrUnparser)
   extends NamedGram(sc) {
 
   final override def deref = gram
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
index 3918f7f..77b2c90 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
@@ -23,13 +23,13 @@ import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.grammar.Gram
 import org.apache.daffodil.grammar.NamedGram
 import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.processors.parsers.ElementParser
-import org.apache.daffodil.processors.parsers.ElementParserInputValueCalc
-import org.apache.daffodil.processors.parsers.Parser
 import org.apache.daffodil.processors.parsers.CaptureEndOfContentLengthParser
 import org.apache.daffodil.processors.parsers.CaptureEndOfValueLengthParser
 import org.apache.daffodil.processors.parsers.CaptureStartOfContentLengthParser
 import org.apache.daffodil.processors.parsers.CaptureStartOfValueLengthParser
+import org.apache.daffodil.processors.parsers.ElementParser
+import org.apache.daffodil.processors.parsers.ElementParserInputValueCalc
+import org.apache.daffodil.processors.parsers.Parser
 import org.apache.daffodil.processors.unparsers.CaptureEndOfContentLengthUnparser
 import org.apache.daffodil.processors.unparsers.CaptureEndOfValueLengthUnparser
 import org.apache.daffodil.processors.unparsers.CaptureStartOfContentLengthUnparser
@@ -41,10 +41,10 @@ import org.apache.daffodil.processors.unparsers.ElementUnparserInputValueCalc
 import org.apache.daffodil.processors.unparsers.ElementUnspecifiedLengthUnparser
 import org.apache.daffodil.processors.unparsers.ElementUnusedUnparser
 import org.apache.daffodil.processors.unparsers.LeftCenteredPaddingUnparser
-import org.apache.daffodil.processors.unparsers.SimpleTypeRetryUnparser
 import org.apache.daffodil.processors.unparsers.OnlyPaddingUnparser
 import org.apache.daffodil.processors.unparsers.RightCenteredPaddingUnparser
 import org.apache.daffodil.processors.unparsers.RightFillUnparser
+import org.apache.daffodil.processors.unparsers.SimpleTypeRetryUnparser
 import org.apache.daffodil.processors.unparsers.Unparser
 import org.apache.daffodil.schema.annotation.props.gen.LengthKind
 import org.apache.daffodil.schema.annotation.props.gen.Representation
@@ -67,18 +67,18 @@ import org.apache.daffodil.grammar.EmptyGram
  * is going to make it worse.
  */
 class ElementCombinator(
-  context: ElementBase,
+  override val context: ElementBase,
   eBeforeContent: Gram,
   eValue: Gram,
   eAfterValue: Gram,
   repTypeElementGram: Gram = EmptyGram
-  )
+)
   extends NamedGram(context)
   with Padded {
 
   override def toString = subComb.toString()
 
-  private lazy val subComb = new ElementParseAndUnspecifiedLength(context, eBeforeContent,
+  lazy val subComb = new ElementParseAndUnspecifiedLength(context, eBeforeContent,
     eValue, eAfterValue, repTypeElementGram)
 
   override lazy val parser: Parser = {
@@ -105,7 +105,7 @@ class ElementCombinator(
     else Maybe(eAfterValue.unparser)
 
   private lazy val eReptypeUnparser: Maybe[Unparser] = repTypeElementGram.maybeUnparser
-    
+
   override lazy val unparser: Unparser = {
     if (context.isOutputValueCalc) {
       new ElementOVCSpecifiedLengthUnparser(
@@ -282,8 +282,11 @@ case class CaptureValueLengthEnd(ctxt: ElementBase)
       new NadaUnparser(ctxt.erd)
 }
 
-
-class ElementParseAndUnspecifiedLength(context: ElementBase, eBeforeGram: Gram, eGram: Gram, eAfterGram: Gram, repTypeElementGram: Gram)
+class ElementParseAndUnspecifiedLength(override val context: ElementBase,
+                                       val eBeforeGram: Gram,
+                                       val eGram: Gram,
+                                       val eAfterGram: Gram,
+                                       val repTypeElementGram: Gram)
   extends ElementCombinatorBase(context, eBeforeGram, eGram, eAfterGram, repTypeElementGram) {
 
   lazy val parser: Parser =
@@ -330,10 +333,10 @@ class ElementParseAndUnspecifiedLength(context: ElementBase, eBeforeGram: Gram,
   }
 }
 
-abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eGram: Gram, eGramAfter: Gram, repTypeElementGram:Gram)
+abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eGram: Gram, eGramAfter: Gram, repTypeElementGram: Gram)
   extends NamedGram(context) {
 
-  override def toString() = "<element name='" + name + "'>" + eGram.toString() + "</element>"
+  override def toString: String = "<element name='" + name + "'>" + eGram.toString + "</element>"
 
   // The order of things matters in some cases, so to be consistent we'll always use the
   // same order even when it doesn't matter
@@ -352,10 +355,10 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
   lazy val patDiscrim = {
     val pd = context.discriminatorStatements.filter(_.testKind == TestKind.Pattern)
     Assert.invariant(pd.size <= 1)
-    if (pd.size == 0) {
+    if (pd.isEmpty) {
       Maybe.Nope
     } else {
-      pd(0).gram(context).maybeParser
+      pd.head.gram(context).maybeParser
     }
   }
   lazy val patAssert = context.assertStatements.filter(_.testKind == TestKind.Pattern).map(_.gram(context).parser).toArray
@@ -363,10 +366,10 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
   lazy val testDiscrim = {
     val td = context.discriminatorStatements.filter(_.testKind == TestKind.Expression)
     Assert.invariant(td.size <= 1)
-    if (td.size == 0) {
+    if (td.isEmpty) {
       Maybe.Nope
     } else {
-      td(0).gram(context).maybeParser
+      td.head.gram(context).maybeParser
     }
   }
   lazy val testAssert = context.assertStatements.filter(_.testKind == TestKind.Expression).map(_.gram(context).parser).toArray
@@ -376,7 +379,7 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
   lazy val eParser: Maybe[Parser] = eGram.maybeParser
 
   lazy val eAfterParser: Maybe[Parser] = eGramAfter.maybeParser
-  
+
   lazy val eRepTypeParser: Maybe[Parser] = repTypeElementGram.maybeParser
 
   def parser: Parser
@@ -388,7 +391,7 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
   lazy val eUnparser: Maybe[Unparser] = eGram.maybeUnparser
 
   lazy val eAfterUnparser: Maybe[Unparser] = eGramAfter.maybeUnparser
-  
+
   lazy val eRepTypeUnparser: Maybe[Unparser] = repTypeElementGram.maybeUnparser
 
   def unparser: Unparser
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesBinaryNumber.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesBinaryNumber.scala
index 3e1423c..c930f2f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesBinaryNumber.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesBinaryNumber.scala
@@ -47,7 +47,9 @@ class BinaryIntegerRuntimeLength(val e: ElementBase, signed: Boolean) extends Te
   override lazy val unparser: Unparser = new BinaryIntegerRuntimeLengthUnparser(e.elementRuntimeData, signed, e.lengthEv, e.lengthUnits)
 }
 
-class BinaryIntegerKnownLength(val e: ElementBase, signed: Boolean, lengthInBits: Long) extends Terminal(e, true) {
+class BinaryIntegerKnownLength(val e: ElementBase,
+                               val signed: Boolean,
+                               val lengthInBits: Long) extends Terminal(e, true) {
 
   override lazy val parser = {
     new BinaryIntegerKnownLengthParser(e.elementRuntimeData, signed, lengthInBits.toInt)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala
index f6f9185..2a4b035 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala
@@ -17,12 +17,12 @@
 
 package org.apache.daffodil.grammar.primitives
 
-import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.grammar.Gram
 import org.apache.daffodil.dsom._
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.grammar.Terminal
 import org.apache.daffodil.processors.parsers._
-import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
 import org.apache.daffodil.processors.unparsers._
+import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
 import org.apache.daffodil.util.Misc
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.MaybeInt
@@ -31,14 +31,14 @@ import org.apache.daffodil.util.MaybeInt
  * Base class for all kinds of sequences.
  */
 abstract class SequenceCombinator(sq: SequenceTermBase, sequenceChildren: Seq[SequenceChild])
-  extends Terminal(sq, sequenceChildren.length > 0) {
+  extends Terminal(sq, sequenceChildren.nonEmpty) {
 
   /**
    * Shorthand for getting the sequence runtime data.
    */
   val srd = sq.sequenceRuntimeData
 
-  override def toString() =
+  override def toString: String =
     "<" + Misc.getNameFromClass(this) + ">" +
       sequenceChildren.map { _.toString() }.mkString +
       "</" + Misc.getNameFromClass(this) + ">"
@@ -70,7 +70,7 @@ class OrderedSequence(sq: SequenceTermBase, sequenceChildrenArg: Seq[SequenceChi
   // the mta parser differently to avoid this
   private lazy val sepUnparser = sepGram.unparser
 
-  private lazy val sequenceChildren = sequenceChildrenArg.toVector
+  lazy val sequenceChildren = sequenceChildrenArg.toVector
 
   override lazy val parser: Parser = sq.hasSeparator match {
     case true => new OrderedSeparatedSequenceParser(
@@ -148,10 +148,8 @@ class UnorderedSequence(sq: SequenceTermBase, sequenceChildrenArg: Seq[SequenceC
 
   private lazy val sequenceChildren = sequenceChildrenArg.toVector
 
-
   private lazy val parsers = alternatives.map(_.parser)
 
-
   override lazy val parser: Parser = {
 
     lazy val choiceParser = new ChoiceParser(srd, parsers.toVector)
diff --git a/daffodil-lib/src/main/resources/org/apache/daffodil/xsd/tdml.xsd b/daffodil-lib/src/main/resources/org/apache/daffodil/xsd/tdml.xsd
index 2cd8bfc..8371293 100644
--- a/daffodil-lib/src/main/resources/org/apache/daffodil/xsd/tdml.xsd
+++ b/daffodil-lib/src/main/resources/org/apache/daffodil/xsd/tdml.xsd
@@ -101,6 +101,7 @@
   <simpleType name="implementationItem">
     <restriction base="xs:token">
       <enumeration value="daffodil"/>
+      <enumeration value="daffodil-runtime2"/>
       <enumeration value="ibm"/>
     </restriction>
   </simpleType>
diff --git a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
index fcdb128..40c9a33 100644
--- a/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
+++ b/daffodil-propgen/src/main/resources/org/apache/daffodil/xsd/dafext.xsd
@@ -504,6 +504,14 @@
             </xs:documentation>
           </xs:annotation>
         </xs:element>
+        <xs:element name="tdmlImplementation" type="xs:string" default="daffodil" minOccurs="0">
+          <xs:annotation>
+            <xs:documentation>
+              TDMLDFDLProcessorFactory implementation to use when running TDML tests.
+              Allowed values are "daffodil" (default), "daffodil-runtime2", and "ibm".
+            </xs:documentation>
+          </xs:annotation>
+        </xs:element>
         <xs:element name="tempFilePath" type="xs:string" default="This string is ignored. Default value is taken from java.io.tmpdir property"  minOccurs="0">
           <xs:annotation>
             <xs:documentation>
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/api/DFDLParserUnparser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/api/DFDLParserUnparser.scala
index b0f27ca..1f0ee1a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/api/DFDLParserUnparser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/api/DFDLParserUnparser.scala
@@ -32,6 +32,7 @@ import org.apache.daffodil.util.Coroutine
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe.Nope
 import org.xml.sax.SAXException
+import org.apache.daffodil.xml.RefQName
 
 /**
  * This file contains traits that define an abstract API that any DFDL processor
@@ -111,7 +112,7 @@ object DFDL {
     def setExternalDFDLVariable(name: String, namespace: String, value: String): Unit
 
     /**
-     * Compilation returns a parser factory, which must be interrogated for diagnostics
+     * Compilation returns a [[ProcessorFactory]], which must be interrogated for diagnostics
      * to see if compilation was successful or not.
      */
     def compileSource(
@@ -123,7 +124,8 @@ object DFDL {
   }
 
   /**
-   * The point of processor factory is to allow compilation of the path expression.
+   * The point of [[ProcessorFactory]] is to allow compilation of the path expression
+   * and/or generation of source code to process data matching the compiled schema
    */
   trait ProcessorFactory extends WithDiagnostics {
 
@@ -147,11 +149,41 @@ object DFDL {
     @deprecated("Use arguments to Compiler.compileSource or compileFile.", "2.6.0")
     def setDistinguishedRootNode(name: String, namespace: String = null): Unit
 
+    /**
+     * Returns a [[DataProcessor]] to process data matching a compiled XPath expression
+     * @param xpath XPath expression in DFDL schema that data should match (you can use only "/" at this time)
+     */
     def onPath(xpath: String): DataProcessor
+
+    /**
+     * Returns a [[CodeGenerator]] to generate code from a DFDL schema to parse or unparse data
+     * @param language source language for generated code (you can use only "c" at this time)
+     */
+    def forLanguage(language: String): CodeGenerator
   }
 
-  trait DataProcessor extends WithDiagnostics {
+  /**
+   * Source code generation and compilation is performed with a language-specific [[CodeGenerator]],
+   * which must be interrogated for diagnostics to see if each call was successful or not.
+   */
+  trait CodeGenerator extends WithDiagnostics {
+    /**
+     * Generates language-specific code from a DFDL schema to parse or unparse data
+     * @param rootNS one of the top-level elements of the DFDL schema (if not supplied, uses first top-level element)
+     * @param outputDir output directory in which to generate code
+     * @return path of output directory containing generated code
+     */
+    def generateCode(rootNS: Option[RefQName], outputDir: String): os.Path
+
+    /**
+     * Compiles the generated code so it can be used by a TDML test or something else
+     * @param outputDir path of output directory containing generated code
+     * @return path of executable built from generated code
+     */
+    def compileCode(outputDir: os.Path): os.Path
+  }
 
+  trait DataProcessorBase {
     /**
      * Returns a data processor with all the same state, but the validation mode changed to that of the argument.
      *
@@ -184,7 +216,9 @@ object DFDL {
     def setTunable(tunable: String, value: String): Unit
     @deprecated("Use withTunables.", "2.6.0")
     def setTunables(tunables: Map[String,String]): Unit
+}
 
+  trait DataProcessor extends DataProcessorBase with WithDiagnostics {
     /**
      * Creates a new instance of XMLReader for SAX Parsing
      */
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
index ef6c426..3ecca0d 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
@@ -30,14 +30,13 @@ import scala.collection.immutable.Queue
 
 import org.apache.daffodil.Implicits._; object INoWarn4 {
   ImplicitsSuppressUnusedImportWarning() }
+import org.apache.daffodil.api.DFDL
+import org.apache.daffodil.api.DaffodilTunables
 import org.apache.daffodil.api.ValidationException
 import org.apache.daffodil.api.ValidationFailure
+import org.apache.daffodil.api.ValidationMode
 import org.apache.daffodil.api.ValidationResult
 import org.apache.daffodil.api.Validator
-import org.apache.daffodil.validation.XercesValidatorFactory
-import org.apache.daffodil.api.DFDL
-import org.apache.daffodil.api.DaffodilTunables
-import org.apache.daffodil.api.ValidationMode
 import org.apache.daffodil.api.WithDiagnostics
 import org.apache.daffodil.debugger.Debugger
 import org.apache.daffodil.dsom.TunableLimitExceededError
@@ -67,7 +66,7 @@ import org.apache.daffodil.processors.unparsers.UnparseError
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe._
 import org.apache.daffodil.util.Misc
-
+import org.apache.daffodil.validation.XercesValidatorFactory
 
 /**
  * Implementation mixin - provides simple helper methods
@@ -685,7 +684,6 @@ class DataProcessor private (
       UnparseError(Nope, One(state.currentLocation), "Expected no remaining events, but received %s.", ev.get)
     }
   }
-
 }
 
 class ParseResult(dp: DataProcessor, override val resultState: PState)
@@ -696,7 +694,7 @@ class ParseResult(dp: DataProcessor, override val resultState: PState)
    * To be successful here, we need to capture parse/validation
    * errors and add them to the Diagnostics list in the PState.
    *
-   * @param bytes the parsed Infoset
+   * @param bytes the XML infoset from the parse
    */
   def validateResult(bytes: Array[Byte]): Unit = {
     Assert.usage(resultState.processorStatus eq Success)
@@ -739,5 +737,3 @@ class UnparseResult(dp: DataProcessor, ustate: UState)
     encodingInfo.knownEncodingName
   }
 }
-
-
diff --git a/daffodil-runtime2/src/main/resources/.clang-format b/daffodil-runtime2/src/main/resources/.clang-format
new file mode 100644
index 0000000..908818d
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/.clang-format
@@ -0,0 +1,24 @@
+# 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.
+
+AlignConsecutiveDeclarations: true
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: true
+AlwaysBreakAfterReturnType: TopLevelDefinitions
+BasedOnStyle: llvm
+BreakBeforeBraces: Allman
+ColumnLimit: 110
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
diff --git a/daffodil-runtime2/src/main/resources/c/Makefile b/daffodil-runtime2/src/main/resources/c/Makefile
new file mode 100644
index 0000000..d8b9e90
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/Makefile
@@ -0,0 +1,55 @@
+#
+# 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.
+
+# Here's how to compile the C sources into a program for running
+# parse/unparse tests (.dat <-> .xml)
+
+PROGRAM = ./daffodil
+HEADERS = libcli/*.h libruntime/*.h
+SOURCES = libcli/*.c libruntime/*.c
+INCLUDES = -I libcli -I libruntime
+CFLAGS = -g -Wall -Wextra
+LIBS = -lmxml
+
+$(PROGRAM): $(HEADERS) $(SOURCES)
+	$(CC) $(CFLAGS) $(INCLUDES) $(SOURCES) $(LIBS) -o $(PROGRAM)
+
+# Here's how to run parse/unparse tests (.dat <-> .xml, although you
+# will need to create the .dat and .xml files first)
+
+PARSE_DAT = parse.dat
+UNPARSE_XML = unparse.xml
+
+clean:
+	rm -f $(PROGRAM) test_$(PARSE_DAT) test_$(UNPARSE_XML)
+
+tests: parse-test unparse-test
+
+parse-test: $(PROGRAM)
+	$(PROGRAM) parse $(PARSE_DAT) -o test_$(UNPARSE_XML)
+	xmldiff $(UNPARSE_XML) test_$(UNPARSE_XML)
+
+unparse-test: $(PROGRAM)
+	$(PROGRAM) unparse $(UNPARSE_XML) -o test_$(PARSE_DAT)
+	diff $(PARSE_DAT) test_$(PARSE_DAT)
+
+# You will need the Mini-XML library and xmldiff - here's how to
+# install both in Ubuntu 20.04
+
+deps:
+	sudo apt install libmxml-dev xmldiff
+
+.PHONY: deps tests parse-test unparse-test
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
new file mode 100644
index 0000000..fa65c4b
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
@@ -0,0 +1,300 @@
+/*
+ * 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 "daffodil_argp.h"
+#include <argp.h>    // for argp_state, argp_error, error_t, argp_parse, ARGP_ERR_UNKNOWN, ARGP_IN_ORDER, ARGP_KEY_ARG, argp, argp_option, ARGP_KEY_END
+#include <stdio.h>   // for sprintf, NULL
+#include <stdlib.h>  // for putenv
+#include <string.h>  // for strlen, strcmp
+
+// Initialize our "daffodil parse" CLI options
+
+struct daffodil_parse_cli daffodil_parse = {
+    "xml", // default infoset type
+    "-",   // default infile
+    "-",   // default outfile
+};
+
+static const struct argp_option parse_options[] = {
+    {"infoset-type", 'I', "<infoset_type>", 0, "Infoset type to output. Must be one of 'xml' or 'null'", 0},
+
+    {"output", 'o', "<file>", 0,
+     "Write output to a given file. If not given or is -, output is written to "
+     "stdout",
+     0},
+
+    {0}};
+
+static error_t parse_handler(int key, char *arg, struct argp_state *state);
+
+static const char parse_args_doc[] = "[infile]";
+
+static const char parse_doc[] = "\n"
+                                "Parse a file using a DFDL schema\n"
+                                "\n"
+                                "Parse Options:"
+                                "\v"
+                                " Trailing arguments:\n"
+                                "  infile (not required)      input file to parse. "
+                                "If not specified, or a value of -, reads from stdin";
+
+static const struct argp parse_argp = {
+    parse_options,  // array of CLI options
+    parse_handler,  // function to get these CLI options
+    parse_args_doc, // short usage documentation
+    parse_doc,      // long help documentation
+    0,              // child argps parsed after this argp
+    0,              // function to replace help messages
+    0               // domain name for translation lookup
+};
+
+// Handle callbacks to get our "daffodil parse" CLI options
+
+static error_t
+parse_handler(int key, char *arg, struct argp_state *state)
+{
+    struct daffodil_parse_cli *parse = state->input;
+
+    switch (key)
+    {
+    case 'I':
+        parse->infoset_converter = arg;
+        break;
+
+    case 'o':
+        parse->outfile = arg;
+        break;
+
+    case ARGP_KEY_ARG:
+        if (state->arg_num)
+        {
+            argp_error(state, "too many arguments: %s", arg);
+        }
+        parse->infile = arg;
+        break;
+
+    default:
+        return ARGP_ERR_UNKNOWN;
+    }
+
+    return 0;
+}
+
+// Parse our "daffodil parse" command line interface
+
+static error_t
+parse_daffodil_parse_cli(struct argp_state *state)
+{
+    int    argc = state->argc - state->next + 1;
+    char **argv = &state->argv[state->next - 1];
+    char * old_cmd = argv[0];
+    char   new_cmd[strlen(state->name) + strlen(" parse") + 1];
+
+    sprintf(new_cmd, "%s parse", state->name);
+    argv[0] = new_cmd;
+
+    error_t status = argp_parse(&parse_argp, argc, argv, ARGP_IN_ORDER, &argc, &daffodil_parse);
+
+    argv[0] = old_cmd;
+    state->next += argc - 1;
+
+    return status;
+}
+
+// Initialize our "daffodil unparse" CLI options
+
+struct daffodil_unparse_cli daffodil_unparse = {
+    "xml", // default infoset type
+    "-",   // default infile
+    "-",   // default outfile
+};
+
+static const struct argp_option unparse_options[] = {
+    {"infoset-type", 'I', "<infoset_type>", 0, "Infoset type to unparse. Must be 'xml'", 0},
+
+    {"output", 'o', "<file>", 0,
+     "Write output to file. If not given or is -, output is written to "
+     "standard output",
+     0},
+
+    {0}};
+
+static error_t unparse_handler(int key, char *arg, struct argp_state *state);
+
+static const char unparse_args_doc[] = "[infile]";
+
+static const char unparse_doc[] = "\n"
+                                  "Unparse an infoset file using a DFDL schema\n"
+                                  "\n"
+                                  "Unparse Options:"
+                                  "\v"
+                                  " Trailing arguments:\n"
+                                  "  infile (not required)      input file to unparse. If not specified, or "
+                                  "a value of -, reads from stdin";
+
+static const struct argp unparse_argp = {
+    unparse_options,  // array of CLI options
+    unparse_handler,  // function to get these CLI options
+    unparse_args_doc, // short usage documentation
+    unparse_doc,      // long help documentation
+    0,                // child argps parsed after this argp
+    0,                // function to replace help messages
+    0                 // domain name for translation lookup
+};
+
+// Handle callbacks to get our "daffodil unparse" CLI options
+
+static error_t
+unparse_handler(int key, char *arg, struct argp_state *state)
+{
+    struct daffodil_unparse_cli *unparse = state->input;
+
+    switch (key)
+    {
+    case 'I':
+        unparse->infoset_converter = arg;
+        break;
+
+    case 'o':
+        unparse->outfile = arg;
+        break;
+
+    case ARGP_KEY_ARG:
+        if (state->arg_num)
+        {
+            argp_error(state, "too many arguments: %s", arg);
+        }
+        unparse->infile = arg;
+        break;
+
+    default:
+        return ARGP_ERR_UNKNOWN;
+    }
+
+    return 0;
+}
+
+// Parse our "daffodil unparse" command line interface
+
+static error_t
+parse_daffodil_unparse_cli(struct argp_state *state)
+{
+    int    argc = state->argc - state->next + 1;
+    char **argv = &state->argv[state->next - 1];
+    char * old_cmd = argv[0];
+    char   new_cmd[strlen(state->name) + strlen(" unparse") + 1];
+
+    sprintf(new_cmd, "%s unparse", state->name);
+    argv[0] = new_cmd;
+
+    error_t status = argp_parse(&unparse_argp, argc, argv, ARGP_IN_ORDER, &argc, &daffodil_unparse);
+
+    argv[0] = old_cmd;
+    state->next += argc - 1;
+
+    return status;
+}
+
+// Initialize our "daffodil" CLI options
+
+struct daffodil_cli daffodil_cli = {
+    DAFFODIL_NONE, // default subcommand
+    0,             // default verbosity
+};
+
+static const struct argp_option daffodil_options[] = {
+    {"verbose", 'v', 0, 0, "Increment verbosity level, one level for each -v", -1},
+
+    {0}};
+
+static error_t daffodil_handler(int key, char *arg, struct argp_state *state);
+
+static const char daffodil_args_doc[] = "<subcommand> [SUBCOMMAND_OPTION...]";
+
+static const char daffodil_doc[] = "\n"
+                                   "Global Options:"
+                                   "\v"
+                                   "Subcommands:\n"
+                                   "  parse         Parse data to a DFDL infoset\n"
+                                   "  unparse       Unparse a DFDL infoset\n"
+                                   "\n"
+                                   "Run 'daffodil <subcommand> --help' for subcommand specific options";
+
+static const struct argp daffodil_argp = {
+    daffodil_options,  // array of CLI options
+    daffodil_handler,  // function to get these CLI options
+    daffodil_args_doc, // short usage documentation
+    daffodil_doc,      // long help documentation
+    0,                 // child argps parsed after this argp
+    0,                 // function to replace help messages
+    0                  // domain name for translation lookup
+};
+
+// Handle callbacks to get our "daffodil" CLI options
+
+static error_t
+daffodil_handler(int key, char *arg, struct argp_state *state)
+{
+    struct daffodil_cli *cli = state->input;
+    error_t              status = 0;
+
+    switch (key)
+    {
+    case 'v':
+        cli->verbosity++;
+        break;
+
+    case ARGP_KEY_ARG:
+        if (strcmp(arg, "parse") == 0)
+        {
+            cli->subcommand = DAFFODIL_PARSE;
+            status = parse_daffodil_parse_cli(state);
+        }
+        else if (strcmp(arg, "unparse") == 0)
+        {
+            cli->subcommand = DAFFODIL_UNPARSE;
+            status = parse_daffodil_unparse_cli(state);
+        }
+        else
+        {
+            argp_error(state, "%s is not a valid subcommand", arg);
+        }
+        break;
+
+    case ARGP_KEY_END:
+        if (cli->subcommand == DAFFODIL_NONE)
+        {
+            argp_error(state, "missing subcommand");
+        }
+        break;
+
+    default:
+        return ARGP_ERR_UNKNOWN;
+    }
+
+    return status;
+}
+
+// Parse our "daffodil" command line interface
+
+error_t
+parse_daffodil_cli(int argc, char **argv)
+{
+    static char *argp_help_fmt = "ARGP_HELP_FMT=no-dup-args-note";
+    putenv(argp_help_fmt); // Do not pass an automatic variable to putenv
+    return argp_parse(&daffodil_argp, argc, argv, ARGP_IN_ORDER, NULL, &daffodil_cli);
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.h
similarity index 51%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.h
index f0bc051..c870123 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.h
@@ -15,20 +15,42 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+#ifndef DAFFODIL_ARGP_H
+#define DAFFODIL_ARGP_H
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+// Parse our "daffodil" command line interface
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+extern int parse_daffodil_cli(int argc, char **argv);
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
-  }
+// Get our "daffodil" CLI options
 
-  private def documentElement = enclosedElement
+extern struct daffodil_cli
+{
+    enum daffodil_subcommand
+    {
+        DAFFODIL_NONE,
+        DAFFODIL_PARSE,
+        DAFFODIL_UNPARSE
+    } subcommand;
+    int verbosity;
+} daffodil_cli;
 
-}
+// Get our "daffodil parse" CLI options
+
+extern struct daffodil_parse_cli
+{
+    const char *infoset_converter;
+    const char *infile;
+    const char *outfile;
+} daffodil_parse;
+
+// Get our "daffodil unparse" CLI options
+
+extern struct daffodil_unparse_cli
+{
+    const char *infoset_converter;
+    const char *infile;
+    const char *outfile;
+} daffodil_unparse;
+
+#endif // DAFFODIL_ARGP_H
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c
new file mode 100644
index 0000000..af9aee4
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c
@@ -0,0 +1,146 @@
+/*
+ * 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 <stdio.h>          // for NULL, perror, FILE, fclose, fflush, fopen, stdin, stdout
+#include <string.h>         // for strcmp
+#include "daffodil_argp.h"  // for daffodil_parse, daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli, daffodil_cli, parse_daffodil_cli, DAFFODIL_PARSE, DAFFODIL_UNPARSE
+#include "errors.h"         // for continue_or_exit, Error, print_diagnostics, PState, UState, ERR_FILE_CLOSE, ERR_FILE_FLUSH, ERR_FILE_OPEN, ERR_INFOSET_READ, ERR_INFOSET_WRITE
+#include "infoset.h"        // for walkInfoset, InfosetBase, rootElement, ERD, VisitEventHandler
+#include "xml_reader.h"     // for xmlReaderMethods, XMLReader
+#include "xml_writer.h"     // for xmlWriterMethods, XMLWriter
+
+// Open a file or exit if it can't be opened
+
+static FILE *
+fopen_or_exit(FILE *stream, const char *pathname, const char *mode)
+{
+    if (strcmp(pathname, "-") != 0)
+    {
+        stream = fopen(pathname, mode);
+        if (!stream)
+        {
+            perror("fopen");
+            const Error error = {ERR_FILE_OPEN, {pathname}};
+            continue_or_exit(&error);
+        }
+    }
+    return stream;
+}
+
+// Flush all output to a file or exit if it can't be written, also
+// print and exit if any previous error occurred or continue otherwise
+
+static void
+fflush_continue_or_exit(FILE *output, const Error *error)
+{
+    if (fflush(output) != 0)
+    {
+        perror("fflush");
+        if (!error)
+        {
+            const Error error = {ERR_FILE_FLUSH, {NULL}};
+            continue_or_exit(&error);
+        }
+    }
+    continue_or_exit(error);
+}
+
+// Close a file or exit if it can't be closed
+
+static void
+fclose_or_exit(FILE *stream, FILE *stdin_or_stdout)
+{
+    if (stream != stdin_or_stdout && fclose(stream) != 0)
+    {
+        perror("fclose");
+        const Error error = {ERR_FILE_CLOSE, {NULL}};
+        continue_or_exit(&error);
+    }
+}
+
+// Define our daffodil program's main entry point
+
+int
+main(int argc, char *argv[])
+{
+    // Parse our "daffodil" command line options
+    int status = parse_daffodil_cli(argc, argv);
+
+    if (status == 0)
+    {
+        FILE *       input = stdin;
+        FILE *       output = stdout;
+        InfosetBase *root = rootElement();
+
+        if (daffodil_cli.subcommand == DAFFODIL_PARSE)
+        {
+            // Open our input and output files if given as arguments.
+            input = fopen_or_exit(input, daffodil_parse.infile, "r");
+            output = fopen_or_exit(output, daffodil_parse.outfile, "w");
+
+            // Parse the input file into our infoset.
+            PState pstate = {input, 0, NULL, NULL};
+            root->erd->parseSelf(root, &pstate);
+            print_diagnostics(pstate.diagnostics);
+            continue_or_exit(pstate.error);
+
+            if (strcmp(daffodil_parse.infoset_converter, "xml") == 0)
+            {
+                // Visit the infoset and print XML from it.
+                XMLWriter    xmlWriter = {xmlWriterMethods, output, {NULL, NULL, 0}};
+                const Error *error = walkInfoset((VisitEventHandler *)&xmlWriter, root);
+                fflush_continue_or_exit(output, error);
+            }
+            else
+            {
+                const Error error = {ERR_INFOSET_WRITE, {daffodil_parse.infoset_converter}};
+                continue_or_exit(&error);
+            }
+        }
+        else if (daffodil_cli.subcommand == DAFFODIL_UNPARSE)
+        {
+            // Open our input and output files if given as arguments.
+            input = fopen_or_exit(input, daffodil_unparse.infile, "r");
+            output = fopen_or_exit(output, daffodil_unparse.outfile, "w");
+
+            if (strcmp(daffodil_unparse.infoset_converter, "xml") == 0)
+            {
+                // Initialize our infoset's values from the XML data.
+                XMLReader    xmlReader = {xmlReaderMethods, input, root, NULL, NULL};
+                const Error *error = walkInfoset((VisitEventHandler *)&xmlReader, root);
+                continue_or_exit(error);
+            }
+            else
+            {
+                const Error error = {ERR_INFOSET_READ, {daffodil_unparse.infoset_converter}};
+                continue_or_exit(&error);
+            }
+
+            // Unparse our infoset to the output file.
+            UState ustate = {output, 0, NULL, NULL};
+            root->erd->unparseSelf(root, &ustate);
+            print_diagnostics(ustate.diagnostics);
+            continue_or_exit(ustate.error);
+        }
+
+        // Close our input and out files if we opened them.
+        fclose_or_exit(input, stdin);
+        fclose_or_exit(output, stdout);
+    }
+
+    return status;
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/stack.c b/daffodil-runtime2/src/main/resources/c/libcli/stack.c
new file mode 100644
index 0000000..8297bcd
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/stack.c
@@ -0,0 +1,87 @@
+/*
+ * 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 "stack.h"
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for NULL, ptrdiff_t
+#include "errors.h"   // for continue_or_exit, Error, ERR_STACK_EMPTY, ERR_STACK_OVERFLOW, ERR_STACK_UNDERFLOW
+
+// Initialize stack with preallocated array
+
+void
+stack_init(stack_t *p_stack, stack_item_t *p_array, ptrdiff_t capacity)
+{
+    p_stack->p_after = p_array;
+    p_stack->p_array = p_array;
+    p_stack->capacity = capacity;
+}
+
+// Check whether stack is empty
+
+bool
+stack_is_empty(stack_t *p_stack)
+{
+    return p_stack->p_after == p_stack->p_array;
+}
+
+// Check whether stack is full
+
+bool
+stack_is_full(stack_t *p_stack)
+{
+    ptrdiff_t count = p_stack->p_after - p_stack->p_array;
+    return count >= p_stack->capacity;
+}
+
+// Pop element from stack
+
+stack_item_t
+stack_pop(stack_t *p_stack)
+{
+    if (stack_is_empty(p_stack))
+    {
+        const Error error = {ERR_STACK_UNDERFLOW, {NULL}};
+        continue_or_exit(&error);
+    }
+    return *(--p_stack->p_after);
+}
+
+// Push element into stack
+
+void
+stack_push(stack_t *p_stack, stack_item_t item)
+{
+    if (stack_is_full(p_stack))
+    {
+        const Error error = {ERR_STACK_OVERFLOW, {NULL}};
+        continue_or_exit(&error);
+    }
+    *(p_stack->p_after++) = item;
+}
+
+// Get stack's top element
+
+stack_item_t
+stack_top(stack_t *p_stack)
+{
+    if (stack_is_empty(p_stack))
+    {
+        const Error error = {ERR_STACK_EMPTY, {NULL}};
+        continue_or_exit(&error);
+    }
+    return *(p_stack->p_after - 1);
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/stack.h b/daffodil-runtime2/src/main/resources/c/libcli/stack.h
new file mode 100644
index 0000000..c63a24e
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/stack.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef STACK_H
+#define STACK_H
+
+#include <mxml.h>     // for mxml_node_t
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for ptrdiff_t
+
+// Type of element pushed into stack
+
+typedef mxml_node_t *stack_item_t;
+
+// Implement stack using preallocated array
+
+typedef struct
+{
+    stack_item_t *p_after;  // Pointer to one past top element
+    stack_item_t *p_array;  // Pointer to stack's array
+    ptrdiff_t     capacity; // Size of stack's array
+} stack_t;
+
+// Initialize stack with preallocated array
+
+extern void stack_init(stack_t *p_stack, stack_item_t *p_array, ptrdiff_t capacity);
+
+// Check whether stack is empty
+
+extern bool stack_is_empty(stack_t *p_stack);
+
+// Check whether stack is full
+
+extern bool stack_is_full(stack_t *p_stack);
+
+// Pop element from stack
+
+extern stack_item_t stack_pop(stack_t *p_stack);
+
+// Push element into stack
+
+extern void stack_push(stack_t *p_stack, stack_item_t item);
+
+// Get stack's top element
+
+extern stack_item_t stack_top(stack_t *p_stack);
+
+#endif // STACK_H
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c
new file mode 100644
index 0000000..c39804f
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c
@@ -0,0 +1,414 @@
+/*
+ * 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 "xml_reader.h"
+#include <assert.h>    // for assert
+#include <errno.h>     // for errno
+#include <inttypes.h>  // for strtoimax, strtoumax
+#include <mxml.h>      // for mxmlWalkNext, mxmlGetElement, mxmlGetType, MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile, MXML_OPAQUE_CALLBACK
+#include <stdbool.h>   // for bool, false, true
+#include <stdint.h>    // for intmax_t, uintmax_t, int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t, INT16_MAX, INT16_MIN, INT32_MAX, INT32_MIN, INT64_MAX, INT64_MIN, INT8_MAX, INT8_MIN, UINT16_MAX, UINT32_MAX, UINT64_MAX, UINT8_MAX
+#include <stdio.h>     // for NULL
+#include <stdlib.h>    // for strtod, strtof
+#include <string.h>    // for strcmp, strlen, strncmp
+#include "errors.h"    // for Error, Error::(anonymous), ERR_STRTONUM_EMPTY, ERR_STRTONUM_NOT, ERR_XML_GONE, ERR_STRTOD_ERRNO, ERR_STRTOI_ERRNO, ERR_STRTONUM_RANGE, ERR_XML_MISMATCH, ERR_STRTOBOOL, ERR_XML_ERD, ERR_XML_INPUT, ERR_XML_LEFT, UNUSED
+
+// Convert an XML element's text to a boolean with error checking
+
+static bool
+strtobool(const char *numptr, const Error **errorptr)
+{
+    // The lexical space of xs:boolean accepts true, false, 1, and 0
+    bool value = false;
+
+    // Check for any errors converting the string to a boolean
+    if (strcmp(numptr, "true") == 0)
+    {
+        value = true;
+    }
+    else if (strcmp(numptr, "false") == 0)
+    {
+        value = false;
+    }
+    else if (strcmp(numptr, "1") == 0)
+    {
+        value = true;
+    }
+    else if (strcmp(numptr, "0") == 0)
+    {
+        value = false;
+    }
+    else
+    {
+        static Error error = {ERR_STRTOBOOL, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+
+    return value;
+}
+
+// Convert an XML element's text to a double (call strtod with our own
+// error checking)
+
+static double
+strtodnum(const char *numptr, const Error **errorptr)
+{
+    char *endptr = NULL;
+
+    // Clear errno to detect error after calling strtod
+    errno = 0;
+    const double value = strtod(numptr, &endptr);
+
+    // Check for any errors converting the string to a number
+    if (errno != 0)
+    {
+        static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (endptr == numptr)
+    {
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (*endptr != '\0')
+    {
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+
+    return value;
+}
+
+// Convert an XML element's text to a float (call strtof with our own
+// error checking)
+
+static float
+strtofnum(const char *numptr, const Error **errorptr)
+{
+    char *endptr = NULL;
+
+    // Clear errno to detect error after calling strtof
+    errno = 0;
+    const float value = strtof(numptr, &endptr);
+
+    // Check for any errors converting the string to a number
+    if (errno != 0)
+    {
+        static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (endptr == numptr)
+    {
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (*endptr != '\0')
+    {
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+
+    return value;
+}
+
+// Convert an XML element's text to a signed integer (call strtoimax
+// with our own error checking)
+
+static intmax_t
+strtonum(const char *numptr, intmax_t minval, intmax_t maxval, const Error **errorptr)
+{
+    char *endptr = NULL;
+    assert(minval < maxval);
+
+    // Clear errno to detect error after calling strtoimax
+    errno = 0;
+    const intmax_t value = strtoimax(numptr, &endptr, 10);
+
+    // Check for any errors converting the string to a number
+    if (errno != 0)
+    {
+        static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (endptr == numptr)
+    {
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (*endptr != '\0')
+    {
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (value < minval || value > maxval)
+    {
+        static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+
+    return value;
+}
+
+// Convert an XML element's text to an unsigned integer (call strtoumax
+// with our own error checking)
+
+static uintmax_t
+strtounum(const char *numptr, uintmax_t maxval, const Error **errorptr)
+{
+    char *endptr = NULL;
+
+    // Clear errno to detect error after calling strtoumax
+    errno = 0;
+    const uintmax_t value = strtoumax(numptr, &endptr, 10);
+
+    // Check for any errors converting the string to a number
+    if (errno != 0)
+    {
+        static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (endptr == numptr)
+    {
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (*endptr != '\0')
+    {
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+    else if (value > maxval)
+    {
+        static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
+    }
+
+    return value;
+}
+
+// Read XML data from file before walking infoset
+
+static const Error *
+xmlStartDocument(XMLReader *reader)
+{
+    // Load the XML data into memory
+    reader->xml = mxmlLoadFile(NULL, reader->stream, MXML_OPAQUE_CALLBACK);
+    reader->node = reader->xml;
+    if (!reader->node)
+    {
+        static Error error = {ERR_XML_INPUT, {NULL}};
+        return &error;
+    }
+
+    // Consume the <?xml line if there is one
+    const char *name = mxmlGetElement(reader->node);
+    if (name && strncmp(name, "?xml", strlen("?xml")) == 0)
+    {
+        do
+        {
+            reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+        } while (mxmlGetType(reader->node) == MXML_OPAQUE);
+        name = mxmlGetElement(reader->node);
+    }
+
+    // Consume a comment if there is one
+    if (name && strncmp(name, "!--", strlen("!--")) == 0)
+    {
+        do
+        {
+            reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+        } while (mxmlGetType(reader->node) == MXML_OPAQUE);
+    }
+
+    static Error error = {ERR_XML_GONE, {NULL}};
+    return reader->node ? NULL : &error;
+}
+
+// Delete XML data after walking infoset
+
+static const Error *
+xmlEndDocument(XMLReader *reader)
+{
+    // Consume any remaining newlines or whitespace
+    while (mxmlGetType(reader->node) == MXML_OPAQUE)
+    {
+        reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+    }
+
+    // Check whether we have consumed all of the XML data
+    if (reader->node)
+    {
+        // This code path exits the program - no need to call mxmlDelete
+        static Error error = {ERR_XML_LEFT, {NULL}};
+        error.s = mxmlGetElement(reader->node);
+        return &error;
+    }
+
+    // Free the storage allocated to hold the XML data
+    mxmlDelete(reader->xml);
+    reader->xml = NULL;
+    reader->node = NULL;
+    return NULL;
+}
+
+// Continue walking both XML data and infoset in lockstep
+
+static const Error *
+xmlStartComplex(XMLReader *reader, const InfosetBase *base)
+{
+    // Consume any newlines or whitespace before the element
+    while (mxmlGetType(reader->node) == MXML_OPAQUE)
+    {
+        reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+    }
+
+    // Get the element and consume it
+    const char *name_from_xml = mxmlGetElement(reader->node);
+    const char *name_from_erd = get_erd_name(base->erd);
+    reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+
+    // Check whether we are walking both XML data and infoset in lockstep
+    if (name_from_xml && name_from_erd)
+    {
+        static Error error = {ERR_XML_MISMATCH, {NULL}};
+        error.s = name_from_erd;
+        return strcmp(name_from_xml, name_from_erd) == 0 ? NULL : &error;
+    }
+    else
+    {
+        static Error error = {ERR_XML_GONE, {NULL}};
+        return &error;
+    }
+}
+
+// Consume XML data only on start events, not end events
+
+static const Error *
+xmlEndComplex(XMLReader *reader, const InfosetBase *base)
+{
+    UNUSED(reader); // because nothing to read
+    UNUSED(base);   // because nothing to check
+    return NULL;
+}
+
+// Read a boolean, 32-bit or 64-bit real number, or 8, 16, 32, or
+// 64-bit signed or unsigned integer from XML data
+
+static const Error *
+xmlNumberElem(XMLReader *reader, const ERD *erd, void *number)
+{
+    // Consume any newlines or whitespace before the element
+    while (mxmlGetType(reader->node) == MXML_OPAQUE)
+    {
+        reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+    }
+
+    // Get the element and consume it
+    const char *name_from_xml = mxmlGetElement(reader->node);
+    const char *name_from_erd = get_erd_name(erd);
+    const char *number_from_xml = mxmlGetOpaque(reader->node);
+    reader->node = mxmlWalkNext(reader->node, reader->xml, MXML_DESCEND);
+
+    // Check whether we are walking both XML data and infoset in lockstep
+    if (name_from_xml && name_from_erd)
+    {
+        if (strcmp(name_from_xml, name_from_erd) == 0)
+        {
+            // Check for any errors getting the number
+            const Error *error = NULL;
+
+            // Handle varying bit lengths of both signed & unsigned numbers
+            const enum TypeCode typeCode = erd->typeCode;
+            switch (typeCode)
+            {
+            case PRIMITIVE_BOOLEAN:
+                *(bool *)number = strtobool(number_from_xml, &error);
+                return error;
+            case PRIMITIVE_FLOAT:
+                *(float *)number = strtofnum(number_from_xml, &error);
+                return error;
+            case PRIMITIVE_DOUBLE:
+                *(double *)number = strtodnum(number_from_xml, &error);
+                return error;
+            case PRIMITIVE_INT16:
+                *(int16_t *)number = (int16_t)strtonum(number_from_xml, INT16_MIN, INT16_MAX, &error);
+                return error;
+            case PRIMITIVE_INT32:
+                *(int32_t *)number = (int32_t)strtonum(number_from_xml, INT32_MIN, INT32_MAX, &error);
+                return error;
+            case PRIMITIVE_INT64:
+                *(int64_t *)number = (int64_t)strtonum(number_from_xml, INT64_MIN, INT64_MAX, &error);
+                return error;
+            case PRIMITIVE_INT8:
+                *(int8_t *)number = (int8_t)strtonum(number_from_xml, INT8_MIN, INT8_MAX, &error);
+                return error;
+            case PRIMITIVE_UINT16:
+                *(uint16_t *)number = (uint16_t)strtounum(number_from_xml, UINT16_MAX, &error);
+                return error;
+            case PRIMITIVE_UINT32:
+                *(uint32_t *)number = (uint32_t)strtounum(number_from_xml, UINT32_MAX, &error);
+                return error;
+            case PRIMITIVE_UINT64:
+                *(uint64_t *)number = (uint64_t)strtounum(number_from_xml, UINT64_MAX, &error);
+                return error;
+            case PRIMITIVE_UINT8:
+                *(uint8_t *)number = (uint8_t)strtounum(number_from_xml, UINT8_MAX, &error);
+                return error;
+            default:
+            {
+                static Error error_erd = {ERR_XML_ERD, {NULL}};
+                error_erd.d64 = typeCode;
+                return &error_erd;
+            }
+            }
+        }
+        else
+        {
+            static Error error = {ERR_XML_MISMATCH, {NULL}};
+            error.s = name_from_erd;
+            return &error;
+        }
+    }
+    else
+    {
+        static Error error = {ERR_XML_GONE, {NULL}};
+        return &error;
+    }
+}
+
+// Initialize a struct with our visitor event handler methods
+
+const VisitEventHandler xmlReaderMethods = {
+    (VisitStartDocument)&xmlStartDocument, (VisitEndDocument)&xmlEndDocument,
+    (VisitStartComplex)&xmlStartComplex,   (VisitEndComplex)&xmlEndComplex,
+    (VisitNumberElem)&xmlNumberElem,
+};
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
similarity index 58%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
index f0bc051..952d39e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
@@ -15,20 +15,26 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+#ifndef XML_READER_H
+#define XML_READER_H
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+#include <mxml.h>     // for mxml_node_t
+#include <stdio.h>    // for FILE
+#include "infoset.h"  // for VisitEventHandler, InfosetBase
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+// XMLReader - infoset visitor with methods to read XML
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
-  }
+typedef struct XMLReader
+{
+    const VisitEventHandler handler;
+    FILE *                  stream;
+    InfosetBase *           root;
+    mxml_node_t *           xml;
+    mxml_node_t *           node;
+} XMLReader;
 
-  private def documentElement = enclosedElement
+// XMLReader methods to pass to walkInfoset method
 
-}
+extern const VisitEventHandler xmlReaderMethods;
+
+#endif // XML_READER_H
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c
new file mode 100644
index 0000000..bfb336a
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c
@@ -0,0 +1,204 @@
+/*
+ * 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 "xml_writer.h"
+#include <assert.h>   // for assert
+#include <mxml.h>     // for mxmlNewOpaquef, mxml_node_t, mxmlElementSetAttr, mxmlGetOpaque, mxmlNewElement, mxmlDelete, mxmlGetElement, mxmlNewXML, mxmlSaveFile, MXML_NO_CALLBACK
+#include <stdbool.h>  // for bool
+#include <stdint.h>   // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t
+#include <stdio.h>    // for NULL
+#include <string.h>   // for strcmp
+#include "errors.h"   // for Error, ERR_XML_DECL, ERR_XML_ELEMENT, ERR_XML_WRITE, LIMIT_XML_NESTING, Error::(anonymous)
+#include "stack.h"    // for stack_is_empty, stack_pop, stack_push, stack_top, stack_init
+
+// Push new XML document on stack (note the stack is stored in a
+// static array which could overflow and stop the program; it also
+// means none of those functions are thread-safe)
+
+static const Error *
+xmlStartDocument(XMLWriter *writer)
+{
+    static mxml_node_t *array[LIMIT_XML_NESTING];
+    stack_init(&writer->stack, array, LIMIT_XML_NESTING);
+
+    mxml_node_t *xml = mxmlNewXML("1.0");
+    if (xml)
+    {
+        stack_push(&writer->stack, xml);
+        return NULL;
+    }
+    else
+    {
+        static Error error = {ERR_XML_DECL, {NULL}};
+        return &error;
+    }
+}
+
+// Pop completed XML document off stack and write it to stream (note
+// stack underflow will stop program)
+
+static const Error *
+xmlEndDocument(XMLWriter *writer)
+{
+    mxml_node_t *xml = stack_pop(&writer->stack);
+    assert(stack_is_empty(&writer->stack));
+
+    int status = mxmlSaveFile(xml, writer->stream, MXML_NO_CALLBACK);
+    if (status < 0)
+    {
+        static Error error = {ERR_XML_WRITE, {NULL}};
+        return &error;
+    }
+    mxmlDelete(xml);
+    return NULL;
+}
+
+// Push new complex element on stack (note stack overflow will stop
+// program)
+
+static const Error *
+xmlStartComplex(XMLWriter *writer, const InfosetBase *base)
+{
+    mxml_node_t *parent = stack_top(&writer->stack);
+    const char * name = get_erd_name(base->erd);
+    const char * xmlns = get_erd_xmlns(base->erd);
+    mxml_node_t *complex = mxmlNewElement(parent, name);
+    if (xmlns)
+    {
+        const char *ns = get_erd_ns(base->erd);
+        mxmlElementSetAttr(complex, xmlns, ns);
+    }
+    stack_push(&writer->stack, complex);
+    return NULL;
+}
+
+// Pop completed complex element off stack (note stack underflow will
+// stop program)
+
+static const Error *
+xmlEndComplex(XMLWriter *writer, const InfosetBase *base)
+{
+    mxml_node_t *complex = stack_pop(&writer->stack);
+
+    const char *name_from_xml = mxmlGetElement(complex);
+    const char *name_from_erd = get_erd_name(base->erd);
+    assert(strcmp(name_from_xml, name_from_erd) == 0);
+
+    return NULL;
+}
+
+// Fix a real number to conform to xsd:float syntax if needed
+
+static void
+fixNumberIfNeeded(const char *text)
+{
+    if (text[0] == 'N' && text[1] == 'A')
+    {
+        // xsd:float requires NaN to be capitalized correctly
+        char *modifyInPlace = (char *)text;
+        modifyInPlace[1] = 'a';
+    }
+    // These are not required by xsd:float, only to match runtime1 better
+    //  - Strip + from <f>E+<e> to get <f>E<e>
+    //  - Add .0 to 1 to get 1.0
+    // It would be better to compare floats as numbers, not strings, though
+}
+
+// Write a boolean, 32-bit or 64-bit real number, or 8, 16, 32, or
+// 64-bit signed or unsigned integer as an XML element's value
+
+static const Error *
+xmlNumberElem(XMLWriter *writer, const ERD *erd, const void *number)
+{
+    mxml_node_t *parent = stack_top(&writer->stack);
+    const char * name = get_erd_name(erd);
+    mxml_node_t *simple = mxmlNewElement(parent, name);
+
+    // Set namespace declaration if necessary
+    const char *xmlns = get_erd_xmlns(erd);
+    if (xmlns)
+    {
+        const char *ns = get_erd_ns(erd);
+        mxmlElementSetAttr(simple, xmlns, ns);
+    }
+
+    // Handle varying bit lengths of both signed & unsigned numbers
+    const enum TypeCode typeCode = erd->typeCode;
+    mxml_node_t *       text = NULL;
+    switch (typeCode)
+    {
+    case PRIMITIVE_BOOLEAN:
+        text = mxmlNewOpaquef(simple, "%s", *(const bool *)number ? "true" : "false");
+        break;
+    case PRIMITIVE_FLOAT:
+        // Round-trippable float, shortest possible
+        text = mxmlNewOpaquef(simple, "%.9G", *(const float *)number);
+        fixNumberIfNeeded(mxmlGetOpaque(text));
+        break;
+    case PRIMITIVE_DOUBLE:
+        // Round-trippable double, shortest possible
+        text = mxmlNewOpaquef(simple, "%.17lG", *(const double *)number);
+        fixNumberIfNeeded(mxmlGetOpaque(text));
+        break;
+    case PRIMITIVE_INT16:
+        text = mxmlNewOpaquef(simple, "%hi", *(const int16_t *)number);
+        break;
+    case PRIMITIVE_INT32:
+        text = mxmlNewOpaquef(simple, "%i", *(const int32_t *)number);
+        break;
+    case PRIMITIVE_INT64:
+        text = mxmlNewOpaquef(simple, "%li", *(const int64_t *)number);
+        break;
+    case PRIMITIVE_INT8:
+        text = mxmlNewOpaquef(simple, "%hhi", *(const int8_t *)number);
+        break;
+    case PRIMITIVE_UINT16:
+        text = mxmlNewOpaquef(simple, "%hu", *(const uint16_t *)number);
+        break;
+    case PRIMITIVE_UINT32:
+        text = mxmlNewOpaquef(simple, "%u", *(const uint32_t *)number);
+        break;
+    case PRIMITIVE_UINT64:
+        text = mxmlNewOpaquef(simple, "%lu", *(const uint64_t *)number);
+        break;
+    case PRIMITIVE_UINT8:
+        text = mxmlNewOpaquef(simple, "%hhu", *(const uint8_t *)number);
+        break;
+    default:
+        // Let text remain NULL and report error below
+        break;
+    }
+
+    if (simple && text)
+    {
+        return NULL;
+    }
+    else
+    {
+        static Error error = {ERR_XML_ELEMENT, {NULL}};
+        error.s = name;
+        return &error;
+    }
+}
+
+// Initialize a struct with our visitor event handler methods
+
+const VisitEventHandler xmlWriterMethods = {
+    (VisitStartDocument)&xmlStartDocument, (VisitEndDocument)&xmlEndDocument,
+    (VisitStartComplex)&xmlStartComplex,   (VisitEndComplex)&xmlEndComplex,
+    (VisitNumberElem)&xmlNumberElem,
+};
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
similarity index 62%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
index f0bc051..00fe45a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
@@ -15,20 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+#ifndef XML_WRITER_H
+#define XML_WRITER_H
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+#include <stdio.h>    // for FILE
+#include "infoset.h"  // for VisitEventHandler
+#include "stack.h"    // for stack_t
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+// XMLWriter - infoset visitor with methods to output XML
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
-  }
+typedef struct XMLWriter
+{
+    const VisitEventHandler handler;
+    FILE *                  stream;
+    stack_t                 stack;
+} XMLWriter;
 
-  private def documentElement = enclosedElement
+// XMLWriter methods to pass to walkInfoset method
 
-}
+extern const VisitEventHandler xmlWriterMethods;
+
+#endif // XML_WRITER_H
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/errors.c b/daffodil-runtime2/src/main/resources/c/libruntime/errors.c
new file mode 100644
index 0000000..4bd657c
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.c
@@ -0,0 +1,203 @@
+/*
+ * 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 "errors.h"
+#include <assert.h>    // for assert
+#include <error.h>     // for error
+#include <inttypes.h>  // for PRId64
+#include <stdbool.h>   // for bool, false, true
+#include <stdio.h>     // for NULL, feof, ferror, FILE, size_t
+#include <stdlib.h>    // for EXIT_FAILURE
+
+// error_message - return an internationalized error message
+
+static const char *
+error_message(enum ErrorCode code)
+{
+    switch (code)
+    {
+    case ERR_CHOICE_KEY:
+        return "no match between choice dispatch key %" PRId64 " and any branch key";
+    case ERR_FILE_CLOSE:
+        return "error closing file";
+    case ERR_FILE_FLUSH:
+        return "error flushing stream to file";
+    case ERR_FILE_OPEN:
+        return "error opening file '%s'";
+    case ERR_FIXED_VALUE:
+        return "value of element '%s' does not match value of its "
+               "'fixed' attribute";
+    case ERR_INFOSET_READ:
+        return "cannot read infoset type '%s'";
+    case ERR_INFOSET_WRITE:
+        return "cannot write infoset type '%s'";
+    case ERR_PARSE_BOOL:
+        return "error parsing binary value %" PRId64 " as either true or false";
+    case ERR_STACK_EMPTY:
+        return "stack empty, stopping program";
+    case ERR_STACK_OVERFLOW:
+        return "stack overflow, stopping program";
+    case ERR_STACK_UNDERFLOW:
+        return "stack underflow, stopping program";
+    case ERR_STREAM_EOF:
+        return "EOF in stream, stopping program";
+    case ERR_STREAM_ERROR:
+        return "error in stream, stopping program";
+    case ERR_STRTOBOOL:
+        return "error converting XML data '%s' to boolean";
+    case ERR_STRTOD_ERRNO:
+        return "error converting XML data '%s' to number";
+    case ERR_STRTOI_ERRNO:
+        return "error converting XML data '%s' to integer";
+    case ERR_STRTONUM_EMPTY:
+        return "found no number in XML data '%s'";
+    case ERR_STRTONUM_NOT:
+        return "found non-number characters in XML data '%s'";
+    case ERR_STRTONUM_RANGE:
+        return "number in XML data '%s' out of range";
+    case ERR_XML_DECL:
+        return "error making new XML declaration";
+    case ERR_XML_ELEMENT:
+        return "error making new XML element '%s'";
+    case ERR_XML_ERD:
+        return "unexpected ERD typeCode %" PRId64 " while reading XML data";
+    case ERR_XML_GONE:
+        return "ran out of XML data";
+    case ERR_XML_INPUT:
+        return "unable to read XML data from input file";
+    case ERR_XML_LEFT:
+        return "did not consume all of the XML data, '%s' left";
+    case ERR_XML_MISMATCH:
+        return "found mismatch between XML data and infoset '%s'";
+    case ERR_XML_WRITE:
+        return "error writing XML document";
+    default:
+        assert("invalid code" && 0);
+        return "unrecognized error code, shouldn't happen";
+    }
+}
+
+// print_maybe_stop - print a message and maybe stop the program
+
+static void
+print_maybe_stop(const Error *err, int status)
+{
+    const int   errnum = 0;
+    const char *format = "%s";
+    const char *msg = error_message(err->code);
+
+    switch (err->code)
+    {
+    case ERR_FILE_OPEN:
+    case ERR_FIXED_VALUE:
+    case ERR_INFOSET_READ:
+    case ERR_INFOSET_WRITE:
+    case ERR_STRTOBOOL:
+    case ERR_STRTOD_ERRNO:
+    case ERR_STRTOI_ERRNO:
+    case ERR_STRTONUM_EMPTY:
+    case ERR_STRTONUM_NOT:
+    case ERR_STRTONUM_RANGE:
+    case ERR_XML_ELEMENT:
+    case ERR_XML_LEFT:
+    case ERR_XML_MISMATCH:
+        error(status, errnum, msg, err->s);
+        break;
+    case ERR_CHOICE_KEY:
+    case ERR_PARSE_BOOL:
+    case ERR_XML_ERD:
+        error(status, errnum, msg, err->d64);
+        break;
+    default:
+        error(status, errnum, format, msg);
+        break;
+    }
+}
+
+// get_diagnostics - get pointer to validation diagnostics
+
+Diagnostics *
+get_diagnostics(void)
+{
+    static Diagnostics diagnostics;
+    return &diagnostics;
+}
+
+// add_diagnostic - add a new error to validation diagnostics
+
+bool
+add_diagnostic(Diagnostics *diagnostics, const Error *error)
+{
+    if (diagnostics && error)
+    {
+        if (diagnostics->length < LIMIT_DIAGNOSTICS)
+        {
+            Error *err = &diagnostics->array[diagnostics->length++];
+            err->code = error->code;
+            err->s = error->s;
+            return true;
+        }
+    }
+    return false;
+}
+
+// print_diagnostics - print any validation diagnostics
+
+void
+print_diagnostics(const Diagnostics *diagnostics)
+{
+    if (diagnostics)
+    {
+        for (size_t i = 0; i < diagnostics->length; i++)
+        {
+            const Error *error = &diagnostics->array[i];
+            print_maybe_stop(error, 0);
+        }
+    }
+}
+
+// continue_or_exit - print and exit if an error occurred or continue otherwise
+
+void
+continue_or_exit(const Error *error)
+{
+    if (error)
+    {
+        print_maybe_stop(error, EXIT_FAILURE);
+    }
+}
+
+// eof_or_error - return an error if a stream has its eof or error indicator set
+
+const Error *
+eof_or_error(FILE *stream)
+{
+    if (feof(stream))
+    {
+        static Error error = {ERR_STREAM_EOF, {NULL}};
+        return &error;
+    }
+    else if (ferror(stream))
+    {
+        static Error error = {ERR_STREAM_ERROR, {NULL}};
+        return &error;
+    }
+    else
+    {
+        return NULL;
+    }
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/errors.h b/daffodil-runtime2/src/main/resources/c/libruntime/errors.h
new file mode 100644
index 0000000..c654181
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.h
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#ifndef ERRORS_H
+#define ERRORS_H
+
+#include <stdbool.h>  // for bool
+#include <stdint.h>   // for int64_t
+#include <stdio.h>    // for FILE, size_t
+
+enum Limits
+{
+    LIMIT_DIAGNOSTICS = 100,  // limits how many diagnostics can accumulate
+    LIMIT_NAME_LENGTH = 9999, // limits how long infoset names can become
+    LIMIT_XML_NESTING = 100   // limits how deep infoset elements can nest
+};
+
+// ErrorCode - types of errors which could occur
+
+enum ErrorCode
+{
+    ERR_CHOICE_KEY,
+    ERR_FILE_CLOSE,
+    ERR_FILE_FLUSH,
+    ERR_FILE_OPEN,
+    ERR_FIXED_VALUE,
+    ERR_INFOSET_READ,
+    ERR_INFOSET_WRITE,
+    ERR_PARSE_BOOL,
+    ERR_STACK_EMPTY,
+    ERR_STACK_OVERFLOW,
+    ERR_STACK_UNDERFLOW,
+    ERR_STREAM_EOF,
+    ERR_STREAM_ERROR,
+    ERR_STRTOBOOL,
+    ERR_STRTOD_ERRNO,
+    ERR_STRTOI_ERRNO,
+    ERR_STRTONUM_EMPTY,
+    ERR_STRTONUM_NOT,
+    ERR_STRTONUM_RANGE,
+    ERR_XML_DECL,
+    ERR_XML_ELEMENT,
+    ERR_XML_ERD,
+    ERR_XML_GONE,
+    ERR_XML_INPUT,
+    ERR_XML_LEFT,
+    ERR_XML_MISMATCH,
+    ERR_XML_WRITE
+};
+
+// Error - specific error occuring now
+
+typedef struct Error
+{
+    enum ErrorCode code;
+    union
+    {
+        const char *s;   // for %s
+        int64_t     d64; // for %d64
+    };
+} Error;
+
+// Diagnostics - array of validation errors
+
+typedef struct Diagnostics
+{
+    Error  array[LIMIT_DIAGNOSTICS];
+    size_t length;
+} Diagnostics;
+
+// PState - mutable state while parsing data
+
+typedef struct PState
+{
+    FILE *       stream;      // input to read data from
+    size_t       position;    // 0-based position in stream
+    Diagnostics *diagnostics; // any validation diagnostics
+    const Error *error;       // any error which stops program
+} PState;
+
+// UState - mutable state while unparsing infoset
+
+typedef struct UState
+{
+    FILE *       stream;      // output to write data to
+    size_t       position;    // 0-based position in stream
+    Diagnostics *diagnostics; // any validation diagnostics
+    const Error *error;       // any error which stops program
+} UState;
+
+// get_diagnostics - get pointer to validation diagnostics
+
+extern Diagnostics *get_diagnostics(void);
+
+// add_diagnostic - add a new error to validation diagnostics
+
+extern bool add_diagnostic(Diagnostics *diagnostics, const Error *error);
+
+// print_diagnostics - print any validation diagnostics
+
+extern void print_diagnostics(const Diagnostics *diagnostics);
+
+// continue_or_exit - print and exit if an error occurred or continue otherwise
+
+extern void continue_or_exit(const Error *error);
+
+// eof_or_error - return an error if a stream has its eof or error indicator set
+
+extern const Error *eof_or_error(FILE *stream);
+
+// UNUSED - suppress compiler warning about unused variable
+
+#define UNUSED(x) (void)(x)
+
+#endif // ERRORS_H
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
new file mode 100644
index 0000000..107aa55
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
@@ -0,0 +1,198 @@
+/*
+ * 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 "infoset.h"
+#include <string.h>  // for memccpy
+#include "errors.h"  // for Error, LIMIT_NAME_LENGTH
+
+// get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns
+// attribute/value from ERD to use on XML element
+
+const char *
+get_erd_name(const ERD *erd)
+{
+    static char name[LIMIT_NAME_LENGTH];
+    char *      next = name;
+    char *      last = name + sizeof(name) - 1;
+
+    if (next && erd->namedQName.prefix)
+    {
+        next = memccpy(next, erd->namedQName.prefix, 0, last - next);
+        if (next)
+        {
+            --next;
+        }
+    }
+    if (next && erd->namedQName.prefix)
+    {
+        next = memccpy(next, ":", 0, last - next);
+        if (next)
+        {
+            --next;
+        }
+    }
+    if (next)
+    {
+        next = memccpy(next, erd->namedQName.local, 0, last - next);
+        if (next)
+        {
+            --next;
+        }
+    }
+    if (!next)
+    {
+        *last = 0;
+    }
+
+    return name;
+}
+
+const char *
+get_erd_xmlns(const ERD *erd)
+{
+    if (erd->namedQName.ns)
+    {
+        static char xmlns[LIMIT_NAME_LENGTH];
+        char *      next = xmlns;
+        char *      last = xmlns + sizeof(xmlns) - 1;
+
+        next = memccpy(next, "xmlns", 0, last - next);
+        if (next)
+        {
+            --next;
+        }
+
+        if (next && erd->namedQName.prefix)
+        {
+            next = memccpy(next, ":", 0, last - next);
+            if (next)
+            {
+                --next;
+            }
+        }
+        if (next && erd->namedQName.prefix)
+        {
+            next = memccpy(next, erd->namedQName.prefix, 0, last - next);
+            if (next)
+            {
+                --next;
+            }
+        }
+        if (!next)
+        {
+            *last = 0;
+        }
+
+        return xmlns;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+const char *
+get_erd_ns(const ERD *erd)
+{
+    return erd->namedQName.ns;
+}
+
+// walkInfosetNode - recursively walk an infoset node and call
+// VisitEventHandler methods
+
+static const Error *
+walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode)
+{
+    const Error *error = NULL;
+
+    // Start visiting the node
+    if (!error)
+    {
+        error = handler->visitStartComplex(handler, infoNode);
+    }
+
+    // Walk the node's children recursively
+    const size_t      count = infoNode->erd->numChildren;
+    const ERD **const childrenERDs = infoNode->erd->childrenERDs;
+    const size_t *    offsets = infoNode->erd->offsets;
+
+    for (size_t i = 0; i < count && !error; i++)
+    {
+        const size_t offset = offsets[i];
+        const ERD *  childERD = childrenERDs[i];
+        // We use only one of these variables below depending on typeCode
+        const InfosetBase *childNode = (const InfosetBase *)((const char *)infoNode + offset);
+        const void *       number = (const void *)((const char *)infoNode + offset);
+
+        // Will need to handle more element types
+        const enum TypeCode typeCode = childERD->typeCode;
+        switch (typeCode)
+        {
+        case CHOICE:
+            // Point next ERD to choice of alternative elements' ERDs
+            error = infoNode->erd->initChoice(infoNode, rootElement());
+            break;
+        case COMPLEX:
+            error = walkInfosetNode(handler, childNode);
+            break;
+        case PRIMITIVE_BOOLEAN:
+        case PRIMITIVE_DOUBLE:
+        case PRIMITIVE_FLOAT:
+        case PRIMITIVE_INT16:
+        case PRIMITIVE_INT32:
+        case PRIMITIVE_INT64:
+        case PRIMITIVE_INT8:
+        case PRIMITIVE_UINT16:
+        case PRIMITIVE_UINT32:
+        case PRIMITIVE_UINT64:
+        case PRIMITIVE_UINT8:
+            error = handler->visitNumberElem(handler, childERD, number);
+            break;
+        }
+    }
+
+    // End visiting the node
+    if (!error)
+    {
+        error = handler->visitEndComplex(handler, infoNode);
+    }
+
+    return error;
+}
+
+// walkInfoset - walk an infoset and call VisitEventHandler methods
+
+const Error *
+walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoset)
+{
+    const Error *error = NULL;
+
+    if (!error)
+    {
+        error = handler->visitStartDocument(handler);
+    }
+    if (!error)
+    {
+        error = walkInfosetNode(handler, infoset);
+    }
+    if (!error)
+    {
+        error = handler->visitEndDocument(handler);
+    }
+
+    return error;
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
new file mode 100644
index 0000000..50281b9
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef INFOSET_H
+#define INFOSET_H
+
+#include <stddef.h>  // for size_t
+#include "errors.h"  // for Error, PState, UState
+
+// Prototypes needed for compilation
+
+typedef struct ElementRuntimeData ERD;
+typedef struct InfosetBase        InfosetBase;
+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 const Error *(*InitChoiceRD)(const InfosetBase *infoNode, const InfosetBase *rootElement);
+
+typedef const Error *(*VisitStartDocument)(const VisitEventHandler *handler);
+typedef const Error *(*VisitEndDocument)(const VisitEventHandler *handler);
+typedef const Error *(*VisitStartComplex)(const VisitEventHandler *handler, const InfosetBase *base);
+typedef const Error *(*VisitEndComplex)(const VisitEventHandler *handler, const InfosetBase *base);
+typedef const Error *(*VisitNumberElem)(const VisitEventHandler *handler, const ERD *erd, const void *number);
+
+// NamedQName - name of an infoset element
+
+typedef struct NamedQName
+{
+    const char *prefix; // prefix (optional, may be NULL)
+    const char *local;  // local name
+    const char *ns;     // namespace URI (optional, may be NULL)
+} NamedQName;
+
+// TypeCode - types of infoset elements
+
+enum TypeCode
+{
+    CHOICE,
+    COMPLEX,
+    PRIMITIVE_BOOLEAN,
+    PRIMITIVE_DOUBLE,
+    PRIMITIVE_FLOAT,
+    PRIMITIVE_INT16,
+    PRIMITIVE_INT32,
+    PRIMITIVE_INT64,
+    PRIMITIVE_INT8,
+    PRIMITIVE_UINT16,
+    PRIMITIVE_UINT32,
+    PRIMITIVE_UINT64,
+    PRIMITIVE_UINT8
+};
+
+// ERD - element runtime data needed to parse/unparse objects
+
+typedef struct ElementRuntimeData
+{
+    const NamedQName    namedQName;
+    const enum TypeCode typeCode;
+    const size_t        numChildren;
+    const size_t *      offsets;
+    const ERD **        childrenERDs;
+
+    const ERDInitSelf    initSelf;
+    const ERDParseSelf   parseSelf;
+    const ERDUnparseSelf unparseSelf;
+    const InitChoiceRD   initChoice;
+} ERD;
+
+// InfosetBase - metadata of an infoset element
+
+typedef struct InfosetBase
+{
+    const ERD *erd;
+} InfosetBase;
+
+// VisitEventHandler - methods to be called when walking an infoset
+
+typedef struct VisitEventHandler
+{
+    const VisitStartDocument visitStartDocument;
+    const VisitEndDocument   visitEndDocument;
+    const VisitStartComplex  visitStartComplex;
+    const VisitEndComplex    visitEndComplex;
+    const VisitNumberElem    visitNumberElem;
+} VisitEventHandler;
+
+// get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns
+// attribute/value from ERD to use for XML element
+
+extern const char *get_erd_name(const ERD *erd);
+extern const char *get_erd_xmlns(const ERD *erd);
+extern const char *get_erd_ns(const ERD *erd);
+
+// rootElement - return an infoset's root element for parsing,
+// walking, or unparsing (implementation actually is generated in
+// generated_code.c, not defined in infoset.c)
+
+extern InfosetBase *rootElement(void);
+
+// walkInfoset - walk an infoset and call VisitEventHandler methods
+
+extern const Error *walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoset);
+
+#endif // INFOSET_H
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c
new file mode 100644
index 0000000..81b2b69
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c
@@ -0,0 +1,167 @@
+/*
+ * 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 "parsers.h"
+#include <endian.h>   // for be32toh, le32toh, be16toh, be64toh, le16toh, le64toh
+#include <stdbool.h>  // for bool, false, true
+#include <stdio.h>    // for fread
+#include "errors.h"   // for PState, eof_or_error, Error, ERR_PARSE_BOOL, Error::(anonymous), add_diagnostic, get_diagnostics, ERR_FIXED_VALUE, Diagnostics
+
+// Macros not defined by <endian.h> which we need for uniformity
+
+#define be8toh(var) var
+#define le8toh(var) var
+
+// Helper macro to reduce duplication of C code reading stream,
+// updating position, and checking for errors
+
+#define read_stream_update_position                                                                          \
+    size_t count = fread(&buffer.c_val, 1, sizeof(buffer), pstate->stream);                                  \
+    pstate->position += count;                                                                               \
+    if (count < sizeof(buffer))                                                                              \
+    {                                                                                                        \
+        pstate->error = eof_or_error(pstate->stream);                                                        \
+        if (pstate->error) return;                                                                           \
+    }
+
+// Macros to define parse_<endian>_<type> functions
+
+#define define_parse_endian_bool(endian, bits)                                                               \
+    void parse_##endian##_bool##bits(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate)     \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(uint##bits##_t)];                                                    \
+            uint##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        read_stream_update_position;                                                                         \
+        buffer.i_val = endian##bits##toh(buffer.i_val);                                                      \
+        if (true_rep < 0)                                                                                    \
+        {                                                                                                    \
+            *number = (buffer.i_val != false_rep);                                                           \
+        }                                                                                                    \
+        else if (buffer.i_val == (uint32_t)true_rep)                                                         \
+        {                                                                                                    \
+            *number = true;                                                                                  \
+        }                                                                                                    \
+        else if (buffer.i_val == false_rep)                                                                  \
+        {                                                                                                    \
+            *number = false;                                                                                 \
+        }                                                                                                    \
+        else                                                                                                 \
+        {                                                                                                    \
+            static Error error = {ERR_PARSE_BOOL, {NULL}};                                                   \
+            error.d64 = (int64_t)buffer.i_val;                                                               \
+            pstate->error = &error;                                                                          \
+        }                                                                                                    \
+    }
+
+#define define_parse_endian_real(endian, type, bits)                                                         \
+    void parse_##endian##_##type(type *number, PState *pstate)                                               \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(type)];                                                              \
+            type           f_val;                                                                            \
+            uint##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        read_stream_update_position;                                                                         \
+        buffer.i_val = endian##bits##toh(buffer.i_val);                                                      \
+        *number = buffer.f_val;                                                                              \
+    }
+
+#define define_parse_endian_integer(endian, type, bits)                                                      \
+    void parse_##endian##_##type##bits(type##bits##_t *number, PState *pstate)                               \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(type##bits##_t)];                                                    \
+            type##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        read_stream_update_position;                                                                         \
+        *number = endian##bits##toh(buffer.i_val);                                                           \
+    }
+
+// Parse binary booleans, real numbers, and integers
+
+define_parse_endian_bool(be, 16);
+define_parse_endian_bool(be, 32);
+define_parse_endian_bool(be, 8);
+
+define_parse_endian_real(be, double, 64)
+define_parse_endian_real(be, float, 32)
+
+define_parse_endian_integer(be, int, 16)
+define_parse_endian_integer(be, int, 32)
+define_parse_endian_integer(be, int, 64)
+define_parse_endian_integer(be, int, 8)
+
+define_parse_endian_integer(be, uint, 16)
+define_parse_endian_integer(be, uint, 32)
+define_parse_endian_integer(be, uint, 64)
+define_parse_endian_integer(be, uint, 8)
+
+define_parse_endian_bool(le, 16);
+define_parse_endian_bool(le, 32);
+define_parse_endian_bool(le, 8);
+
+define_parse_endian_real(le, double, 64)
+define_parse_endian_real(le, float, 32)
+
+define_parse_endian_integer(le, int, 16)
+define_parse_endian_integer(le, int, 32)
+define_parse_endian_integer(le, int, 64)
+define_parse_endian_integer(le, int, 8)
+
+define_parse_endian_integer(le, uint, 16)
+define_parse_endian_integer(le, uint, 32)
+define_parse_endian_integer(le, uint, 64)
+define_parse_endian_integer(le, uint, 8)
+
+// Parse fill bytes until end position is reached
+
+void
+parse_fill_bytes(size_t end_position, PState *pstate)
+{
+    union
+    {
+        char c_val[1];
+    } buffer;
+
+    while (pstate->position < end_position)
+    {
+        read_stream_update_position;
+    }
+}
+
+// Validate parsed number is same as fixed value
+
+void
+parse_validate_fixed(bool same, const char *element, PState *pstate)
+{
+    if (!same)
+    {
+        Diagnostics *diagnostics = get_diagnostics();
+        const Error  error = {ERR_FIXED_VALUE, {element}};
+
+        add_diagnostic(diagnostics, &error);
+        pstate->diagnostics = diagnostics;
+    }
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h
new file mode 100644
index 0000000..0de7de7
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef PARSERS_H
+#define PARSERS_H
+
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for size_t
+#include <stdint.h>   // for int64_t, uint32_t, int16_t, int32_t, int8_t, uint16_t, uint64_t, uint8_t
+#include "errors.h"   // for PState
+
+// Parse binary booleans, real numbers, and integers
+
+extern void parse_be_bool16(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+extern void parse_be_bool32(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+extern void parse_be_bool8(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+
+extern void parse_be_double(double *number, PState *pstate);
+extern void parse_be_float(float *number, PState *pstate);
+
+extern void parse_be_int16(int16_t *number, PState *pstate);
+extern void parse_be_int32(int32_t *number, PState *pstate);
+extern void parse_be_int64(int64_t *number, PState *pstate);
+extern void parse_be_int8(int8_t *number, PState *pstate);
+
+extern void parse_be_uint16(uint16_t *number, PState *pstate);
+extern void parse_be_uint32(uint32_t *number, PState *pstate);
+extern void parse_be_uint64(uint64_t *number, PState *pstate);
+extern void parse_be_uint8(uint8_t *number, PState *pstate);
+
+extern void parse_le_bool16(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+extern void parse_le_bool32(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+extern void parse_le_bool8(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate);
+
+extern void parse_le_double(double *number, PState *pstate);
+extern void parse_le_float(float *number, PState *pstate);
+
+extern void parse_le_int16(int16_t *number, PState *pstate);
+extern void parse_le_int32(int32_t *number, PState *pstate);
+extern void parse_le_int64(int64_t *number, PState *pstate);
+extern void parse_le_int8(int8_t *number, PState *pstate);
+
+extern void parse_le_uint16(uint16_t *number, PState *pstate);
+extern void parse_le_uint32(uint32_t *number, PState *pstate);
+extern void parse_le_uint64(uint64_t *number, PState *pstate);
+extern void parse_le_uint8(uint8_t *number, PState *pstate);
+
+// Parse fill bytes until end position is reached
+
+extern void parse_fill_bytes(size_t end_position, PState *pstate);
+
+// Validate parsed number is same as fixed value
+
+extern void parse_validate_fixed(bool same, const char *element, PState *pstate);
+
+#endif // PARSERS_H
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c
new file mode 100644
index 0000000..d64b785
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c
@@ -0,0 +1,151 @@
+/*
+ * 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 "unparsers.h"
+#include <endian.h>   // for htobe32, htole32, htobe16, htobe64, htole16, htole64
+#include <stdbool.h>  // for bool
+#include <stdio.h>    // for fwrite
+#include "errors.h"   // for UState, eof_or_error, add_diagnostic, get_diagnostics, ERR_FIXED_VALUE, Diagnostics, Error
+
+// Macros not defined by <endian.h> which we need for uniformity
+
+#define htobe8(var) var
+#define htole8(var) var
+
+// Helper macro to reduce duplication of C code writing stream,
+// updating position, and checking for errors
+
+#define write_stream_update_position                                                                         \
+    size_t count = fwrite(buffer.c_val, 1, sizeof(buffer), ustate->stream);                                  \
+    ustate->position += count;                                                                               \
+    if (count < sizeof(buffer))                                                                              \
+    {                                                                                                        \
+        ustate->error = eof_or_error(ustate->stream);                                                        \
+        if (ustate->error) return;                                                                           \
+    }
+
+// Macros to define unparse_<endian>_<type> functions
+
+#define define_unparse_endian_bool(endian, bits)                                                             \
+    void unparse_##endian##_bool##bits(bool number, uint32_t true_rep, uint32_t false_rep, UState *ustate)   \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(uint##bits##_t)];                                                    \
+            uint##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        buffer.i_val = hto##endian##bits(number ? true_rep : false_rep);                                     \
+        write_stream_update_position;                                                                        \
+    }
+
+#define define_unparse_endian_real(endian, type, bits)                                                       \
+    void unparse_##endian##_##type(type number, UState *ustate)                                              \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(type)];                                                              \
+            type           f_val;                                                                            \
+            uint##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        buffer.f_val = number;                                                                               \
+        buffer.i_val = hto##endian##bits(buffer.i_val);                                                      \
+        write_stream_update_position;                                                                        \
+    }
+
+#define define_unparse_endian_integer(endian, type, bits)                                                    \
+    void unparse_##endian##_##type##bits(type##bits##_t number, UState *ustate)                              \
+    {                                                                                                        \
+        union                                                                                                \
+        {                                                                                                    \
+            char           c_val[sizeof(type##bits##_t)];                                                    \
+            type##bits##_t i_val;                                                                            \
+        } buffer;                                                                                            \
+                                                                                                             \
+        buffer.i_val = hto##endian##bits(number);                                                            \
+        write_stream_update_position;                                                                        \
+    }
+
+// Unparse binary booleans, real numbers, and integers
+
+define_unparse_endian_bool(be, 16);
+define_unparse_endian_bool(be, 32);
+define_unparse_endian_bool(be, 8);
+
+define_unparse_endian_real(be, double, 64)
+define_unparse_endian_real(be, float, 32)
+
+define_unparse_endian_integer(be, int, 16)
+define_unparse_endian_integer(be, int, 32)
+define_unparse_endian_integer(be, int, 64)
+define_unparse_endian_integer(be, int, 8)
+
+define_unparse_endian_integer(be, uint, 16)
+define_unparse_endian_integer(be, uint, 32)
+define_unparse_endian_integer(be, uint, 64)
+define_unparse_endian_integer(be, uint, 8)
+
+define_unparse_endian_bool(le, 16);
+define_unparse_endian_bool(le, 32);
+define_unparse_endian_bool(le, 8);
+
+define_unparse_endian_real(le, double, 64)
+define_unparse_endian_real(le, float, 32)
+
+define_unparse_endian_integer(le, int, 16)
+define_unparse_endian_integer(le, int, 32)
+define_unparse_endian_integer(le, int, 64)
+define_unparse_endian_integer(le, int, 8)
+
+define_unparse_endian_integer(le, uint, 16)
+define_unparse_endian_integer(le, uint, 32)
+define_unparse_endian_integer(le, uint, 64)
+define_unparse_endian_integer(le, uint, 8)
+
+// Unparse fill bytes until end position is reached
+
+void
+unparse_fill_bytes(size_t end_position, const char fill_byte, UState *ustate)
+{
+    union
+    {
+        char c_val[1];
+    } buffer;
+
+    buffer.c_val[0] = fill_byte;
+
+    while (ustate->position < end_position)
+    {
+        write_stream_update_position;
+    }
+}
+
+// Validate unparsed number is same as fixed value
+
+void
+unparse_validate_fixed(bool same, const char *element, UState *ustate)
+{
+    if (!same)
+    {
+        Diagnostics *diagnostics = get_diagnostics();
+        const Error  error = {ERR_FIXED_VALUE, {element}};
+
+        add_diagnostic(diagnostics, &error);
+        ustate->diagnostics = diagnostics;
+    }
+}
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h
new file mode 100644
index 0000000..4b5f5ab
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef UNPARSERS_H
+#define UNPARSERS_H
+
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for size_t
+#include <stdint.h>   // for uint32_t, int16_t, int32_t, int64_t, int8_t, uint16_t, uint64_t, uint8_t
+#include "errors.h"   // for UState
+
+// Unparse binary booleans, real numbers, and integers
+
+extern void unparse_be_bool16(bool number, uint32_t true_rep,
+                              uint32_t false_rep, UState *ustate);
+extern void unparse_be_bool32(bool number, uint32_t true_rep,
+                              uint32_t false_rep, UState *ustate);
+extern void unparse_be_bool8(bool number, uint32_t true_rep, uint32_t false_rep,
+                             UState *ustate);
+
+extern void unparse_be_double(double number, UState *ustate);
+extern void unparse_be_float(float number, UState *ustate);
+
+extern void unparse_be_int16(int16_t number, UState *ustate);
+extern void unparse_be_int32(int32_t number, UState *ustate);
+extern void unparse_be_int64(int64_t number, UState *ustate);
+extern void unparse_be_int8(int8_t number, UState *ustate);
+
+extern void unparse_be_uint16(uint16_t number, UState *ustate);
+extern void unparse_be_uint32(uint32_t number, UState *ustate);
+extern void unparse_be_uint64(uint64_t number, UState *ustate);
+extern void unparse_be_uint8(uint8_t number, UState *ustate);
+
+extern void unparse_le_bool16(bool number, uint32_t true_rep,
+                              uint32_t false_rep, UState *ustate);
+extern void unparse_le_bool32(bool number, uint32_t true_rep,
+                              uint32_t false_rep, UState *ustate);
+extern void unparse_le_bool8(bool number, uint32_t true_rep, uint32_t false_rep,
+                             UState *ustate);
+
+extern void unparse_le_double(double number, UState *ustate);
+extern void unparse_le_float(float number, UState *ustate);
+
+extern void unparse_le_int16(int16_t number, UState *ustate);
+extern void unparse_le_int32(int32_t number, UState *ustate);
+extern void unparse_le_int64(int64_t number, UState *ustate);
+extern void unparse_le_int8(int8_t number, UState *ustate);
+
+extern void unparse_le_uint16(uint16_t number, UState *ustate);
+extern void unparse_le_uint32(uint32_t number, UState *ustate);
+extern void unparse_le_uint64(uint64_t number, UState *ustate);
+extern void unparse_le_uint8(uint8_t number, UState *ustate);
+
+// Unparse fill bytes until end position is reached
+
+extern void unparse_fill_bytes(size_t end_position, const char fill_byte,
+                               UState *ustate);
+
+// Validate unparsed number is same as fixed value
+
+extern void unparse_validate_fixed(bool same, const char *element,
+                                   UState *ustate);
+
+#endif // UNPARSERS_H
diff --git a/daffodil-runtime2/src/main/resources/examples/NestedUnion.c b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c
new file mode 100644
index 0000000..f5f1f89
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c
@@ -0,0 +1,442 @@
+/*
+ * 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 <math.h>       // for NAN
+#include <stdbool.h>    // for bool, true, false
+#include <stddef.h>     // for NULL, size_t
+#include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED
+#include "parsers.h"    // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint16, parse_le_uint32, parse_le_uint64
+#include "unparsers.h"  // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unparse_le_uint1 [...]
+
+// Initialize our program's name and version
+
+const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
+
+// Declare prototypes for easier 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 const Error *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);
+
+// Define metadata for the infoset
+
+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
+    (InitChoiceRD)&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 for parsing or unparsing the infoset
+
+InfosetBase *
+rootElement(void)
+{
+    static bool initialized;
+    static NestedUnion root;
+    if (!initialized)
+    {
+        NestedUnion_initSelf(&root);
+        initialized = true;
+    }
+    return &root._base;
+}
+
+// Initialize, parse, and unparse nodes of the infoset
+
+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);
+    if (pstate->error) return;
+    parse_be_int32(&instance->b, pstate);
+    if (pstate->error) return;
+    parse_be_int32(&instance->c, pstate);
+    if (pstate->error) return;
+}
+
+static void
+foo_unparseSelf(const foo *instance, UState *ustate)
+{
+    unparse_be_int32(instance->a, ustate);
+    if (ustate->error) return;
+    unparse_be_int32(instance->b, ustate);
+    if (ustate->error) return;
+    unparse_be_int32(instance->c, ustate);
+    if (ustate->error) return;
+}
+
+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);
+    if (pstate->error) return;
+    parse_be_double(&instance->y, pstate);
+    if (pstate->error) return;
+    parse_be_double(&instance->z, pstate);
+    if (pstate->error) return;
+}
+
+static void
+bar_unparseSelf(const bar *instance, UState *ustate)
+{
+    unparse_be_double(instance->x, ustate);
+    if (ustate->error) return;
+    unparse_be_double(instance->y, ustate);
+    if (ustate->error) return;
+    unparse_be_double(instance->z, ustate);
+    if (ustate->error) return;
+}
+
+static void
+data_initSelf(data *instance)
+{
+    instance->_base.erd = &data_NestedUnionType_ERD;
+    instance->_choice = 0xFFFFFFFFFFFFFFFF;
+    foo_initSelf(&instance->foo);
+    bar_initSelf(&instance->bar);
+}
+
+static const Error *
+data_initChoice(data *instance, const NestedUnion *rootElement)
+{
+    static Error error = {ERR_CHOICE_KEY, {NULL}};
+
+    int64_t key = rootElement->tag;
+    switch (key)
+    {
+    case 1:
+    case 2:
+        instance->_choice = 0;
+        break;
+    case 3:
+    case 4:
+        instance->_choice = 1;
+        break;
+    default:
+        error.d64 = key;
+        return &error;
+    }
+
+    // Point next ERD to choice of alternative elements' ERDs
+    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 NULL;
+}
+
+static void
+data_parseSelf(data *instance, PState *pstate)
+{
+    static Error error = {ERR_CHOICE_KEY, {NULL}};
+
+    pstate->error = instance->_base.erd->initChoice(&instance->_base, rootElement());
+    if (pstate->error) return;
+
+    switch (instance->_choice)
+    {
+    case 0:
+        foo_parseSelf(&instance->foo, pstate);
+        if (pstate->error) return;
+        break;
+    case 1:
+        bar_parseSelf(&instance->bar, pstate);
+        if (pstate->error) return;
+        break;
+    default:
+        // Should never happen because initChoice would return an error first
+        error.d64 = (int64_t)instance->_choice;
+        pstate->error = &error;
+        return;
+    }
+}
+
+static void
+data_unparseSelf(const data *instance, UState *ustate)
+{
+    static Error error = {ERR_CHOICE_KEY, {NULL}};
+
+    ustate->error = instance->_base.erd->initChoice(&instance->_base, rootElement());
+    if (ustate->error) return;
+
+    switch (instance->_choice)
+    {
+    case 0:
+        foo_unparseSelf(&instance->foo, ustate);
+        if (ustate->error) return;
+        break;
+    case 1:
+        bar_unparseSelf(&instance->bar, ustate);
+        if (ustate->error) return;
+        break;
+    default:
+        // Should never happen because initChoice would return an error first
+        error.d64 = (int64_t)instance->_choice;
+        ustate->error = &error;
+        return;
+    }
+}
+
+static void
+NestedUnion_initSelf(NestedUnion *instance)
+{
+    instance->_base.erd = &NestedUnion_ERD;
+    instance->tag = 0xCCCCCCCC;
+    data_initSelf(&instance->data);
+}
+
+static void
+NestedUnion_parseSelf(NestedUnion *instance, PState *pstate)
+{
+    parse_be_int32(&instance->tag, pstate);
+    if (pstate->error) return;
+    data_parseSelf(&instance->data, pstate);
+    if (pstate->error) return;
+}
+
+static void
+NestedUnion_unparseSelf(const NestedUnion *instance, UState *ustate)
+{
+    unparse_be_int32(instance->tag, ustate);
+    if (ustate->error) return;
+    data_unparseSelf(&instance->data, ustate);
+    if (ustate->error) return;
+}
+
diff --git a/daffodil-runtime2/src/main/resources/examples/NestedUnion.h b/daffodil-runtime2/src/main/resources/examples/NestedUnion.h
new file mode 100644
index 0000000..b729693
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef GENERATED_CODE_H
+#define GENERATED_CODE_H
+
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for size_t
+#include <stdint.h>   // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t
+#include "infoset.h"  // for InfosetBase
+
+// Define infoset structures
+
+typedef struct foo
+{
+    InfosetBase _base;
+    int32_t     a;
+    int32_t     b;
+    int32_t     c;
+} foo;
+
+typedef struct bar
+{
+    InfosetBase _base;
+    double      x;
+    double      y;
+    double      z;
+} bar;
+
+typedef struct data
+{
+    InfosetBase _base;
+    size_t      _choice; // choice of which union field to use
+    union
+    {
+        foo foo;
+        bar bar;
+    };
+} data;
+
+typedef struct NestedUnion
+{
+    InfosetBase _base;
+    int32_t     tag;
+    data data;
+} NestedUnion;
+
+#endif // GENERATED_CODE_H
diff --git a/daffodil-runtime2/src/main/resources/examples/ex_nums.c b/daffodil-runtime2/src/main/resources/examples/ex_nums.c
new file mode 100644
index 0000000..b64e7f6
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.c
@@ -0,0 +1,1008 @@
+/*
+ * 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 "ex_nums.h"
+#include <math.h>       // for NAN
+#include <stdbool.h>    // for bool, true, false
+#include <stddef.h>     // for NULL, size_t
+#include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED
+#include "parsers.h"    // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint16, parse_le_uint32, parse_le_uint64
+#include "unparsers.h"  // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unparse_le_uint1 [...]
+
+// Initialize our program's name and version
+
+const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT";
+
+// Declare prototypes for easier compilation
+
+static void array_initSelf(array *instance);
+static void array_parseSelf(array *instance, PState *pstate);
+static void array_unparseSelf(const array *instance, UState *ustate);
+static void bigEndian_initSelf(bigEndian *instance);
+static void bigEndian_parseSelf(bigEndian *instance, PState *pstate);
+static void bigEndian_unparseSelf(const bigEndian *instance, UState *ustate);
+static void littleEndian_initSelf(littleEndian *instance);
+static void littleEndian_parseSelf(littleEndian *instance, PState *pstate);
+static void littleEndian_unparseSelf(const littleEndian *instance, UState *ustate);
+static void fixed_initSelf(fixed *instance);
+static void fixed_parseSelf(fixed *instance, PState *pstate);
+static void fixed_unparseSelf(const fixed *instance, UState *ustate);
+static void ex_nums_initSelf(ex_nums *instance);
+static void ex_nums_parseSelf(ex_nums *instance, PState *pstate);
+static void ex_nums_unparseSelf(const ex_nums *instance, UState *ustate);
+
+// Define metadata for the infoset
+
+static const ERD be_bool16_array_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_bool16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_float_array_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_float", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_FLOAT, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_int16_array_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_int16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const array array_compute_offsets;
+
+static const size_t array_offsets[8] = {
+    (const char *)&array_compute_offsets.be_bool16[0] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_bool16[1] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_float[0] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_float[1] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_float[2] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_int16[0] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_int16[1] - (const char *)&array_compute_offsets,
+    (const char *)&array_compute_offsets.be_int16[2] - (const char *)&array_compute_offsets
+};
+
+static const ERD *array_childrenERDs[8] = {
+    &be_bool16_array_ex_nums_ERD,
+    &be_bool16_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
+};
+
+static const ERD array_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "array", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    8, // numChildren
+    array_offsets, // offsets
+    array_childrenERDs, // childrenERDs
+    (ERDInitSelf)&array_initSelf, // initSelf
+    (ERDParseSelf)&array_parseSelf, // parseSelf
+    (ERDUnparseSelf)&array_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD be_bool16_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_bool16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_bool32_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_bool32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_bool8_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_bool8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_boolean_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_boolean", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_double_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_double", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_float_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_float", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_FLOAT, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_int16_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_int16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_int32_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_int32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_int64_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_int64", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT64, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_int8_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_int8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT8, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_integer16_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_integer16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_uint16_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_uint16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_uint32_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_uint32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_uint64_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_uint64", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT64, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_uint8_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_uint8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT8, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD be_nonNegativeInteger32_bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "be_nonNegativeInteger32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bigEndian bigEndian_compute_offsets;
+
+static const size_t bigEndian_offsets[16] = {
+    (const char *)&bigEndian_compute_offsets.be_bool16 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_bool32 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_bool8 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_boolean - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_double - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_float - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_int16 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_int32 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_int64 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_int8 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_integer16 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_uint16 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_uint32 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_uint64 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_uint8 - (const char *)&bigEndian_compute_offsets,
+    (const char *)&bigEndian_compute_offsets.be_nonNegativeInteger32 - (const char *)&bigEndian_compute_offsets
+};
+
+static const ERD *bigEndian_childrenERDs[16] = {
+    &be_bool16_bigEndian_ex_nums_ERD,
+    &be_bool32_bigEndian_ex_nums_ERD,
+    &be_bool8_bigEndian_ex_nums_ERD,
+    &be_boolean_bigEndian_ex_nums_ERD,
+    &be_double_bigEndian_ex_nums_ERD,
+    &be_float_bigEndian_ex_nums_ERD,
+    &be_int16_bigEndian_ex_nums_ERD,
+    &be_int32_bigEndian_ex_nums_ERD,
+    &be_int64_bigEndian_ex_nums_ERD,
+    &be_int8_bigEndian_ex_nums_ERD,
+    &be_integer16_bigEndian_ex_nums_ERD,
+    &be_uint16_bigEndian_ex_nums_ERD,
+    &be_uint32_bigEndian_ex_nums_ERD,
+    &be_uint64_bigEndian_ex_nums_ERD,
+    &be_uint8_bigEndian_ex_nums_ERD,
+    &be_nonNegativeInteger32_bigEndian_ex_nums_ERD
+};
+
+static const ERD bigEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "bigEndian", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    16, // 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_bool16_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_bool16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_bool32_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_bool32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_bool8_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_bool8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_boolean_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_boolean", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_double_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_double", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_DOUBLE, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_float_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_float", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_FLOAT, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_int16_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_int16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_int32_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_int32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_int64_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_int64", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT64, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_int8_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_int8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT8, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_integer64_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_integer64", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT64, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_uint16_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_uint16", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT16, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_uint32_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_uint32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_uint64_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_uint64", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT64, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_uint8_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_uint8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT8, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD le_nonNegativeInteger8_littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "le_nonNegativeInteger8", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_UINT8, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const littleEndian littleEndian_compute_offsets;
+
+static const size_t littleEndian_offsets[16] = {
+    (const char *)&littleEndian_compute_offsets.le_bool16 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_bool32 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_bool8 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_boolean - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_double - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_float - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_int16 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_int32 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_int64 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_int8 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_integer64 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_uint16 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_uint32 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_uint64 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_uint8 - (const char *)&littleEndian_compute_offsets,
+    (const char *)&littleEndian_compute_offsets.le_nonNegativeInteger8 - (const char *)&littleEndian_compute_offsets
+};
+
+static const ERD *littleEndian_childrenERDs[16] = {
+    &le_bool16_littleEndian_ex_nums_ERD,
+    &le_bool32_littleEndian_ex_nums_ERD,
+    &le_bool8_littleEndian_ex_nums_ERD,
+    &le_boolean_littleEndian_ex_nums_ERD,
+    &le_double_littleEndian_ex_nums_ERD,
+    &le_float_littleEndian_ex_nums_ERD,
+    &le_int16_littleEndian_ex_nums_ERD,
+    &le_int32_littleEndian_ex_nums_ERD,
+    &le_int64_littleEndian_ex_nums_ERD,
+    &le_int8_littleEndian_ex_nums_ERD,
+    &le_integer64_littleEndian_ex_nums_ERD,
+    &le_uint16_littleEndian_ex_nums_ERD,
+    &le_uint32_littleEndian_ex_nums_ERD,
+    &le_uint64_littleEndian_ex_nums_ERD,
+    &le_uint8_littleEndian_ex_nums_ERD,
+    &le_nonNegativeInteger8_littleEndian_ex_nums_ERD
+};
+
+static const ERD littleEndian_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "littleEndian", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    16, // numChildren
+    littleEndian_offsets, // offsets
+    littleEndian_childrenERDs, // childrenERDs
+    (ERDInitSelf)&littleEndian_initSelf, // initSelf
+    (ERDParseSelf)&littleEndian_parseSelf, // parseSelf
+    (ERDUnparseSelf)&littleEndian_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ERD boolean_false_fixed_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "boolean_false", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD boolean_true_fixed_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "boolean_true", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_BOOLEAN, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD float_1_5_fixed_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "float_1_5", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_FLOAT, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD int_32_fixed_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "int_32", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    PRIMITIVE_INT32, // typeCode
+    0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const fixed fixed_compute_offsets;
+
+static const size_t fixed_offsets[4] = {
+    (const char *)&fixed_compute_offsets.boolean_false - (const char *)&fixed_compute_offsets,
+    (const char *)&fixed_compute_offsets.boolean_true - (const char *)&fixed_compute_offsets,
+    (const char *)&fixed_compute_offsets.float_1_5 - (const char *)&fixed_compute_offsets,
+    (const char *)&fixed_compute_offsets.int_32 - (const char *)&fixed_compute_offsets
+};
+
+static const ERD *fixed_childrenERDs[4] = {
+    &boolean_false_fixed_ex_nums_ERD,
+    &boolean_true_fixed_ex_nums_ERD,
+    &float_1_5_fixed_ex_nums_ERD,
+    &int_32_fixed_ex_nums_ERD
+};
+
+static const ERD fixed_ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "fixed", // namedQName.local
+        NULL, // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    4, // numChildren
+    fixed_offsets, // offsets
+    fixed_childrenERDs, // childrenERDs
+    (ERDInitSelf)&fixed_initSelf, // initSelf
+    (ERDParseSelf)&fixed_parseSelf, // parseSelf
+    (ERDUnparseSelf)&fixed_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+static const ex_nums ex_nums_compute_offsets;
+
+static const size_t ex_nums_offsets[4] = {
+    (const char *)&ex_nums_compute_offsets.array - (const char *)&ex_nums_compute_offsets,
+    (const char *)&ex_nums_compute_offsets.bigEndian - (const char *)&ex_nums_compute_offsets,
+    (const char *)&ex_nums_compute_offsets.littleEndian - (const char *)&ex_nums_compute_offsets,
+    (const char *)&ex_nums_compute_offsets.fixed - (const char *)&ex_nums_compute_offsets
+};
+
+static const ERD *ex_nums_childrenERDs[4] = {
+    &array_ex_nums_ERD,
+    &bigEndian_ex_nums_ERD,
+    &littleEndian_ex_nums_ERD,
+    &fixed_ex_nums_ERD
+};
+
+static const ERD ex_nums_ERD = {
+    {
+        NULL, // namedQName.prefix
+        "ex_nums", // namedQName.local
+        "http://example.com", // namedQName.ns
+    },
+    COMPLEX, // typeCode
+    4, // numChildren
+    ex_nums_offsets, // offsets
+    ex_nums_childrenERDs, // childrenERDs
+    (ERDInitSelf)&ex_nums_initSelf, // initSelf
+    (ERDParseSelf)&ex_nums_parseSelf, // parseSelf
+    (ERDUnparseSelf)&ex_nums_unparseSelf, // unparseSelf
+    NULL // initChoice
+};
+
+// Return a root element for parsing or unparsing the infoset
+
+InfosetBase *
+rootElement(void)
+{
+    static bool initialized;
+    static ex_nums root;
+    if (!initialized)
+    {
+        ex_nums_initSelf(&root);
+        initialized = true;
+    }
+    return &root._base;
+}
+
+// Initialize, parse, and unparse nodes of the infoset
+
+static void
+array_initSelf(array *instance)
+{
+    instance->_base.erd = &array_ex_nums_ERD;
+    instance->be_bool16[0] = true;
+    instance->be_bool16[1] = true;
+    instance->be_float[0] = NAN;
+    instance->be_float[1] = NAN;
+    instance->be_float[2] = NAN;
+    instance->be_int16[0] = 0xCCCC;
+    instance->be_int16[1] = 0xCCCC;
+    instance->be_int16[2] = 0xCCCC;
+}
+
+static void
+array_parseSelf(array *instance, PState *pstate)
+{
+    parse_be_bool16(&instance->be_bool16[0], -1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_bool16(&instance->be_bool16[1], -1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_float(&instance->be_float[0], pstate);
+    if (pstate->error) return;
+    parse_be_float(&instance->be_float[1], pstate);
+    if (pstate->error) return;
+    parse_be_float(&instance->be_float[2], pstate);
+    if (pstate->error) return;
+    parse_be_int16(&instance->be_int16[0], pstate);
+    if (pstate->error) return;
+    parse_be_int16(&instance->be_int16[1], pstate);
+    if (pstate->error) return;
+    parse_be_int16(&instance->be_int16[2], pstate);
+    if (pstate->error) return;
+}
+
+static void
+array_unparseSelf(const array *instance, UState *ustate)
+{
+    unparse_be_bool16(instance->be_bool16[0], ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_bool16(instance->be_bool16[1], ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_float(instance->be_float[0], ustate);
+    if (ustate->error) return;
+    unparse_be_float(instance->be_float[1], ustate);
+    if (ustate->error) return;
+    unparse_be_float(instance->be_float[2], ustate);
+    if (ustate->error) return;
+    unparse_be_int16(instance->be_int16[0], ustate);
+    if (ustate->error) return;
+    unparse_be_int16(instance->be_int16[1], ustate);
+    if (ustate->error) return;
+    unparse_be_int16(instance->be_int16[2], ustate);
+    if (ustate->error) return;
+}
+
+static void
+bigEndian_initSelf(bigEndian *instance)
+{
+    instance->_base.erd = &bigEndian_ex_nums_ERD;
+    instance->be_bool16 = true;
+    instance->be_bool32 = true;
+    instance->be_bool8 = true;
+    instance->be_boolean = true;
+    instance->be_double = NAN;
+    instance->be_float = NAN;
+    instance->be_int16 = 0xCCCC;
+    instance->be_int32 = 0xCCCCCCCC;
+    instance->be_int64 = 0xCCCCCCCCCCCCCCCC;
+    instance->be_int8 = 0xCC;
+    instance->be_integer16 = 0xCCCC;
+    instance->be_uint16 = 0xCCCC;
+    instance->be_uint32 = 0xCCCCCCCC;
+    instance->be_uint64 = 0xCCCCCCCCCCCCCCCC;
+    instance->be_uint8 = 0xCC;
+    instance->be_nonNegativeInteger32 = 0xCCCCCCCC;
+}
+
+static void
+bigEndian_parseSelf(bigEndian *instance, PState *pstate)
+{
+    parse_be_bool16(&instance->be_bool16, 1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_bool32(&instance->be_bool32, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_bool8(&instance->be_bool8, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_bool32(&instance->be_boolean, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_be_double(&instance->be_double, pstate);
+    if (pstate->error) return;
+    parse_be_float(&instance->be_float, pstate);
+    if (pstate->error) return;
+    parse_be_int16(&instance->be_int16, pstate);
+    if (pstate->error) return;
+    parse_be_int32(&instance->be_int32, pstate);
+    if (pstate->error) return;
+    parse_be_int64(&instance->be_int64, pstate);
+    if (pstate->error) return;
+    parse_be_int8(&instance->be_int8, pstate);
+    if (pstate->error) return;
+    parse_be_int16(&instance->be_integer16, pstate);
+    if (pstate->error) return;
+    parse_be_uint16(&instance->be_uint16, pstate);
+    if (pstate->error) return;
+    parse_be_uint32(&instance->be_uint32, pstate);
+    if (pstate->error) return;
+    parse_be_uint64(&instance->be_uint64, pstate);
+    if (pstate->error) return;
+    parse_be_uint8(&instance->be_uint8, pstate);
+    if (pstate->error) return;
+    parse_be_uint32(&instance->be_nonNegativeInteger32, pstate);
+    if (pstate->error) return;
+}
+
+static void
+bigEndian_unparseSelf(const bigEndian *instance, UState *ustate)
+{
+    unparse_be_bool16(instance->be_bool16, 1, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_bool32(instance->be_bool32, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_bool8(instance->be_bool8, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_bool32(instance->be_boolean, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_be_double(instance->be_double, ustate);
+    if (ustate->error) return;
+    unparse_be_float(instance->be_float, ustate);
+    if (ustate->error) return;
+    unparse_be_int16(instance->be_int16, ustate);
+    if (ustate->error) return;
+    unparse_be_int32(instance->be_int32, ustate);
+    if (ustate->error) return;
+    unparse_be_int64(instance->be_int64, ustate);
+    if (ustate->error) return;
+    unparse_be_int8(instance->be_int8, ustate);
+    if (ustate->error) return;
+    unparse_be_int16(instance->be_integer16, ustate);
+    if (ustate->error) return;
+    unparse_be_uint16(instance->be_uint16, ustate);
+    if (ustate->error) return;
+    unparse_be_uint32(instance->be_uint32, ustate);
+    if (ustate->error) return;
+    unparse_be_uint64(instance->be_uint64, ustate);
+    if (ustate->error) return;
+    unparse_be_uint8(instance->be_uint8, ustate);
+    if (ustate->error) return;
+    unparse_be_uint32(instance->be_nonNegativeInteger32, ustate);
+    if (ustate->error) return;
+}
+
+static void
+littleEndian_initSelf(littleEndian *instance)
+{
+    instance->_base.erd = &littleEndian_ex_nums_ERD;
+    instance->le_bool16 = true;
+    instance->le_bool32 = true;
+    instance->le_bool8 = true;
+    instance->le_boolean = true;
+    instance->le_double = NAN;
+    instance->le_float = NAN;
+    instance->le_int16 = 0xCCCC;
+    instance->le_int32 = 0xCCCCCCCC;
+    instance->le_int64 = 0xCCCCCCCCCCCCCCCC;
+    instance->le_int8 = 0xCC;
+    instance->le_integer64 = 0xCCCCCCCCCCCCCCCC;
+    instance->le_uint16 = 0xCCCC;
+    instance->le_uint32 = 0xCCCCCCCC;
+    instance->le_uint64 = 0xCCCCCCCCCCCCCCCC;
+    instance->le_uint8 = 0xCC;
+    instance->le_nonNegativeInteger8 = 0xCC;
+}
+
+static void
+littleEndian_parseSelf(littleEndian *instance, PState *pstate)
+{
+    parse_le_bool16(&instance->le_bool16, 1, 0, pstate);
+    if (pstate->error) return;
+    parse_le_bool32(&instance->le_bool32, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_le_bool8(&instance->le_bool8, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_le_bool32(&instance->le_boolean, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_le_double(&instance->le_double, pstate);
+    if (pstate->error) return;
+    parse_le_float(&instance->le_float, pstate);
+    if (pstate->error) return;
+    parse_le_int16(&instance->le_int16, pstate);
+    if (pstate->error) return;
+    parse_le_int32(&instance->le_int32, pstate);
+    if (pstate->error) return;
+    parse_le_int64(&instance->le_int64, pstate);
+    if (pstate->error) return;
+    parse_le_int8(&instance->le_int8, pstate);
+    if (pstate->error) return;
+    parse_le_int64(&instance->le_integer64, pstate);
+    if (pstate->error) return;
+    parse_le_uint16(&instance->le_uint16, pstate);
+    if (pstate->error) return;
+    parse_le_uint32(&instance->le_uint32, pstate);
+    if (pstate->error) return;
+    parse_le_uint64(&instance->le_uint64, pstate);
+    if (pstate->error) return;
+    parse_le_uint8(&instance->le_uint8, pstate);
+    if (pstate->error) return;
+    parse_le_uint8(&instance->le_nonNegativeInteger8, pstate);
+    if (pstate->error) return;
+}
+
+static void
+littleEndian_unparseSelf(const littleEndian *instance, UState *ustate)
+{
+    unparse_le_bool16(instance->le_bool16, 1, 0, ustate);
+    if (ustate->error) return;
+    unparse_le_bool32(instance->le_bool32, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_le_bool8(instance->le_bool8, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_le_bool32(instance->le_boolean, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_le_double(instance->le_double, ustate);
+    if (ustate->error) return;
+    unparse_le_float(instance->le_float, ustate);
+    if (ustate->error) return;
+    unparse_le_int16(instance->le_int16, ustate);
+    if (ustate->error) return;
+    unparse_le_int32(instance->le_int32, ustate);
+    if (ustate->error) return;
+    unparse_le_int64(instance->le_int64, ustate);
+    if (ustate->error) return;
+    unparse_le_int8(instance->le_int8, ustate);
+    if (ustate->error) return;
+    unparse_le_int64(instance->le_integer64, ustate);
+    if (ustate->error) return;
+    unparse_le_uint16(instance->le_uint16, ustate);
+    if (ustate->error) return;
+    unparse_le_uint32(instance->le_uint32, ustate);
+    if (ustate->error) return;
+    unparse_le_uint64(instance->le_uint64, ustate);
+    if (ustate->error) return;
+    unparse_le_uint8(instance->le_uint8, ustate);
+    if (ustate->error) return;
+    unparse_le_uint8(instance->le_nonNegativeInteger8, ustate);
+    if (ustate->error) return;
+}
+
+static void
+fixed_initSelf(fixed *instance)
+{
+    instance->_base.erd = &fixed_ex_nums_ERD;
+    instance->boolean_false = true;
+    instance->boolean_true = true;
+    instance->float_1_5 = NAN;
+    instance->int_32 = 0xCCCCCCCC;
+}
+
+static void
+fixed_parseSelf(fixed *instance, PState *pstate)
+{
+    parse_be_bool32(&instance->boolean_false, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_validate_fixed(instance->boolean_false == false, "boolean_false", pstate);
+    if (pstate->error) return;
+    parse_be_bool32(&instance->boolean_true, -1, 0, pstate);
+    if (pstate->error) return;
+    parse_validate_fixed(instance->boolean_true == true, "boolean_true", pstate);
+    if (pstate->error) return;
+    parse_be_float(&instance->float_1_5, pstate);
+    if (pstate->error) return;
+    parse_validate_fixed(instance->float_1_5 == 1.5, "float_1_5", pstate);
+    if (pstate->error) return;
+    parse_be_int32(&instance->int_32, pstate);
+    if (pstate->error) return;
+    parse_validate_fixed(instance->int_32 == 32, "int_32", pstate);
+    if (pstate->error) return;
+}
+
+static void
+fixed_unparseSelf(const fixed *instance, UState *ustate)
+{
+    unparse_be_bool32(instance->boolean_false, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_validate_fixed(instance->boolean_false == false, "boolean_false", ustate);
+    if (ustate->error) return;
+    unparse_be_bool32(instance->boolean_true, ~0, 0, ustate);
+    if (ustate->error) return;
+    unparse_validate_fixed(instance->boolean_true == true, "boolean_true", ustate);
+    if (ustate->error) return;
+    unparse_be_float(instance->float_1_5, ustate);
+    if (ustate->error) return;
+    unparse_validate_fixed(instance->float_1_5 == 1.5, "float_1_5", ustate);
+    if (ustate->error) return;
+    unparse_be_int32(instance->int_32, ustate);
+    if (ustate->error) return;
+    unparse_validate_fixed(instance->int_32 == 32, "int_32", ustate);
+    if (ustate->error) return;
+}
+
+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);
+    fixed_initSelf(&instance->fixed);
+}
+
+static void
+ex_nums_parseSelf(ex_nums *instance, PState *pstate)
+{
+    array_parseSelf(&instance->array, pstate);
+    if (pstate->error) return;
+    bigEndian_parseSelf(&instance->bigEndian, pstate);
+    if (pstate->error) return;
+    littleEndian_parseSelf(&instance->littleEndian, pstate);
+    if (pstate->error) return;
+    fixed_parseSelf(&instance->fixed, pstate);
+    if (pstate->error) return;
+}
+
+static void
+ex_nums_unparseSelf(const ex_nums *instance, UState *ustate)
+{
+    array_unparseSelf(&instance->array, ustate);
+    if (ustate->error) return;
+    bigEndian_unparseSelf(&instance->bigEndian, ustate);
+    if (ustate->error) return;
+    littleEndian_unparseSelf(&instance->littleEndian, ustate);
+    if (ustate->error) return;
+    fixed_unparseSelf(&instance->fixed, ustate);
+    if (ustate->error) return;
+}
+
diff --git a/daffodil-runtime2/src/main/resources/examples/ex_nums.h b/daffodil-runtime2/src/main/resources/examples/ex_nums.h
new file mode 100644
index 0000000..937c93c
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#ifndef GENERATED_CODE_H
+#define GENERATED_CODE_H
+
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for size_t
+#include <stdint.h>   // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t
+#include "infoset.h"  // for InfosetBase
+
+// Define infoset structures
+
+typedef struct array
+{
+    InfosetBase _base;
+    bool        be_bool16[2];
+    float       be_float[3];
+    int16_t     be_int16[3];
+} array;
+
+typedef struct bigEndian
+{
+    InfosetBase _base;
+    bool        be_bool16;
+    bool        be_bool32;
+    bool        be_bool8;
+    bool        be_boolean;
+    double      be_double;
+    float       be_float;
+    int16_t     be_int16;
+    int32_t     be_int32;
+    int64_t     be_int64;
+    int8_t      be_int8;
+    int16_t     be_integer16;
+    uint16_t    be_uint16;
+    uint32_t    be_uint32;
+    uint64_t    be_uint64;
+    uint8_t     be_uint8;
+    uint32_t    be_nonNegativeInteger32;
+} bigEndian;
+
+typedef struct littleEndian
+{
+    InfosetBase _base;
+    bool        le_bool16;
+    bool        le_bool32;
+    bool        le_bool8;
+    bool        le_boolean;
+    double      le_double;
+    float       le_float;
+    int16_t     le_int16;
+    int32_t     le_int32;
+    int64_t     le_int64;
+    int8_t      le_int8;
+    int64_t     le_integer64;
+    uint16_t    le_uint16;
+    uint32_t    le_uint32;
+    uint64_t    le_uint64;
+    uint8_t     le_uint8;
+    uint8_t     le_nonNegativeInteger8;
+} littleEndian;
+
+typedef struct fixed
+{
+    InfosetBase _base;
+    bool        boolean_false;
+    bool        boolean_true;
+    float       float_1_5;
+    int32_t     int_32;
+} fixed;
+
+typedef struct ex_nums
+{
+    InfosetBase _base;
+    array array;
+    bigEndian bigEndian;
+    littleEndian littleEndian;
+    fixed fixed;
+} ex_nums;
+
+#endif // GENERATED_CODE_H
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
new file mode 100644
index 0000000..809c3c3
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/CodeGenerator.scala
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2
+
+import java.io.File
+import java.nio.file.FileSystems
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.util.Collections
+
+import org.apache.daffodil.api.DFDL
+import org.apache.daffodil.api.Diagnostic
+import org.apache.daffodil.dsom.Root
+import org.apache.daffodil.dsom.SchemaDefinitionError
+import org.apache.daffodil.runtime2.generators.CodeGeneratorState
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.xml.RefQName
+
+/**
+ * We need a mutux object for exclusive access to a code block
+  */
+private object mutex {}
+
+/**
+ * Generates and compiles C source files from a DFDL schema encapsulated in the parameter.
+ * Implements the DFDL.CodeGenerator trait to allow it to be called by Daffodil code.
+ * @param root Provides the DFDL schema for code generation
+ */
+class CodeGenerator(root: Root) extends DFDL.CodeGenerator {
+  // Used by compileCode and pickCompiler methods
+  private lazy val isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows")
+  // Used by WithDiagnostics methods
+  private var diagnostics: Seq[Diagnostic] = Nil
+  private var errorStatus: Boolean = false
+
+  /**
+   * Writes C source files into a "c" subdirectory of the given output directory.
+   * Removes the "c" subdirectory if it existed before.  Returns the "c" subdirectory.
+  */
+  override def generateCode(rootNS: Option[RefQName], outputDirArg: String): os.Path = {
+    // Get the paths of the output directory and its code subdirectory
+    val outputDir = os.Path(Paths.get(outputDirArg).toAbsolutePath)
+    val codeDir = outputDir/"c"
+
+    // Ensure our output directory exists while our code subdirectory does not
+    os.makeDir.all(outputDir)
+    os.remove.all(codeDir)
+
+    // Copy our resource directory and all its C source files to our code subdirectory
+    // (using synchronized to avoid calling FileSystems.newFileSystem concurrently)
+    val resourceUri = Misc.getRequiredResource("/c")
+    mutex.synchronized {
+      val fileSystem = if (resourceUri.getScheme == "jar") {
+        val env: java.util.Map[String, String] = Collections.emptyMap()
+        FileSystems.newFileSystem(resourceUri, env)
+      } else {
+        null
+      }
+      try {
+        val resourceDir = os.Path(if (fileSystem != null) fileSystem.getPath("/c") else Paths.get(resourceUri))
+        os.copy(resourceDir, codeDir)
+      }
+      finally
+        if (fileSystem != null) fileSystem.close()
+    }
+
+    // Generate C code from the DFDL schema, appending any warnings to our diagnostics
+    val rootElementName = rootNS.getOrElse(root.refQName).local
+    val codeGeneratorState = new CodeGeneratorState()
+    Runtime2CodeGenerator.generateCode(root.document, codeGeneratorState)
+    diagnostics = diagnostics ++ root.warnings
+    val codeHeaderText = codeGeneratorState.generateCodeHeader
+    val codeFileText = codeGeneratorState.generateCodeFile(rootElementName)
+
+    // Write the generated C code into our code subdirectory
+    val generatedCodeHeader = codeDir/"libruntime"/"generated_code.h"
+    val generatedCodeFile = codeDir/"libruntime"/"generated_code.c"
+    os.write(generatedCodeHeader, codeHeaderText)
+    os.write(generatedCodeFile, codeFileText)
+
+    // Return our code directory in case caller wants to call compileCode next
+    codeDir
+  }
+
+  /**
+   * Compiles any C source files inside the given code directory.  Returns the path
+   * of the newly created executable to use in TDML tests or somewhere else.
+   */
+  override def compileCode(codeDir: os.Path): os.Path = {
+    // Get the path of the executable we will build
+    val exe = if (isWindows) codeDir/"daffodil.exe" else codeDir/"daffodil"
+
+    try {
+      // Assemble the compiler's command line arguments
+      val compiler = pickCompiler
+      val files = os.walk(codeDir).filter(_.ext == "c")
+      val libs = if (isWindows) Seq("-largp", "-lmxml") else Seq("-lmxml")
+
+      // Run the compiler in the code directory (if we found "zig cc"
+      // as a compiler, it will cache previously built files in zig's
+      // global cache directory, not a local zig_cache directory)
+      if (compiler.nonEmpty) {
+        val result = os.proc(compiler, "-I", codeDir/"libcli", "-I", codeDir/"libruntime",
+          files, libs, "-o", exe).call(cwd = codeDir, stderr = os.Pipe)
+
+        // Report any compiler output as a warning
+        if (result.out.text.nonEmpty || result.err.text.nonEmpty) {
+          warning("Unexpected compiler output on stdout: %s on stderr: %s", result.out.text, result.err.text)
+        }
+      }
+    } catch {
+      // Report any subprocess termination error as an error
+      case e: os.SubprocessException =>
+        error("Error compiling generated code: %s wd: %s", Misc.getSomeMessage(e).get, codeDir.toString)
+    }
+
+    // Report any failure to build the executable as an error
+    if (!os.exists(exe)) error("No executable was built: %s", exe.toString)
+    exe
+  }
+
+  /**
+   * Searches for any available C compiler on the system.  Tries to find the
+   * compiler given by `CC` if `CC` exists in the environment, then tries to
+   * find any compiler from the following list:
+   *
+   *   - zig cc
+   *   - gcc
+   *   - clang
+   *   - cc
+   *
+   * Returns the first compiler found as a sequence of strings in case the
+   * compiler is a program with a subcommand argument.  Returns the empty
+   * sequence if no compiler could be found in the user's PATH.
+   */
+  lazy val pickCompiler: Seq[String] = {
+    val ccEnv = System.getenv("CC")
+    val compilers = Seq(ccEnv, "zig cc", "gcc", "clang", "cc")
+    val path = System.getenv("PATH").split(File.pathSeparatorChar)
+    def inPath(compiler: String): Boolean = {
+      (compiler != null) && {
+        val exec = compiler.takeWhile(_ != ' ')
+        val exec2 = exec + ".exe"
+        path.exists(dir => Files.isExecutable(Paths.get(dir, exec))
+          || (isWindows && Files.isExecutable(Paths.get(dir, exec2))))
+      }
+    }
+    val compiler = compilers.find(inPath)
+    if (compiler.isDefined)
+      compiler.get.split(' ').toSeq
+    else
+      Seq.empty[String]
+  }
+
+  /**
+   * Adds a warning message to the diagnostics
+   */
+  def warning(formatString: String, args: Any*): Unit = {
+    val sde = new SchemaDefinitionError(None, None, formatString, args: _*)
+    diagnostics :+= sde
+  }
+
+  /**
+   * Adds an error message to the diagnostics and sets isError true
+   */
+  def error(formatString: String, args: Any*): Unit = {
+    val sde = new SchemaDefinitionError(None, None, formatString, args: _*)
+    diagnostics :+= sde
+    errorStatus = true
+  }
+
+  // Implements the WithDiagnostics methods
+  override def getDiagnostics: Seq[Diagnostic] = diagnostics
+  override def isError: Boolean = errorStatus
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2CodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2CodeGenerator.scala
new file mode 100644
index 0000000..ce494c5
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2CodeGenerator.scala
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2
+
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.grammar.Prod
+import org.apache.daffodil.grammar.RootGrammarMixin
+import org.apache.daffodil.grammar.SeqComp
+import org.apache.daffodil.grammar.primitives.BinaryBoolean
+import org.apache.daffodil.grammar.primitives.BinaryDouble
+import org.apache.daffodil.grammar.primitives.BinaryFloat
+import org.apache.daffodil.grammar.primitives.BinaryIntegerKnownLength
+import org.apache.daffodil.grammar.primitives.CaptureContentLengthEnd
+import org.apache.daffodil.grammar.primitives.CaptureContentLengthStart
+import org.apache.daffodil.grammar.primitives.CaptureValueLengthEnd
+import org.apache.daffodil.grammar.primitives.CaptureValueLengthStart
+import org.apache.daffodil.grammar.primitives.ElementCombinator
+import org.apache.daffodil.grammar.primitives.ElementParseAndUnspecifiedLength
+import org.apache.daffodil.grammar.primitives.OrderedSequence
+import org.apache.daffodil.grammar.primitives.ScalarOrderedSequenceChild
+import org.apache.daffodil.grammar.primitives.SpecifiedLengthImplicit
+import org.apache.daffodil.runtime2.generators.BinaryBooleanCodeGenerator
+import org.apache.daffodil.runtime2.generators.BinaryFloatCodeGenerator
+import org.apache.daffodil.runtime2.generators.BinaryIntegerKnownLengthCodeGenerator
+import org.apache.daffodil.runtime2.generators.CodeGeneratorState
+import org.apache.daffodil.runtime2.generators.ElementParseAndUnspecifiedLengthCodeGenerator
+import org.apache.daffodil.runtime2.generators.OrderedSequenceCodeGenerator
+import org.apache.daffodil.runtime2.generators.SeqCompCodeGenerator
+import org.apache.daffodil.util.Misc
+
+import scala.annotation.tailrec
+
+object Runtime2CodeGenerator
+  extends BinaryBooleanCodeGenerator
+    with BinaryIntegerKnownLengthCodeGenerator
+    with BinaryFloatCodeGenerator
+    with ElementParseAndUnspecifiedLengthCodeGenerator
+    with OrderedSequenceCodeGenerator
+    with SeqCompCodeGenerator {
+
+  @tailrec
+  def generateCode(gram: Gram, state: CodeGeneratorState): Unit = {
+    gram match {
+      case g: RootGrammarMixin => Runtime2CodeGenerator.generateCode(g.documentElement, state)
+      case g: Prod if (g.guard) => Runtime2CodeGenerator.generateCode(g.gram, state)
+      case g: ElementCombinator => Runtime2CodeGenerator.generateCode(g.subComb, state)
+      case g: SpecifiedLengthImplicit => Runtime2CodeGenerator.generateCode(g.eGram, state)
+      case g: ScalarOrderedSequenceChild => Runtime2CodeGenerator.generateCode(g.term.termContentBody, state)
+      case g: BinaryBoolean => binaryBooleanGenerateCode(g.e, state)
+      case g: BinaryDouble => binaryFloatGenerateCode(g.e, 64, state)
+      case g: BinaryFloat => binaryFloatGenerateCode(g.e, 32, state)
+      case g: BinaryIntegerKnownLength => binaryIntegerKnownLengthGenerateCode(g, state)
+      case g: ElementParseAndUnspecifiedLength => elementParseAndUnspecifiedLengthGenerateCode(g, state)
+      case g: OrderedSequence => orderedSequenceGenerateCode(g, state)
+      case g: SeqComp => seqCompGenerateCode(g, state)
+      case _: CaptureContentLengthStart => noop
+      case _: CaptureContentLengthEnd => noop
+      case _: CaptureValueLengthStart => noop
+      case _: CaptureValueLengthEnd => noop
+      case _ => gram.SDE("Code generation not supported for: %s", Misc.getNameFromClass(gram))
+    }
+  }
+
+  private def noop: Unit = {
+    // Not generating code here, but can use as a breakpoint
+  }
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
new file mode 100644
index 0000000..9e47349
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/Runtime2DataProcessor.scala
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2
+
+import java.io.File
+import java.io.InputStream
+import java.io.OutputStream
+
+import org.apache.daffodil.api.DFDL
+import org.apache.daffodil.api.DaffodilTunables
+import org.apache.daffodil.api.DataLocation
+import org.apache.daffodil.api.ValidationMode
+import org.apache.daffodil.externalvars.Binding
+import org.apache.daffodil.processors.Failure
+import org.apache.daffodil.processors.ProcessorResult
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.VariableMap
+import org.apache.daffodil.processors.WithDiagnosticsImpl
+import org.apache.daffodil.processors.parsers.ParseError
+import org.apache.daffodil.processors.unparsers.UnparseError
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.Maybe.Nope
+
+/**
+ * Effectively a scala proxy object that does its work via the underlying C-code.
+ * Will need to consider how to use features of underlying C-code to get infoset,
+ * walk infoset, generate XML for use by TDML tests.
+ */
+class Runtime2DataProcessor(executableFile: os.Path) extends DFDL.DataProcessorBase {
+
+  override def withValidationMode(mode: ValidationMode.Type): DFDL.DataProcessor = ???
+
+  override def withTunable(name: String, value: String): DFDL.DataProcessor = ???
+
+  override def withTunables(tunables: Map[String, String]): DFDL.DataProcessor = ???
+
+  override def withExternalVariables(extVars: Map[String, String]): DFDL.DataProcessor = ???
+
+  override def withExternalVariables(extVars: File): DFDL.DataProcessor = ???
+
+  override def withExternalVariables(extVars: Seq[Binding]): DFDL.DataProcessor = ???
+
+  override def validationMode: ValidationMode.Type = ???
+
+  override def getTunables(): DaffodilTunables = ???
+
+  override def save(output: DFDL.Output): Unit = ???
+
+  override def variableMap: VariableMap = ???
+
+  override def setValidationMode(mode: ValidationMode.Type): Unit = ???
+
+  override def setExternalVariables(extVars: Map[String, String]): Unit = ???
+
+  override def setExternalVariables(extVars: File): Unit = ???
+
+  override def setExternalVariables(extVars: File, tunable: DaffodilTunables): Unit = ???
+
+  override def setExternalVariables(extVars: Seq[Binding]): Unit = ???
+
+  override def setTunable(tunable: String, value: String): Unit = ???
+
+  override def setTunables(tunables: Map[String, String]): Unit = ???
+
+  /**
+   * Returns an object which contains the result, and/or diagnostic information.
+   */
+  def parse(input: InputStream): ParseResult = {
+    val tempDir = os.temp.dir()
+    val infile = tempDir/"infile"
+    val outfile = tempDir/"outfile"
+    try {
+      os.write(infile, input)
+      val result = os.proc(executableFile, "parse", "-I", "xml", "-o", outfile, infile).call(cwd = tempDir, stderr = os.Pipe)
+      if (result.out.text.isEmpty && result.err.text.isEmpty) {
+        val parseResult = new ParseResult(outfile, Success)
+        parseResult
+      } else {
+        val msg = s"Unexpected daffodil output on stdout: ${result.out.text} on stderr: ${result.err.text}"
+        val parseError = new ParseError(Nope, Nope, Nope, Maybe(msg))
+        val parseResult = new ParseResult(outfile, Failure(parseError))
+        parseResult.addDiagnostic(parseError)
+        parseResult
+      }
+    } catch {
+      case e: os.SubprocessException =>
+        val parseError = if (e.result.out.text.isEmpty && e.result.err.text.isEmpty) {
+          new ParseError(Nope, Nope, Maybe(e), Nope)
+        } else {
+          val msg = s"${e.getMessage} with stdout: ${e.result.out.text} and stderr: ${e.result.err.text}"
+          new ParseError(Nope, Nope, Nope, Maybe(msg))
+        }
+        val parseResult = new ParseResult(outfile, Failure(parseError))
+        parseResult.addDiagnostic(parseError)
+        parseResult
+    } finally {
+      os.remove.all(tempDir)
+    }
+  }
+
+  /**
+   * Unparses (that is, serializes) data to the output, returns an object which contains any diagnostics.
+   */
+  def unparse(input: InputStream, output: OutputStream): UnparseResult = {
+    val tempDir = os.temp.dir()
+    val infile = tempDir/"infile"
+    val outfile = tempDir/"outfile"
+    try {
+      os.write(infile, input)
+      val result = os.proc(executableFile, "unparse", "-I", "xml", "-o", outfile, infile).call(cwd = tempDir, stderr = os.Pipe)
+      val finalBitPos0b = os.size(outfile) * 8 // File sizes are bytes, so must multiply to get final position in bits
+      os.read.stream(outfile).writeBytesTo(output)
+      if (result.out.text.isEmpty && result.err.text.isEmpty) {
+        val unparseResult = new UnparseResult(finalBitPos0b, Success)
+        unparseResult
+      } else {
+        val msg = s"Unexpected daffodil output on stdout: ${result.out.text} on stderr: ${result.err.text}"
+        val unparseError = new UnparseError(Nope, Nope, Nope, Maybe(msg))
+        val unparseResult = new UnparseResult(finalBitPos0b, Failure(unparseError))
+        unparseResult.addDiagnostic(unparseError)
+        unparseResult
+      }
+    } catch {
+      case e: os.SubprocessException =>
+        val unparseError = if (e.result.out.text.isEmpty && e.result.err.text.isEmpty) {
+          new UnparseError(Nope, Nope, Maybe(e), Nope)
+        } else {
+          val msg = s"${e.getMessage} with stdout: ${e.result.out.text} and stderr: ${e.result.err.text}"
+          new UnparseError(Nope, Nope, Nope, Maybe(msg))
+        }
+        val finalBitPos0b = 0L
+        val unparseResult = new UnparseResult(finalBitPos0b, Failure(unparseError))
+        unparseResult.addDiagnostic(unparseError)
+        unparseResult
+    } finally {
+      os.remove.all(tempDir)
+    }
+  }
+}
+
+object Runtime2DataLocation {
+  class Runtime2DataLocation(_isAtEnd: Boolean,
+                             _bitPos1b: Long,
+                             _bytePos1b: Long) extends DataLocation {
+    override def isAtEnd: Boolean = _isAtEnd
+    override def bitPos1b: Long = _bitPos1b
+    override def bytePos1b: Long = _bytePos1b
+  }
+
+  def apply(isAtEnd: Boolean = true,
+            bitPos1b: Long = 0L,
+            bytePos1b: Long = 0L): DataLocation = {
+    new Runtime2DataLocation(isAtEnd, bitPos1b, bytePos1b)
+  }
+}
+
+final class ParseResult(outfile: os.Path,
+                        override val processorStatus: ProcessorResult,
+                        loc: DataLocation = Runtime2DataLocation())
+  extends DFDL.ParseResult
+    with DFDL.State
+    with WithDiagnosticsImpl {
+
+  override def resultState: DFDL.State = this
+
+  override def validationStatus: Boolean = processorStatus.isSuccess
+
+  override def currentLocation: DataLocation = loc
+
+  val infosetAsXML : scala.xml.Elem = scala.xml.XML.loadFile(outfile.toIO)
+}
+
+final class UnparseResult(val finalBitPos0b: Long,
+                          override val processorStatus: ProcessorResult,
+                          loc: DataLocation = Runtime2DataLocation())
+  extends DFDL.UnparseResult
+    with DFDL.State
+    with WithDiagnosticsImpl {
+  /**
+   * Data is 'scannable' if it consists entirely of textual data, and that data
+   * is all in the same encoding.
+   */
+  override def isScannable: Boolean = false // Safest answer since we don't know for sure
+
+  override def encodingName: String = ??? // We don't need encoding unless isScannable is true
+
+  override def validationStatus: Boolean = processorStatus.isSuccess
+
+  override def currentLocation: DataLocation = loc
+
+  override def resultState: DFDL.State = this
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala
new file mode 100644
index 0000000..772a3b5
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2.generators
+
+import org.apache.daffodil.dsom.ElementBase
+import org.apache.daffodil.schema.annotation.props.gen.BitOrder
+import org.apache.daffodil.schema.annotation.props.gen.ByteOrder
+import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
+
+trait BinaryAbstractCodeGenerator {
+
+  def binaryAbstractGenerateCode(e: ElementBase, initialValue: String, prim: String,
+    parseArgs: String, unparseArgs: String, cgState: CodeGeneratorState): Unit = {
+
+    // For the time being this is a very limited back end.
+    // So there are some restrictions to enforce.
+    e.schemaDefinitionUnless(e.bitOrder eq BitOrder.MostSignificantBitFirst, "Only dfdl:bitOrder 'mostSignificantBitFirst' is supported.")
+    e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.")
+    e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime dfdl:length expressions not supported.")
+
+    val fieldName = e.namedQName.local
+    val byteOrder = e.byteOrderEv.constValue
+    val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le"
+    val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) e.maxOccurs else 0
+
+    def addStatements(deref: String): Unit = {
+      val initStatement = s"    instance->$fieldName$deref = $initialValue;"
+      val parseStatement =
+        s"""    parse_${conv}_$prim(&instance->$fieldName$deref, $parseArgs);
+           |    if (pstate->error) return;""".stripMargin
+      val unparseStatement =
+        s"""    unparse_${conv}_$prim(instance->$fieldName$deref, $unparseArgs);
+           |    if (ustate->error) return;""".stripMargin
+      cgState.addSimpleTypeStatements(initStatement, parseStatement, unparseStatement)
+
+      if (e.hasFixedValue) {
+        val fixedValue = e.fixedValue.value.toString
+        val init2 = ""
+        val parse2 =
+          s"""    parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate);
+             |    if (pstate->error) return;""".stripMargin
+        val unparse2 =
+          s"""    unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate);
+             |    if (ustate->error) return;""".stripMargin
+        cgState.addSimpleTypeStatements(init2, parse2, unparse2)
+      }
+    }
+    if (arraySize > 0)
+      for (i <- 0 until arraySize)
+        addStatements(s"[$i]")
+    else
+      addStatements("")
+  }
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala
new file mode 100644
index 0000000..32c4bbe
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2.generators
+
+import org.apache.daffodil.dsom.ElementBase
+import org.apache.daffodil.exceptions.Assert
+import passera.unsigned.ULong
+
+trait BinaryBooleanCodeGenerator extends BinaryAbstractCodeGenerator {
+
+  def binaryBooleanGenerateCode(e: ElementBase, cgState: CodeGeneratorState): Unit = {
+    Assert.invariant(e.binaryBooleanTrueRep.isEmpty || e.binaryBooleanTrueRep.getULong >= ULong(0))
+    Assert.invariant(e.binaryBooleanFalseRep >= ULong(0))
+    Assert.invariant(e.elementLengthInBitsEv.isConstant)
+
+    val lengthInBits = e.elementLengthInBitsEv.constValue.get
+    val initialValue = lengthInBits match {
+      case 8 | 16 | 32 => "true"
+      case _ => e.SDE("Boolean lengths other than 8, 16, or 32 bits are not supported.")
+    }
+    val prim = s"bool$lengthInBits"
+    val trueRep = if (e.binaryBooleanTrueRep.isDefined) e.binaryBooleanTrueRep.getULong else -1
+    val falseRep = e.binaryBooleanFalseRep
+    val parseArgs = s"$trueRep, $falseRep, pstate"
+    val unparseTrueRep = if (e.binaryBooleanTrueRep.isDefined) s"$trueRep" else s"~$falseRep"
+    val unparseArgs = s"$unparseTrueRep, $falseRep, ustate"
+
+    binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState)
+  }
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
similarity index 51%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
index f0bc051..3d071b6 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
@@ -15,20 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+package org.apache.daffodil.runtime2.generators
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+import org.apache.daffodil.dsom.ElementBase
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+trait BinaryFloatCodeGenerator extends BinaryAbstractCodeGenerator {
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
-  }
+  def binaryFloatGenerateCode(e: ElementBase, lengthInBits: Int, cgState: CodeGeneratorState): Unit = {
 
-  private def documentElement = enclosedElement
+    // Use a NAN to mark our field as uninitialized in case parsing or unparsing
+    // fails to set the field.
+    val initialValue = lengthInBits match {
+      case 32 | 64 => "NAN"
+      case _ => e.SDE("Floating point lengths other than 32 or 64 bits are not supported.")
+    }
+    val prim = if (lengthInBits == 32) "float" else "double"
+    val parseArgs = "pstate"
+    val unparseArgs = "ustate"
 
+    binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState)
+  }
 }
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala
new file mode 100644
index 0000000..0c39979
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2.generators
+
+import org.apache.daffodil.grammar.primitives.BinaryIntegerKnownLength
+
+trait BinaryIntegerKnownLengthCodeGenerator extends BinaryAbstractCodeGenerator {
+
+  def binaryIntegerKnownLengthGenerateCode(g: BinaryIntegerKnownLength, cgState: CodeGeneratorState): Unit = {
+
+    // Use an unusual memory bit pattern (magic debug value) to mark our field
+    // as uninitialized in case parsing or unparsing fails to set the field.
+    val e = g.e
+    val initialValue = g.lengthInBits match {
+      case 8 => "0xCC"
+      case 16 => "0xCCCC"
+      case 32 => "0xCCCCCCCC"
+      case 64 => "0xCCCCCCCCCCCCCCCC"
+      case _ => e.SDE("Integer lengths other than 8, 16, 32, or 64 bits are not supported.")
+    }
+    val prim = if (g.signed) s"int${g.lengthInBits}" else s"uint${g.lengthInBits}"
+    val parseArgs = "pstate"
+    val unparseArgs = "ustate"
+
+    binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState)
+  }
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
new file mode 100644
index 0000000..f045f03
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
@@ -0,0 +1,635 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2.generators
+
+import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.cookers.ChoiceBranchKeyCooker
+import org.apache.daffodil.dpath.NodeInfo.PrimType
+import org.apache.daffodil.dsom.AbstractElementRef
+import org.apache.daffodil.dsom.Choice
+import org.apache.daffodil.dsom.ElementBase
+import org.apache.daffodil.dsom.GlobalComplexTypeDef
+import org.apache.daffodil.dsom.GlobalElementDecl
+import org.apache.daffodil.dsom.SchemaComponent
+import org.apache.daffodil.exceptions.ThrowsSDE
+import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
+
+import java.net.URI
+import scala.collection.mutable
+
+/**
+ * Builds up the state of generated code.
+ */
+class CodeGeneratorState {
+  private val structs = mutable.Stack[ComplexCGState]()
+  private val prototypes = mutable.ArrayBuffer[String]()
+  private val erds = mutable.ArrayBuffer[String]()
+  private val finalStructs = mutable.ArrayBuffer[String]()
+  private val finalImplementation = mutable.ArrayBuffer[String]()
+
+  // Builds an ERD name for the given element that needs to be unique in C file scope
+  private def erdName(context: ElementBase): String = {
+    def buildName(sc: SchemaComponent, sb: StringBuilder): StringBuilder = {
+      sc match {
+        case eb: ElementBase => sb ++= eb.namedQName.local += '_'
+        case gd: GlobalElementDecl => sb ++= gd.namedQName.local += '_'
+        case ct: GlobalComplexTypeDef => sb ++= ct.namedQName.local += '_'
+        case _ => // don't include other schema components in qualified name
+      }
+      sc.optLexicalParent.foreach {
+        buildName(_, sb)
+      }
+      sb
+    }
+    val sb = buildName(context, new StringBuilder) ++= "ERD"
+    sb.toString()
+  }
+
+  // Returns the given element's local name (doesn't have to be unique)
+  private def localName(context: ElementBase): String = context.namedQName.local
+
+  def addImplementation(context: ElementBase): Unit = {
+    val C = localName(context)
+    val initStatements = structs.top.initStatements.mkString("\n")
+    val initChoiceStatements = structs.top.initChoiceStatements.mkString("\n")
+    val parserStatements = if (structs.top.parserStatements.nonEmpty)
+      structs.top.parserStatements.mkString("\n")
+    else
+      s"""    // Empty struct, but need to prevent compiler warnings
+         |    UNUSED(${C}_compute_offsets);
+         |    UNUSED(instance);
+         |    UNUSED(pstate);""".stripMargin
+    val unparserStatements = if (structs.top.unparserStatements.nonEmpty)
+      structs.top.unparserStatements.mkString("\n")
+    else
+      s"""    // Empty struct, but need to prevent compiler warnings
+         |    UNUSED(${C}_compute_offsets);
+         |    UNUSED(instance);
+         |    UNUSED(ustate);""".stripMargin
+    val hasChoice = structs.top.initChoiceStatements.nonEmpty
+    val root = structs.elems.last.C
+    val prototypeInitChoice = if (hasChoice)
+      s"\nstatic const Error *${C}_initChoice($C *instance, const $root *rootElement);"
+    else
+      ""
+    val implementInitChoice = if (hasChoice)
+      s"""
+         |static const Error *
+         |${C}_initChoice($C *instance, const $root *rootElement)
+         |{
+         |$initChoiceStatements
+         |}
+         |""".stripMargin
+    else
+      ""
+    val prototypeFunctions =
+      s"""static void ${C}_initSelf($C *instance);$prototypeInitChoice
+         |static void ${C}_parseSelf($C *instance, PState *pstate);
+         |static void ${C}_unparseSelf(const $C *instance, UState *ustate);""".stripMargin
+    val functions =
+      s"""static void
+         |${C}_initSelf($C *instance)
+         |{
+         |$initStatements
+         |}
+         |$implementInitChoice
+         |static void
+         |${C}_parseSelf($C *instance, PState *pstate)
+         |{
+         |$parserStatements
+         |}
+         |
+         |static void
+         |${C}_unparseSelf(const $C *instance, UState *ustate)
+         |{
+         |$unparserStatements
+         |}
+         |""".stripMargin
+
+    prototypes += prototypeFunctions
+    finalImplementation += functions
+  }
+
+  private def defineQNameInit(context: ElementBase): String = {
+    val prefix = context.namedQName.prefix.map(p => s""""$p"""").getOrElse("NULL")
+    val local = localName(context)
+    val nsUri = context.namedQName.namespace.toStringOrNullIfNoNS
+    // Optimize away ns declaration if possible, although this approach may not be entirely correct
+    val parentNsUri = context.enclosingElements.headOption.map(_.namedQName.namespace.toStringOrNullIfNoNS).getOrElse("no-ns")
+    val ns = if (nsUri == null || nsUri == parentNsUri) "NULL" else s""""$nsUri""""
+    val qnameInit =
+      s"""    {
+         |        $prefix, // namedQName.prefix
+         |        "$local", // namedQName.local
+         |        $ns, // namedQName.ns
+         |    },""".stripMargin
+    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
+  }
+
+  // We know context is a complex type.  We need to 1) support choice groups; 2) support
+  // padding complex elements to explicit lengths with fill bytes
+  def addBeforeSwitchStatements(context: ElementBase): Unit = {
+    val erd = erdName(context)
+    val initStatement = s"    instance->_base.erd = &$erd;"
+
+    structs.top.initStatements += initStatement
+
+    // Implement padding if complex type has an explicit length
+    if (context.maybeFixedLengthInBits.isDefined && context.maybeFixedLengthInBits.get > 0) {
+      val lengthInBytes = context.maybeFixedLengthInBits.get / 8;
+      val parseStatement = s"    const size_t end_position = pstate->position + $lengthInBytes;"
+      val unparseStatement = s"    const size_t end_position = ustate->position + $lengthInBytes;"
+
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+
+    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 = 0xFFFFFFFFFFFFFFFF;"
+      val initChoiceStatement =
+        s"""    static Error error = {ERR_CHOICE_KEY, {NULL}};
+           |
+           |    int64_t key = rootElement->$dispatchField;
+           |    switch (key)
+           |    {""".stripMargin
+      val parseStatement =
+        s"""    static Error error = {ERR_CHOICE_KEY, {NULL}};
+           |
+           |    pstate->error = instance->_base.erd->initChoice(&instance->_base, rootElement());
+           |    if (pstate->error) return;
+           |
+           |    switch (instance->_choice)
+           |    {""".stripMargin
+      val unparseStatement =
+        s"""    static Error error = {ERR_CHOICE_KEY, {NULL}};
+           |
+           |    ustate->error = instance->_base.erd->initChoice(&instance->_base, rootElement());
+           |    if (ustate->error) return;
+           |
+           |    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
+    }
+  }
+
+  // We know context is a complex type.  We need to 1) support choice groups; 2) support
+  // padding complex elements to explicit lengths with fill bytes
+  def addAfterSwitchStatements(context: ElementBase): Unit = {
+    if (structs.top.initChoiceStatements.nonEmpty) {
+      val declaration = s"    };"
+      val initChoiceStatement =
+        s"""    default:
+           |        error.d64 = key;
+           |        return &error;
+           |    }
+           |
+           |    // Point next ERD to choice of alternative elements' ERDs
+           |    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 NULL;""".stripMargin
+      val parseStatement =
+        s"""    default:
+           |        // Should never happen because initChoice would return an error first
+           |        error.d64 = (int64_t)instance->_choice;
+           |        pstate->error = &error;
+           |        return;
+           |    }""".stripMargin
+      val unparseStatement =
+        s"""    default:
+           |        // Should never happen because initChoice would return an error first
+           |        error.d64 = (int64_t)instance->_choice;
+           |        ustate->error = &error;
+           |        return;
+           |    }""".stripMargin
+
+      structs.top.declarations += declaration
+      structs.top.initChoiceStatements += initChoiceStatement
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+
+    // Implement padding if complex type has an explicit length
+    if (context.maybeFixedLengthInBits.isDefined && context.maybeFixedLengthInBits.get > 0) {
+      val octalFillByte = context.fillByteEv.constValue.toByte.toOctalString
+      val parseStatement =
+        s"""    parse_fill_bytes(end_position, pstate);
+           |    if (pstate->error) return;""".stripMargin
+      val unparseStatement =
+        s"""    unparse_fill_bytes(end_position, '\\$octalFillByte', ustate);
+           |    if (ustate->error) return;""".stripMargin
+
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+  }
+
+  def addComplexTypeERD(context: ElementBase): Unit = {
+    val C = localName(context)
+    val erd = erdName(context)
+    val count = structs.top.offsetComputations.length
+    val offsetComputations = structs.top.offsetComputations.mkString(",\n")
+    val erdComputations = structs.top.erdComputations.mkString(",\n")
+    val qnameInit = defineQNameInit(context)
+    val hasChoice = structs.top.initChoiceStatements.nonEmpty
+    val numChildren = if (hasChoice) 2 else count
+    val initChoice = if (hasChoice) s"(InitChoiceRD)&${C}_initChoice" else "NULL"
+    val complexERD =
+      s"""static const $C ${C}_compute_offsets;
+         |
+         |static const size_t ${C}_offsets[$count] = {
+         |$offsetComputations
+         |};
+         |
+         |static const ERD *${C}_childrenERDs[$count] = {
+         |$erdComputations
+         |};
+         |
+         |static const ERD $erd = {
+         |$qnameInit
+         |    COMPLEX, // typeCode
+         |    $numChildren, // numChildren
+         |    ${C}_offsets, // offsets
+         |    ${C}_childrenERDs, // childrenERDs
+         |    (ERDInitSelf)&${C}_initSelf, // initSelf
+         |    (ERDParseSelf)&${C}_parseSelf, // parseSelf
+         |    (ERDUnparseSelf)&${C}_unparseSelf, // unparseSelf
+         |    $initChoice // initChoice
+         |};
+         |""".stripMargin
+
+    erds += complexERD
+  }
+
+  def addStruct(context: ElementBase): Unit = {
+    val C = localName(context)
+    val declarations = structs.top.declarations.mkString("\n")
+    val struct =
+      s"""typedef struct $C
+         |{
+         |    InfosetBase _base;
+         |$declarations
+         |} $C;
+         |""".stripMargin
+
+    finalStructs += struct
+  }
+
+  def addSimpleTypeStatements(initStatement: String, parseStatement: String, unparseStatement: String): Unit = {
+    if (initStatement.nonEmpty) structs.top.initStatements += initStatement
+    if (parseStatement.nonEmpty) structs.top.parserStatements += parseStatement
+    if (unparseStatement.nonEmpty) structs.top.unparserStatements += unparseStatement
+  }
+
+  def addComplexTypeStatements(child: ElementBase): Unit = {
+    val C = localName(child)
+    val e = child.name
+    val hasChoice = structs.top.initChoiceStatements.nonEmpty
+    val arraySize = if (child.occursCountKind == OccursCountKind.Fixed) child.maxOccurs else 0
+
+    if (hasChoice) {
+      val offset = child.position - 1
+      val initChoiceStatement =
+        s"""        instance->_choice = $offset;
+           |        break;""".stripMargin
+      val parseStatement = s"    case $offset:"
+      val unparseStatement = s"    case $offset:"
+
+      structs.top.initChoiceStatements ++= ChoiceBranchKeyCooker.convertConstant(
+        child.choiceBranchKey, child, forUnparse = false).map { key => s"    case $key:"}
+      structs.top.initChoiceStatements += initChoiceStatement
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+
+    def addStatements(deref: String): Unit = {
+      val moreIndent = if (hasChoice) "    " else ""
+      val initStatement = s"    ${C}_initSelf(&instance->$e$deref);"
+      val parseStatement =
+        s"""$moreIndent    ${C}_parseSelf(&instance->$e$deref, pstate);
+           |$moreIndent    if (pstate->error) return;""".stripMargin
+      val unparseStatement =
+        s"""$moreIndent    ${C}_unparseSelf(&instance->$e$deref, ustate);
+           |$moreIndent    if (ustate->error) return;""".stripMargin
+
+      structs.top.initStatements += initStatement
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+    if (arraySize > 0)
+      for (i <- 0 until arraySize)
+        addStatements(s"[$i]")
+    else
+      addStatements("")
+
+    if (hasChoice) {
+      val parseStatement = s"        break;"
+      val unparseStatement = s"        break;"
+
+      structs.top.parserStatements += parseStatement
+      structs.top.unparserStatements += unparseStatement
+    }
+  }
+
+  def pushComplexElement(context: ElementBase): Unit = {
+    val C = localName(context)
+    structs.push(new ComplexCGState(C))
+  }
+
+  def popComplexElement(): Unit = {
+    structs.pop()
+  }
+
+  // Gets length from explicit length declaration if any, otherwise from base type's implicit length
+  private def getLengthInBits(e: ElementBase): Long = {
+    e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime dfdl:length expressions are not supported.")
+    e.elementLengthInBitsEv.constValue.get
+  }
+
+  // Because schema authors don't always get types right, allows explicit lengths to override implicit lengths
+  private def getPrimType(e: ElementBase): PrimType = {
+    val primType = e.optPrimType.get match {
+      case PrimType.Byte
+         | PrimType.Short
+         | PrimType.Int
+         | PrimType.Long
+         | PrimType.Integer =>
+        getLengthInBits(e) match {
+          case 8 =>  PrimType.Byte
+          case 16 => PrimType.Short
+          case 32 => PrimType.Int
+          case 64 => PrimType.Long
+          case _ =>  e.SDE("Integer lengths other than 8, 16, 32, or 64 bits are not supported.")
+        }
+      case PrimType.UnsignedByte
+         | PrimType.UnsignedShort
+         | PrimType.UnsignedInt
+         | PrimType.UnsignedLong
+         | PrimType.NonNegativeInteger =>
+        getLengthInBits(e) match {
+          case 8 =>  PrimType.UnsignedByte
+          case 16 => PrimType.UnsignedShort
+          case 32 => PrimType.UnsignedInt
+          case 64 => PrimType.UnsignedLong
+          case _ =>  e.SDE("Unsigned integer lengths other than 8, 16, 32, or 64 bits are not supported.")
+        }
+      case PrimType.Double
+         | PrimType.Float =>
+        getLengthInBits(e) match {
+          case 32 => PrimType.Float
+          case 64 => PrimType.Double
+          case _ =>  e.SDE("Floating point lengths other than 32 or 64 bits are not supported.")
+        }
+      case PrimType.Boolean =>
+        getLengthInBits(e) match {
+          case 8 | 16 | 32 => PrimType.Boolean
+          case _ => e.SDE("Boolean lengths other than 8, 16, or 32 bits are not supported.")
+        }
+      case p => e.SDE("PrimType %s is not supported in C code generator.", p.toString)
+    }
+    if (primType != e.optPrimType.get)
+      e.SDW(WarnID.IgnoreDFDLProperty, "Ignoring PrimType %s, using %s", e.optPrimType.get.toString, primType.toString)
+    primType
+  }
+
+  def addSimpleTypeERD(context: ElementBase): Unit = {
+    val erd = erdName(context)
+    val qnameInit = defineQNameInit(context)
+    val typeCode = getPrimType(context) match {
+      case PrimType.Boolean => "PRIMITIVE_BOOLEAN"
+      case PrimType.Double => "PRIMITIVE_DOUBLE"
+      case PrimType.Float => "PRIMITIVE_FLOAT"
+      case PrimType.Short => "PRIMITIVE_INT16"
+      case PrimType.Int => "PRIMITIVE_INT32"
+      case PrimType.Long => "PRIMITIVE_INT64"
+      case PrimType.Byte => "PRIMITIVE_INT8"
+      case PrimType.UnsignedShort => "PRIMITIVE_UINT16"
+      case PrimType.UnsignedInt => "PRIMITIVE_UINT32"
+      case PrimType.UnsignedLong => "PRIMITIVE_UINT64"
+      case PrimType.UnsignedByte => "PRIMITIVE_UINT8"
+      case p => context.SDE("PrimType %s is not supported.", p.toString)
+    }
+    val erdDef =
+      s"""static const ERD $erd = {
+         |$qnameInit
+         |    $typeCode, // typeCode
+         |    0, NULL, NULL, NULL, NULL, NULL, NULL
+         |};
+         |""".stripMargin
+    erds += erdDef
+    addComputations(context)
+  }
+
+  def addComputations(child: ElementBase): Unit = {
+    val C = structs.top.C
+    val e = localName(child)
+    val erd = erdName(child)
+    val arraySize = if (child.occursCountKind == OccursCountKind.Fixed) child.maxOccurs else 0
+    def addComputation(deref: String): Unit = {
+      val offsetComputation = s"    (const char *)&${C}_compute_offsets.$e$deref - (const char *)&${C}_compute_offsets"
+      val erdComputation = s"    &$erd"
+      structs.top.offsetComputations += offsetComputation
+      structs.top.erdComputations += erdComputation
+    }
+    if (arraySize > 0)
+      for (i <- 0 until arraySize)
+        addComputation(s"[$i]")
+    else
+      addComputation("")
+  }
+
+  def addFieldDeclaration(context: ThrowsSDE, child: ElementBase): Unit = {
+    val definition = if (child.isSimpleType) {
+      getPrimType(child) match {
+        case PrimType.Boolean => "bool       "
+        case PrimType.Double => "double     "
+        case PrimType.Float => "float      "
+        case PrimType.Short => "int16_t    "
+        case PrimType.Int => "int32_t    "
+        case PrimType.Long => "int64_t    "
+        case PrimType.Byte => "int8_t     "
+        case PrimType.UnsignedShort => "uint16_t   "
+        case PrimType.UnsignedInt => "uint32_t   "
+        case PrimType.UnsignedLong => "uint64_t   "
+        case PrimType.UnsignedByte => "uint8_t    "
+        case p => child.SDE("PrimType %s is not supported: ", p.toString)
+      }
+    } else {
+      localName(child)
+    }
+    val e = child.name
+    val arrayDef = if (child.occursCountKind == OccursCountKind.Fixed) s"[${child.maxOccurs}]" else ""
+    val indent = if (structs.top.initChoiceStatements.nonEmpty) "    " else ""
+    val declaration = s"$indent    $definition $e$arrayDef;"
+
+    structs.top.declarations += declaration
+  }
+
+  def generateCodeHeader: String = {
+    val structs = finalStructs.mkString("\n")
+    val header =
+      s"""#ifndef GENERATED_CODE_H
+         |#define GENERATED_CODE_H
+         |
+         |#include <stdbool.h>  // for bool
+         |#include <stddef.h>   // for size_t
+         |#include <stdint.h>   // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t
+         |#include "infoset.h"  // for InfosetBase
+
+         |// Define infoset structures
+         |
+         |$structs
+         |#endif // GENERATED_CODE_H
+         |""".stripMargin
+    header
+  }
+
+  def generateCodeFile(rootElementName: String): String = {
+    val program = this.getClass.getPackage.getImplementationTitle
+    val version = this.getClass.getPackage.getImplementationVersion
+    val prototypes = this.prototypes.mkString("\n")
+    val erds = this.erds.mkString("\n")
+    val finalImplementation = this.finalImplementation.mkString("\n")
+    val code =
+      s"""#include "generated_code.h"
+         |#include <math.h>       // for NAN
+         |#include <stdbool.h>    // for bool, true, false
+         |#include <stddef.h>     // for NULL, size_t
+         |#include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED
+         |#include "parsers.h"    // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint16, parse_le_uint32, parse_le_uint64
+         |#include "unparsers.h"  // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unpars [...]
+         |
+         |// Initialize our program's name and version
+         |
+         |const char *argp_program_version = "$program $version";
+         |
+         |// Declare prototypes for easier compilation
+         |
+         |$prototypes
+         |
+         |// Define metadata for the infoset
+         |
+         |$erds
+         |// Return a root element for parsing or unparsing the infoset
+         |
+         |InfosetBase *
+         |rootElement(void)
+         |{
+         |    static bool initialized;
+         |    static $rootElementName root;
+         |    if (!initialized)
+         |    {
+         |        ${rootElementName}_initSelf(&root);
+         |        initialized = true;
+         |    }
+         |    return &root._base;
+         |}
+         |
+         |// Initialize, parse, and unparse nodes of the infoset
+         |
+         |$finalImplementation
+         |""".stripMargin
+    code
+  }
+}
+
+/**
+ * Accumulates strings of generated C code for nested elements inside
+ * complex elements.
+ */
+class ComplexCGState(val C: String) {
+  val declarations = mutable.ArrayBuffer[String]()
+  val offsetComputations = mutable.ArrayBuffer[String]()
+  val erdComputations = mutable.ArrayBuffer[String]()
+  val initStatements = mutable.ArrayBuffer[String]()
+  val initChoiceStatements = mutable.ArrayBuffer[String]()
+  val parserStatements = mutable.ArrayBuffer[String]()
+  val unparserStatements = mutable.ArrayBuffer[String]()
+}
diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/ElementParseAndUnspecifiedLengthCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/ElementParseAndUnspecifiedLengthCodeGenerator.scala
new file mode 100644
index 0000000..553d2c7
--- /dev/null
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/ElementParseAndUnspecifiedLengthCodeGenerator.scala
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2.generators
+
+import org.apache.daffodil.grammar.primitives.AlignmentFill
+import org.apache.daffodil.grammar.primitives.ElementParseAndUnspecifiedLength
+import org.apache.daffodil.runtime2.Runtime2CodeGenerator
+
+trait ElementParseAndUnspecifiedLengthCodeGenerator {
+
+  def elementParseAndUnspecifiedLengthGenerateCode(g: ElementParseAndUnspecifiedLength,
+                                                   cgState: CodeGeneratorState): Unit = {
+    val context = g.context
+    val elementContentGram = g.eGram // a Gram isA ParserGenerator
+
+    context.schemaDefinitionWhen(context.inputValueCalcOption.isDefined, "Elements with inputValueCalc are not supported.")
+    context.schemaDefinitionWhen(context.outputValueCalcOption.isDefined, "Elements with outputValueCalc are not supported.")
+    context.schemaDefinitionUnless(g.eBeforeGram.isEmpty
+      || g.eBeforeGram == AlignmentFill(context), "Statements associated with elements are not supported.")
+    context.schemaDefinitionUnless(g.eAfterGram.isEmpty, "Statements associated with elements are not supported.")
+    context.schemaDefinitionUnless(g.repTypeElementGram.isEmpty, "dfdlx:repType is not supported.")
+
+    if (context.isSimpleType) {
+      cgState.addSimpleTypeERD(context) // ERD static initializer
+      Runtime2CodeGenerator.generateCode(elementContentGram, cgState) // initSelf, parseSelf, unparseSelf
+    } else {
+      cgState.pushComplexElement(context)
+      cgState.addBeforeSwitchStatements(context) // switch statements for choices
+      context.elementChildren.foreach { child =>
+        if (!child.isSimpleType) {
+          cgState.addComplexTypeStatements(child) // recursive calls to parse, unparse, init
+          cgState.addComputations(child) // offset, ERD computations
+        }
+        cgState.addFieldDeclaration(context, child) // struct member for child
+        Runtime2CodeGenerator.generateCode(child.enclosedElement, cgState) // generate children too
+      }
+      cgState.addAfterSwitchStatements(context) // switch statements for choices
+      cgState.addStruct(context) // struct definition
+      cgState.addImplementation(context) // initSelf, parseSelf, unparseSelf
+      cgState.addComplexTypeERD(context) // ERD static initializer
+      cgState.popComplexElement()
+    }
+  }
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/OrderedSequenceCodeGenerator.scala
similarity index 55%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/OrderedSequenceCodeGenerator.scala
index f0bc051..fe1ee33 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/OrderedSequenceCodeGenerator.scala
@@ -15,20 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+package org.apache.daffodil.runtime2.generators
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+import org.apache.daffodil.grammar.primitives.OrderedSequence
+import org.apache.daffodil.runtime2.Runtime2CodeGenerator
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+trait OrderedSequenceCodeGenerator {
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
+  def orderedSequenceGenerateCode(g: OrderedSequence, cgState: CodeGeneratorState): Unit = {
+    //
+    // To lift this draconian restriction, we have to
+    // generate code for each of the children, and combine them into a block
+    //
+    g.schemaDefinitionUnless(g.sequenceChildren.length == 1, "Only a single child of a sequence is supported.")
+    val child1 = g.sequenceChildren(0)
+    Runtime2CodeGenerator.generateCode(child1, cgState)
   }
-
-  private def documentElement = enclosedElement
-
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/SeqCompCodeGenerator.scala
similarity index 63%
copy from daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
copy to daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/SeqCompCodeGenerator.scala
index f0bc051..2954fdb 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementDeclGrammarMixin.scala
+++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/SeqCompCodeGenerator.scala
@@ -15,20 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.daffodil.grammar
+package org.apache.daffodil.runtime2.generators
 
-import org.apache.daffodil.grammar.primitives.UnicodeByteOrderMark
-import org.apache.daffodil.dsom.Root
+import org.apache.daffodil.grammar.SeqComp
+import org.apache.daffodil.runtime2.Runtime2CodeGenerator
 
-trait RootGrammarMixin
-  extends LocalElementGrammarMixin // can be repeating if not root
-  { self: Root =>
+trait SeqCompCodeGenerator {
 
-  final lazy val document = prod("document") {
-    schemaDefinitionUnless(isScalar, "The document element cannot be an array.")
-    UnicodeByteOrderMark(this) ~ documentElement
+  def seqCompGenerateCode(g: SeqComp, state: CodeGeneratorState): Unit = {
+    for (elem <- g.children) {
+      Runtime2CodeGenerator.generateCode(elem, state)
+    }
   }
-
-  private def documentElement = enclosedElement
-
 }
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.dfdl.xsd b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.dfdl.xsd
new file mode 100644
index 0000000..af0aa43
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.dfdl.xsd
@@ -0,0 +1,161 @@
+<?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.
+-->
+
+<xs:schema
+        elementFormDefault="qualified"
+        targetNamespace="http://example.com"
+        xmlns="http://example.com"
+        xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+        xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+    <xs:annotation>
+        <xs:appinfo source="http://www.ogf.org/dfdl/">
+            <dfdl:format
+                    binaryBooleanFalseRep="0"
+                    binaryBooleanTrueRep=""
+                    byteOrder="bigEndian"
+                    encoding="UTF-8"
+                    lengthUnits="bits"
+                    representation="binary"
+                    ref="GeneralFormat"/>
+        </xs:appinfo>
+    </xs:annotation>
+
+    <xs:element name="ex_nums">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="array">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="be_bool16" type="xs:boolean"
+                                        dfdl:length="16"
+                                        dfdl:lengthKind="explicit"
+                                        dfdl:occursCountKind="fixed"
+                                        maxOccurs="2" minOccurs="2"/>
+                            <xs:element name="be_float" type="xs:float"
+                                        dfdl:occursCountKind="fixed"
+                                        minOccurs="3" maxOccurs="3"/>
+                            <xs:element name="be_int16" type="xs:short"
+                                        dfdl:occursCountKind="fixed"
+                                        minOccurs="3" maxOccurs="3"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="bigEndian">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="be_bool16" type="xs:boolean"
+                                        dfdl:binaryBooleanTrueRep="1"
+                                        dfdl:length="16"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="be_bool32" type="xs:boolean"
+                                        dfdl:length="32"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="be_bool8" type="xs:boolean"
+                                        dfdl:length="8"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="be_boolean" type="xs:boolean"/>
+                            <xs:element name="be_double" type="xs:double"/>
+                            <xs:element name="be_float" type="xs:float"/>
+                            <xs:element name="be_int16" type="xs:short"/>
+                            <xs:element name="be_int32" type="xs:int"/>
+                            <xs:element name="be_int64" type="xs:long"/>
+                            <xs:element name="be_int8" type="xs:byte"/>
+                            <xs:element name="be_integer16" type="xs:integer"
+                                        dfdl:length="16"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="be_uint16" type="xs:unsignedShort"/>
+                            <xs:element name="be_uint32" type="xs:unsignedInt"/>
+                            <xs:element name="be_uint64" type="xs:unsignedLong"/>
+                            <xs:element name="be_uint8" type="xs:unsignedByte"/>
+                            <xs:element name="be_nonNegativeInteger32" type="xs:nonNegativeInteger"
+                                        dfdl:length="32"
+                                        dfdl:lengthKind="explicit"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="littleEndian">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="le_bool16" type="xs:boolean"
+                                        dfdl:binaryBooleanTrueRep="1"
+                                        dfdl:byteOrder="littleEndian"
+                                        dfdl:length="16"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="le_bool32" type="xs:boolean"
+                                        dfdl:byteOrder="littleEndian"
+                                        dfdl:length="32"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="le_bool8" type="xs:boolean"
+                                        dfdl:byteOrder="littleEndian"
+                                        dfdl:length="8"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="le_boolean" type="xs:boolean"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_double" type="xs:double"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_float" type="xs:float"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_int16" type="xs:short"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_int32" type="xs:int"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_int64" type="xs:long"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_int8" type="xs:byte"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_integer64" type="xs:integer"
+                                        dfdl:byteOrder="littleEndian"
+                                        dfdl:length="64"
+                                        dfdl:lengthKind="explicit"/>
+                            <xs:element name="le_uint16" type="xs:unsignedShort"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_uint32" type="xs:unsignedInt"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_uint64" type="xs:unsignedLong"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_uint8" type="xs:unsignedByte"
+                                        dfdl:byteOrder="littleEndian"/>
+                            <xs:element name="le_nonNegativeInteger8" type="xs:nonNegativeInteger"
+                                        dfdl:byteOrder="littleEndian"
+                                        dfdl:length="8"
+                                        dfdl:lengthKind="explicit"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+                <xs:element name="fixed">
+                    <xs:complexType>
+                        <xs:sequence>
+                            <xs:element name="boolean_false" type="xs:boolean"
+                                        fixed="false"/>
+                            <xs:element name="boolean_true" type="xs:boolean"
+                                        fixed="true"/>
+                            <xs:element name="float_1_5" type="xs:float"
+                                        fixed="1.5"/>
+                            <xs:element name="int_32" type="xs:int"
+                                        fixed="32"/>
+                        </xs:sequence>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+
+</xs:schema>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.tdml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.tdml
new file mode 100644
index 0000000..812c327
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums.tdml
@@ -0,0 +1,102 @@
+<?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
+  defaultImplementations="daffodil daffodil-runtime2"
+  defaultRoundTrip="none"
+  description="TDML tests for ex_nums"
+  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
+    name="ex_nums_parse_runtime1"
+    model="ex_nums.dfdl.xsd"
+    config="config-runtime1">
+    <tdml:document>
+      <tdml:documentPart type="file">ex_nums_parse.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ex_nums_unparse_runtime1.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    name="ex_nums_unparse_runtime1"
+    model="ex_nums.dfdl.xsd"
+    config="config-runtime1">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ex_nums_unparse_runtime1.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">ex_nums_parse.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    name="ex_nums_parse_runtime2"
+    model="ex_nums.dfdl.xsd"
+    config="config-runtime2">
+    <tdml:document>
+      <tdml:documentPart type="file">ex_nums_parse.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ex_nums_unparse_runtime2.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    name="ex_nums_unparse_runtime2"
+    model="ex_nums.dfdl.xsd"
+    config="config-runtime2">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ex_nums_unparse_runtime2.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">ex_nums_parse.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:unparserTestCase
+    name="ex_nums_unparse_errors"
+    model="ex_nums.dfdl.xsd"
+    config="config-runtime2">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ex_nums_unparse_errors.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:errors>
+      <tdml:error>value</tdml:error>
+      <tdml:error>does not match</tdml:error>
+      <tdml:error>fixed</tdml:error>
+      <tdml:error>attribute</tdml:error>
+    </tdml:errors>
+  </tdml:unparserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_parse.dat b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_parse.dat
new file mode 100644
index 0000000..d3285c0
Binary files /dev/null and b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_parse.dat differ
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_errors.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_errors.xml
new file mode 100644
index 0000000..deb8839
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_errors.xml
@@ -0,0 +1,72 @@
+<?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.
+-->
+
+<ex_nums xmlns="http://example.com">
+  <array>
+    <be_bool16>false</be_bool16>
+    <be_bool16>true</be_bool16>
+    <be_float>1.0</be_float>
+    <be_float>INF</be_float>
+    <be_float>NaN</be_float>
+    <be_int16>1</be_int16>
+    <be_int16>2</be_int16>
+    <be_int16>3</be_int16>
+  </array>
+  <bigEndian>
+    <be_bool16>true</be_bool16>
+    <be_bool32>true</be_bool32>
+    <be_bool8>false</be_bool8>
+    <be_boolean>false</be_boolean>
+    <be_double>1.7976931348623157E308</be_double>
+    <be_float>3.4028235E38</be_float>
+    <be_int16>-16</be_int16>
+    <be_int32>-32</be_int32>
+    <be_int64>-64</be_int64>
+    <be_int8>-8</be_int8>
+    <be_integer16>-16</be_integer16>
+    <be_uint16>16</be_uint16>
+    <be_uint32>32</be_uint32>
+    <be_uint64>64</be_uint64>
+    <be_uint8>8</be_uint8>
+    <be_nonNegativeInteger32>32</be_nonNegativeInteger32>
+  </bigEndian>
+  <littleEndian>
+    <le_bool16>true</le_bool16>
+    <le_bool32>true</le_bool32>
+    <le_bool8>false</le_bool8>
+    <le_boolean>false</le_boolean>
+    <le_double>-2.2250738585072014E-308</le_double>
+    <le_float>-1.17549435E-38</le_float>
+    <le_int16>-16</le_int16>
+    <le_int32>-32</le_int32>
+    <le_int64>-64</le_int64>
+    <le_int8>-8</le_int8>
+    <le_integer64>-64</le_integer64>
+    <le_uint16>16</le_uint16>
+    <le_uint32>32</le_uint32>
+    <le_uint64>64</le_uint64>
+    <le_uint8>8</le_uint8>
+    <le_nonNegativeInteger8>8</le_nonNegativeInteger8>
+  </littleEndian>
+  <fixed>
+    <boolean_false>true</boolean_false>
+    <boolean_true>false</boolean_true>
+    <float_1_5>0.0</float_1_5>
+    <int_32>0</int_32>
+  </fixed>
+</ex_nums>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime1.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime1.xml
new file mode 100644
index 0000000..ae38f12
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime1.xml
@@ -0,0 +1,72 @@
+<?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.
+-->
+
+<ex_nums xmlns="http://example.com">
+  <array>
+    <be_bool16>false</be_bool16>
+    <be_bool16>true</be_bool16>
+    <be_float>1.0</be_float>
+    <be_float>INF</be_float>
+    <be_float>NaN</be_float>
+    <be_int16>1</be_int16>
+    <be_int16>2</be_int16>
+    <be_int16>3</be_int16>
+  </array>
+  <bigEndian>
+    <be_bool16>true</be_bool16>
+    <be_bool32>true</be_bool32>
+    <be_bool8>false</be_bool8>
+    <be_boolean>false</be_boolean>
+    <be_double>1.7976931348623157E308</be_double>
+    <be_float>3.4028235E38</be_float>
+    <be_int16>-16</be_int16>
+    <be_int32>-32</be_int32>
+    <be_int64>-64</be_int64>
+    <be_int8>-8</be_int8>
+    <be_integer16>-16</be_integer16>
+    <be_uint16>16</be_uint16>
+    <be_uint32>32</be_uint32>
+    <be_uint64>64</be_uint64>
+    <be_uint8>8</be_uint8>
+    <be_nonNegativeInteger32>32</be_nonNegativeInteger32>
+  </bigEndian>
+  <littleEndian>
+    <le_bool16>true</le_bool16>
+    <le_bool32>true</le_bool32>
+    <le_bool8>false</le_bool8>
+    <le_boolean>false</le_boolean>
+    <le_double>-2.2250738585072014E-308</le_double>
+    <le_float>-1.17549435E-38</le_float>
+    <le_int16>-16</le_int16>
+    <le_int32>-32</le_int32>
+    <le_int64>-64</le_int64>
+    <le_int8>-8</le_int8>
+    <le_integer64>-64</le_integer64>
+    <le_uint16>16</le_uint16>
+    <le_uint32>32</le_uint32>
+    <le_uint64>64</le_uint64>
+    <le_uint8>8</le_uint8>
+    <le_nonNegativeInteger8>8</le_nonNegativeInteger8>
+  </littleEndian>
+  <fixed>
+    <boolean_false>false</boolean_false>
+    <boolean_true>true</boolean_true>
+    <float_1_5>1.5</float_1_5>
+    <int_32>32</int_32>
+  </fixed>
+</ex_nums>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime2.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime2.xml
new file mode 100644
index 0000000..a656481
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ex_nums_unparse_runtime2.xml
@@ -0,0 +1,72 @@
+<?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.
+-->
+
+<ex_nums xmlns="http://example.com">
+  <array>
+    <be_bool16>false</be_bool16>
+    <be_bool16>true</be_bool16>
+    <be_float>1</be_float>
+    <be_float>INF</be_float>
+    <be_float>NaN</be_float>
+    <be_int16>1</be_int16>
+    <be_int16>2</be_int16>
+    <be_int16>3</be_int16>
+  </array>
+  <bigEndian>
+    <be_bool16>true</be_bool16>
+    <be_bool32>true</be_bool32>
+    <be_bool8>false</be_bool8>
+    <be_boolean>false</be_boolean>
+    <be_double>1.7976931348623157E+308</be_double>
+    <be_float>3.40282347E+38</be_float>
+    <be_int16>-16</be_int16>
+    <be_int32>-32</be_int32>
+    <be_int64>-64</be_int64>
+    <be_int8>-8</be_int8>
+    <be_integer16>-16</be_integer16>
+    <be_uint16>16</be_uint16>
+    <be_uint32>32</be_uint32>
+    <be_uint64>64</be_uint64>
+    <be_uint8>8</be_uint8>
+    <be_nonNegativeInteger32>32</be_nonNegativeInteger32>
+  </bigEndian>
+  <littleEndian>
+    <le_bool16>true</le_bool16>
+    <le_bool32>true</le_bool32>
+    <le_bool8>false</le_bool8>
+    <le_boolean>false</le_boolean>
+    <le_double>-2.2250738585072014E-308</le_double>
+    <le_float>-1.17549435E-38</le_float>
+    <le_int16>-16</le_int16>
+    <le_int32>-32</le_int32>
+    <le_int64>-64</le_int64>
+    <le_int8>-8</le_int8>
+    <le_integer64>-64</le_integer64>
+    <le_uint16>16</le_uint16>
+    <le_uint32>32</le_uint32>
+    <le_uint64>64</le_uint64>
+    <le_uint8>8</le_uint8>
+    <le_nonNegativeInteger8>8</le_nonNegativeInteger8>
+  </littleEndian>
+  <fixed>
+    <boolean_false>false</boolean_false>
+    <boolean_true>true</boolean_true>
+    <float_1_5>1.5</float_1_5>
+    <int_32>32</int_32>
+  </fixed>
+</ex_nums>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.dfdl.xsd b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.dfdl.xsd
new file mode 100644
index 0000000..2faebc7
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.dfdl.xsd
@@ -0,0 +1,84 @@
+<?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.
+-->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:idl="urn:idl:1.0" targetNamespace="urn:idl:1.0">
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:defineFormat name="defaults">
+        <dfdl:format alignment="8" alignmentUnits="bits" binaryBooleanFalseRep="0" binaryBooleanTrueRep="1" binaryFloatRep="ieee" binaryNumberCheckPolicy="lax" binaryNumberRep="binary" bitOrder="mostSignificantBitFirst" byteOrder="bigEndian" choiceLengthKind="implicit" encoding="utf-8" encodingErrorPolicy="replace" escapeSchemeRef="" fillByte="%#r20;" floating="no" ignoreCase="no" initiatedContent="no" initiator="" leadingSkip="0" lengthKind="implicit" lengthUnits="bits" occursCountKind= [...]
+      </dfdl:defineFormat>
+      <dfdl:format ref="idl:defaults"/>
+    </xs:appinfo>
+  </xs:annotation>
+
+  <xs:simpleType name="double" dfdl:length="64" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:double"/>
+  </xs:simpleType>
+
+  <xs:simpleType name="int32" dfdl:length="32" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:int"/>
+  </xs:simpleType>
+
+  <xs:complexType name="FooType">
+    <xs:sequence>
+      <xs:element name="a" type="idl:int32"/>
+      <xs:element name="b" type="idl:int32"/>
+      <xs:element name="c" type="idl:int32"/>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:complexType name="BarType">
+    <xs:sequence>
+      <xs:element name="x" type="idl:double"/>
+      <xs:element name="y" type="idl:double"/>
+      <xs:element name="z" type="idl:double"/>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:complexType name="NestedStructType">
+    <xs:sequence>
+      <xs:element name="data">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="foo" type="idl:FooType"/>
+            <xs:element name="bar" type="idl:BarType"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:complexType name="NestedUnionType">
+    <xs:sequence>
+      <xs:element name="tag" type="idl:int32"/>
+      <xs:element name="data">
+        <xs:complexType>
+          <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:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+
+  <xs:element name="NestedStruct" type="idl:NestedStructType"/>
+
+  <xs:element name="NestedUnion" type="idl:NestedUnionType"/>
+
+</xs:schema>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.tdml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.tdml
new file mode 100644
index 0000000..91adc0b
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested.tdml
@@ -0,0 +1,94 @@
+<?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 nested"
+  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 name="nested_struct_parse" model="nested.dfdl.xsd">
+    <tdml:document>
+      <tdml:documentPart type="file">nested_struct_parse.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_struct_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="nested_struct_unparse" model="nested.dfdl.xsd">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_struct_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">nested_struct_parse.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="nested_union_parse_2" model="nested.dfdl.xsd">
+    <tdml:document>
+      <tdml:documentPart type="file">nested_union_parse_2.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_union_unparse_2.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="nested_union_unparse_2" model="nested.dfdl.xsd">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_union_unparse_2.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">nested_union_parse_2.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="nested_union_parse_4" model="nested.dfdl.xsd">
+    <tdml:document>
+      <tdml:documentPart type="file">nested_union_parse_4.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_union_unparse_4.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="nested_union_unparse_4" model="nested.dfdl.xsd">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">nested_union_unparse_4.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">nested_union_parse_4.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_parse.dat b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_parse.dat
new file mode 100644
index 0000000..1782b59
Binary files /dev/null and b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_parse.dat differ
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_unparse.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_unparse.xml
new file mode 100644
index 0000000..89f2902
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_struct_unparse.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<idl:NestedStruct xmlns:idl="urn:idl:1.0">
+  <data>
+    <foo>
+      <a>1</a>
+      <b>2</b>
+      <c>3</c>
+    </foo>
+    <bar>
+      <x>4.5</x>
+      <y>5.5</y>
+      <z>6.5</z>
+    </bar>
+  </data>
+</idl:NestedStruct>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_2.dat b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_2.dat
new file mode 100644
index 0000000..08a4355
Binary files /dev/null and b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_2.dat differ
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_4.dat b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_4.dat
new file mode 100644
index 0000000..a8d472c
Binary files /dev/null and b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_parse_4.dat differ
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_2.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_2.xml
new file mode 100644
index 0000000..fe089b3
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_2.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<idl:NestedUnion xmlns:idl="urn:idl:1.0">
+  <tag>2</tag>
+  <data>
+    <foo>
+      <a>1</a>
+      <b>2</b>
+      <c>3</c>
+    </foo>
+  </data>
+</idl:NestedUnion>
diff --git a/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_4.xml b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_4.xml
new file mode 100644
index 0000000..6688d57
--- /dev/null
+++ b/daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/nested_union_unparse_4.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<idl:NestedUnion xmlns:idl="urn:idl:1.0">
+  <tag>4</tag>
+  <data>
+    <bar>
+      <x>1.5</x>
+      <y>2.5</y>
+      <z>3.5</z>
+    </bar>
+  </data>
+</idl:NestedUnion>
diff --git a/daffodil-runtime2/src/test/scala/org/apache/daffodil/runtime2/TestCodeGenerator.scala b/daffodil-runtime2/src/test/scala/org/apache/daffodil/runtime2/TestCodeGenerator.scala
new file mode 100644
index 0000000..85af8c5
--- /dev/null
+++ b/daffodil-runtime2/src/test/scala/org/apache/daffodil/runtime2/TestCodeGenerator.scala
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.runtime2
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.nio.channels.Channels
+import org.apache.daffodil.Implicits.intercept
+import org.apache.daffodil.compiler.Compiler
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.util.SchemaUtils
+import org.apache.daffodil.util.TestUtils
+import org.junit.After
+import org.junit.Assert.assertArrayEquals
+import org.junit.Test
+
+/**
+ * Checks that we can create a [[CodeGenerator]] and call its methods.
+ * The value of this test is to debug the call path from [[Compiler]]
+ * to [[CodeGenerator]] for a single test DFDL schema.  Running TDML
+ * tests with daffodil-runtime2 is a more effective way to check that
+ * CodeGenerator can generate appropriate code for as many DFDL schemas
+ * as you could want.
+ */
+class TestCodeGenerator {
+  // Ensure all tests remove tempDir after creating it
+  val tempDir: os.Path = os.temp.dir()
+
+  @After def after(): Unit = {
+    os.remove.all(tempDir)
+  }
+
+  // Define a simple DFDL test schema for debugging our code path
+  private val testSchema = SchemaUtils.dfdlTestSchema(
+      <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>,
+      <dfdl:format representation="binary" ref="GeneralFormat"/>,
+    <xs:element name="e1">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="x" type="xs:int"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>)
+
+  @Test def test_forLanguageC_success(): Unit = {
+    // Create a ProcessorFactory from the test schema
+    val pf = Compiler().compileNode(testSchema)
+    assert(!pf.isError, pf.getDiagnostics.map(_.getMessage()).mkString("\n"))
+
+    // Create a CodeGenerator from the ProcessorFactory for a supported language
+    val cg = pf.forLanguage("c")
+    assert(!cg.isError, cg.getDiagnostics.map(_.getMessage()).mkString("\n"))
+  }
+
+  @Test def test_forLanguage_error(): Unit = {
+    // Create a ProcessorFactory from the test schema
+    val pf = Compiler().compileNode(testSchema)
+    assert(!pf.isError, pf.getDiagnostics.map(_.getMessage()).mkString("\n"))
+
+    // Create a CodeGenerator from the ProcessorFactory for an unsupported language
+    val e = intercept[Exception] {
+      pf.forLanguage("vhld")
+    }
+    assert(e.getMessage.contains("source language vhld is not supported"))
+  }
+
+  @Test def test_generateCode_success(): Unit = {
+    // Create a ProcessorFactory and CodeGenerator from the test schema
+    val pf = Compiler().compileNode(testSchema)
+    val cg = pf.forLanguage("c")
+
+    // Generate code from the test schema successfully
+    val codeDir = cg.generateCode(None, tempDir.toString)
+    assert(!cg.isError, cg.getDiagnostics.map(_.getMessage()).mkString("\n"))
+    assert(os.exists(codeDir))
+    assert(os.exists(codeDir/"libruntime"/"generated_code.c"))
+  }
+
+  @Test def test_compileCode_success(): Unit = {
+    // Create a CodeGenerator and generate code from the test schema
+    val pf = Compiler().compileNode(testSchema)
+    val cg = pf.forLanguage("c")
+    val codeDir = cg.generateCode(None, tempDir.toString)
+
+    // Compile the generated code into an executable successfully
+    val executable = cg.compileCode(codeDir)
+    assert(!cg.isError, cg.getDiagnostics.map(_.getMessage()).mkString("\n"))
+    assert(os.exists(executable))
+  }
+
+  @Test def test_parse_success(): Unit = {
+    // Compile the test schema into a C executable
+    val pf = Compiler().compileNode(testSchema)
+    val cg = pf.forLanguage("c")
+    val codeDir = cg.generateCode(None, tempDir.toString)
+    val executable = cg.compileCode(codeDir)
+
+    // Create a Runtime2DataProcessor and parse a binary int32 number successfully
+    val dp = new Runtime2DataProcessor(executable)
+    val b = Misc.hex2Bytes("00000005")
+    val input = new ByteArrayInputStream(b)
+    val pr = dp.parse(input)
+    assert(!pr.isError && pf.getDiagnostics.isEmpty, pr.getDiagnostics.map(_.getMessage()).mkString("\n"))
+    val expected = <e1><x>5</x></e1>
+    TestUtils.assertEqualsXMLElements(expected, pr.infosetAsXML)
+  }
+
+  @Test def test_unparse_success(): Unit = {
+    // Compile the test schema into a C executable
+    val pf = Compiler().compileNode(testSchema)
+    val cg = pf.forLanguage("c")
+    val codeDir = cg.generateCode(None, tempDir.toString)
+    val executable = cg.compileCode(codeDir)
+
+    // Create a Runtime2DataProcessor and unparse a binary int32 number successfully
+    val dp = new Runtime2DataProcessor(executable)
+    val input = Channels.newInputStream(Misc.stringToReadableByteChannel("<e1><x>5</x></e1>"))
+    val output = new ByteArrayOutputStream()
+    val pr = dp.unparse(input, output)
+    assert(!pr.isError && pf.getDiagnostics.isEmpty, pr.getDiagnostics.map(_.getMessage()).mkString("\n"))
+    val expected = Misc.hex2Bytes("00000005")
+    assertArrayEquals(expected, output.toByteArray)
+  }
+}
diff --git a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
index 0ac8fa6..28f52ae 100644
--- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
+++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/TDMLRunner.scala
@@ -28,7 +28,6 @@ import java.nio.CharBuffer
 import java.nio.LongBuffer
 import java.nio.charset.CoderResult
 import java.nio.charset.{Charset => JavaCharset}
-import java.nio.file.Files
 
 import scala.language.postfixOps
 import scala.util.Try
@@ -477,7 +476,13 @@ abstract class TestCase(testCaseXML: NodeSeq, val parent: DFDLTestSuite)
   lazy val tdmlDFDLProcessorFactory: AbstractTDMLDFDLProcessorFactory = {
     import scala.language.existentials
 
-    val className = "org.apache.daffodil.tdml.processor.TDMLDFDLProcessorFactory"
+    // tdmlImplementation is a tunable choice with three values.
+    val className = tunableObj.tdmlImplementation match {
+      // Right now daffodil and ibm use the same ProcessFactory name
+      case "daffodil" | "ibm" => "org.apache.daffodil.tdml.processor.TDMLDFDLProcessorFactory"
+      case "daffodil-runtime2" => "org.apache.daffodil.tdml.processor.Runtime2TDMLDFDLProcessorFactory"
+      case other => Assert.invariantFailed("'%s' not valid for tdmlImplementation".format(other))
+    }
 
     //
     // If you haven't seen it before. Check out this Try(...) idiom.
@@ -895,9 +900,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
 
         // we should never need blobs if we're expecting an error even if we
         // don't get errors. So clean them up immediately
-        actual.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
+        actual.cleanUp()
 
         val isErr: Boolean =
           if (actual.isProcessingError) true
@@ -1150,9 +1153,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
         // Done with the first parse result, safe to clean up blobs if there
         // was success. This won't get called on failure, which is fine--leave
         // blobs around for debugging
-        firstParseResult.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
+        firstParseResult.cleanUp()
       }
       case OnePassRoundTrip => {
         val outStream = new java.io.ByteArrayOutputStream()
@@ -1167,9 +1168,7 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
         // Done with the first parse result, safe to clean up blobs if there
         // was success. This won't get called on failure, which is fine--leave
         // blobs around for debugging
-        firstParseResult.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
+        firstParseResult.cleanUp()
       }
       case TwoPassRoundTrip => {
         //
@@ -1194,12 +1193,8 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
         // Done with the first and second parse resultrs, safe to clean up
         // blobs if there was success. This won't get called on failure, which
         // is fine--leave blobs around for debugging
-        firstParseResult.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
-        actual.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
+        firstParseResult.cleanUp()
+        actual.cleanUp()
       }
       case ThreePassRoundTrip => {
         //
@@ -1243,12 +1238,8 @@ case class ParserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
         // Done with the first parse result and second parse results. Safe to
         // clean up blobs if there was success. Leave them around for debugging
         // if there was a failure
-        firstParseResult.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
-        secondParseResult.getBlobPaths.foreach { blobPath =>
-          Files.delete(blobPath)
-        }
+        firstParseResult.cleanUp()
+        secondParseResult.cleanUp()
       }
     }
 
@@ -1334,7 +1325,7 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
     if (testDataLength >= 0) {
       val fullBytesNeeded = (testDataLength + 7) / 8
       if (testData.length != fullBytesNeeded) {
-        throw TDMLException("Unparse result data was was %d bytes, but the result length (%d bits) requires %d bytes.".format(
+        throw TDMLException("Unparse result data was %d bytes, but the result length (%d bits) requires %d bytes.".format(
           testData.length, testDataLength, fullBytesNeeded), implString)
       }
     }
@@ -1404,11 +1395,12 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
       // Done with the parse results, safe to clean up blobs if there was
       // success. This won't get called on failure, which is fine--leave blobs
       // around for debugging
-      parseActual.getBlobPaths.foreach { blobPath =>
-        Files.delete(blobPath)
-      }
-
+      parseActual.cleanUp()
     }
+
+    // Done with the unparse results, safe to clean up any temporary files
+    // if they were not already cleaned up by parseActual.cleanUp() above
+    actual.cleanUp()
   }
 
   def runUnparserExpectErrors(
@@ -1472,6 +1464,10 @@ case class UnparserTestCase(ptc: NodeSeq, parentArg: DFDLTestSuite)
             }
           }
         }
+
+        // Done with the unparse results, safe to clean up any temporary files
+        actual.cleanUp()
+
         processor.getDiagnostics ++ actual.getDiagnostics ++ dataErrors
       }
     }
diff --git a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala
index 48fa414..f8a7769 100644
--- a/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala
+++ b/daffodil-tdml-lib/src/main/scala/org/apache/daffodil/tdml/processor/TDMLDFDLProcessor.scala
@@ -124,7 +124,10 @@ trait TDMLResult {
   def isValidationError: Boolean
   def isProcessingError: Boolean
   def getDiagnostics: Seq[Diagnostic]
-
+  /**
+   * Deletes any temporary files that have been generated
+   */
+  def cleanUp(): Unit
 }
 
 trait TDMLParseResult extends TDMLResult {
diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
index 523b8c0..fc9141b 100644
--- a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
+++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/DaffodilTDMLDFDLProcessor.scala
@@ -21,6 +21,7 @@ import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.OutputStreamWriter
 import java.nio.channels.Channels
+import java.nio.file.Files
 import java.nio.file.Path
 import java.nio.file.Paths
 
@@ -433,6 +434,8 @@ final class DaffodilTDMLParseResult(actual: DFDL.ParseResult, outputter: TDMLInf
   override def currentLocation: DataLocation = actual.resultState.currentLocation
 
   override def addDiagnostic(diag: Diagnostic): Unit = actual.addDiagnostic(diag)
+
+  override def cleanUp(): Unit = getBlobPaths.foreach { Files.delete }
 }
 
 final class DaffodilTDMLUnparseResult(actual: DFDL.UnparseResult, outStream: java.io.OutputStream) extends TDMLUnparseResult {
@@ -452,6 +455,8 @@ final class DaffodilTDMLUnparseResult(actual: DFDL.UnparseResult, outStream: jav
   override def getDiagnostics: Seq[Diagnostic] = actual.getDiagnostics
 
   override def bitPos0b: Long = ustate.bitPos0b
+
+  override def cleanUp(): Unit = { /* do nothing */ }
 }
 
 class DaffodilTDMLSAXErrorHandler extends ErrorHandler with WithDiagnostics {
diff --git a/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/Runtime2TDMLDFDLProcessor.scala b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/Runtime2TDMLDFDLProcessor.scala
new file mode 100644
index 0000000..5e84cd1
--- /dev/null
+++ b/daffodil-tdml-processor/src/main/scala/org/apache/daffodil/tdml/processor/Runtime2TDMLDFDLProcessor.scala
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.tdml.processor
+
+import org.apache.daffodil.api._
+import org.apache.daffodil.compiler.Compiler
+import org.apache.daffodil.externalvars.Binding
+import org.apache.daffodil.runtime2.ParseResult
+import org.apache.daffodil.runtime2.Runtime2DataProcessor
+import org.apache.daffodil.runtime2.UnparseResult
+import org.apache.daffodil.xml.QName
+import org.apache.daffodil.xml.XMLUtils
+
+import scala.xml.Node
+
+final class Runtime2TDMLDFDLProcessorFactory private(
+  private var compiler: Compiler,
+  private var checkAllTopLevel: Boolean,
+  validateDFDLSchemasArg: Boolean)
+  extends AbstractTDMLDFDLProcessorFactory {
+
+  override def validateDFDLSchemas = validateDFDLSchemasArg
+
+  override type R = Runtime2TDMLDFDLProcessorFactory
+
+  override def implementationName = "daffodil-runtime2"
+
+  def this() = this(compiler = Compiler(validateDFDLSchemas = true),
+    checkAllTopLevel = false,
+    validateDFDLSchemasArg = true)
+
+  private def copy(
+    compiler: Compiler = compiler,
+    checkAllTopLevel: Boolean = checkAllTopLevel,
+    validateDFDLSchemas: Boolean = validateDFDLSchemas) =
+    new Runtime2TDMLDFDLProcessorFactory(compiler, checkAllTopLevel, validateDFDLSchemas)
+
+  /**
+   * Deprecated methods must be implemented. Some are just stubs though now.
+   */
+  @deprecated("Use withValidateDFDLSchemas.", "2.6.0")
+  override def setValidateDFDLSchemas(bool: Boolean): Unit = {
+    compiler = compiler.withValidateDFDLSchemas(bool)
+  }
+
+  override def withValidateDFDLSchemas(bool: Boolean): Runtime2TDMLDFDLProcessorFactory = {
+    copy(compiler = compiler.withValidateDFDLSchemas(bool))
+  }
+
+  @deprecated("Use withCheckAllTopLevel.", "2.6.0")
+  override def setCheckAllTopLevel(checkAllTopLevel: Boolean): Unit = {
+    compiler = compiler.withCheckAllTopLevel(checkAllTopLevel)
+  }
+
+  override def withCheckAllTopLevel(checkAllTopLevel: Boolean): Runtime2TDMLDFDLProcessorFactory = {
+    copy(compiler = compiler.withCheckAllTopLevel(checkAllTopLevel))
+  }
+
+  @deprecated("Use withTunables.", "2.6.0")
+  override def setTunables(tunables: Map[String, String]): Unit =
+    compiler = compiler.withTunables(tunables)
+
+  override def withTunables(tunables: Map[String, String]): Runtime2TDMLDFDLProcessorFactory =
+    copy(compiler = compiler.withTunables(tunables))
+
+  @deprecated("Use DaffodilTDMLDFDLProcessor.setExternalDFDLVariables.", "2.6.0")
+  override def setExternalDFDLVariables(externalVarBindings: Seq[Binding]): Unit =
+    compiler = compiler.withExternalDFDLVariablesImpl(externalVarBindings)
+
+  override def withExternalDFDLVariables(externalVarBindings: Seq[Binding]): Runtime2TDMLDFDLProcessorFactory =
+    copy(compiler = compiler.withExternalDFDLVariablesImpl(externalVarBindings))
+
+  @deprecated("Use arguments to getProcessor()", "2.6.0")
+  override def setDistinguishedRootNode(name: String, namespace: String): Unit =
+    compiler = compiler.withDistinguishedRootNode(name, namespace)
+
+  // Return result is a TDML.CompileResult - so it's the result
+  // of compiling the schema for the test.
+  override def getProcessor(
+    schemaSource: DaffodilSchemaSource,
+    useSerializedProcessor: Boolean,
+    optRootName: Option[String] = None,
+    optRootNamespace: Option[String] = None): TDML.CompileResult = {
+
+    // Compile the DFDL schema into a ProcessorFactory
+    val pf = compiler.compileSource(schemaSource, optRootName, optRootNamespace)
+    val res = if (pf.isError) {
+      Left(pf.getDiagnostics) // DFDL schema compilation diagnostics
+    } else {
+      // Create a CodeGenerator from the DFDL schema for the C language
+      val generator = pf.forLanguage("c")
+
+      // Generate the C source code in a temporary unique directory
+      val rootNS = QName.refQNameFromExtendedSyntax(optRootName.getOrElse("")).toOption
+      val tempDir = os.temp.dir()
+      val codeDir = generator.generateCode(rootNS, tempDir.toString)
+
+      // Compile the generated code into an executable
+      val executable = generator.compileCode(codeDir)
+
+      // Summarize the result of compiling the schema for the test
+      val compileResult = if (generator.isError) {
+        Left(generator.getDiagnostics) // C code compilation diagnostics
+      } else {
+        // Create a processor for running the test using the executable, passing it
+        // generator.diagnostics in order to let us check generator warnings later
+        val processor = new Runtime2TDMLDFDLProcessor(tempDir, executable, generator.getDiagnostics)
+        // Sadly, TDMLRunner never checks generator diagnostics in "Right" tuple below
+        // nor does it check processor diagnostics in cross tests (runtime2's TDML tests)
+        // unless you set defaultShouldDoWarningComparisonOnCrossTests true in RunnerFactory
+        Right((generator.getDiagnostics, processor))
+      }
+      compileResult
+    }
+    res
+  }
+
+}
+
+/**
+ * Delegates all execution, error gathering, error access to the Runtime2DataProcessor.
+ * The responsibility of this class is just for TDML matching up. That is dealing with
+ * TDML XML Infosets, feeding to the unparser, creating XML from the result created by
+ * the Runtime2DataProcessor. All the "real work" is done by Runtime2DataProcessor.
+ */
+class Runtime2TDMLDFDLProcessor(tempDir: os.Path, executable: os.Path,
+                                var diagnostics: Seq[Diagnostic]) extends TDMLDFDLProcessor {
+
+  override type R = Runtime2TDMLDFDLProcessor
+
+  private val dataProcessor = new Runtime2DataProcessor(executable)
+  private var anyErrors: Boolean = false
+
+  @deprecated("Use withDebugging.", "2.6.0")
+  override def setDebugging(b: Boolean) = ???
+  override def withDebugging(b: Boolean): Runtime2TDMLDFDLProcessor = this
+
+  @deprecated("Use withTracing.", "2.6.0")
+  override def setTracing(bool: Boolean): Unit = ???
+  override def withTracing(bool: Boolean): Runtime2TDMLDFDLProcessor = this
+
+  @deprecated("Use withDebugger.", "2.6.0")
+  override def setDebugger(db: AnyRef): Unit = ???
+  override def withDebugger(db: AnyRef): Runtime2TDMLDFDLProcessor = this
+
+  @deprecated("Use withValidationMode.", "2.6.0")
+  override def setValidationMode(validationMode: ValidationMode.Type): Unit = ???
+  override def withValidationMode(validationMode: ValidationMode.Type): Runtime2TDMLDFDLProcessor = this
+
+  @deprecated("Use withExternalDFDLVariables.", "2.6.0")
+  override def setExternalDFDLVariables(externalVarBindings: Seq[Binding]): Unit = ???
+  override def withExternalDFDLVariables(externalVarBindings: Seq[Binding]): Runtime2TDMLDFDLProcessor = this
+
+  // Save any errors from running the C code here to be returned later
+  override def isError: Boolean = anyErrors
+  override def getDiagnostics: Seq[Diagnostic] = diagnostics
+
+  // Run the C code, collect and save the infoset with any errors and
+  // diagnostics, and return a [[TDMLParseResult]] summarizing the result.
+  // The C code will run in a subprocess, parse the input stream, write
+  // an XML infoset on its standard output, and write any error messages
+  // on its standard output (all done in [[Runtime2DataProcessor.parse]]).
+  override def parse(is: java.io.InputStream, lengthLimitInBits: Long): TDMLParseResult = {
+    // TODO: pass lengthLimitInBits to the C program to tell it how big the data is
+    val pr = dataProcessor.parse(is)
+    anyErrors = pr.isError
+    diagnostics = diagnostics ++ pr.getDiagnostics
+    new Runtime2TDMLParseResult(pr, tempDir)
+  }
+
+  // Run the C code, collect and save the unparsed data with any errors and
+  // diagnostics, and return a [[TDMLUnparseResult]] summarizing the result.
+  // The C code will run in a subprocess, unparse the input stream, write
+  // the unparsed data on its standard output, and write any error messages
+  // on its standard output (all done in [[Runtime2DataProcessor.unparse]]).
+  override def unparse(infosetXML: scala.xml.Node, outStream: java.io.OutputStream): TDMLUnparseResult = {
+    val tempInputFile = XMLUtils.convertNodeToTempFile(infosetXML, tempDir.toIO)
+    val inStream = os.read.inputStream(os.Path(tempInputFile))
+    val upr = dataProcessor.unparse(inStream, outStream)
+    anyErrors = upr.isError
+    diagnostics = diagnostics ++ upr.getDiagnostics
+    new Runtime2TDMLUnparseResult(upr, tempDir)
+  }
+
+  def unparse(parseResult: TDMLParseResult, outStream: java.io.OutputStream): TDMLUnparseResult = {
+    unparse(parseResult.getResult, outStream)
+  }
+}
+
+final class Runtime2TDMLParseResult(pr: ParseResult, tempDir: os.Path) extends TDMLParseResult {
+  override def addDiagnostic(failure: Diagnostic): Unit = pr.addDiagnostic(failure)
+
+  override def getResult: Node = pr.infosetAsXML
+
+  override def currentLocation: DataLocation = pr.currentLocation
+
+  override def isValidationError: Boolean = pr.isValidationError
+
+  override def isProcessingError: Boolean = pr.isProcessingError
+
+  override def getDiagnostics: Seq[Diagnostic] = pr.getDiagnostics
+
+  override def cleanUp(): Unit = os.remove.all(tempDir)
+}
+
+final class Runtime2TDMLUnparseResult(upr: UnparseResult, tempDir: os.Path) extends TDMLUnparseResult {
+  override def bitPos0b: Long = upr.finalBitPos0b
+
+  override def finalBitPos0b: Long = upr.finalBitPos0b
+
+  override def isScannable: Boolean = upr.isScannable
+
+  override def encodingName: String = upr.encodingName
+
+  override def isValidationError: Boolean = upr.isValidationError
+
+  override def isProcessingError: Boolean = upr.isProcessingError
+
+  override def getDiagnostics: Seq[Diagnostic] = upr.getDiagnostics
+
+  override def cleanUp(): Unit = os.remove.all(tempDir)
+}
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.dfdl.xsd b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.dfdl.xsd
new file mode 100644
index 0000000..2a74955
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.dfdl.xsd
@@ -0,0 +1,107 @@
+<?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.
+-->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:idl="urn:idl:1.0" targetNamespace="urn:idl:1.0">
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:defineFormat name="defaults">
+        <dfdl:format alignment="1" alignmentUnits="bytes" binaryBooleanFalseRep="0" binaryBooleanTrueRep="1" binaryFloatRep="ieee" binaryNumberCheckPolicy="lax" binaryNumberRep="binary" bitOrder="mostSignificantBitFirst" byteOrder="bigEndian" choiceLengthKind="implicit" encoding="utf-8" encodingErrorPolicy="replace" escapeSchemeRef="" fillByte="%NUL;" floating="no" ignoreCase="no" initiatedContent="no" initiator="" leadingSkip="0" lengthKind="implicit" lengthUnits="bytes" occursCountKind [...]
+      </dfdl:defineFormat>
+      <dfdl:format ref="idl:defaults"/>
+    </xs:appinfo>
+  </xs:annotation>
+  <xs:simpleType name="float" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:float"/>
+  </xs:simpleType>
+  <xs:simpleType name="double" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:double"/>
+  </xs:simpleType>
+  <xs:simpleType name="int8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:byte"/>
+  </xs:simpleType>
+  <xs:simpleType name="int16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:short"/>
+  </xs:simpleType>
+  <xs:simpleType name="int32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:int"/>
+  </xs:simpleType>
+  <xs:simpleType name="int64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:long"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedByte"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedShort"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedInt"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedLong"/>
+  </xs:simpleType>
+  <xs:simpleType name="boolean" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:boolean"/>
+  </xs:simpleType>
+  <xs:complexType name="IsrmEntry">
+    <xs:sequence>
+      <xs:element name="x" type="idl:double"/>
+      <xs:element name="y" type="idl:double"/>
+      <xs:element name="z" type="idl:double"/>
+      <xs:element name="speed" type="idl:double"/>
+      <xs:element name="bearing" type="idl:double"/>
+      <xs:element name="confidence" type="idl:double"/>
+      <xs:element name="id">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="item" minOccurs="80" maxOccurs="80" dfdl:occursCountKind="fixed" type="idl:int8"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      <xs:element name="classification">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="item" minOccurs="80" maxOccurs="80" dfdl:occursCountKind="fixed" type="idl:int8"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:element name="IsrmEntryDecl" type="idl:IsrmEntry"/>
+  <xs:complexType name="IsrmEnd">
+    <xs:sequence/>
+  </xs:complexType>
+  <xs:element name="IsrmEndDecl" type="idl:IsrmEnd"/>
+  <xs:simpleType name="IsrmResultTag">
+    <xs:restriction base="idl:uint32"/>
+  </xs:simpleType>
+  <xs:complexType name="IsrmResult">
+    <xs:sequence>
+      <xs:element name="tag" type="idl:IsrmResultTag"/>
+      <xs:element name="data" dfdl:length="208" dfdl:lengthKind="explicit">
+        <xs:complexType>
+          <xs:choice dfdl:choiceDispatchKey="{xs:string(../tag)}">
+            <xs:element dfdl:choiceBranchKey="0" name="entry" type="idl:IsrmEntry"/>
+            <xs:element dfdl:choiceBranchKey="1" name="end" type="idl:IsrmEnd"/>
+          </xs:choice>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:element name="IsrmResultDecl" type="idl:IsrmResult"/>
+</xs:schema>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.tdml
new file mode 100644
index 0000000..f062372
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000.tdml
@@ -0,0 +1,89 @@
+<?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"
+  defaultValidation="on"
+  description="TDML tests for ISRM_green_to_orange_60000"
+  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
+    model="ISRM_green_to_orange_60000.dfdl.xsd"
+    name="ISRM_green_to_orange_60000_parse_0"
+    root="IsrmResultDecl">
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_green_to_orange_60000_parse_0.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_green_to_orange_60000_unparse_0.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    model="ISRM_green_to_orange_60000.dfdl.xsd"
+    name="ISRM_green_to_orange_60000_unparse_0"
+    root="IsrmResultDecl">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_green_to_orange_60000_unparse_0.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_green_to_orange_60000_parse_0.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase
+    model="ISRM_green_to_orange_60000.dfdl.xsd"
+    name="ISRM_green_to_orange_60000_parse_1"
+    root="IsrmResultDecl">
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_green_to_orange_60000_parse_1.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_green_to_orange_60000_unparse_1.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    model="ISRM_green_to_orange_60000.dfdl.xsd"
+    name="ISRM_green_to_orange_60000_unparse_1"
+    root="IsrmResultDecl">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_green_to_orange_60000_unparse_1.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_green_to_orange_60000_parse_1.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_0.dat b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_0.dat
new file mode 100644
index 0000000..47b87e2
Binary files /dev/null and b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_0.dat differ
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_1.dat b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_1.dat
new file mode 100644
index 0000000..3f698bb
Binary files /dev/null and b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_parse_1.dat differ
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_0.xml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_0.xml
new file mode 100644
index 0000000..73609a4
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_0.xml
@@ -0,0 +1,195 @@
+<?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.
+-->
+
+<idl:IsrmResultDecl xmlns:idl="urn:idl:1.0">
+  <tag>0</tag>
+  <data>
+    <entry>
+      <x>0.5</x>
+      <y>1.5</y>
+      <z>2.5</z>
+      <speed>3.5</speed>
+      <bearing>4.5</bearing>
+      <confidence>5.5</confidence>
+      <id>
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+        <item>8</item>
+        <item>9</item>
+        <item>10</item>
+        <item>11</item>
+        <item>12</item>
+        <item>13</item>
+        <item>14</item>
+        <item>15</item>
+        <item>16</item>
+        <item>17</item>
+        <item>18</item>
+        <item>19</item>
+        <item>20</item>
+        <item>21</item>
+        <item>22</item>
+        <item>23</item>
+        <item>24</item>
+        <item>25</item>
+        <item>26</item>
+        <item>27</item>
+        <item>28</item>
+        <item>29</item>
+        <item>30</item>
+        <item>31</item>
+        <item>32</item>
+        <item>33</item>
+        <item>34</item>
+        <item>35</item>
+        <item>36</item>
+        <item>37</item>
+        <item>38</item>
+        <item>39</item>
+        <item>40</item>
+        <item>41</item>
+        <item>42</item>
+        <item>43</item>
+        <item>44</item>
+        <item>45</item>
+        <item>46</item>
+        <item>47</item>
+        <item>48</item>
+        <item>49</item>
+        <item>50</item>
+        <item>51</item>
+        <item>52</item>
+        <item>53</item>
+        <item>54</item>
+        <item>55</item>
+        <item>56</item>
+        <item>57</item>
+        <item>58</item>
+        <item>59</item>
+        <item>60</item>
+        <item>61</item>
+        <item>62</item>
+        <item>63</item>
+        <item>64</item>
+        <item>65</item>
+        <item>66</item>
+        <item>67</item>
+        <item>68</item>
+        <item>69</item>
+        <item>70</item>
+        <item>71</item>
+        <item>72</item>
+        <item>73</item>
+        <item>74</item>
+        <item>75</item>
+        <item>76</item>
+        <item>77</item>
+        <item>78</item>
+        <item>79</item>
+      </id>
+      <classification>
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+        <item>8</item>
+        <item>9</item>
+        <item>10</item>
+        <item>11</item>
+        <item>12</item>
+        <item>13</item>
+        <item>14</item>
+        <item>15</item>
+        <item>16</item>
+        <item>17</item>
+        <item>18</item>
+        <item>19</item>
+        <item>20</item>
+        <item>21</item>
+        <item>22</item>
+        <item>23</item>
+        <item>24</item>
+        <item>25</item>
+        <item>26</item>
+        <item>27</item>
+        <item>28</item>
+        <item>29</item>
+        <item>30</item>
+        <item>31</item>
+        <item>32</item>
+        <item>33</item>
+        <item>34</item>
+        <item>35</item>
+        <item>36</item>
+        <item>37</item>
+        <item>38</item>
+        <item>39</item>
+        <item>40</item>
+        <item>41</item>
+        <item>42</item>
+        <item>43</item>
+        <item>44</item>
+        <item>45</item>
+        <item>46</item>
+        <item>47</item>
+        <item>48</item>
+        <item>49</item>
+        <item>50</item>
+        <item>51</item>
+        <item>52</item>
+        <item>53</item>
+        <item>54</item>
+        <item>55</item>
+        <item>56</item>
+        <item>57</item>
+        <item>58</item>
+        <item>59</item>
+        <item>60</item>
+        <item>61</item>
+        <item>62</item>
+        <item>63</item>
+        <item>64</item>
+        <item>65</item>
+        <item>66</item>
+        <item>67</item>
+        <item>68</item>
+        <item>69</item>
+        <item>70</item>
+        <item>71</item>
+        <item>72</item>
+        <item>73</item>
+        <item>74</item>
+        <item>75</item>
+        <item>76</item>
+        <item>77</item>
+        <item>78</item>
+        <item>79</item>
+      </classification>
+    </entry>
+  </data>
+</idl:IsrmResultDecl>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_1.xml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_1.xml
new file mode 100644
index 0000000..c824daf
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_green_to_orange_60000_unparse_1.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<idl:IsrmResultDecl xmlns:idl="urn:idl:1.0">
+  <tag>1</tag>
+  <data>
+    <end />
+  </data>
+</idl:IsrmResultDecl>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.dfdl.xsd b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.dfdl.xsd
new file mode 100644
index 0000000..4ffb8ac
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.dfdl.xsd
@@ -0,0 +1,73 @@
+<?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.
+-->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:idl="urn:idl:1.0" targetNamespace="urn:idl:1.0">
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:defineFormat name="defaults">
+        <dfdl:format alignment="1" alignmentUnits="bytes" binaryBooleanFalseRep="0" binaryBooleanTrueRep="1" binaryFloatRep="ieee" binaryNumberCheckPolicy="lax" binaryNumberRep="binary" bitOrder="mostSignificantBitFirst" byteOrder="bigEndian" choiceLengthKind="implicit" encoding="utf-8" encodingErrorPolicy="replace" escapeSchemeRef="" fillByte="%NUL;" floating="no" ignoreCase="no" initiatedContent="no" initiator="" leadingSkip="0" lengthKind="implicit" lengthUnits="bytes" occursCountKind [...]
+      </dfdl:defineFormat>
+      <dfdl:format ref="idl:defaults"/>
+    </xs:appinfo>
+  </xs:annotation>
+  <xs:simpleType name="float" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:float"/>
+  </xs:simpleType>
+  <xs:simpleType name="double" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:double"/>
+  </xs:simpleType>
+  <xs:simpleType name="int8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:byte"/>
+  </xs:simpleType>
+  <xs:simpleType name="int16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:short"/>
+  </xs:simpleType>
+  <xs:simpleType name="int32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:int"/>
+  </xs:simpleType>
+  <xs:simpleType name="int64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:long"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedByte"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedShort"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedInt"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedLong"/>
+  </xs:simpleType>
+  <xs:simpleType name="boolean" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:boolean"/>
+  </xs:simpleType>
+  <xs:complexType name="IsrmRequest">
+    <xs:sequence>
+      <xs:element name="phase">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="item" minOccurs="80" maxOccurs="80" dfdl:occursCountKind="fixed" type="idl:int8"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:element name="IsrmRequestDecl" type="idl:IsrmRequest"/>
+</xs:schema>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.tdml
new file mode 100644
index 0000000..b4537cf
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002.tdml
@@ -0,0 +1,65 @@
+<?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"
+  defaultValidation="on"
+  description="TDML tests for ISRM_orange_to_green_60002"
+  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
+    model="ISRM_orange_to_green_60002.dfdl.xsd"
+    name="ISRM_orange_to_green_60002_parse"
+    root="IsrmRequestDecl">
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_orange_to_green_60002_parse.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_orange_to_green_60002_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    model="ISRM_orange_to_green_60002.dfdl.xsd"
+    name="ISRM_orange_to_green_60002_unparse"
+    root="IsrmRequestDecl">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">ISRM_orange_to_green_60002_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">ISRM_orange_to_green_60002_parse.dat</tdml:documentPart>
+    </tdml:document>
+  </tdml:unparserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_parse.dat b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_parse.dat
new file mode 100644
index 0000000..f82ef2b
Binary files /dev/null and b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_parse.dat differ
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_unparse.xml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_unparse.xml
new file mode 100644
index 0000000..3399c9a
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/ISRM_orange_to_green_60002_unparse.xml
@@ -0,0 +1,102 @@
+<?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.
+-->
+
+<idl:IsrmRequestDecl xmlns:idl="urn:idl:1.0">
+  <phase>
+    <item>0</item>
+    <item>1</item>
+    <item>2</item>
+    <item>3</item>
+    <item>4</item>
+    <item>5</item>
+    <item>6</item>
+    <item>7</item>
+    <item>8</item>
+    <item>9</item>
+    <item>10</item>
+    <item>11</item>
+    <item>12</item>
+    <item>13</item>
+    <item>14</item>
+    <item>15</item>
+    <item>16</item>
+    <item>17</item>
+    <item>18</item>
+    <item>19</item>
+    <item>20</item>
+    <item>21</item>
+    <item>22</item>
+    <item>23</item>
+    <item>24</item>
+    <item>25</item>
+    <item>26</item>
+    <item>27</item>
+    <item>28</item>
+    <item>29</item>
+    <item>30</item>
+    <item>31</item>
+    <item>32</item>
+    <item>33</item>
+    <item>34</item>
+    <item>35</item>
+    <item>36</item>
+    <item>37</item>
+    <item>38</item>
+    <item>39</item>
+    <item>40</item>
+    <item>41</item>
+    <item>42</item>
+    <item>43</item>
+    <item>44</item>
+    <item>45</item>
+    <item>46</item>
+    <item>47</item>
+    <item>48</item>
+    <item>49</item>
+    <item>50</item>
+    <item>51</item>
+    <item>52</item>
+    <item>53</item>
+    <item>54</item>
+    <item>55</item>
+    <item>56</item>
+    <item>57</item>
+    <item>58</item>
+    <item>59</item>
+    <item>60</item>
+    <item>61</item>
+    <item>62</item>
+    <item>63</item>
+    <item>64</item>
+    <item>65</item>
+    <item>66</item>
+    <item>67</item>
+    <item>68</item>
+    <item>69</item>
+    <item>70</item>
+    <item>71</item>
+    <item>72</item>
+    <item>73</item>
+    <item>74</item>
+    <item>75</item>
+    <item>76</item>
+    <item>77</item>
+    <item>78</item>
+    <item>79</item>
+  </phase>
+</idl:IsrmRequestDecl>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.dfdl.xsd b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.dfdl.xsd
new file mode 100644
index 0000000..fe03a94
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.dfdl.xsd
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/" xmlns:idl="urn:idl:1.0" targetNamespace="urn:idl:1.0">
+  <xs:annotation>
+    <xs:appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:defineFormat name="defaults">
+        <dfdl:format alignment="1" alignmentUnits="bytes" binaryBooleanFalseRep="0" binaryBooleanTrueRep="1" binaryFloatRep="ieee" binaryNumberCheckPolicy="lax" binaryNumberRep="binary" bitOrder="mostSignificantBitFirst" byteOrder="bigEndian" choiceLengthKind="implicit" encoding="utf-8" encodingErrorPolicy="replace" escapeSchemeRef="" fillByte="%NUL;" floating="no" ignoreCase="no" initiatedContent="no" initiator="" leadingSkip="0" lengthKind="implicit" lengthUnits="bytes" occursCountKind [...]
+      </dfdl:defineFormat>
+      <dfdl:format ref="idl:defaults"/>
+    </xs:appinfo>
+  </xs:annotation>
+  <xs:simpleType name="float" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:float"/>
+  </xs:simpleType>
+  <xs:simpleType name="double" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:double"/>
+  </xs:simpleType>
+  <xs:simpleType name="int8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:byte"/>
+  </xs:simpleType>
+  <xs:simpleType name="int16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:short"/>
+  </xs:simpleType>
+  <xs:simpleType name="int32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:int"/>
+  </xs:simpleType>
+  <xs:simpleType name="int64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:long"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint8" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedByte"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint16" dfdl:length="2" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedShort"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint32" dfdl:length="4" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedInt"/>
+  </xs:simpleType>
+  <xs:simpleType name="uint64" dfdl:length="8" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:unsignedLong"/>
+  </xs:simpleType>
+  <xs:simpleType name="boolean" dfdl:length="1" dfdl:lengthKind="explicit">
+    <xs:restriction base="xs:boolean"/>
+  </xs:simpleType>
+  <xs:complexType name="ComponentReady">
+    <xs:sequence>
+      <xs:element name="name">
+        <xs:complexType>
+          <xs:sequence>
+            <xs:element name="item" minOccurs="80" maxOccurs="80" dfdl:occursCountKind="fixed" type="idl:int8"/>
+          </xs:sequence>
+        </xs:complexType>
+      </xs:element>
+      <xs:element name="status" type="idl:boolean"/>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:element name="ComponentReadyDecl" type="idl:ComponentReady"/>
+</xs:schema>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.tdml
new file mode 100644
index 0000000..a7bab3e
--- /dev/null
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/runtime2/MPU_green_to_orange_60004.tdml
@@ -0,0 +1,65 @@
+<?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"
+  defaultValidation="on"
+  description="TDML tests for MPU_green_to_orange_60004"
+  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
+    model="MPU_green_to_orange_60004.dfdl.xsd"
+    name="MPU_green_to_orange_60004_parse"
+    root="ComponentReadyDecl">
+    <tdml:document>
+      <tdml:documentPart type="file">MPU_green_to_orange_60004_parse.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">MPU_green_to_orange_60004_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase
+    model="MPU_green_to_orange_60004.dfdl.xsd"
+    name="MPU_green_to_orange_60004_unparse"
+    root="ComponentReadyDecl">
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">MPU_green_to_orange_60004_unparse.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+    <tdml:document>
+      <tdml:documentPart type="file">MPU_green_to_orange_60004_parse.dat</tdml:documentPart>
... 5189 lines suppressed ...