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 b84dd05592ceaad5ea254b877afcedbbd3f649fa
Author: John Interrante <[email protected]>
AuthorDate: Wed Apr 7 17:17:12 2021 -0400

    Make some daffodil-runtime2 improvements
    
    Refactor duplicate code in Binary*CodeGenerator.scala files.
    
    Change PState/UState member field from a string error message to a
    struct error object.  Make parse/unparse functions initialize that
    member field if any error happens.  Move checks for errors from
    parse/unparse functions to their callers, e.g., "if (ustate->error)
    return;".
    
    Collect validation errors without stopping parsing or unparsing by
    initializing another PState/UState member field pointing to a
    validation struct with some fields.  As last step, update all C files
    using include-what-you-use to document what's used from each include.
    
    Add instructions to README.md how to setup required C tool-chain
    elements on Linux and Windows.
    
    Generate daffodil-runtime2's name and version into generated_code.c
    automatically so we won't need to keep it up to date.
    
    Changelog:
    
    In README.md, fix or disable markdownlint warnings.  Expand Build
    Requirements with instructions how to install C compiler, Mini-XML
    library, java, and sbt on Linux and Windows.  Verify/update old
    hyperlinks and add new MSYS2 and clang hyperlinks.
    
    In build.sbt, add Compile / cCompiler and ccArchiveCommand settings as
    a guide to show developers how to override these default settings if
    they want sbt to call the C compiler with a different name than "cc".
    The sbt-cc plugin doesn't iterate through a list (${CC}, cc, gcc,
    clang, zig cc) to find a C compiler like our runtime2 code does.
    
    In daffodil_argp.c, remove argp_program_version's definition since we
    now generate it automatically within generated_code.c.
    
    In daffodil_main.c, add fflush_or_exit and change fopen_or_exit /
    fclose_or_open to call continue_or_exit with error message instead of
    calling exit directly.  Make main print any validation diagnostics
    before printing any errors since they've been split into separate
    PState/UState member fields.  Also make main call continue_or_exit
    instead of calling error directly and pass Error structs, not strings,
    to all continue_or_exit calls.
    
    In stack.c, make stack functions call continue_or_exit instead of
    calling error directly and pass Error structs, not strings, to all
    continue_or_exit calls.
    
    In xml_reader.c, make conversion functions return Error structs, not
    strings, to indicate errors converting strings to primitive datums.
    Make reader functions return Error structs, not strings, to indicate
    errors reading XML data.
    
    In xml_writer.c, make writer functions return Error structs, not
    strings, to indicate errors writing XML data.  Remove redundant stack
    checking code already performed within stack fuctions.
    
    In errors.c (new file), use switch statement inside error_message
    function to centralize all error messages in one place for easier
    future internationalization.  Use switch statement inside
    print_mybe_stop function to allow error messages to interpolate an
    extra detail (string or integer) if needed.  Define a single static
    array that can be used to store validation diagnostics and define a
    function to print validation diagnostics.  Implement continue_or_exit
    and eof_or_error functions here as well.
    
    In errors.h (new file), define enumerations for each type of error
    which could occur in C code.  Define an Error struct to store an
    enumeration and optionally an extra detail (string or integer).  Also
    define a Diagnostics struct to store an array of data validation
    errors which should be reported separately from parse errors.  Move
    PState and UState structs here from infoset.h and give them member
    fields pointing to data validation and parse errors.  Also declare
    need_diagnostics, print_diagnostics, continue_or_exit, eof_or_error
    prototypes and UNUSED macro in errors.h.
    
    In infoset.c, make walkInfosetNode and walkInfoset functions return
    Error struct, not string.  Remove eof_or_error_msg function (now moved
    to errors.c with shorter name).
    
    In infoset.h, change InitChoiceRD and Visit* prototypes to return
    Error structs, not strings.  Remove PState & UState structs,
    eof_or_error_msg prototype, and UNUSED macro (all moved to errors.h).
    
    In parsers.c, make parse_<endian>_<type> functions stop enclosing
    their code in "if (!pstate->error_msg)" statements and use Error
    structs, not strings.  Callers are now expected to check for errors
    after each call immediately.  Make parse_fill_bytes use
    read_stream_update_position helper macro too.  Make
    parse_validate_fixed report errors in separate pstate->validati field,
    not pstate->error field.
    
    In unparsers.c, make unparse_<endian>_<type> functions stop enclosing
    their code in "if (!ustate->error_msg)" statements and use Error
    structs, not strings.  Callers are now expected to check for errors
    after each call immediately.  Make unparse_fill_bytes use
    write_stream_update_position helper macro too.  Make
    unparse_validate_fixed report errors in separate ustate->validati
    field, not ustate->error field.
    
    In NestedUnion.c and ex_nums.c examples, update with freshly generated
    code to illustrate code generator changes (in particular, defining
    argp_program_version, adding an if statement after each parse/unparse
    call to check for an error and skip rest of calls immediately, and
    returning errors directly from initChoice functions with choice key
    that failed to match any branch key).
    
    In BinaryAbstractCodeGenerator.scala (new file), refactor common code
    that used to be duplicated in BinaryBooleanCodeGenerator.scala,
    BinaryFloatCodeGenerator.scala, and
    BinaryIntegerKnownLengthCodeGenerator.scala; now they pass parameters
    to binaryAbstractGenerateCode instead.
    
    In CodeGeneratorState.scala, make all necessary changes to report
    errors using structs instead of strings in generated code including
    initChoice functions, add if statements after each parse/unparse call,
    and generate the program name and version automatically.
    
    In build.properties, change sbt.version from 1.4.1 to 1.4.9 to ensure
    successful sbt build from scratch in a new Ubuntu or Windows VM (sbt
    was getting java.lang.NoClassDefFound: scala/Serializer).
---
 README.md                                          | 134 +++++++++------
 build.sbt                                          |   2 +
 .../src/main/resources/c/libcli/daffodil_argp.c    |   4 -
 .../src/main/resources/c/libcli/daffodil_main.c    |  76 +++++----
 .../src/main/resources/c/libcli/stack.c            |  14 +-
 .../src/main/resources/c/libcli/xml_reader.c       | 178 +++++++++++---------
 .../src/main/resources/c/libcli/xml_reader.h       |   2 +-
 .../src/main/resources/c/libcli/xml_writer.c       | 109 +++++++-----
 .../src/main/resources/c/libcli/xml_writer.h       |   2 +-
 .../src/main/resources/c/libruntime/errors.c       | 183 +++++++++++++++++++++
 .../src/main/resources/c/libruntime/errors.h       | 117 +++++++++++++
 .../src/main/resources/c/libruntime/infoset.c      |  64 +++----
 .../src/main/resources/c/libruntime/infoset.h      |  65 ++------
 .../src/main/resources/c/libruntime/parsers.c      | 132 +++++++--------
 .../src/main/resources/c/libruntime/parsers.h      |  11 +-
 .../src/main/resources/c/libruntime/unparsers.c    | 102 ++++++------
 .../src/main/resources/c/libruntime/unparsers.h    |  11 +-
 .../src/main/resources/examples/NestedUnion.c      |  98 +++++++----
 .../src/main/resources/examples/NestedUnion.h      |   5 +-
 .../src/main/resources/examples/ex_nums.c          | 123 +++++++++++++-
 .../src/main/resources/examples/ex_nums.h          |   5 +-
 ...tor.scala => BinaryAbstractCodeGenerator.scala} |  37 +++--
 .../generators/BinaryBooleanCodeGenerator.scala    |  53 ++----
 .../generators/BinaryFloatCodeGenerator.scala      |  43 +----
 .../BinaryIntegerKnownLengthCodeGenerator.scala    |  49 +-----
 .../runtime2/generators/CodeGeneratorState.scala   | 103 +++++++-----
 project/build.properties                           |   2 +-
 27 files changed, 1063 insertions(+), 661 deletions(-)

diff --git a/README.md b/README.md
index 9097223..3b3ad76 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,89 +26,114 @@
 [<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 the [Website].
 
 ## Build Requirements
 
 * JDK 8 or higher
 * SBT 0.13.8 or higher
-* C compiler (for daffodil-runtime2 only)
-* Mini-XML Version 3.2 or higher (for daffodil-runtime2 only)
+* C compiler C99 or higher
+* Mini-XML Version 3.2 or higher
+
+Since Daffodil has a DFDL to C backend, you will need a C compiler
+([gcc] or [clang]), the [Mini-XML] library, and possibly the GNU
+[argp] library if your system's C library doesn't include it.  You can
+install gcc and libmxml as system packages on most Unix based
+platforms with distribution-specific packager commands such as (Debian
+and Ubuntu):
+
+    # Just mentioning all other packages you might need too
+    sudo apt install build-essential curl git libmxml-dev
+
+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.  [SDK] offers an easy and uniform way to install both java
+and sbt on any Unix based platform:
+
+    curl -s "https://get.sdkman.io"; | bash
+    sdk install java
+    sdk install sbt
+
+You can edit the Compile / cCompiler setting in build.sbt if you don't
+want sbt to call your C compiler with "cc" as the driver command.
+
+On Windows, the easiest way to install gcc and libargp is to install
+[MSYS2]'s collection of free tools and libraries although MSYS2 has no
+package for libmxml which you'll need to build from source.  First
+install [MSYS2] following its website's installation instructions,
+then run the following commands in a "MSYS2 MSYS" window:
+
+    pacman -S gcc git libargp-devel make pkgconf
+    git clone https://github.com/michaelrsweet/mxml.git
+    cd mxml
+    ./configure --prefix=/usr --disable-shared --disable-threads
+    make
+    make install
+
+You also need to install [JDK} and [SBT] from their Windows
+installation packages and define an environment variable using
+Windows' control panel for editing environment variables.  Define an
+environment variable with the name `MSYS2_PATH_TYPE` and the value
+`inherit`.  Now when you open a new "MSYS2 MSYS" window from the Start
+Menu, you will be able to type your sbt commands in the MSYS2 window
+and both sbt and daffodil will be able to call the C compiler.
 
 ## Getting Started
 
-You will need the full Java Software Development Kit ([JDK] or [SDK]),
-not the Java Runtime Environment (JRE), to build Daffodil.  You also
-will need [SBT] to build Daffodil, run all tests, create packages, and
-more.
-
-In order to build daffodil-runtime2, you will need a C compiler (for
-example, [gcc]), the [Mini-XML] library, and possibly the [argp]
-library if your system doesn't include it in its C library.
-
 Below are some of the more common commands used for Daffodil development.
 
 ### Compile
 
-```text
-$ sbt compile
-```
+    sbt compile
 
 ### Tests
 
 Run all unit tests:
 
-```text
-$ sbt test 
-```
+    sbt test
 
 Run all command line interface tests:
 
-```text
-$ sbt it:test
-```
+    sbt it: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:
+Create Linux and Windows shell scripts in
+`daffodil-cli/target/universal/stage/bin/`.  See the [Command Line
+Interface] documentation for details on its 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:
+Generate an [Apache RAT] license check report located 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/``:
 
-```text
-$ sbt clean coverage test it:test
-$ sbt coverageAggregate
-```
+    sbt clean coverage test it:test
+    sbt coverageAggregate
 
 ## Getting Help
 
 For questions, we can be reached at the [email protected] or
[email protected] mailing lists. Bugs can be reported via the 
[Daffodil JIRA].
[email protected] mailing lists.  Bugs can be reported via the
+[Daffodil JIRA].
 
 ## License
 
@@ -113,17 +141,19 @@ 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/
-[DFDL specification]: http://www.ogf.org/dfdl
-[Daffodil JIRA]: https://issues.apache.org/jira/projects/DAFFODIL
+[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+
-[JDK]: 
https://docs.oracle.com/en/java/javase/11/install/overview-jdk-installation.html
+[JDK]: https://adoptopenjdk.net/
 [Mini-XML]: https://www.msweet.org/mxml/
+[MSYS2]: https://www.msys2.org/
 [Releases]: http://daffodil.apache.org/releases/
-[SBT]: http://www.scala-sbt.org
-[SDK]: https://sdkman.io
-[Website]: https://daffodil.apache.org
+[SBT]: https://www.scala-sbt.org/
+[SDK]: https://sdkman.io/
+[Website]: https://daffodil.apache.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/
-[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage
+[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage/
diff --git a/build.sbt b/build.sbt
index b2fadab..2828483 100644
--- a/build.sbt
+++ b/build.sbt
@@ -68,6 +68,8 @@ lazy val runtime2         = Project("daffodil-runtime2", 
file("daffodil-runtime2
                               .settings(commonSettings)
                               .settings(publishArtifact in (Compile, 
packageDoc) := false)
                               .settings(
+                                Compile / cCompiler := "cc",
+                                Compile / ccArchiveCommand := "ar",
                                 Compile / ccTargets := ListSet(runtime2CFiles),
                                 Compile / cSources  := Map(
                                   runtime2CFiles -> (
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c 
b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
index ddc9b31..cbe33d4 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
+++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
@@ -21,10 +21,6 @@
 #include <stdlib.h>  // for putenv
 #include <string.h>  // for strlen, strcmp
 
-// Initialize our "daffodil" name and version
-
-const char *argp_program_version = "Apache Daffodil (runtime2) 0.1";
-
 // Initialize our "daffodil parse" CLI options
 
 struct daffodil_parse_cli daffodil_parse = {
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c 
b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c
index fa53b57..03b1324 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c
+++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c
@@ -15,14 +15,13 @@
  * limitations under the License.
  */
 
-#include "daffodil_argp.h"  // for daffodil_cli, daffodil_parse, 
daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli, parse_daffodil_cli, 
DAFFODIL_PARSE, DAFFODIL_UNPARSE
-#include "infoset.h"        // for walkInfoset, InfosetBase, rootElement, ERD, 
PState, UState, VisitEventHandler
+#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
-#include <error.h>          // for error
-#include <stdio.h>          // for FILE, perror, fclose, fopen, stdin, stdout
-#include <stdlib.h>         // for exit, EXIT_FAILURE
-#include <string.h>         // for strcmp
 
 // Open a file or exit if it can't be opened
 
@@ -34,33 +33,42 @@ fopen_or_exit(FILE *stream, const char *pathname, const 
char *mode)
         stream = fopen(pathname, mode);
         if (!stream)
         {
-            perror("Error opening file: ");
-            exit(EXIT_FAILURE);
+            perror("fopen");
+            const Error error = {ERR_FILE_OPEN, {pathname}};
+            continue_or_exit(&error);
         }
     }
     return stream;
 }
 
-// Close a stream or exit if it can't be closed
+// 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
-fclose_or_exit(FILE *stream, FILE *stdin_stdout)
+fflush_continue_or_exit(FILE *output, const Error *error)
 {
-    if (stream != stdin_stdout && fclose(stream) != 0)
+    if (fflush(output) != 0)
     {
-        perror("Error closing file: ");
-        exit(EXIT_FAILURE);
+        perror("fflush");
+        if (!error)
+        {
+            const Error error = {ERR_FILE_FLUSH, {NULL}};
+            continue_or_exit(&error);
+        }
     }
+    continue_or_exit(error);
 }
 
-// Print an error and exit if an error occurred or continue otherwise
+// Close a file or exit if it can't be closed
 
 static void
-continue_or_exit(const char *error_msg)
+fclose_or_exit(FILE *stream, FILE *stdin_or_stdout)
 {
-    if (error_msg)
+    if (stream != stdin_or_stdout && fclose(stream) != 0)
     {
-        error(EXIT_FAILURE, 0, "%s", error_msg);
+        perror("fclose");
+        const Error error = {ERR_FILE_CLOSE, {NULL}};
+        continue_or_exit(&error);
     }
 }
 
@@ -85,23 +93,25 @@ main(int argc, char *argv[])
             output = fopen_or_exit(output, daffodil_parse.outfile, "w");
 
             // Parse the input file into our infoset.
-            PState pstate = {input, 0, NULL};
+            PState pstate = {input, 0, NULL, NULL};
             root->erd->parseSelf(root, &pstate);
-            continue_or_exit(pstate.error_msg);
+            print_diagnostics(pstate.validati);
+            continue_or_exit(pstate.error);
 
             if (strcmp(daffodil_parse.infoset_converter, "xml") == 0)
             {
                 // Visit the infoset and print XML from it.
-                XMLWriter   xmlWriter = {
+                XMLWriter xmlWriter = {
                     xmlWriterMethods, output, {NULL, NULL, 0}};
-                const char *error_msg =
+                const Error *error =
                     walkInfoset((VisitEventHandler *)&xmlWriter, root);
-                continue_or_exit(error_msg);
+                fflush_continue_or_exit(output, error);
             }
             else
             {
-                error(EXIT_FAILURE, 0, "Cannot write infoset type '%s'",
-                      daffodil_parse.infoset_converter);
+                const Error error = {ERR_INFOSET_WRITE,
+                                     {daffodil_parse.infoset_converter}};
+                continue_or_exit(&error);
             }
         }
         else if (daffodil_cli.subcommand == DAFFODIL_UNPARSE)
@@ -113,22 +123,24 @@ main(int argc, char *argv[])
             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 char *error_msg =
+                XMLReader    xmlReader = {xmlReaderMethods, input, root, NULL,
+                                       NULL};
+                const Error *error =
                     walkInfoset((VisitEventHandler *)&xmlReader, root);
-                continue_or_exit(error_msg);
+                continue_or_exit(error);
             }
             else
             {
-                error(EXIT_FAILURE, 0, "Cannot read infoset type '%s'",
-                      daffodil_unparse.infoset_converter);
+                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};
+            UState ustate = {output, 0, NULL, NULL};
             root->erd->unparseSelf(root, &ustate);
-            continue_or_exit(ustate.error_msg);
+            print_diagnostics(ustate.validati);
+            continue_or_exit(ustate.error);
         }
 
         // Close our input and out files if we opened them.
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/stack.c 
b/daffodil-runtime2/src/main/resources/c/libcli/stack.c
index 08c1561..8297bcd 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/stack.c
+++ b/daffodil-runtime2/src/main/resources/c/libcli/stack.c
@@ -16,10 +16,9 @@
  */
 
 #include "stack.h"
-#include <error.h>    // for error
 #include <stdbool.h>  // for bool
-#include <stddef.h>   // for ptrdiff_t
-#include <stdlib.h>   // for EXIT_FAILURE
+#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
 
@@ -55,7 +54,8 @@ stack_pop(stack_t *p_stack)
 {
     if (stack_is_empty(p_stack))
     {
-        error(EXIT_FAILURE, 0, "Stack underflow - program terminated");
+        const Error error = {ERR_STACK_UNDERFLOW, {NULL}};
+        continue_or_exit(&error);
     }
     return *(--p_stack->p_after);
 }
@@ -67,7 +67,8 @@ stack_push(stack_t *p_stack, stack_item_t item)
 {
     if (stack_is_full(p_stack))
     {
-        error(EXIT_FAILURE, 0, "Stack overflow - program terminated");
+        const Error error = {ERR_STACK_OVERFLOW, {NULL}};
+        continue_or_exit(&error);
     }
     *(p_stack->p_after++) = item;
 }
@@ -79,7 +80,8 @@ stack_top(stack_t *p_stack)
 {
     if (stack_is_empty(p_stack))
     {
-        error(EXIT_FAILURE, 0, "Stack empty - program terminated");
+        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/xml_reader.c 
b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c
index 3688933..57deee2 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c
@@ -19,22 +19,23 @@
 #include <assert.h>    // for assert
 #include <errno.h>     // for errno
 #include <inttypes.h>  // for strtoimax, strtoumax
-#include <mxml.h>      // for mxmlWalkNext, mxmlGetType, mxmlGetElement, 
MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile, 
MXML_OPAQUE_CALLBACK
+#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 char **errstrp)
+strtobool(const char *numptr, const Error **errorptr)
 {
     // The lexical space of xs:boolean accepts true, false, 1, and 0
-    bool        value = false;
-    const char *error_msg = NULL;
+    bool value = false;
 
-    // Report any issues converting the string to a boolean
+    // Check for any errors converting the string to a boolean
     if (strcmp(numptr, "true") == 0)
     {
         value = true;
@@ -53,10 +54,11 @@ strtobool(const char *numptr, const char **errstrp)
     }
     else
     {
-        error_msg = "Error converting XML data to boolean";
+        static Error error = {ERR_STRTOBOOL, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
 
-    *errstrp = error_msg;
     return value;
 }
 
@@ -64,7 +66,7 @@ strtobool(const char *numptr, const char **errstrp)
 // error checking)
 
 static double
-strtodnum(const char *numptr, const char **errstrp)
+strtodnum(const char *numptr, const Error **errorptr)
 {
     char *endptr = NULL;
 
@@ -72,22 +74,24 @@ strtodnum(const char *numptr, const char **errstrp)
     errno = 0;
     const double value = strtod(numptr, &endptr);
 
-    // Report any issues converting the string to a number
+    // Check for any errors converting the string to a number
     if (errno != 0)
     {
-        *errstrp = "Error converting XML data to number";
+        static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (endptr == numptr)
     {
-        *errstrp = "Found no number in XML data";
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (*endptr != '\0')
     {
-        *errstrp = "Found non-number characters in XML data";
-    }
-    else
-    {
-        *errstrp = NULL;
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
 
     return value;
@@ -97,7 +101,7 @@ strtodnum(const char *numptr, const char **errstrp)
 // error checking)
 
 static float
-strtofnum(const char *numptr, const char **errstrp)
+strtofnum(const char *numptr, const Error **errorptr)
 {
     char *endptr = NULL;
 
@@ -105,33 +109,35 @@ strtofnum(const char *numptr, const char **errstrp)
     errno = 0;
     const float value = strtof(numptr, &endptr);
 
-    // Report any issues converting the string to a number
+    // Check for any errors converting the string to a number
     if (errno != 0)
     {
-        *errstrp = "Error converting XML data to number";
+        static Error error = {ERR_STRTOD_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (endptr == numptr)
     {
-        *errstrp = "Found no number in XML data";
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (*endptr != '\0')
     {
-        *errstrp = "Found non-number characters in XML data";
-    }
-    else
-    {
-        *errstrp = NULL;
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
 
     return value;
 }
 
-// Convert an XML element's text to a signed integer (BSD function not
-// widely available, so call strtoimax with our own error checking)
+// 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 char **errstrp)
+         const Error **errorptr)
 {
     char *endptr = NULL;
     assert(minval < maxval);
@@ -140,26 +146,30 @@ strtonum(const char *numptr, intmax_t minval, intmax_t 
maxval,
     errno = 0;
     const intmax_t value = strtoimax(numptr, &endptr, 10);
 
-    // Report any issues converting the string to a number
+    // Check for any errors converting the string to a number
     if (errno != 0)
     {
-        *errstrp = "Error converting XML data to integer";
+        static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (endptr == numptr)
     {
-        *errstrp = "Found no number in XML data";
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (*endptr != '\0')
     {
-        *errstrp = "Found non-number characters in XML data";
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (value < minval || value > maxval)
     {
-        *errstrp = "Number in XML data out of range";
-    }
-    else
-    {
-        *errstrp = NULL;
+        static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
 
     return value;
@@ -169,7 +179,7 @@ strtonum(const char *numptr, intmax_t minval, intmax_t 
maxval,
 // with our own error checking)
 
 static uintmax_t
-strtounum(const char *numptr, uintmax_t maxval, const char **errstrp)
+strtounum(const char *numptr, uintmax_t maxval, const Error **errorptr)
 {
     char *endptr = NULL;
 
@@ -177,26 +187,30 @@ strtounum(const char *numptr, uintmax_t maxval, const 
char **errstrp)
     errno = 0;
     const uintmax_t value = strtoumax(numptr, &endptr, 10);
 
-    // Report any issues converting the string to a number
+    // Check for any errors converting the string to a number
     if (errno != 0)
     {
-        *errstrp = "Error converting XML data to integer";
+        static Error error = {ERR_STRTOI_ERRNO, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (endptr == numptr)
     {
-        *errstrp = "Found no number in XML data";
+        static Error error = {ERR_STRTONUM_EMPTY, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (*endptr != '\0')
     {
-        *errstrp = "Found non-number characters in XML data";
+        static Error error = {ERR_STRTONUM_NOT, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
     else if (value > maxval)
     {
-        *errstrp = "Number in XML data out of range";
-    }
-    else
-    {
-        *errstrp = NULL;
+        static Error error = {ERR_STRTONUM_RANGE, {NULL}};
+        error.s = numptr;
+        *errorptr = &error;
     }
 
     return value;
@@ -204,7 +218,7 @@ strtounum(const char *numptr, uintmax_t maxval, const char 
**errstrp)
 
 // Read XML data from file before walking infoset
 
-static const char *
+static const Error *
 xmlStartDocument(XMLReader *reader)
 {
     // Load the XML data into memory
@@ -212,7 +226,8 @@ xmlStartDocument(XMLReader *reader)
     reader->node = reader->xml;
     if (!reader->node)
     {
-        return "Unable to read XML data from input file";
+        static Error error = {ERR_XML_INPUT, {NULL}};
+        return &error;
     }
 
     // Consume the <?xml line if there is one
@@ -237,12 +252,13 @@ xmlStartDocument(XMLReader *reader)
         } while (mxmlGetType(reader->node) == MXML_OPAQUE);
     }
 
-    return reader->node ? NULL : "Ran out of XML data";
+    static Error error = {ERR_XML_GONE, {NULL}};
+    return reader->node ? NULL : &error;
 }
 
 // Delete XML data after walking infoset
 
-static const char *
+static const Error *
 xmlEndDocument(XMLReader *reader)
 {
     // Consume any remaining newlines or whitespace
@@ -255,7 +271,9 @@ xmlEndDocument(XMLReader *reader)
     if (reader->node)
     {
         // This code path exits the program - no need to call mxmlDelete
-        return "Did not consume all of the XML data";
+        static Error error = {ERR_XML_LEFT, {NULL}};
+        error.s = mxmlGetElement(reader->node);
+        return &error;
     }
 
     // Free the storage allocated to hold the XML data
@@ -267,7 +285,7 @@ xmlEndDocument(XMLReader *reader)
 
 // Continue walking both XML data and infoset in lockstep
 
-static const char *
+static const Error *
 xmlStartComplex(XMLReader *reader, const InfosetBase *base)
 {
     // Consume any newlines or whitespace before the element
@@ -284,30 +302,31 @@ xmlStartComplex(XMLReader *reader, const InfosetBase 
*base)
     // Check whether we are walking both XML data and infoset in lockstep
     if (name_from_xml && name_from_erd)
     {
-        return strcmp(name_from_xml, name_from_erd) == 0
-                   ? NULL
-                   : "Found mismatch between XML data and infoset";
+        static Error error = {ERR_XML_MISMATCH, {NULL}};
+        error.s = name_from_erd;
+        return strcmp(name_from_xml, name_from_erd) == 0 ? NULL : &error;
     }
     else
     {
-        return "Ran out of XML data";
+        static Error error = {ERR_XML_GONE, {NULL}};
+        return &error;
     }
 }
 
 // Consume XML data only on start events, not end events
 
-static const char *
+static const Error *
 xmlEndComplex(XMLReader *reader, const InfosetBase *base)
 {
     UNUSED(reader); // because nothing to read
-    UNUSED(base); // because nothing to check
+    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 char *
+static const Error *
 xmlNumberElem(XMLReader *reader, const ERD *erd, void *number)
 {
     // Consume any newlines or whitespace before the element
@@ -327,70 +346,75 @@ xmlNumberElem(XMLReader *reader, const ERD *erd, void 
*number)
     {
         if (strcmp(name_from_xml, name_from_erd) == 0)
         {
+            static Error error_erd = {ERR_XML_ERD, {NULL}};
+
             // Check for any errors getting the number
-            const char *errstr = NULL;
+            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, &errstr);
+                *(bool *)number = strtobool(number_from_xml, &error);
                 break;
             case PRIMITIVE_FLOAT:
-                *(float *)number = strtofnum(number_from_xml, &errstr);
+                *(float *)number = strtofnum(number_from_xml, &error);
                 break;
             case PRIMITIVE_DOUBLE:
-                *(double *)number = strtodnum(number_from_xml, &errstr);
+                *(double *)number = strtodnum(number_from_xml, &error);
                 break;
             case PRIMITIVE_INT16:
                 *(int16_t *)number = (int16_t)strtonum(
-                    number_from_xml, INT16_MIN, INT16_MAX, &errstr);
+                    number_from_xml, INT16_MIN, INT16_MAX, &error);
                 break;
             case PRIMITIVE_INT32:
                 *(int32_t *)number = (int32_t)strtonum(
-                    number_from_xml, INT32_MIN, INT32_MAX, &errstr);
+                    number_from_xml, INT32_MIN, INT32_MAX, &error);
                 break;
             case PRIMITIVE_INT64:
                 *(int64_t *)number = (int64_t)strtonum(
-                    number_from_xml, INT64_MIN, INT64_MAX, &errstr);
+                    number_from_xml, INT64_MIN, INT64_MAX, &error);
                 break;
             case PRIMITIVE_INT8:
                 *(int8_t *)number = (int8_t)strtonum(number_from_xml, INT8_MIN,
-                                                     INT8_MAX, &errstr);
+                                                     INT8_MAX, &error);
                 break;
             case PRIMITIVE_UINT16:
                 *(uint16_t *)number =
-                    (uint16_t)strtounum(number_from_xml, UINT16_MAX, &errstr);
+                    (uint16_t)strtounum(number_from_xml, UINT16_MAX, &error);
                 break;
             case PRIMITIVE_UINT32:
                 *(uint32_t *)number =
-                    (uint32_t)strtounum(number_from_xml, UINT32_MAX, &errstr);
+                    (uint32_t)strtounum(number_from_xml, UINT32_MAX, &error);
                 break;
             case PRIMITIVE_UINT64:
                 *(uint64_t *)number =
-                    (uint64_t)strtounum(number_from_xml, UINT64_MAX, &errstr);
+                    (uint64_t)strtounum(number_from_xml, UINT64_MAX, &error);
                 break;
             case PRIMITIVE_UINT8:
                 *(uint8_t *)number =
-                    (uint8_t)strtounum(number_from_xml, UINT8_MAX, &errstr);
+                    (uint8_t)strtounum(number_from_xml, UINT8_MAX, &error);
                 break;
             default:
-                errstr = "Unexpected ERD typeCode while reading number from "
-                         "XML data";
+                error_erd.d64 = typeCode;
+                error = &error_erd;
                 break;
             }
 
-            return errstr;
+            return error;
         }
         else
         {
-            return "Found mismatch between XML data and infoset";
+            static Error error = {ERR_XML_MISMATCH, {NULL}};
+            error.s = name_from_erd;
+            return &error;
         }
     }
     else
     {
-        return "Ran out of XML data";
+        static Error error = {ERR_XML_GONE, {NULL}};
+        return &error;
     }
 }
 
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h 
b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
index 57ce1bd..952d39e 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h
@@ -18,9 +18,9 @@
 #ifndef XML_READER_H
 #define XML_READER_H
 
-#include "infoset.h"  // for VisitEventHandler, InfosetBase
 #include <mxml.h>     // for mxml_node_t
 #include <stdio.h>    // for FILE
+#include "infoset.h"  // for VisitEventHandler, InfosetBase
 
 // XMLReader - infoset visitor with methods to read XML
 
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c 
b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c
index 4f609ce..11d36ba 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c
@@ -16,83 +16,93 @@
  */
 
 #include "xml_writer.h"
-#include "stack.h"    // for stack_is_empty, stack_pop, stack_push, stack_top, 
stack_init, stack_is_full
 #include <assert.h>   // for assert
-#include <mxml.h>     // for mxmlNewOpaquef, mxml_node_t, mxmlElementSetAttr, 
mxmlNewElement, mxmlDelete, mxmlNewXML, mxmlSaveFile, MXML_NO_CALLBACK
+#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, fflush
+#include <stdio.h>    // for NULL
 #include <string.h>   // for strcmp
+#include "errors.h"   // for Error, ERR_XML_DECL, ERR_XML_ELEMENT, 
ERR_XML_WRITE, Error::(anonymous)
+#include "stack.h"    // for stack_is_empty, stack_pop, stack_push, stack_top, 
stack_init
 
-// Push new XML document on stack.  This function is not
-// thread-safe since it uses static storage.
+// 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 char *
+static const Error *
 xmlStartDocument(XMLWriter *writer)
 {
-#define MAX_DEPTH 100
+    enum
+    {
+        MAX_DEPTH = 100
+    };
     static mxml_node_t *array[MAX_DEPTH];
     stack_init(&writer->stack, array, MAX_DEPTH);
 
     mxml_node_t *xml = mxmlNewXML("1.0");
-    stack_push(&writer->stack, xml);
-    return xml ? NULL : "Error making new XML declaration";
+    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
+// Pop completed XML document off stack and write it to stream (note
+// stack underflow will stop program)
 
-static const char *
+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)
     {
-        return "Error writing XML document";
+        static Error error = {ERR_XML_WRITE, {NULL}};
+        return &error;
     }
-    status = fflush(writer->stream);
     mxmlDelete(xml);
-    return status == 0 ? NULL : "Error flushing stream";
+    return NULL;
 }
 
-// Push new complex element on stack
+// Push new complex element on stack (note stack overflow will stop
+// program)
 
-static const char *
+static const Error *
 xmlStartComplex(XMLWriter *writer, const InfosetBase *base)
 {
-    mxml_node_t *complex = NULL;
-    if (!stack_is_full(&writer->stack))
+    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)
     {
-        mxml_node_t *parent = stack_top(&writer->stack);
-        const char * name = get_erd_name(base->erd);
-        const char * xmlns = get_erd_xmlns(base->erd);
-        complex = mxmlNewElement(parent, name);
-        if (xmlns)
-        {
-            const char *ns = get_erd_ns(base->erd);
-            mxmlElementSetAttr(complex, xmlns, ns);
-        }
-        stack_push(&writer->stack, complex);
+        const char *ns = get_erd_ns(base->erd);
+        mxmlElementSetAttr(complex, xmlns, ns);
     }
-    return complex ? NULL : "Error making new complex element";
+    stack_push(&writer->stack, complex);
+    return NULL;
 }
 
-// Pop completed complex element off stack
+// Pop completed complex element off stack (note stack underflow will
+// stop program)
 
-static const char *
+static const Error *
 xmlEndComplex(XMLWriter *writer, const InfosetBase *base)
 {
-    mxml_node_t *complex = NULL;
-    if (!stack_is_empty(&writer->stack))
-    {
-        complex = stack_pop(&writer->stack);
+    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 complex ? NULL : "Underflowed the XML 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
@@ -107,14 +117,15 @@ fixNumberIfNeeded(const 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> (not worth it)
-    //  - Add .0 to 1 to get 1.0 (not worth it)
+    //  - 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 char *
+static const Error *
 xmlNumberElem(XMLWriter *writer, const ERD *erd, const void *number)
 {
     mxml_node_t *parent = stack_top(&writer->stack);
@@ -177,8 +188,16 @@ xmlNumberElem(XMLWriter *writer, const ERD *erd, const 
void *number)
         break;
     }
 
-    return (simple && text) ? NULL
-                            : "Error making new simple numerical element";
+    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
diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h 
b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
index c49e2f0..00fe45a 100644
--- a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
+++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h
@@ -18,9 +18,9 @@
 #ifndef XML_WRITER_H
 #define XML_WRITER_H
 
+#include <stdio.h>    // for FILE
 #include "infoset.h"  // for VisitEventHandler
 #include "stack.h"    // for stack_t
-#include <stdio.h>    // for FILE
 
 // XMLWriter - infoset visitor with methods to output XML
 
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..d624b0a
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <error.h>     // for error
+#include <inttypes.h>  // for PRId64
+#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:
+        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;
+    }
+}
+
+// need_diagnostics - return pointer to validation diagnostics
+
+Diagnostics *
+need_diagnostics(void)
+{
+    static Diagnostics validati;
+    return &validati;
+}
+
+// print_diagnostics - print any validation diagnostics
+
+void
+print_diagnostics(const Diagnostics *validati)
+{
+    if (validati)
+    {
+        for (size_t i = 0; i < validati->length; i++)
+        {
+            const Error *error = &validati->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..da55fb4
--- /dev/null
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.h
@@ -0,0 +1,117 @@
+/*
+ * 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 <stdio.h>    // for FILE, size_t
+#include <stdint.h>   // for int64_t
+
+// 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[100];
+    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 *validati; // 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 *validati; // any validation diagnostics
+    const Error *error;    // any error which stops program
+} UState;
+
+// need_diagnostics - return pointer to validation diagnostics
+
+extern Diagnostics *need_diagnostics(void);
+
+// print_diagnostics - print any validation diagnostics
+
+extern void print_diagnostics(const Diagnostics *validati);
+
+// 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
index 4f102f1..8c52eb2 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
@@ -17,6 +17,7 @@
 
 #include "infoset.h"
 #include <string.h>  // for memccpy
+#include "errors.h"  // for Error, ERR_WALK_KEY
 
 // get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns
 // attribute/value from ERD to use on XML element
@@ -113,15 +114,15 @@ get_erd_ns(const ERD *erd)
 // walkInfosetNode - recursively walk an infoset node and call
 // VisitEventHandler methods
 
-static const char *
+static const Error *
 walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode)
 {
-    const char *error_msg = NULL;
+    const Error *error = NULL;
 
     // Start visiting the node
-    if (!error_msg)
+    if (!error)
     {
-        error_msg = handler->visitStartComplex(handler, infoNode);
+        error = handler->visitStartComplex(handler, infoNode);
     }
 
     // Walk the node's children recursively
@@ -129,7 +130,7 @@ walkInfosetNode(const VisitEventHandler *handler, const 
InfosetBase *infoNode)
     const ERD **const childrenERDs = infoNode->erd->childrenERDs;
     const size_t *    offsets = infoNode->erd->offsets;
 
-    for (size_t i = 0; i < count && !error_msg; i++)
+    for (size_t i = 0; i < count && !error; i++)
     {
         const size_t offset = offsets[i];
         const ERD *  childERD = childrenERDs[i];
@@ -144,14 +145,10 @@ walkInfosetNode(const VisitEventHandler *handler, const 
InfosetBase *infoNode)
         {
         case CHOICE:
             // Point next ERD to choice of alternative elements' ERDs
-            if (!infoNode->erd->initChoice(infoNode, rootElement()))
-            {
-                error_msg = "Walk error: no match between choice dispatch key "
-                            "and any branch key";
-            }
+            error = infoNode->erd->initChoice(infoNode, rootElement());
             break;
         case COMPLEX:
-            error_msg = walkInfosetNode(handler, childNode);
+            error = walkInfosetNode(handler, childNode);
             break;
         case PRIMITIVE_BOOLEAN:
         case PRIMITIVE_DOUBLE:
@@ -164,58 +161,39 @@ walkInfosetNode(const VisitEventHandler *handler, const 
InfosetBase *infoNode)
         case PRIMITIVE_UINT32:
         case PRIMITIVE_UINT64:
         case PRIMITIVE_UINT8:
-            error_msg = handler->visitNumberElem(handler, childERD, number);
+            error = handler->visitNumberElem(handler, childERD, number);
             break;
         }
     }
 
     // End visiting the node
-    if (!error_msg)
+    if (!error)
     {
-        error_msg = handler->visitEndComplex(handler, infoNode);
+        error = handler->visitEndComplex(handler, infoNode);
     }
 
-    return error_msg;
+    return error;
 }
 
 // walkInfoset - walk an infoset and call VisitEventHandler methods
 
-const char *
+const Error *
 walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoset)
 {
-    const char *error_msg = NULL;
+    const Error *error = NULL;
 
-    if (!error_msg)
+    if (!error)
     {
-        error_msg = handler->visitStartDocument(handler);
+        error = handler->visitStartDocument(handler);
     }
-    if (!error_msg)
+    if (!error)
     {
-        error_msg = walkInfosetNode(handler, infoset);
+        error = walkInfosetNode(handler, infoset);
     }
-    if (!error_msg)
+    if (!error)
     {
-        error_msg = handler->visitEndDocument(handler);
+        error = handler->visitEndDocument(handler);
     }
 
-    return error_msg;
-}
-
-// eof_or_error_msg - check if a stream has its eof or error indicator set
-
-const char *
-eof_or_error_msg(FILE *stream)
-{
-    if (feof(stream))
-    {
-        return "Found eof indicator in stream, stopping now";
-    }
-    else if (ferror(stream))
-    {
-        return "Found error indicator in stream, stopping now";
-    }
-    else
-    {
-        return NULL;
-    }
+    return error;
 }
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h 
b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
index bc4315e..77bc57f 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
@@ -18,32 +18,29 @@
 #ifndef INFOSET_H
 #define INFOSET_H
 
-#include <stdbool.h>  // for bool
-#include <stddef.h>   // for size_t
-#include <stdio.h>    // for FILE
+#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 PState             PState;
-typedef struct UState             UState;
 typedef struct VisitEventHandler  VisitEventHandler;
 
 typedef void (*ERDInitSelf)(InfosetBase *infoNode);
 typedef void (*ERDParseSelf)(InfosetBase *infoNode, PState *pstate);
 typedef void (*ERDUnparseSelf)(const InfosetBase *infoNode, UState *ustate);
-typedef bool (*InitChoiceRD)(const InfosetBase *infoNode,
-                             const InfosetBase *rootElement);
-
-typedef const char *(*VisitStartDocument)(const VisitEventHandler *handler);
-typedef const char *(*VisitEndDocument)(const VisitEventHandler *handler);
-typedef const char *(*VisitStartComplex)(const VisitEventHandler *handler,
-                                         const InfosetBase *      base);
-typedef const char *(*VisitEndComplex)(const VisitEventHandler *handler,
-                                       const InfosetBase *      base);
-typedef const char *(*VisitNumberElem)(const VisitEventHandler *handler,
-                                       const ERD *erd, const void *number);
+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
 
@@ -54,7 +51,7 @@ typedef struct NamedQName
     const char *ns;     // namespace URI (optional, may be NULL)
 } NamedQName;
 
-// TypeCode - type of an infoset element
+// TypeCode - types of infoset elements
 
 enum TypeCode
 {
@@ -96,24 +93,6 @@ typedef struct InfosetBase
     const ERD *erd;
 } InfosetBase;
 
-// PState - mutable state while parsing data
-
-typedef struct PState
-{
-    FILE *      stream;    // input to read data from
-    size_t      position;  // 0-based position in stream
-    const char *error_msg; // to stop if an error happens
-} 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
-    const char *error_msg; // to stop if an error happens
-} UState;
-
 // VisitEventHandler - methods to be called when walking an infoset
 
 typedef struct VisitEventHandler
@@ -140,19 +119,7 @@ extern InfosetBase *rootElement(void);
 
 // walkInfoset - walk an infoset and call VisitEventHandler methods
 
-extern const char *walkInfoset(const VisitEventHandler *handler,
-                               const InfosetBase *      infoset);
-
-// eof_or_error_msg - check if a stream has its eof or error indicator set
-
-extern const char *eof_or_error_msg(FILE *stream);
-
-// NO_CHOICE - define value stored in uninitialized _choice field
-
-static const size_t NO_CHOICE = (size_t)-1;
-
-// UNUSED - suppress compiler warning about unused variable
-
-#define UNUSED(x) (void)(x)
+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
index db3a783..c9040da 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c
@@ -16,14 +16,14 @@
  */
 
 #include "parsers.h"
-#include <endian.h>   // for be32toh, be64toh, le32toh, le64toh, be16toh, 
le16toh
-#include <stdbool.h>  // for bool
-#include <stdio.h>    // for fread, size_t
+#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), Diagnostics, need_diagnostics, ERR_FIXED_VALUE
 
-// Macros that are not defined by <endian.h>
+// 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,
@@ -34,7 +34,8 @@
     pstate->position += count;                                                 
\
     if (count < sizeof(buffer))                                                
\
     {                                                                          
\
-        pstate->error_msg = eof_or_error_msg(pstate->stream);                  
\
+        pstate->error = eof_or_error(pstate->stream);                          
\
+        if (pstate->error) return;                                             
\
     }
 
 // Macros to define parse_<endian>_<type> functions
@@ -43,70 +44,63 @@
     void parse_##endian##_bool##bits(bool *number, int64_t true_rep,           
\
                                      uint32_t false_rep, PState *pstate)       
\
     {                                                                          
\
-        if (!pstate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(uint##bits##_t)];                  
\
-                uint##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            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                                                               
\
-            {                                                                  
\
-                pstate->error_msg = "Unable to parse boolean";                 
\
-            }                                                                  
\
+        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)                 
\
     {                                                                          
\
-        if (!pstate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(type)];                            
\
-                type           f_val;                                          
\
-                uint##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            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;                                            
\
-        }                                                                      
\
+        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) 
\
     {                                                                          
\
-        if (!pstate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(type##bits##_t)];                  
\
-                type##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            char           c_val[sizeof(type##bits##_t)];                      
\
+            type##bits##_t i_val;                                              
\
+        } buffer;                                                              
\
                                                                                
\
-            read_stream_update_position;                                       
\
-            *number = endian##bits##toh(buffer.i_val);                         
\
-        }                                                                      
\
+        read_stream_update_position;                                           
\
+        *number = endian##bits##toh(buffer.i_val);                             
\
     }
 
-// Define functions to parse binary real numbers and integers
+// Parse binary booleans, real numbers, and integers
 
 define_parse_endian_bool(be, 16);
 define_parse_endian_bool(be, 32);
@@ -142,36 +136,38 @@ define_parse_endian_integer(le, uint, 32)
 define_parse_endian_integer(le, uint, 64)
 define_parse_endian_integer(le, uint, 8)
 
-// Define function to parse fill bytes until end position is reached
+// Parse fill bytes until end position is reached
 
 void
 parse_fill_bytes(size_t end_position, PState *pstate)
 {
-    while (!pstate->error_msg && pstate->position < end_position)
+    union
     {
-        char   buffer;
-        size_t count = fread(&buffer, 1, sizeof(buffer), pstate->stream);
+        char c_val[1];
+    } buffer;
 
-        pstate->position += count;
-        if (count < sizeof(buffer))
-        {
-            pstate->error_msg = eof_or_error_msg(pstate->stream);
-        }
+    while (pstate->position < end_position)
+    {
+        read_stream_update_position;
     }
 }
 
-// Define function to validate number is same as fixed value after parse
+// Validate parsed number is same as fixed value
 
 void
 parse_validate_fixed(bool same, const char *element, PState *pstate)
 {
-    UNUSED(element); // because managing strings hard in embedded C
-    if (!pstate->error_msg && !same)
+    if (!same)
     {
-        // Error message would be easier to assemble and
-        // internationalize if we used an error struct with multiple
-        // fields instead of a const char string.
-        pstate->error_msg = "Parse: Value of element does not match value of "
-                            "its 'fixed' attribute";
+        Diagnostics *validati = need_diagnostics();
+        pstate->validati = validati;
+
+        if (validati->length <
+            sizeof(validati->array) / sizeof(*validati->array))
+        {
+            Error *error = &validati->array[validati->length++];
+            error->code = ERR_FIXED_VALUE;
+            error->s = element;
+        }
     }
 }
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h 
b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h
index 42b8d9a..c005d94 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h
@@ -18,11 +18,12 @@
 #ifndef PARSERS_H
 #define PARSERS_H
 
-#include "infoset.h"  // for PState
 #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 <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
 
-// Functions to parse binary booleans, real numbers, and integers
+// Parse binary booleans, real numbers, and integers
 
 extern void parse_be_bool16(bool *number, int64_t true_rep, uint32_t false_rep,
                             PState *pstate);
@@ -64,11 +65,11 @@ 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);
 
-// Function to parse fill bytes until end position is reached
+// Parse fill bytes until end position is reached
 
 extern void parse_fill_bytes(size_t end_position, PState *pstate);
 
-// Function to validate number is same as fixed value after parse
+// Validate parsed number is same as fixed value
 
 extern void parse_validate_fixed(bool same, const char *element,
                                  PState *pstate);
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c 
b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c
index 90bdd17..de65700 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c
@@ -16,14 +16,14 @@
  */
 
 #include "unparsers.h"
-#include <endian.h>   // for htobe32, htobe64, htole32, htole64, htobe16, 
htole16
+#include <endian.h>   // for htobe32, htole32, htobe16, htobe64, htole16, 
htole64
 #include <stdbool.h>  // for bool
-#include <stdio.h>    // for fwrite, size_t
+#include <stdio.h>    // for fwrite
+#include "errors.h"   // for UState, eof_or_error, Diagnostics, Error, 
need_diagnostics, ERR_FIXED_VALUE, Error::(anonymous)
 
-// Macros that are not defined by <endian.h>
+// 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,
@@ -34,7 +34,8 @@
     ustate->position += count;                                                 
\
     if (count < sizeof(buffer))                                                
\
     {                                                                          
\
-        ustate->error_msg = eof_or_error_msg(ustate->stream);                  
\
+        ustate->error = eof_or_error(ustate->stream);                          
\
+        if (ustate->error) return;                                             
\
     }
 
 // Macros to define unparse_<endian>_<type> functions
@@ -43,55 +44,46 @@
     void unparse_##endian##_bool##bits(bool number, uint32_t true_rep,         
\
                                        uint32_t false_rep, UState *ustate)     
\
     {                                                                          
\
-        if (!ustate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(uint##bits##_t)];                  
\
-                uint##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            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;                                      
\
-        }                                                                      
\
+        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)                
\
     {                                                                          
\
-        if (!ustate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(type)];                            
\
-                type           f_val;                                          
\
-                uint##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            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;                                      
\
-        }                                                                      
\
+        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)                
\
     {                                                                          
\
-        if (!ustate->error_msg)                                                
\
+        union                                                                  
\
         {                                                                      
\
-            union                                                              
\
-            {                                                                  
\
-                char           c_val[sizeof(type##bits##_t)];                  
\
-                type##bits##_t i_val;                                          
\
-            } buffer;                                                          
\
+            char           c_val[sizeof(type##bits##_t)];                      
\
+            type##bits##_t i_val;                                              
\
+        } buffer;                                                              
\
                                                                                
\
-            buffer.i_val = hto##endian##bits(number);                          
\
-            write_stream_update_position;                                      
\
-        }                                                                      
\
+        buffer.i_val = hto##endian##bits(number);                              
\
+        write_stream_update_position;                                          
\
     }
 
-// Define functions to unparse binary real numbers and integers
+// Unparse binary booleans, real numbers, and integers
 
 define_unparse_endian_bool(be, 16);
 define_unparse_endian_bool(be, 32);
@@ -127,35 +119,41 @@ define_unparse_endian_integer(le, uint, 32)
 define_unparse_endian_integer(le, uint, 64)
 define_unparse_endian_integer(le, uint, 8)
 
-// Define function to unparse fill bytes until end position is reached
+// Unparse fill bytes until end position is reached
 
 void
-unparse_fill_bytes(size_t end_position, const char fill_byte, UState *ustate)
+unparse_fill_bytes(size_t end_position, const char fill_byte,
+                   UState *ustate)
 {
-    while (!ustate->error_msg && ustate->position < end_position)
+    union
     {
-        size_t count = fwrite(&fill_byte, 1, sizeof(fill_byte), 
ustate->stream);
+        char c_val[1];
+    } buffer;
 
-        ustate->position += count;
-        if (count < sizeof(fill_byte))
-        {
-            ustate->error_msg = eof_or_error_msg(ustate->stream);
-        }
+    buffer.c_val[0] = fill_byte;
+
+    while (ustate->position < end_position)
+    {
+        write_stream_update_position;
     }
 }
 
-// Define function to validate number is same as fixed value during unparse
+// Validate unparsed number is same as fixed value
 
 void
 unparse_validate_fixed(bool same, const char *element, UState *ustate)
 {
-    if (!ustate->error_msg && !same)
+    if (!same)
     {
-        // Error message would be easier to assemble and
-        // internationalize if we used an error struct with multiple
-        // fields instead of a const char string.
-        ustate->error_msg = "Unparse: Value of element does not match value of 
"
-                            "its 'fixed' attribute";
-        UNUSED(element); // unused because managing strings hard in embedded C
+        Diagnostics *validati = need_diagnostics();
+        ustate->validati = validati;
+
+        if (validati->length <
+            sizeof(validati->array) / sizeof(*validati->array))
+        {
+            Error *error = &validati->array[validati->length++];
+            error->code = ERR_FIXED_VALUE;
+            error->s = element;
+        }
     }
 }
diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h 
b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h
index b676f7a..4b5f5ab 100644
--- a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h
+++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h
@@ -18,11 +18,12 @@
 #ifndef UNPARSERS_H
 #define UNPARSERS_H
 
-#include "infoset.h"  // for UState
 #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 <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
 
-// Functions to unparse binary booleans, real numbers, and integers
+// Unparse binary booleans, real numbers, and integers
 
 extern void unparse_be_bool16(bool number, uint32_t true_rep,
                               uint32_t false_rep, UState *ustate);
@@ -64,12 +65,12 @@ 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);
 
-// Function to unparse fill bytes until end position is reached
+// Unparse fill bytes until end position is reached
 
 extern void unparse_fill_bytes(size_t end_position, const char fill_byte,
                                UState *ustate);
 
-// Function to validate number is same as fixed value during unparse
+// Validate unparsed number is same as fixed value
 
 extern void unparse_validate_fixed(bool same, const char *element,
                                    UState *ustate);
diff --git a/daffodil-runtime2/src/main/resources/examples/NestedUnion.c 
b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c
index da4a0a4..f5f1f89 100644
--- a/daffodil-runtime2/src/main/resources/examples/NestedUnion.c
+++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c
@@ -16,13 +16,18 @@
  */
 
 #include "NestedUnion.h"
-#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
-#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
 #include <math.h>       // for NAN
-#include <stdbool.h>    // for bool, false, true
+#include <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 [...]
 
-// Prototypes needed for compilation
+// 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);
@@ -31,14 +36,14 @@ static void bar_initSelf(bar *instance);
 static void bar_parseSelf(bar *instance, PState *pstate);
 static void bar_unparseSelf(const bar *instance, UState *ustate);
 static void data_initSelf(data *instance);
-static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static 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);
 
-// Metadata singletons
+// Define metadata for the infoset
 
 static const ERD tag_NestedUnionType_ERD = {
     {
@@ -238,7 +243,7 @@ static const ERD NestedUnion_ERD = {
     NULL // initChoice
 };
 
-// Return a root element to be used for parsing or unparsing
+// Return a root element for parsing or unparsing the infoset
 
 InfosetBase *
 rootElement(void)
@@ -253,7 +258,7 @@ rootElement(void)
     return &root._base;
 }
 
-// Methods to initialize, parse, and unparse infoset nodes
+// Initialize, parse, and unparse nodes of the infoset
 
 static void
 foo_initSelf(foo *instance)
@@ -268,16 +273,22 @@ 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
@@ -293,30 +304,38 @@ 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 = NO_CHOICE;
+    instance->_choice = 0xFFFFFFFFFFFFFFFF;
     foo_initSelf(&instance->foo);
     bar_initSelf(&instance->bar);
 }
 
-static bool
+static const Error *
 data_initChoice(data *instance, const NestedUnion *rootElement)
 {
+    static Error error = {ERR_CHOICE_KEY, {NULL}};
+
     int64_t key = rootElement->tag;
     switch (key)
     {
@@ -329,60 +348,69 @@ data_initChoice(data *instance, const NestedUnion 
*rootElement)
         instance->_choice = 1;
         break;
     default:
-        instance->_choice = NO_CHOICE;
-        break;
+        error.d64 = key;
+        return &error;
     }
 
-    if (instance->_choice != NO_CHOICE)
-    {
-        const size_t choice = instance->_choice + 1; // skip the _choice field
-        const size_t offset = instance->_base.erd->offsets[choice];
-        const ERD *  childERD = instance->_base.erd->childrenERDs[choice];
-        InfosetBase *childNode = (InfosetBase *)((const char *)instance + 
offset);
-        childNode->erd = childERD;
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    // 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)
 {
-    instance->_base.erd->initChoice(&instance->_base, rootElement());
+    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:
-        pstate->error_msg =
-            "Parse error: no match between choice dispatch key and any branch 
key";
-        break;
+        // 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)
 {
-    instance->_base.erd->initChoice(&instance->_base, rootElement());
+    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:
-        ustate->error_msg =
-            "Unparse error: no match between choice dispatch key and any 
branch key";
-        break;
+        // Should never happen because initChoice would return an error first
+        error.d64 = (int64_t)instance->_choice;
+        ustate->error = &error;
+        return;
     }
 }
 
@@ -398,13 +426,17 @@ 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
index 620e1b5..b729693 100644
--- a/daffodil-runtime2/src/main/resources/examples/NestedUnion.h
+++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.h
@@ -18,9 +18,10 @@
 #ifndef GENERATED_CODE_H
 #define GENERATED_CODE_H
 
-#include "infoset.h"  // for InfosetBase
 #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 <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
 
diff --git a/daffodil-runtime2/src/main/resources/examples/ex_nums.c 
b/daffodil-runtime2/src/main/resources/examples/ex_nums.c
index 88b7247..b64e7f6 100644
--- a/daffodil-runtime2/src/main/resources/examples/ex_nums.c
+++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.c
@@ -16,13 +16,18 @@
  */
 
 #include "ex_nums.h"
-#include "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
-#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
 #include <math.h>       // for NAN
-#include <stdbool.h>    // for bool, false, true
+#include <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 [...]
 
-// Prototypes needed for compilation
+// 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);
@@ -40,7 +45,7 @@ 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);
 
-// Metadata singletons
+// Define metadata for the infoset
 
 static const ERD be_bool16_array_ex_nums_ERD = {
     {
@@ -648,7 +653,7 @@ static const ERD ex_nums_ERD = {
     NULL // initChoice
 };
 
-// Return a root element to be used for parsing or unparsing
+// Return a root element for parsing or unparsing the infoset
 
 InfosetBase *
 rootElement(void)
@@ -663,7 +668,7 @@ rootElement(void)
     return &root._base;
 }
 
-// Methods to initialize, parse, and unparse infoset nodes
+// Initialize, parse, and unparse nodes of the infoset
 
 static void
 array_initSelf(array *instance)
@@ -683,26 +688,42 @@ 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
@@ -731,42 +752,74 @@ 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
@@ -795,42 +848,74 @@ 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
@@ -847,26 +932,42 @@ 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
@@ -883,17 +984,25 @@ 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
index ae33283..937c93c 100644
--- a/daffodil-runtime2/src/main/resources/examples/ex_nums.h
+++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.h
@@ -18,9 +18,10 @@
 #ifndef GENERATED_CODE_H
 #define GENERATED_CODE_H
 
-#include "infoset.h"  // for InfosetBase
 #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 <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
 
diff --git 
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
 
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala
similarity index 58%
copy from 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
copy to 
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala
index 332ba66..f5aa79b 100644
--- 
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
+++ 
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala
@@ -18,41 +18,46 @@
 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 BinaryFloatCodeGenerator {
+trait BinaryAbstractCodeGenerator {
+
+  def binaryAbstractGenerateCode(e: ElementBase, initialValue: String, prim: 
String,
+    parseArgs: String, unparseArgs: String, cgState: CodeGeneratorState): Unit 
= {
 
-  def binaryFloatGenerateCode(e: ElementBase, lengthInBits: Int, cgState: 
CodeGeneratorState): Unit = {
     // For the time being this is a very limited back end.
     // So there are some restrictions to enforce.
-    assert(lengthInBits == 32 || lengthInBits == 64)
-    val byteOrder: ByteOrder = {
-      e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime 
dfdl:byteOrder expressions not supported.")
-      val bo = e.byteOrderEv.constValue
-      bo
-    }
+    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.")
 
-    // Use a NAN to mark our field as uninitialized in case parsing or 
unparsing
-    // fails to set the field.
-    val initialValue = "NAN"
     val fieldName = e.namedQName.local
+    val byteOrder = e.byteOrderEv.constValue
     val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le"
-    val prim = if (lengthInBits == 32) "float" else "double"
     val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) 
e.maxOccurs else 0
     val fixed = e.xml.attribute("fixed")
     val fixedValue = if (fixed.isDefined) fixed.get.text else ""
 
     def addStatements(deref: String): Unit = {
       val initStatement = s"    instance->$fieldName$deref = $initialValue;"
-      val parseStatement = s"    
parse_${conv}_$prim(&instance->$fieldName$deref, pstate);"
-      val unparseStatement = s"    
unparse_${conv}_$prim(instance->$fieldName$deref, ustate);"
+      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 (fixedValue.nonEmpty) {
         val init2 = ""
-        val parse2 = s"""    parse_validate_fixed(instance->$fieldName$deref 
== $fixedValue, "$fieldName", pstate);"""
-        val unparse2 = s"""    
unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", 
ustate);"""
+        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)
       }
     }
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
index e5a7d3a..32c4bbe 100644
--- 
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
@@ -19,60 +19,27 @@ package org.apache.daffodil.runtime2.generators
 
 import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.exceptions.Assert
-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
 import passera.unsigned.ULong
 
-trait BinaryBooleanCodeGenerator {
+trait BinaryBooleanCodeGenerator extends BinaryAbstractCodeGenerator {
 
   def binaryBooleanGenerateCode(e: ElementBase, 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.")
-    val byteOrder: ByteOrder = {
-      e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime 
dfdl:byteOrder expressions not supported.")
-      e.byteOrderEv.constValue
-    }
-    val lengthInBits: Long = {
-      e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime 
dfdl:length expressions not supported.")
-      val len = e.elementLengthInBitsEv.constValue.get
-      len match {
-        case 8 | 16 | 32 => len
-        case _ => e.SDE("Boolean lengths other than 8, 16, or 32 bits are not 
supported.")
-      }
-    }
     Assert.invariant(e.binaryBooleanTrueRep.isEmpty || 
e.binaryBooleanTrueRep.getULong >= ULong(0))
     Assert.invariant(e.binaryBooleanFalseRep >= ULong(0))
+    Assert.invariant(e.elementLengthInBitsEv.isConstant)
 
-    val initialValue = "true"
-    val fieldName = e.namedQName.local
-    val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le"
+    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 arraySize = if (e.occursCountKind == OccursCountKind.Fixed) 
e.maxOccurs else 0
-    val fixed = e.xml.attribute("fixed")
-    val fixedValue = if (fixed.isDefined) fixed.get.text else ""
+    val unparseArgs = s"$unparseTrueRep, $falseRep, ustate"
 
-    def addStatements(deref: String): Unit = {
-      val initStatement = s"    instance->$fieldName$deref = $initialValue;"
-      val parseStatement = s"    
parse_${conv}_$prim(&instance->$fieldName$deref, $trueRep, $falseRep, pstate);"
-      val unparseStatement = s"    
unparse_${conv}_$prim(instance->$fieldName$deref, $unparseTrueRep, $falseRep, 
ustate);"
-      cgState.addSimpleTypeStatements(initStatement, parseStatement, 
unparseStatement)
-
-      if (fixedValue.nonEmpty) {
-        val init2 = ""
-        val parse2 = s"""    parse_validate_fixed(instance->$fieldName$deref 
== $fixedValue, "$fieldName", pstate);"""
-        val unparse2 = s"""    
unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", 
ustate);"""
-        cgState.addSimpleTypeStatements(init2, parse2, unparse2)
-      }
-    }
-    if (arraySize > 0)
-      for (i <- 0 until arraySize)
-        addStatements(s"[$i]")
-    else
-      addStatements("")
+    binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, 
cgState)
   }
 }
diff --git 
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
 
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
index 332ba66..3d071b6 100644
--- 
a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
+++ 
b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala
@@ -18,48 +18,21 @@
 package org.apache.daffodil.runtime2.generators
 
 import org.apache.daffodil.dsom.ElementBase
-import org.apache.daffodil.schema.annotation.props.gen.ByteOrder
-import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
 
-trait BinaryFloatCodeGenerator {
+trait BinaryFloatCodeGenerator extends BinaryAbstractCodeGenerator {
 
   def binaryFloatGenerateCode(e: ElementBase, lengthInBits: Int, cgState: 
CodeGeneratorState): Unit = {
-    // For the time being this is a very limited back end.
-    // So there are some restrictions to enforce.
-    assert(lengthInBits == 32 || lengthInBits == 64)
-    val byteOrder: ByteOrder = {
-      e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime 
dfdl:byteOrder expressions not supported.")
-      val bo = e.byteOrderEv.constValue
-      bo
-    }
 
     // Use a NAN to mark our field as uninitialized in case parsing or 
unparsing
     // fails to set the field.
-    val initialValue = "NAN"
-    val fieldName = e.namedQName.local
-    val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le"
+    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 arraySize = if (e.occursCountKind == OccursCountKind.Fixed) 
e.maxOccurs else 0
-    val fixed = e.xml.attribute("fixed")
-    val fixedValue = if (fixed.isDefined) fixed.get.text else ""
+    val parseArgs = "pstate"
+    val unparseArgs = "ustate"
 
-    def addStatements(deref: String): Unit = {
-      val initStatement = s"    instance->$fieldName$deref = $initialValue;"
-      val parseStatement = s"    
parse_${conv}_$prim(&instance->$fieldName$deref, pstate);"
-      val unparseStatement = s"    
unparse_${conv}_$prim(instance->$fieldName$deref, ustate);"
-      cgState.addSimpleTypeStatements(initStatement, parseStatement, 
unparseStatement)
-
-      if (fixedValue.nonEmpty) {
-        val init2 = ""
-        val parse2 = s"""    parse_validate_fixed(instance->$fieldName$deref 
== $fixedValue, "$fieldName", pstate);"""
-        val unparse2 = s"""    
unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", 
ustate);"""
-        cgState.addSimpleTypeStatements(init2, parse2, unparse2)
-      }
-    }
-    if (arraySize > 0)
-      for (i <- 0 until arraySize)
-        addStatements(s"[$i]")
-    else
-      addStatements("")
+    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
index 9761b3b..0c39979 100644
--- 
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
@@ -18,60 +18,25 @@
 package org.apache.daffodil.runtime2.generators
 
 import org.apache.daffodil.grammar.primitives.BinaryIntegerKnownLength
-import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
-import org.apache.daffodil.schema.annotation.props.gen.{ BitOrder, ByteOrder }
 
-trait BinaryIntegerKnownLengthCodeGenerator {
+trait BinaryIntegerKnownLengthCodeGenerator extends 
BinaryAbstractCodeGenerator {
 
   def binaryIntegerKnownLengthGenerateCode(g: BinaryIntegerKnownLength, 
cgState: CodeGeneratorState): Unit = {
-    // For the time being this is a very limited back end.
-    // So there are some restrictions to enforce.
-    val e = g.e
-    val lengthInBits: Long = {
-      e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime 
dfdl:length expressions not supported.")
-      val len = e.elementLengthInBitsEv.constValue.get
-      len
-    }
-    e.schemaDefinitionUnless(e.bitOrder eq BitOrder.MostSignificantBitFirst, 
"Only dfdl:bitOrder 'mostSignificantBitFirst' is supported.")
-    val byteOrder: ByteOrder = {
-      e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime 
dfdl:byteOrder expressions not supported.")
-      val bo = e.byteOrderEv.constValue
-      bo
-    }
 
     // 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 initialValue = lengthInBits match {
+    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 fieldName = e.namedQName.local
-    val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le"
-    val prim = if (g.signed) s"int${lengthInBits}" else s"uint${lengthInBits}"
-    val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) 
e.maxOccurs else 0
-    val fixed = e.xml.attribute("fixed")
-    val fixedValue = if (fixed.isDefined) fixed.get.text else ""
-
-    def addStatements(deref: String): Unit = {
-      val initStatement = s"    instance->$fieldName$deref = $initialValue;"
-      val parseStatement = s"    
parse_${conv}_$prim(&instance->$fieldName$deref, pstate);"
-      val unparseStatement = s"    
unparse_${conv}_$prim(instance->$fieldName$deref, ustate);"
-      cgState.addSimpleTypeStatements(initStatement, parseStatement, 
unparseStatement)
+    val prim = if (g.signed) s"int${g.lengthInBits}" else 
s"uint${g.lengthInBits}"
+    val parseArgs = "pstate"
+    val unparseArgs = "ustate"
 
-      if (fixedValue.nonEmpty) {
-        val init2 = ""
-        val parse2 = s"""    parse_validate_fixed(instance->$fieldName$deref 
== $fixedValue, "$fieldName", pstate);"""
-        val unparse2 = s"""    
unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", 
ustate);"""
-        cgState.addSimpleTypeStatements(init2, parse2, unparse2)
-      }
-    }
-    if (arraySize > 0)
-      for (i <- 0 until arraySize)
-        addStatements(s"[$i]")
-    else
-      addStatements("")
+    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
index 0fd7922..577a542 100644
--- 
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
@@ -85,12 +85,12 @@ class CodeGeneratorState {
     val hasChoice = structs.top.initChoiceStatements.nonEmpty
     val root = structs.elems.last.C
     val prototypeInitChoice = if (hasChoice)
-      s"\nstatic bool ${C}_initChoice($C *instance, const $root *rootElement);"
+      s"\nstatic const Error *${C}_initChoice($C *instance, const $root 
*rootElement);"
     else
       ""
     val implementInitChoice = if (hasChoice)
       s"""
-         |static bool
+         |static const Error *
          |${C}_initChoice($C *instance, const $root *rootElement)
          |{
          |$initChoiceStatements
@@ -222,17 +222,27 @@ class CodeGeneratorState {
            |""".stripMargin
       val offsetComputation = s"    (const char 
*)&${C}_compute_offsets._choice - (const char *)&${C}_compute_offsets"
       val erdComputation = s"    &_choice_$erd"
-      val initStatement = s"    instance->_choice = NO_CHOICE;"
+      val initStatement = s"    instance->_choice = 0xFFFFFFFFFFFFFFFF;"
       val initChoiceStatement =
-        s"""    int64_t key = rootElement->$dispatchField;
+        s"""    static Error error = {ERR_CHOICE_KEY, {NULL}};
+           |
+           |    int64_t key = rootElement->$dispatchField;
            |    switch (key)
            |    {""".stripMargin
       val parseStatement =
-        s"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+        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"""    instance->_base.erd->initChoice(&instance->_base, 
rootElement());
+        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
 
@@ -254,34 +264,31 @@ class CodeGeneratorState {
       val declaration = s"    };"
       val initChoiceStatement =
         s"""    default:
-           |        instance->_choice = NO_CHOICE;
-           |        break;
+           |        error.d64 = key;
+           |        return &error;
            |    }
            |
-           |    if (instance->_choice != NO_CHOICE)
-           |    {
-           |        const size_t choice = instance->_choice + 1; // skip the 
_choice field
-           |        const size_t offset = instance->_base.erd->offsets[choice];
-           |        const ERD *  childERD = 
instance->_base.erd->childrenERDs[choice];
-           |        InfosetBase *childNode = (InfosetBase *)((const char 
*)instance + offset);
-           |        childNode->erd = childERD;
-           |        return true;
-           |    }
-           |    else
-           |    {
-           |        return false;
-           |    }""".stripMargin
+           |    // 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:
-           |        pstate->error_msg =
-           |            "Parse error: no match between choice dispatch key and 
any branch key";
-           |        break;
+           |        // 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:
-           |        ustate->error_msg =
-           |            "Unparse error: no match between choice dispatch key 
and any branch key";
-           |        break;
+           |        // 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
@@ -293,8 +300,12 @@ class CodeGeneratorState {
     // 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);"
-      val unparseStatement = s"    unparse_fill_bytes(end_position, 
'\\$octalFillByte', ustate);"
+      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
@@ -382,8 +393,12 @@ class CodeGeneratorState {
     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);"
-      val unparseStatement = s"    
$moreIndent${C}_unparseSelf(&instance->$e$deref, ustate);"
+      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
@@ -544,9 +559,10 @@ class CodeGeneratorState {
       s"""#ifndef GENERATED_CODE_H
          |#define GENERATED_CODE_H
          |
-         |#include "infoset.h"  // for InfosetBase
          |#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 <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
          |
@@ -557,25 +573,32 @@ class CodeGeneratorState {
   }
 
   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 "parsers.h"    // for parse_be_double, parse_be_float, 
parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, 
parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_double, 
parse_le_float, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, 
parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
-         |#include "unparsers.h"  // for unparse_be_double, unparse_be_float, 
unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, 
unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, 
unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, 
unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, 
unparse_le_uint64, unparse_le_uint8
          |#include <math.h>       // for NAN
-         |#include <stdbool.h>    // for bool, false, true
+         |#include <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";
          |
-         |// Prototypes needed for compilation
+         |// Declare prototypes for easier compilation
          |
          |$prototypes
          |
-         |// Metadata singletons
+         |// Define metadata for the infoset
          |
          |$erds
-         |// Return a root element to be used for parsing or unparsing
+         |// Return a root element for parsing or unparsing the infoset
          |
          |InfosetBase *
          |rootElement(void)
@@ -590,7 +613,7 @@ class CodeGeneratorState {
          |    return &root._base;
          |}
          |
-         |// Methods to initialize, parse, and unparse infoset nodes
+         |// Initialize, parse, and unparse nodes of the infoset
          |
          |$finalImplementation
          |""".stripMargin
diff --git a/project/build.properties b/project/build.properties
index 18f5ff8..0e52c79 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -15,4 +15,4 @@
  * limitations under the License.
  */
 
-sbt.version=1.4.1
+sbt.version=1.4.9

Reply via email to