You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@daffodil.apache.org by GitBox <gi...@apache.org> on 2018/07/20 13:54:34 UTC

[GitHub] mbeckerle closed pull request #74: Daffodil trailing sep

mbeckerle closed pull request #74: Daffodil trailing sep
URL: https://github.com/apache/incubator-daffodil/pull/74
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/AnnotatedSchemaComponent.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/AnnotatedSchemaComponent.scala
index fb5944c85..456aacea1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/AnnotatedSchemaComponent.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/AnnotatedSchemaComponent.scala
@@ -47,9 +47,6 @@ import org.apache.daffodil.api.WarnID
 trait ResolvesProperties
   extends FindPropertyMixin { self: AnnotatedSchemaComponent =>
 
-  /** Returns Term corresponding to this object. */
-  def term: Term
-
   private def findNonDefaultProperty(pname: String): PropertyLookupResult = {
     val result = findDefaultOrNonDefaultProperty(pname, nonDefaultPropertySources)
     result match {
@@ -81,12 +78,12 @@ trait ResolvesProperties
   override def findPropertyOption(pname: String): PropertyLookupResult = {
     ExecutionMode.requireCompilerMode
     // first try in regular properties
-    val regularResult = resolver.findNonDefaultProperty(pname)
+    val regularResult = findNonDefaultProperty(pname)
     regularResult match {
       case f: Found => f
       case NotFound(nonDefaultLocsTried1, defaultLocsTried1, _) => {
         Assert.invariant(defaultLocsTried1.isEmpty)
-        val defaultResult = resolver.findDefaultProperty(pname)
+        val defaultResult = findDefaultProperty(pname)
         defaultResult match {
           case f: Found => f
           case NotFound(nonDefaultLocsTried2, defaultLocsTried2, _) => {
@@ -122,34 +119,34 @@ trait AnnotatedSchemaComponent
   requiredEvaluations(nonDefaultPropertySources)
   requiredEvaluations(defaultPropertySources)
 
-  /** Returns the Term corresponding to this component. */
-  final lazy val term: Term = this match {
-    case gr: GroupRef => gr.asModelGroup
-    //    case mg: ModelGroup => mg.parent match {
-    //      case ggd: GlobalGroupDef => {
-    //        // this is the model group that is the definition of
-    //        // a global group, so our term is the group reference referring to this.
-    //        ggd.groupRef.asModelGroup
-    //      }
-    //      case _ => mg
-    //    }
-    case t: Term => t
-    case ged: GlobalElementDecl => ged.elementRef
-    case ty: SimpleTypeDefBase => ty.elementBase
-    case ty: ComplexTypeBase => ty.elementBase
-    case ggd: GlobalGroupDef => ggd.groupRef.asModelGroup
-    case sd: SchemaDocument =>
-      Assert.usageError("not to be called for schema documents")
-  }
+  //  /** Returns the Term corresponding to this component. */
+  //  final lazy val term: Term = this match {
+  //    case gr: GroupRef => gr.asModelGroup
+  //    //    case mg: ModelGroup => mg.parent match {
+  //    //      case ggd: GlobalGroupDef => {
+  //    //        // this is the model group that is the definition of
+  //    //        // a global group, so our term is the group reference referring to this.
+  //    //        ggd.groupRef.asModelGroup
+  //    //      }
+  //    //      case _ => mg
+  //    //    }
+  //    case t: Term => t
+  //    case ged: GlobalElementDecl => ged.elementRef
+  //    case ty: SimpleTypeDefBase => ty.elementBase
+  //    case ty: ComplexTypeBase => ty.elementBase
+  //    case ggd: GlobalGroupDef => ggd.groupRef.asModelGroup
+  //    case sd: SchemaDocument =>
+  //      Assert.usageError("not to be called for schema documents")
+  //  }
 
-  /** Returns the property resolver for this component. */
-  final lazy val resolver: ResolvesProperties = {
-    val res = this match {
-      case sd: SchemaDocument => sd
-      case _ => term
-    }
-    res
-  }
+  //  /** Returns the property resolver for this component. */
+  //  final lazy val resolver: ResolvesProperties = {
+  //    val res = this match {
+  //      case sd: SchemaDocument => sd
+  //      case _ => term
+  //    }
+  //    res
+  //  }
 
   /**
    * Since validation of extra attributes on XML Schema elements is
@@ -265,8 +262,7 @@ trait AnnotatedSchemaComponent
  * or made smaller anyway.
  *
  */
-trait AnnotatedMixin
-  extends EscapeSchemeRefMixin { self: AnnotatedSchemaComponent =>
+trait AnnotatedMixin { self: AnnotatedSchemaComponent =>
 
   /**
    * Anything annotated must be able to construct the
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
index 428ac9eb9..a06e3b8ac 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ChoiceGroup.scala
@@ -236,7 +236,9 @@ abstract class ChoiceTermBase( final override val xml: Node,
     noDupes
   }.value
 
-  final lazy val modelGroupRuntimeData = {
+  final lazy val modelGroupRuntimeData = choiceRuntimeData
+
+  final lazy val choiceRuntimeData = {
     new ChoiceRuntimeData(
       schemaSet.variableMap,
       encodingInfo,
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ComplexTypes.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ComplexTypes.scala
index a62e9b0a4..3f5bf5aaa 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ComplexTypes.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ComplexTypes.scala
@@ -18,15 +18,13 @@
 package org.apache.daffodil.dsom
 
 import scala.xml.Node
-import org.apache.daffodil.grammar.ComplexTypeBaseGrammarMixin
 import org.apache.daffodil.dpath.NodeInfo
 import org.apache.daffodil.xml.QName
 
 abstract class ComplexTypeBase(xmlArg: Node, parentArg: SchemaComponent)
   extends SchemaComponentImpl(xmlArg, parentArg)
   with TypeBase
-  with NonPrimTypeMixin
-  with ComplexTypeBaseGrammarMixin {
+  with NonPrimTypeMixin {
 
   final override def optRestriction = None
   final override def optUnion = None
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAnnotation.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAnnotation.scala
index abac9c9f5..74c2b44f5 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAnnotation.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAnnotation.scala
@@ -19,8 +19,6 @@ package org.apache.daffodil.dsom
 
 import scala.xml.Node
 import org.apache.daffodil.util.Misc
-import org.apache.daffodil.schema.annotation.props.PropertyLookupResult
-import org.apache.daffodil.schema.annotation.props.FindPropertyMixin
 
 /**
  * Base class for any DFDL annotation
@@ -38,19 +36,18 @@ import org.apache.daffodil.schema.annotation.props.FindPropertyMixin
  */
 abstract class DFDLAnnotation(xmlArg: Node, annotatedSCArg: AnnotatedSchemaComponent)
   extends SchemaComponent
-  with NestingLexicalMixin
-  with FindPropertyMixin // only needed because unit tests look at properties on annotations
+  with NestingLexicalMixin //with  FindPropertyMixin // only needed because unit tests look at properties on annotations
   {
 
   final override val xml = xmlArg
   final override def parent = annotatedSCArg
   final override val context: AnnotatedSchemaComponent = annotatedSCArg
 
-  // delegate to the annotated component.
-  override def findPropertyOption(pname: String): PropertyLookupResult = {
-    val res = annotatedSC.resolver.findPropertyOption(pname)
-    res
-  }
+  //  // delegate to the annotated component.
+  //  override def findPropertyOption(pname: String): PropertyLookupResult = {
+  //    val res = annotatedSC.resolver.findPropertyOption(pname)
+  //    res
+  //  }
 
   final lazy val annotatedSC = annotatedSCArg
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAssertion.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAssertion.scala
index dd2dd8592..e9b78f5c1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAssertion.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLAssertion.scala
@@ -118,10 +118,10 @@ abstract class DFDLAssertionBase(node: Node, decl: AnnotatedSchemaComponent)
 final class DFDLAssert(node: Node, decl: AnnotatedSchemaComponent)
   extends DFDLAssertionBase(node, decl) { // with Assert_AnnotationMixin // Note: don't use these generated mixins. Statements don't have format properties
 
-  final def gram = LV('gram) {
+  final def gram(term: Term) = LV('gram) {
     testKind match {
-      case TestKind.Pattern => AssertPatternPrim(decl.term, this)
-      case TestKind.Expression => AssertBooleanPrim(decl.term, this)
+      case TestKind.Pattern => AssertPatternPrim(term, this)
+      case TestKind.Expression => AssertBooleanPrim(term, this)
     }
   }.value
 }
@@ -129,10 +129,10 @@ final class DFDLAssert(node: Node, decl: AnnotatedSchemaComponent)
 final class DFDLDiscriminator(node: Node, decl: AnnotatedSchemaComponent)
   extends DFDLAssertionBase(node, decl) { // with Discriminator_AnnotationMixin
 
-  final def gram = LV('gram) {
+  final def gram(term: Term) = LV('gram) {
     testKind match {
-      case TestKind.Pattern => DiscriminatorPatternPrim(decl.term, this)
-      case TestKind.Expression => DiscriminatorBooleanPrim(decl.term, this)
+      case TestKind.Pattern => DiscriminatorPatternPrim(term, this)
+      case TestKind.Expression => DiscriminatorBooleanPrim(term, this)
     }
   }.value
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
index 424307b56..e066265f8 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLDefineVariable.scala
@@ -28,8 +28,6 @@ import org.apache.daffodil.xml.QName
 import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.grammar.primitives.SetVariable
-import org.apache.daffodil.grammar.primitives.NewVariableInstanceStart
-import org.apache.daffodil.grammar.primitives.NewVariableInstanceEnd
 import org.apache.daffodil.schema.annotation.props.Found
 
 class DFDLDefineVariable(node: Node, doc: SchemaDocument)
@@ -107,12 +105,10 @@ abstract class VariableReference(node: Node, decl: AnnotatedSchemaComponent)
 final class DFDLNewVariableInstance(node: Node, decl: AnnotatedSchemaComponent)
   extends VariableReference(node, decl) // with NewVariableInstance_AnnotationMixin
   {
-  requiredEvaluations(endGram)
-
   lazy val defaultValue = getAttributeOption("defaultValue")
 
-  lazy val gram: Gram = NewVariableInstanceStart(decl, this)
-  lazy val endGram: Gram = NewVariableInstanceEnd(decl, this)
+  def gram(term: Term): Gram = ??? // NewVariableInstanceStart(decl, term)
+  def endGram(term: Term): Gram = ??? // NewVariableInstanceEnd(decl, this)
 
   lazy val newVariableInstance = defv.newVariableInstance
 
@@ -131,7 +127,7 @@ final class DFDLSetVariable(node: Node, decl: AnnotatedSchemaComponent)
     case (None, "") => decl.SDE("Must have either a value attribute or an element value: %s", node)
   }
 
-  final def gram = LV('gram) {
-    SetVariable(decl, this)
+  final def gram(term: Term) = LV('gram) {
+    SetVariable(this)
   }.value
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLEscapeScheme.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLEscapeScheme.scala
index a4565eeda..05eedb11f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLEscapeScheme.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLEscapeScheme.scala
@@ -68,6 +68,12 @@ final class DFDLEscapeScheme(node: Node, decl: AnnotatedSchemaComponent, defES:
     }
   }
 
+  /**
+   * For unit testing. Must override because of multiple inheritance.
+   */
+  override def verifyPropValue(key: String, value: String): Boolean =
+    super.verifyPropValue(key, value)
+
   final def escapeCharacterEv = LV('escapeCharacterEv) {
     val qn = this.qNameForProperty("escapeCharacter")
     val expr = ExpressionCompilers.String.compileProperty(qn, NodeInfo.NonEmptyString, escapeCharacterRaw, this)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLFormatAnnotation.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLFormatAnnotation.scala
index 679fcfbb9..f0f78f646 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLFormatAnnotation.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLFormatAnnotation.scala
@@ -238,4 +238,20 @@ abstract class DFDLFormatAnnotation(nodeArg: Node, annotatedSCArg: AnnotatedSche
     res
   }.value
 
+  /**
+   * For unit testing convenience, or for use when debugging.
+   */
+  def getPropertyForUnitTest(propName: String) =
+    justThisOneProperties.get(propName).get._1
+
+  /**
+   * For unit testing convenience, or for use when debugging.
+   */
+  def verifyPropValue(propName: String, expectedValue: String): Boolean = {
+    val optInfo = justThisOneProperties.get(propName)
+    optInfo match {
+      case None => false
+      case Some((actualValue, _)) => actualValue == expectedValue
+    }
+  }
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatement.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatement.scala
index 0f5adc7cf..90a8c2041 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatement.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatement.scala
@@ -28,7 +28,7 @@ abstract class DFDLStatement(node: Node,
   extends DFDLAnnotation(node, annotatedSCArg)
   with NestingLexicalMixin {
 
-  requiredEvaluations(gram)
+  /// requiredEvaluations(gram)
 
-  def gram: Gram
+  def gram(term: Term): Gram
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
index ee901ec1e..1695767a1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
@@ -37,7 +37,7 @@ trait ResolvesDFDLStatementMixin
     f: ContentValueReferencedElementInfoMixin => Set[DPathElementCompileInfo]) = {
     s match {
       case a: DFDLAssertionBase if (a.testKind eq TestKind.Expression) => {
-        a.gram match {
+        a.gram(self) match {
           case ab: AssertBase => f(ab.expr)
           case _ => ReferencedElementInfos.None
         }
@@ -100,7 +100,7 @@ trait ResolvesDFDLStatementMixin
 trait ProvidesDFDLStatementMixin extends ThrowsSDE { self: AnnotatedSchemaComponent =>
 
   final def annotationFactoryForDFDLStatement(node: Node, self: AnnotatedSchemaComponent): Option[DFDLAnnotation] = {
-    val term = self.term
+    val term = self
     node match {
       case <dfdl:assert>{ content @ _* }</dfdl:assert> => Some(new DFDLAssert(node, term))
       case <dfdl:discriminator>{ content @ _* }</dfdl:discriminator> => Some(new DFDLDiscriminator(node, term))
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
index 7972f1ddb..c951a4390 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementBase.scala
@@ -42,6 +42,7 @@ import org.apache.daffodil.infoset.OnlyOnePossibilityForNextElement
 import org.apache.daffodil.infoset.NextElementResolver
 import org.apache.daffodil.infoset.ChildResolver
 import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.util.Maybe
 
 /**
  * Note about DSOM design versus say XSOM or Apache XSD library.
@@ -522,8 +523,9 @@ trait ElementBase
       targetNamespace,
       thisElementsNamespace,
       optSimpleTypeRuntimeData,
-      optMinOccurs,
-      optMaxOccurs,
+      minOccurs,
+      maxOccurs,
+      Maybe(occursCountKind),
       name,
       targetNamespacePrefix,
       thisElementsNamespacePrefix,
@@ -792,8 +794,8 @@ trait ElementBase
   private def NVDP = NilValueDelimiterPolicy
   private def EVDP = EmptyValueDelimiterPolicy
 
-  private lazy val hasNilValueInitiator = initTermTestExpression(initiatorParseEv, nilValueDelimiterPolicy, NVDP.Both, NVDP.Initiator)
-  private lazy val hasNilValueTerminator = initTermTestExpression(terminatorParseEv, nilValueDelimiterPolicy, NVDP.Both, NVDP.Terminator)
+  protected final lazy val hasNilValueInitiator = initTermTestExpression(initiatorParseEv, nilValueDelimiterPolicy, NVDP.Both, NVDP.Initiator)
+  protected final lazy val hasNilValueTerminator = initTermTestExpression(terminatorParseEv, nilValueDelimiterPolicy, NVDP.Both, NVDP.Terminator)
 
   /**
    * We need the nil values in raw form for diagnostic messages.
@@ -1087,12 +1089,12 @@ trait ElementBase
           SDW(WarnID.NoEmptyDefault, "Element with no empty representation. XSD default='%s' can only be used when unparsing.", defaultValueAsString)
         schemaDefinitionWhen(isOptional, "Optional elements cannot have default values but default='%s' was found.", defaultValueAsString)
         if (isArray && !isRequiredArrayElement) {
-          (optMinOccurs, occursCountKind) match {
+          (minOccurs, occursCountKind) match {
             case (_, OccursCountKind.Parsed) |
               (_, OccursCountKind.StopValue) =>
               SDE("XSD default='%s' can never be used since an element with dfdl:occursCountKind='%s' has no required occurrences.",
                 defaultValueAsString, occursCountKind)
-            case (Some(0), _) => SDE("XSD default='%s' can never be used since an element with XSD minOccurs='0' has no required occurrences.",
+            case (0, _) => SDE("XSD default='%s' can never be used since an element with XSD minOccurs='0' has no required occurrences.",
               defaultValueAsString)
             case _ => // ok
           }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
index cce469f01..f01fe10f1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ElementRef.scala
@@ -18,9 +18,7 @@
 package org.apache.daffodil.dsom
 
 import scala.xml.Node
-import org.apache.daffodil.grammar._
 import org.apache.daffodil.xml._
-import org.apache.daffodil.grammar.ElementReferenceGrammarMixin
 import org.apache.daffodil.dpath.NodeInfo
 
 /**
@@ -34,7 +32,6 @@ abstract class AbstractElementRef(xmlArg: Node,
   parentArg: SchemaComponent,
   positionArg: Int)
   extends ElementBase
-  with ElementReferenceGrammarMixin
   with HasRefMixin
   with NamedMixin
   with NestingLexicalMixin {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/EscapeSchemeRefMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/EscapeSchemeRefMixin.scala
index 463372f9f..461f8552d 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/EscapeSchemeRefMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/EscapeSchemeRefMixin.scala
@@ -21,7 +21,7 @@ import org.apache.daffodil.api.WarnID
 import org.apache.daffodil.schema.annotation.props.Found
 import org.apache.daffodil.schema.annotation.props.NotFound
 
-trait EscapeSchemeRefMixin { self: AnnotatedSchemaComponent =>
+trait EscapeSchemeRefMixin { self: Term =>
   /**
    * Changed to use findProperty, and to resolve the namespace properly.
    *
@@ -43,7 +43,7 @@ trait EscapeSchemeRefMixin { self: AnnotatedSchemaComponent =>
    * issue DFDL-77)
    */
   final lazy val optionEscapeScheme: Option[DFDLEscapeScheme] = {
-    val er = self.resolver.findPropertyOption("escapeSchemeRef")
+    val er = self.findPropertyOption("escapeSchemeRef")
     er match {
       case _: NotFound => {
         SDW(WarnID.EscapeSchemeRefUndefined,
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ModelGroup.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ModelGroup.scala
index f9d3b3757..06cee914c 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ModelGroup.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ModelGroup.scala
@@ -184,14 +184,17 @@ abstract class ModelGroup
 
   final override lazy val termRuntimeData: TermRuntimeData = modelGroupRuntimeData
 
-  protected lazy val groupMembersRuntimeData = this match {
-    case mg: ModelGroup => mg.groupMembers.map {
-      _ match {
-        case eb: ElementBase => eb.erd
-        case t: Term => t.termRuntimeData
+  protected lazy val groupMembersRuntimeData = {
+    val res = this match {
+      case mg: ModelGroup => mg.groupMembers.map {
+        _ match {
+          case eb: ElementBase => eb.erd
+          case t: Term => t.termRuntimeData
+        }
       }
+      case _ => Nil
     }
-    case _ => Nil
+    res
   }
 
   def modelGroupRuntimeData: ModelGroupRuntimeData
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ParticleMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ParticleMixin.scala
index b17974814..b3e86075c 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ParticleMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/ParticleMixin.scala
@@ -29,9 +29,9 @@ trait RequiredOptionalMixin { self: ElementBase =>
 
   final override lazy val isOptional = {
     // minOccurs == 0
-    (optMinOccurs, optMaxOccurs) match {
-      case (Some(1), Some(1)) => false // scalars are not optional
-      case (Some(0), max) => {
+    (minOccurs, maxOccurs) match {
+      case (1, 1) => false // scalars are not optional
+      case (0, max) => {
         // now we must check on occursCountKind.
         // if parsed or stopValue then we consider it an array
         if (occursCountKind =:= OccursCountKind.Parsed ||
@@ -41,9 +41,8 @@ trait RequiredOptionalMixin { self: ElementBase =>
           false
         } else {
           max match {
-            case Some(1) => true
-            case None => true
-            case Some(_) => false
+            case 1 => true
+            case _ => false
           }
         }
       }
@@ -67,18 +66,17 @@ trait RequiredOptionalMixin { self: ElementBase =>
     if (isOptional) false
     else {
       val UNBOUNDED = -1
-      (optMinOccurs, optMaxOccurs) match {
-        case (None, None) => false
-        case (Some(1), Some(1)) => false
-        case (_, Some(n)) if n > 1 => true
-        case (_, Some(UNBOUNDED)) => true
+      (minOccurs, maxOccurs) match {
+        case (1, 1) => false
+        case (_, n) if n > 1 => true
+        case (_, UNBOUNDED) => true
         /**
          * This next case is for occursCountKinds parsed and stopValue.
          * These only use min/maxOccurs for validation, so anything
          * with these occursCountKinds is an array (so long as it isn't
          * scalar)
          */
-        case (_, Some(1)) if (occursCountKind == OccursCountKind.Parsed ||
+        case (_, 1) if (occursCountKind == OccursCountKind.Parsed ||
           occursCountKind == OccursCountKind.StopValue ||
           occursCountKind == OccursCountKind.Expression) => true
         case _ => false
@@ -98,8 +96,8 @@ trait RequiredOptionalMixin { self: ElementBase =>
 // A Particle is something that can be repeating.
 trait ParticleMixin extends RequiredOptionalMixin { self: ElementBase =>
 
-  final lazy val optMinOccurs: Option[Int] = Some(minOccurs)
-  final lazy val optMaxOccurs: Option[Int] = Some(maxOccurs)
+  //  final lazy val optMinOccurs: Option[Int] = Some(minOccurs)
+  //  final lazy val optMaxOccurs: Option[Int] = Some(maxOccurs)
 
   lazy val minOccurs = {
     val min = (self.xml \ "@minOccurs").text.toString
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SequenceGroup.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SequenceGroup.scala
index e5f6a5967..2a435d42a 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SequenceGroup.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/SequenceGroup.scala
@@ -67,6 +67,11 @@ abstract class SequenceTermBase(
       groupMembers.exists { _.hasStaticallyRequiredInstances }
   }
 
+  final override def hasPotentiallyTrailingInstances = {
+    isPotentiallyTrailing ||
+      groupMembers.exists { _.hasPotentiallyTrailingInstances }
+  }
+
   final override def hasKnownRequiredSyntax = LV('hasKnownRequiredSyntax) {
     lazy val memberHasRequiredSyntax = groupMembers.exists(_.hasKnownRequiredSyntax)
     lazy val prefixOrPostfixAndStaticallyRequiredInstance =
@@ -221,7 +226,9 @@ abstract class SequenceTermBase(
 
   } else None
 
-  final lazy val modelGroupRuntimeData = {
+  final lazy val modelGroupRuntimeData = sequenceRuntimeData
+
+  final lazy val sequenceRuntimeData = {
     new SequenceRuntimeData(
       schemaSet.variableMap,
       encodingInfo,
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/Term.scala b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/Term.scala
index 9d1597660..0e1ef439e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/Term.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/Term.scala
@@ -44,7 +44,8 @@ trait Term
   with TermGrammarMixin
   with DelimitedRuntimeValuedPropertiesMixin
   with InitiatedTerminatedMixin
-  with TermEncodingMixin {
+  with TermEncodingMixin
+  with EscapeSchemeRefMixin {
 
   def position: Int
 
@@ -359,6 +360,25 @@ trait Term
   def isKnownRequiredElement = false
   def hasKnownRequiredSyntax = false
 
+  def hasPotentiallyTrailingInstances: Boolean = false
+  final def isPotentiallyTrailing = LV('isPotentiallyTrailing) {
+    if (!isRequired) {
+      val es = nearestEnclosingSequence
+      val res = es match {
+        case None => true
+        case Some(s) => {
+          val allRequired = s.groupMembers.filter(_.isRequired)
+          val lastDeclaredRequired = allRequired.last
+          if (s.groupMembers.indexOf(lastDeclaredRequired) < s.groupMembers.indexOf(this)) true
+          else false
+        }
+      }
+      res
+      // Since we can't determine at compile time, return false so that we can continue processing.
+      // Runtime checks will make final determination.
+    } else false
+  }.value
+
   /**
    * Returns a tuple, where the first item in the tuple is the list of sibling
    * terms that could appear before this. The second item in the tuple is a
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
index 76c4be200..b65389b39 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementBaseGrammarMixin.scala
@@ -89,7 +89,6 @@ import org.apache.daffodil.grammar.primitives.LiteralCharacterNilOfSpecifiedLeng
 import org.apache.daffodil.grammar.primitives.LiteralNilDelimitedEndOfData
 import org.apache.daffodil.grammar.primitives.LiteralValueNilOfSpecifiedLength
 import org.apache.daffodil.grammar.primitives.LogicalNilValue
-import org.apache.daffodil.grammar.primitives.NilLiteralCharacter
 import org.apache.daffodil.grammar.primitives.OVCRetry
 import org.apache.daffodil.grammar.primitives.OnlyPadding
 import org.apache.daffodil.grammar.primitives.PackedDecimalDelimitedEndOfData
@@ -128,6 +127,7 @@ import org.apache.daffodil.schema.annotation.props.gen.YesNo
 import org.apache.daffodil.util.PackedSignCodes
 import org.apache.daffodil.xml.GlobalQName
 import org.apache.daffodil.xml.XMLUtils
+import org.apache.daffodil.grammar.primitives.ComplexTypeCombinator
 
 /////////////////////////////////////////////////////////////////
 // Elements System
@@ -299,10 +299,10 @@ trait ElementBaseGrammarMixin
       ((lengthKind eq LengthKind.Implicit) ||
         ((lengthKind eq LengthKind.Explicit) &&
           lengthEv.optConstant.isDefined)) // TODO: consider if delimiters matter, alignment, nil values,.... Are we excluding
-          // that stuff here? For example, if the element is nillable, but
-          // if isNilled, the representation will still be exactly the same
-          // length, then we'd like this to be true.
-          ) ||
+    // that stuff here? For example, if the element is nillable, but
+    // if isNilled, the representation will still be exactly the same
+    // length, then we'd like this to be true.
+    ) ||
           (
             // a fixed-length textual number, where a legal value
             // (or nil value) must by definition be smaller than the
@@ -329,7 +329,7 @@ trait ElementBaseGrammarMixin
             // for the largest possible textNumber matching the pattern.
             //
             false // TODO: implement this
-            ) ||
+          ) ||
             (isComplexType && !isNillable &&
               (
                 //
@@ -339,7 +339,7 @@ trait ElementBaseGrammarMixin
                 // alignment regions.
                 //
                 false // TODO: implement this
-                ))
+              ))
   }
 
   /**
@@ -400,18 +400,7 @@ trait ElementBaseGrammarMixin
 
   private lazy val rightFill = new RightFill(context)
 
-  // maybe can be private - if it is used still
-  protected lazy val elementUnused = new ElementUnused(context)
-
-  /**
-   * provided by ElementBase for array considerations, and GlobalElementDecl - scalar only
-   */
-  protected def allowedValue: Gram
-  //
-  // This silly redundancy where the variable name has to also be passed as a string,
-  // is, by the way, a good reason Scala needs real Lisp-style macros, that can take an argument and
-  // turn it into a type/class, object, def, or val/var name, as well as a string, etc.
-  //
+  private lazy val elementUnused = new ElementUnused(context)
 
   private lazy val parsedNil = prod("parsedNil", NYI && isNillable && nilKind == NilKind.LogicalValue) {
     nilElementInitiator ~
@@ -435,7 +424,7 @@ trait ElementBaseGrammarMixin
   private lazy val parsedValue = prod("parsedValue", isSimpleType) {
     initiatorRegion ~
       valueMTA ~
-      captureLengthRegions(leftPadding, ovcRetry(allowedValue), rightPadding ~ rightFill) ~
+      captureLengthRegions(leftPadding, ovcRetry(value), rightPadding ~ rightFill) ~
       terminatorRegion
   }
 
@@ -511,7 +500,6 @@ trait ElementBaseGrammarMixin
   }
 
   private lazy val stringDelimitedEndOfData = prod("stringDelimitedEndOfData") { StringDelimitedEndOfData(this) }
-  //  private lazy val stringPatternMatched = prod("stringPatternMatched") { StringPatternMatched(this) }
 
   private lazy val stringValue = prod("stringValue") { stringPrim }
 
@@ -787,13 +775,13 @@ trait ElementBaseGrammarMixin
         (primType, binaryCalendarRep) match {
           case (PrimType.DateTime, BinaryCalendarRep.BinarySeconds) => (lengthUnits, binaryNumberKnownLengthInBits) match {
             case (LengthUnits.Bytes, 32) => new ConvertBinaryDateTimeSecMilliPrim(this, binaryNumberKnownLengthInBits)
-            case (_, 32)  => SDE("lengthUnits must be 'bytes' when binaryCalendarRep='binarySeconds'")
+            case (_, 32) => SDE("lengthUnits must be 'bytes' when binaryCalendarRep='binarySeconds'")
             case (_, n) => SDE("binary xs:dateTime must be 32 bits when binaryCalendarRep='binarySeconds'. Length in bits was %s.", n)
           }
           case (_, BinaryCalendarRep.BinarySeconds) => SDE("binaryCalendarRep='binarySeconds' is not allowed with type %s", primType.name)
           case (PrimType.DateTime, BinaryCalendarRep.BinaryMilliseconds) => (lengthUnits, binaryNumberKnownLengthInBits) match {
             case (LengthUnits.Bytes, 64) => new ConvertBinaryDateTimeSecMilliPrim(this, binaryNumberKnownLengthInBits)
-            case (_, 64)  => SDE("lengthUnits must be 'bytes' when binaryCalendarRep='binaryMilliseconds'")
+            case (_, 64) => SDE("lengthUnits must be 'bytes' when binaryCalendarRep='binaryMilliseconds'")
             case (_, n) => SDE("binary xs:dateTime must be 64 bits when binaryCalendarRep='binaryMilliseconds'. Length in bits was %s.", n)
           }
           case (_, BinaryCalendarRep.BinaryMilliseconds) => SDE("binaryCalendarRep='binaryMilliseconds' is not allowed with type %s", primType.name)
@@ -840,41 +828,14 @@ trait ElementBaseGrammarMixin
 
   protected final lazy val empty = prod("empty", NYI && emptyIsAnObservableConcept) { EmptyGram }
 
-  //  private lazy val emptyRepresentation = prod("emptyRepresentation") {
-  //    simpleOrNonImplicitComplexEmpty | complexImplicitEmpty
-  //  }
-
-  //  private lazy val simpleOrNonImplicitComplexEmpty = prod("simpleOrNonImplicitComplexEmpty",
-  //    isSimpleType | isComplexType && lengthKind != LengthKind.Implicit) {
-  //      emptyElementInitiator ~
-  //        valueMTA ~
-  //        captureLengthRegions(EmptyGram, EmptyGram, EmptyGram) ~
-  //        emptyElementTerminator
-  //    }
-  //
-  //  /**
-  //   * This is about the case where we take an empty, parse a complex type recursively from it
-  //   * and potentially succeed.
-  //   */
-  //  private lazy val complexImplicitEmpty = prod("complexImplicitEmpty",
-  //    isComplexType && lengthKind == LengthKind.Implicit) {
-  //      SaveInputStream(this) ~ SetEmptyInputStream(this) ~ complexType.mainGrammar ~
-  //        RestoreInputStream(this) ~ emptyElementTerminator
-  //    }
-
-  //  private lazy val emptyDefaulted = prod("emptyDefaulted",
-  //    isDefaultable && emptyIsAnObservableConcept) {
-  //      empty ~ TheDefaultValue(this)
-  //    }
-
-  private lazy val nilElementInitiator = prod("nilElementInitiator", hasInitiator) { delimMTA ~ Initiator(this) }
-  private lazy val nilElementTerminator = prod("nilElementTerminator", hasTerminator) { delimMTA ~ Terminator(this) }
+  private lazy val nilElementInitiator = prod("nilElementInitiator", hasNilValueInitiator) { delimMTA ~ Initiator(this) }
+  private lazy val nilElementTerminator = prod("nilElementTerminator", hasNilValueTerminator) { delimMTA ~ Terminator(this) }
 
   //  private lazy val emptyElementInitiator = prod("emptyElementInitiator", NYI && hasEmptyValueInitiator) { delimMTA ~ Initiator(this) }
   //  private lazy val emptyElementTerminator = prod("emptyElementTerminator", NYI && hasEmptyValueTerminator) { delimMTA ~ Terminator(this) }
 
   private lazy val complexContent = prod("complexContent", isComplexType) {
-    complexType.mainGrammar
+    ComplexTypeCombinator(complexType, complexType.group.termContentBody)
   }
 
   private lazy val isNilLit = isNillable && ((nilKind == NilKind.LiteralValue) || (nilKind == NilKind.LiteralCharacter))
@@ -894,8 +855,7 @@ trait ElementBaseGrammarMixin
 
   private lazy val nilLitSimple = prod("nilLitSimple", isSimpleType) {
     captureLengthRegions(leftPadding,
-      specifiedLength(nilLitContent) ~ // for parser
-        NilLiteralCharacter(context), // for unparser
+      specifiedLength(nilLitContent),
       rightPadding ~ rightFill)
   }
 
@@ -911,7 +871,11 @@ trait ElementBaseGrammarMixin
 
   }
 
-  private lazy val nilLitMTA = prod("nilLitMTA", isNilLit) { mtaBase }
+  /**
+   * mandatory text alignment for a literal nil value is only needed
+   * if there is no initiator that gets its own mandatory text alignment.
+   */
+  private lazy val nilLitMTA = prod("nilLitMTA", isNilLit && !hasNilValueInitiator) { mtaBase }
 
   private lazy val nilLitContent = prod("nilLitContent",
     isNillable && (nilKind == NilKind.LiteralValue || nilKind == NilKind.LiteralCharacter)) {
@@ -956,31 +920,16 @@ trait ElementBaseGrammarMixin
     else body
   }
 
-  private lazy val nilOrEmptyOrValue = prod("nilOrEmptyOrValue") {
-    // anyOfNilOrEmptyOrValue ||
-    nilOrValue ||
-      //   emptyOrValue ||
-      nonNilNonEmptyParsedValue
-  }
-
-  //  private lazy val anyOfNilOrEmptyOrValue = prod("anyOfNilOrEmptyOrValue", isNillable && NYI && emptyIsAnObservableConcept) {
-  //    SimpleNilOrEmptyOrValue(this, nilLit || parsedNil, empty, parsedValue)
-  //  }
-
   private lazy val nilOrValue = prod("nilOrValue", isNillable) { // TODO: make it exclude emptyness once emptyness is implemented
     SimpleNilOrValue(this, nilLit || parsedNil, parsedValue)
   }
 
-  //  private lazy val emptyOrValue = prod("emptyOrValue", NYI && emptyIsAnObservableConcept && !isNillable) {
-  //    SimpleEmptyOrValue(this, empty, parsedValue)
-  //  }
-
-  private lazy val nonNilNonEmptyParsedValue = prod("nonNilnonEmptyParsedValue", !isNillable) { // TODO: make it exclude emptyness once emptyness is implemented
+  private lazy val nonNillableParsedValue = prod("nonNilnonEmptyParsedValue", !isNillable) {
     parsedValue
   }
 
   private lazy val scalarDefaultableSimpleContent = prod("scalarDefaultableSimpleContent", isSimpleType) {
-    nilOrEmptyOrValue
+    nilOrValue || nonNillableParsedValue
   }
 
   /**
@@ -1067,7 +1016,7 @@ trait ElementBaseGrammarMixin
 
   private lazy val elementRightFraming = prod("elementRightFraming") { TrailingSkipRegion(this) }
 
-  protected lazy val enclosedElement = prod("enclosedElement") {
+  lazy val enclosedElement = prod("enclosedElement") {
     //
     // not isScalar, because this is reused inside arrays
     // that is, we're counting on reusuing this production for array elements
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementReferenceGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementReferenceGrammarMixin.scala
index 0f3aa35c0..b0a05a34f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementReferenceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ElementReferenceGrammarMixin.scala
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.daffodil.grammar
-
-import org.apache.daffodil.dsom.AbstractElementRef
-
-trait ElementReferenceGrammarMixin { self: AbstractElementRef =>
-
-}
+//
+//package org.apache.daffodil.grammar
+//
+//import org.apache.daffodil.dsom.AbstractElementRef
+//
+//trait ElementReferenceGrammarMixin { self: AbstractElementRef =>
+//
+//}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
index eeb7bc22e..bfe6edbac 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/Grammar.scala
@@ -21,14 +21,11 @@ import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.Implicits._; object INoWarn { ImplicitsSuppressUnusedImportWarning() }
 import org.apache.daffodil.processors.unparsers.SeqCompUnparser
 import org.apache.daffodil.dsom._
-import org.apache.daffodil.grammar.primitives.Nada
 import org.apache.daffodil.processors.parsers.SeqCompParser
-import org.apache.daffodil.processors.parsers.AltCompParser
 import org.apache.daffodil.compiler.ForUnparser
 import org.apache.daffodil.compiler.ForParser
 import org.apache.daffodil.processors.unparsers.NadaUnparser
 import org.apache.daffodil.processors.parsers.NadaParser
-import org.apache.daffodil.processors.unparsers.EmptyGramUnparser
 
 abstract class UnaryGram(context: Term, rr: => Gram) extends NamedGram(context) {
   private lazy val r = rr
@@ -95,8 +92,6 @@ class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends Bi
   protected final override def open = "("
   protected final override def close = ")"
 
-  Assert.invariant(!children.exists { _.isInstanceOf[Nada] })
-
   lazy val parserChildren = children.filter(_.forWhat != ForUnparser).map { _.parser }.filterNot { _.isInstanceOf[NadaParser] }
 
   final override lazy val parser = {
@@ -124,56 +119,20 @@ class SeqComp private (context: SchemaComponent, children: Seq[Gram]) extends Bi
   }
 }
 
-/**
- * Alternative composition of grammar terms.
- *
- * Flattens nests of these into a single flat list.
- */
-object AltComp {
-  def apply(context: SchemaComponent, p: => Gram, q: => Gram) = {
-    val children = (p, q) match {
-      case (ps: AltComp, qs: AltComp) => {
-        ps.children ++ qs.children
-      }
-      case (ps: AltComp, _) => ps.children ++ List(q)
-      case (_, qs: AltComp) => p +: qs.children
-      case (_, _) => List(p, q)
-    }
-    val res = new AltComp(context, children)
-    res
-  }
-}
-
-class AltComp private (context: SchemaComponent, children: Seq[Gram]) extends BinaryGram(context, children)
-  with HasNoUnparser {
-  protected final override def op = "|"
-  protected final override def open = "["
-  protected final override def close = "]"
-
-  final override lazy val parser = new AltCompParser(context.runtimeData, children.map { _.parser })
-}
-
 object EmptyGram extends Gram(null) {
   override def isEmpty = true
   override def toString = "Empty"
 
   override lazy val parser = new NadaParser(null)
-  override lazy val unparser = // hasNoUnparser
-    // we depend on this unparser being returned, even though it cannot be called to unparse anything.
-    // As there are unit tests which test attributes where those attributes cause a DummyUnparser to be created.
-    // That is later optimized out (or needs to be).
-    //
-    // FIXME: switch back to hasNoUnparser, find where this is being used and have it not ask for
-    // this unparser.
-    new EmptyGramUnparser(null) // DummyUnparser(Misc.getNameFromClass(this))
-
+  override lazy val unparser = new NadaUnparser(null)
 }
 
-object ErrorGram extends Gram(null) with HasNoUnparser {
+object ErrorGram extends Gram(null) {
   override def isEmpty = false
   override def toString = "Error"
 
-  override lazy val parser = hasNoParser // new ErrorParser
+  override lazy val parser = hasNoParser
+  override lazy val unparser = hasNoUnparser
 }
 
 abstract class NamedGram(context: SchemaComponent) extends Gram(context) {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarMixins.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarMixins.scala
index 8c3d7dc86..bddd4d4d0 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarMixins.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarMixins.scala
@@ -14,25 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.daffodil.grammar
-import org.apache.daffodil.dsom.ComplexTypeBase
-import org.apache.daffodil.dsom.GroupRef
-import org.apache.daffodil.grammar.primitives.ComplexTypeCombinator
-
-trait GroupRefGrammarMixin extends GrammarMixin { self: GroupRef =>
-
-}
-
-/////////////////////////////////////////////////////////////////
-// Types System
-/////////////////////////////////////////////////////////////////
-
-trait ComplexTypeBaseGrammarMixin extends GrammarMixin { self: ComplexTypeBase =>
-
-  override protected final def grammarContext = this
-
-  lazy val mainGrammar = prod("mainGrammar") {
-    ComplexTypeCombinator(this, group.asChildOfComplexType)
-  }
-}
+//
+//package org.apache.daffodil.grammar
+//import org.apache.daffodil.dsom.ComplexTypeBase
+//import org.apache.daffodil.dsom.GroupRef
+//import org.apache.daffodil.grammar.primitives.ComplexTypeCombinator
+//
+///////////////////////////////////////////////////////////////////
+//// Types System
+///////////////////////////////////////////////////////////////////
+//
+//trait ComplexTypeBaseGrammarMixin extends GrammarMixin { self: ComplexTypeBase =>
+//
+//  override protected final def grammarContext = this
+//
+//  lazy val mainGrammar = prod("mainGrammar") {
+//    ComplexTypeCombinator(this, group.asChildOfComplexType)
+//  }
+//}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
index 14b3796f1..3b3d33b2f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/GrammarTerm.scala
@@ -20,25 +20,20 @@ package org.apache.daffodil.grammar
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.processors.parsers.Parser
 import org.apache.daffodil.processors.unparsers.Unparser
-import org.apache.daffodil.grammar.primitives.Nada
 import org.apache.daffodil.dsom.SchemaComponent
 import org.apache.daffodil.oolag.OOLAG.OOLAGHostImpl
 import org.apache.daffodil.compiler.ParserOrUnparser
-import org.apache.daffodil.util.Misc
 import org.apache.daffodil.compiler.BothParserAndUnparser
 import org.apache.daffodil.api.WarnID
-
-trait HasNoUnparser {
-  final lazy val unparser: Unparser = hasNoUnparser
-  private def hasNoUnparser = Assert.invariantFailed("no unparser for " + Misc.getNameFromClass(this))
-}
+import org.apache.daffodil.util.Maybe
 
 /**
  * Gram - short for "Grammar Term"
  *
- * These are the objects in the grammar. The grammar is supposed to be
- * roughly the grammar in the DFDL specification, but some differences are expected
- * because this one has to actually be operationalized.
+ * These are the objects in the grammar.
+ *
+ * This grammar really differs a great deal from what we find in the DFDL specification
+ * because it actually has to be operationalized.
  */
 abstract class Gram(contextArg: SchemaComponent)
   extends OOLAGHostImpl(contextArg) {
@@ -75,37 +70,21 @@ abstract class Gram(contextArg: SchemaComponent)
   def ~(qq: => Gram) = {
     lazy val q = qq.deref
     val self = this.deref
-    //
-    // The Nada terminal also behaves like empty for sequential composition
-    // It is not empty for alternative composition though.
-    //
+
     val res =
-      if (self.isEmpty || self.isInstanceOf[Nada]) {
-        if (q.isEmpty || q.isInstanceOf[Nada]) // Nada might get through to this point. Let's optimize it out.
+      if (self.isEmpty) {
+        if (q.isEmpty)
           EmptyGram
         else q
-      } else if (q.isEmpty || q.isInstanceOf[Nada]) self
+      } else if (q.isEmpty) self
       else {
-        Assert.invariant(!self.isInstanceOf[Nada])
         Assert.invariant(!self.isEmpty)
-        Assert.invariant(!q.isInstanceOf[Nada])
         Assert.invariant(!q.isEmpty)
         SeqComp(context, self, q)
       }
     res
   }
 
-  def |(qq: => Gram) = {
-    lazy val q = qq.deref
-    val self = this.deref
-    if (self.isEmpty)
-      if (q.isEmpty) EmptyGram
-      else q
-    else if (q.isEmpty) self
-    else
-      AltComp(context, self, q)
-  }
-
   /**
    * This || operator means one of the operands ONLY. I.e., statically
    * they are supposed to be mutually exclusive, so only one (or none) of them should
@@ -127,9 +106,27 @@ abstract class Gram(contextArg: SchemaComponent)
    */
   def parser: Parser
 
+  final def maybeParser: Maybe[Parser] = {
+    if (this.isEmpty) Maybe.Nope
+    else {
+      val p = this.parser
+      if (p.isEmpty) Maybe.Nope
+      else Maybe(p)
+    }
+  }
+
   protected final def hasNoParser: Parser = Assert.invariantFailed("Has no parser.")
   protected final def hasNoUnparser: Unparser = Assert.invariantFailed("Has no unparser.")
 
-  def unparser: Unparser // = DummyUnparser(Misc.getNameFromClass(this)) // context.runtimeData
+  def unparser: Unparser
+
+  final def maybeUnparser: Maybe[Unparser] = {
+    if (this.isEmpty) Maybe.Nope
+    else {
+      val u = this.unparser
+      if (u.isEmpty) Maybe.Nope
+      else Maybe(u)
+    }
+  }
 
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
index d6fc27f74..463c99830 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
@@ -21,7 +21,7 @@ import org.apache.daffodil.dsom.Term
 
 trait HasStatementsGrammarMixin extends GrammarMixin { self: Term =>
 
-  private lazy val statementGrams = statements.map { _.gram }
+  private lazy val statementGrams = statements.map { _.gram(self) }
   // TODO: statements (but specifically not newVariableInstance) can appear on simple type definitions as well as terms.
 
   final lazy val dfdlStatementEvaluations = prod("dfdlStatementEvaluations", statementGrams.length > 0) {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/LocalElementGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/LocalElementGrammarMixin.scala
index ce33817c4..30726cdf8 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/LocalElementGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/LocalElementGrammarMixin.scala
@@ -16,206 +16,190 @@
  */
 
 package org.apache.daffodil.grammar
-import org.apache.daffodil.schema.annotation.props._
-import org.apache.daffodil.schema.annotation.props.gen._
 import org.apache.daffodil.dsom.ElementBase
-import org.apache.daffodil.equality._; object ENoWarn2 { EqualitySuppressUnusedImportWarning() }
-import org.apache.daffodil.grammar.primitives.StopValue
-import org.apache.daffodil.grammar.primitives.NotStopValue
-import org.apache.daffodil.grammar.primitives.RepUnbounded
-import org.apache.daffodil.grammar.primitives.RepExactlyTotalOccursCount
-import org.apache.daffodil.grammar.primitives.RepExactlyTotalN
-import org.apache.daffodil.grammar.primitives.RepExactlyN
-import org.apache.daffodil.grammar.primitives.RepAtMostTotalN
-import org.apache.daffodil.grammar.primitives.RepAtMostOccursCount
-import org.apache.daffodil.grammar.primitives.OccursCountExpression
-import org.apache.daffodil.grammar.primitives.OptionalCombinator
-import org.apache.daffodil.grammar.primitives.ArrayCombinator
 
 trait LocalElementGrammarMixin extends GrammarMixin { self: ElementBase =>
 
-  override lazy val termContentBody = prod("termContentBody") { // override in ElementRef
-    (if (isScalar) enclosedElement else recurrance)
-  }
-
-  protected final lazy val allowedValue = prod("allowedValue") { notStopValue | value }
-
-  private lazy val notStopValue = prod("notStopValue", hasStopValue) { NotStopValue(this) }
-
-  private lazy val separatedEmpty = prod("separatedEmpty",
-    emptyIsAnObservableConcept && !isScalar) {
-      separatedForArrayPosition(empty)
-    }
-
-  private lazy val separatedRecurringDefaultable = prod("separatedRecurringDefaultable", !isScalar) {
-    separatedForArrayPosition(enclosedElement)
-  }
-
-  private lazy val separatedRecurringNonDefault = prod("separatedRecurringNonDefault", !isScalar) {
-    separatedForArrayPosition(enclosedElementNonDefault)
-  }
-
-  private lazy val nonSeparatedScalarDefaultable = prod("nonSeparatedScalarDefaultable", isScalar) { enclosedElement }
-
-  private lazy val recurrance = prod("recurrance", !isScalar) {
-    if (isOptional) {
-      OptionalCombinator(this, arrayContents)
-    } else {
-      ArrayCombinator(this, arrayContents)
-    }
-  }
-
-  final override lazy val asTermInChoice = prod("asTermInChoice") {
-    nonSeparatedScalarDefaultable || recurrance
-  }
-
-  /**
-   * speculate parsing forward until we get an error
-   */
-  private lazy val separatedContentWithMinUnboundedWithoutTrailingEmpties = prod("separatedContentWithMinUnboundedWithoutTrailingEmpties", !isScalar) {
-    RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~
-      RepUnbounded(self, separatedRecurringNonDefault) ~
-      StopValue(this)
-  }
-
-  private lazy val separatedContentWithMinAndMaxWithoutTrailingEmpties = prod("separatedContentWithMinAndMaxWithoutTrailingEmpties", !isScalar) {
-    RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~
-      RepAtMostTotalN(self, maxOccurs, separatedRecurringNonDefault) ~
-      StopValue(this)
-  }
-
-  private lazy val separatedContentWithMinUnbounded = prod("separatedContentWithMinUnbounded", !isScalar) {
-    separatedContentWithMinUnboundedWithoutTrailingEmpties // These are for tolerating trailing empties. Let's not tolerate them for now.
-  }
-
-  private lazy val separatedContentWithMinAndMax = prod("separatedContentWithMinAndMax", !isScalar) {
-    separatedContentWithMinAndMaxWithoutTrailingEmpties // These are for tolerating trailing empties. Let's not tolerate them for now.
-  }
-
-  private lazy val separatedContentZeroToUnbounded = prod("separatedContentZeroToUnbounded", !isScalar) {
-    RepUnbounded(self, separatedRecurringNonDefault) ~
-      StopValue(this)
-  }
-
-  private lazy val separatedContentAtMostNWithoutTrailingEmpties = prod("separatedContentAtMostNWithoutTrailingEmpties", !isScalar) {
-    RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~
-      RepAtMostTotalN(this, maxOccurs, separatedRecurringNonDefault) ~
-      StopValue(this)
-  }
-
-  // TODO: Do we have to adjust the count to take stopValue into account?
-  // Answer: No because the counts are never used when there is a stopValue (at least in current
-  // thinking about how occursCountKind='stopValue' works.)
-
-  private lazy val separatedContentAtMostN = prod("separatedContentAtMostN") {
-    (separatedContentAtMostNWithoutTrailingEmpties // FIXME: We don't know whether we can absorb trailing separators or not here.
-    // We don't know if this repeating thing is in trailing position, or in the middle
-    // of a sequence. There is also ambiguity if the enclosing sequence and this sequence
-    // have the same separator.
-    //      ~
-    //      RepAtMostTotalN(self, maxOccurs, separatedEmpty) // absorb extra separators, if found.
-    )
-  }
-
-  /**
-   *  parse counted number of occurrences exactly.
-   */
-  private lazy val stopValueSize = if (hasStopValue) 1 else 0
-
-  // TODO FIXME: We really want to have different productions for parsing and unparsing in these
-  // complex cases where there is defaulting, etc. Unparsing has many fewer cases, and is just not
-  // symmetric with parsing in these situations.
-  private def separatedContentExactlyN(count: Long) = prod("separatedContentExactlyN") {
-    if (minOccurs == maxOccurs) {
-      // fixed length case. All are defaultable. Still might have a stop value tho.
-      RepExactlyN(self, count, separatedRecurringDefaultable) ~
-        StopValue(this)
-    } else {
-      // variable length case. So some defaultable, some not.
-      RepExactlyN(self, minOccurs, separatedRecurringDefaultable) ~
-        RepAtMostTotalN(self, count, separatedRecurringNonDefault) ~
-        StopValue(this) ~
-        RepExactlyTotalN(self, maxOccurs + stopValueSize, separatedEmpty) // absorb remaining separators after stop value.
-    }
-  }
-
-  private lazy val separatedContentExactlyNComputed = prod("separatedContentExactlyNComputed") {
-    OccursCountExpression(this) ~
-      RepAtMostOccursCount(this, minOccurs, separatedRecurringDefaultable) ~
-      RepExactlyTotalOccursCount(this, separatedRecurringNonDefault)
-  }
-
-  // keep in mind that anything here that scans for a representation either knows the length it is going after, or knows what the terminating markup is, and
-  // our invariant is, that it does NOT consume that markup ever. The parser consumes it with appropriate grammar terminals.
-
-  private val UNB = -1 // UNBOUNDED
-  private val ZERO = 0 // ZERO
-
-  lazy val arrayContents = prod("arrayContents", !isScalar) {
-    arrayContentsNoSeparators || arrayContentsWithSeparators
-  }
-
-  private lazy val contentUnbounded = prod("contentUnbounded") {
-    RepUnbounded(self, separatedRecurringDefaultable)
-  }
+  override lazy val termContentBody = enclosedElement
 
+  //  override lazy val termContentBody = prod("termContentBody") {
+  //    (if (isScalar) enclosedElement else recurrance)
+  //  }
   //
-  // Silly constants to make the lookup tables below more readable without using fragile whitespace
+  //  private def separatedForArrayPosition(bodyArg: => Gram): Gram = {
+  //    val body = bodyArg
+  //    val (isElementWithNoRep, isRepeatingElement) = body.context match {
+  //      case e: ElementBase => (!e.isRepresented, !e.isScalar)
+  //      case other => (false, false)
+  //    }
+  //    Assert.usage(isRepeatingElement)
+  //    Assert.invariant(!isElementWithNoRep) //inputValueCalc not allowed on arrays in DFDL v1.0
+  //    // val res = prefixSep ~ infixSepRule ~ body ~ postfixSep
+  //    val res = new OptionalInfixSep(this, arraySeparator) ~ body
+  //    res
+  //  }
   //
-  private val Never______ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Never
-  private val Trailing___ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmpty
-  private val TrailingStr: SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmptyStrict
-  private val Always_____ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.AnyEmpty
-
-  private val StopValue_ = OccursCountKind.StopValue
-  private val Implicit__ = OccursCountKind.Implicit
-  private val Parsed____ = OccursCountKind.Parsed
-  private val Fixed_____ = OccursCountKind.Fixed
-  private val Expression = OccursCountKind.Expression
-
-  private lazy val arrayContentsNoSeparators = prod("arrayContentsNoSeparators", !isScalar && !hasSep) {
-    val res = (occursCountKind, minOccurs, maxOccurs) match {
-      case (Expression, ____, __2) => separatedContentExactlyNComputed
-      case (Fixed_____, ____, UNB) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs")
-      case (Fixed_____, min_, max) if (min_ != max) => SDE("occursCountKind='fixed' requires minOccurs and maxOccurs to be equal (%d != %d)", min_, max)
-      case (Fixed_____, ____, max) => separatedContentExactlyN(max)
-      case (Implicit__, ZERO, UNB) => contentUnbounded // same as parsed
-      case (Implicit__, min_, UNB) => RepExactlyN(self, min_, separatedRecurringDefaultable) ~ contentUnbounded // respects minOccurs
-      case (Implicit__, ____, __2) => separatedContentAtMostN // uses min and maxOccurs
-      case (Parsed____, ____, __2) => contentUnbounded
-      case (StopValue_, ____, __2) => contentUnbounded
-    }
-    res
-  }
-
-  /**
-   * Matches the table about separator suppression policy.
-   *
-   * TODO: Update this table to match the final spec.
-   */
-  private lazy val arrayContentsWithSeparators = prod("arrayContentsWithSeparators", !isScalar && hasSep) {
-    val triple = (separatorSuppressionPolicy, occursCountKind, maxOccurs, minOccurs)
-    val res = triple match {
-      case (___________, Expression, ___, __2) => separatedContentExactlyNComputed
-      case (___________, Fixed_____, UNB, ___) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs")
-      case (___________, Fixed_____, max, min) if (max != min) => SDE("occursCountKind='fixed' requires minOccurs to equal maxOccurs (%d != %d)", minOccurs, max)
-      case (___________, Fixed_____, max, ___) => separatedContentExactlyN(max)
-      case (Never______, Implicit__, UNB, ___) => SDE("separatorSuppressionPolicy='never' with occursCountKind='implicit' required bounded maxOccurs.")
-      case (Never______, Implicit__, max, ___) => separatedContentExactlyN(max)
-      case (Never______, ock /****/ , ___, __2) => SDE("separatorSuppressionPolicy='never' not allowed in combination with occursCountKind='" + ock + "'.")
-      case (Trailing___, Implicit__, UNB, ___) if (!isLastDeclaredRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
-      case (Trailing___, Implicit__, UNB, min) => separatedContentWithMinUnbounded
-      case (Trailing___, Implicit__, max, min) if min > 0 => separatedContentWithMinAndMax
-      case (Trailing___, Implicit__, max, ___) => separatedContentAtMostN // FIXME: have to have all of them - not trailing position
-      case (TrailingStr, Implicit__, UNB, ___) if (!isLastDeclaredRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
-      case (TrailingStr, Implicit__, UNB, ___) => separatedContentWithMinUnboundedWithoutTrailingEmpties // we're depending on optionalEmptyPart failing on empty content.
-      case (TrailingStr, Implicit__, max, ___) => separatedContentAtMostNWithoutTrailingEmpties
-      case (Always_____, Implicit__, UNB, ___) => separatedContentWithMinUnbounded
-      case (Always_____, Implicit__, max, ___) => separatedContentAtMostN
-      case (Always_____, Parsed____, ___, __2) => separatedContentZeroToUnbounded
-      case (Always_____, StopValue_, ___, __2) => separatedContentZeroToUnbounded
-      case (policy /**/ , ock /****/ , max, __2) => SDE("separatorSuppressionPolicy='" + policy + "' not allowed with occursCountKind='" + ock + "'.")
-    }
-    res
-  }
+  //  private lazy val arraySeparator = prod("separator", !ignoreES && hasES) {
+  //    delimMTA ~ ArrayElementSeparator(es, self)
+  //  }
+  //
+  //  private lazy val separatedEmpty = prod("separatedEmpty",
+  //    emptyIsAnObservableConcept && !isScalar) {
+  //      separatedForArrayPosition(empty)
+  //    }
+  //
+  //  private lazy val separatedRecurring = prod("separatedRecurring", !isScalar) {
+  //    separatedForArrayPosition(enclosedElement)
+  //  }
+  //
+  //  //  private lazy val separatedRecurringNonDefault = prod("separatedRecurringNonDefault", !isScalar) {
+  //  //    separatedForArrayPosition(enclosedElementNonDefault)
+  //  //  }
+  //
+  //  private lazy val nonSeparatedScalarDefaultable = prod("nonSeparatedScalarDefaultable", isScalar) { enclosedElement }
+  //
+  //  final override lazy val asTermInChoice = prod("asTermInChoice") {
+  //    nonSeparatedScalarDefaultable || recurrance
+  //  }
+  //
+  //  /**
+  //   * speculate parsing forward until we get an error
+  //   */
+  //  private lazy val separatedContentWithMinUnboundedWithoutTrailingEmpties = prod("separatedContentWithMinUnboundedWithoutTrailingEmpties", !isScalar) {
+  //    RepExactlyN(self, minOccurs, separatedRecurring) ~
+  //      RepUnbounded(self, separatedRecurring)
+  //  }
+  //
+  //  private lazy val separatedContentWithMinAndMaxWithoutTrailingEmpties = prod("separatedContentWithMinAndMaxWithoutTrailingEmpties", !isScalar) {
+  //    RepExactlyN(self, minOccurs, separatedRecurring) ~
+  //      RepAtMostTotalN(self, maxOccurs, separatedRecurring)
+  //  }
+  //
+  //  private lazy val separatedContentWithMinUnbounded = prod("separatedContentWithMinUnbounded", !isScalar) {
+  //    separatedContentWithMinUnboundedWithoutTrailingEmpties ~ unboundedTrailingEmpties
+  //  }
+  //
+  //  private lazy val unboundedTrailingEmpties = prod("unboundedTrailingEmpties", couldBeLastElementInModelGroup, forWhat = ForParser) {
+  //    RepUnbounded(self, separatedEmpty)
+  //  }
+  //
+  //  private lazy val separatedContentWithMinAndMax = prod("separatedContentWithMinAndMax", !isScalar) {
+  //    separatedContentWithMinAndMaxWithoutTrailingEmpties ~ unboundedTrailingEmpties
+  //  }
+  //
+  //  private lazy val separatedContentZeroToUnbounded = prod("separatedContentZeroToUnbounded", !isScalar) {
+  //    RepUnbounded(self, separatedRecurring)
+  //  }
+  //
+  //  private lazy val separatedContentAtMostNWithoutTrailingEmpties = prod("separatedContentAtMostNWithoutTrailingEmpties", !isScalar) {
+  //    RepExactlyN(self, minOccurs, separatedRecurring) ~
+  //      RepAtMostTotalN(this, maxOccurs, separatedRecurring)
+  //  }
+  //
+  //  // Question: Do we have to adjust the count to take stopValue into account?
+  //  // Answer: No because the counts are never used when there is a stopValue (at least in current
+  //  // thinking about how occursCountKind='stopValue' works.)
+  //
+  //  private lazy val separatedContentAtMostN = prod("separatedContentAtMostN") {
+  //    separatedContentAtMostNWithoutTrailingEmpties ~ repAtMostTotalNTrailingEmpties
+  //  }
+  //
+  //  private lazy val repAtMostTotalNTrailingEmpties = prod("repAtMostTotalNTrailingEmpties", couldBeLastElementInModelGroup, forWhat = ForParser) {
+  //    RepAtMostTotalN(self, maxOccurs, separatedEmpty)
+  //  }
+  //
+  //  // TODO FIXME: We really want to have different productions for parsing and unparsing in these
+  //  // complex cases where there is defaulting, etc. Unparsing has many fewer cases, and is just not
+  //  // symmetric with parsing in these situations.
+  //  private def separatedContentExactlyN(count: Long) = prod("separatedContentExactlyN") {
+  //    if (minOccurs == maxOccurs) {
+  //      // fixed length case. All are defaultable. Still might have a stop value tho.
+  //      RepExactlyN(self, count, separatedRecurring)
+  //    } else {
+  //      // variable length case. So some defaultable, some not.
+  //      RepExactlyN(self, minOccurs, separatedRecurring) ~
+  //        RepAtMostTotalN(self, count, separatedRecurring) ~
+  //        RepExactlyTotalN(self, maxOccurs, separatedEmpty) // absorb remaining separators after stop value.
+  //    }
+  //  }
+  //
+  //  private lazy val separatedContentExactlyNComputed = prod("separatedContentExactlyNComputed") {
+  //    OccursCountExpression(this) ~
+  //      RepAtMostOccursCount(this, minOccurs, separatedRecurring) ~
+  //      RepExactlyTotalOccursCount(this, separatedRecurring)
+  //  }
+  //
+  //  // keep in mind that anything here that scans for a representation either knows the length it is going after, or knows what the terminating markup is, and
+  //  // our invariant is, that it does NOT consume that markup ever. The parser consumes it with appropriate grammar terminals.
+  //
+  //  private val UNB = -1 // UNBOUNDED
+  //  private val ZERO = 0 // ZERO
+  //
+  //  lazy val recurrance = prod("recurrance", !isScalar) {
+  //    arrayContentsNoSeparators || arrayContentsWithSeparators
+  //  }
+  //
+  //  private lazy val contentUnbounded = prod("contentUnbounded") {
+  //    RepUnbounded(self, separatedRecurring)
+  //  }
+  //
+  //  //
+  //  // Silly constants to make the lookup tables below more readable without using fragile whitespace
+  //  //
+  //  private val Never______ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Never
+  //  private val Trailing___ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmpty
+  //  private val TrailingStr: SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmptyStrict
+  //  private val Always_____ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.AnyEmpty
+  //
+  //  private val StopValue_ = OccursCountKind.StopValue
+  //  private val Implicit__ = OccursCountKind.Implicit
+  //  private val Parsed____ = OccursCountKind.Parsed
+  //  private val Fixed_____ = OccursCountKind.Fixed
+  //  private val Expression = OccursCountKind.Expression
+  //
+  //  private lazy val arrayContentsNoSeparators = prod("arrayContentsNoSeparators", !isScalar && !hasSep) {
+  //    val res = (occursCountKind, minOccurs, maxOccurs) match {
+  //      case (Expression, ____, __2) => separatedContentExactlyNComputed
+  //      case (Fixed_____, ____, UNB) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs")
+  //      case (Fixed_____, min_, max) if (min_ != max) => SDE("occursCountKind='fixed' requires minOccurs and maxOccurs to be equal (%d != %d)", min_, max)
+  //      case (Fixed_____, ____, max) => separatedContentExactlyN(max)
+  //      case (Implicit__, ZERO, UNB) => contentUnbounded // same as parsed
+  //      case (Implicit__, min_, UNB) => RepExactlyN(self, min_, separatedRecurring) ~ contentUnbounded // respects minOccurs
+  //      case (Implicit__, ____, __2) => separatedContentAtMostN // uses min and maxOccurs
+  //      case (Parsed____, ____, __2) => contentUnbounded
+  //      case (StopValue_, ____, __2) => contentUnbounded
+  //    }
+  //    res
+  //  }
+  //
+  //  /**
+  //   * Matches the table about separator suppression policy.
+  //   *
+  //   * TODO: Update this table to match the final spec.
+  //   */
+  //  private lazy val arrayContentsWithSeparators = prod("arrayContentsWithSeparators", !isScalar && hasSep) {
+  //    val triple = (separatorSuppressionPolicy, occursCountKind, maxOccurs, minOccurs)
+  //    val res = triple match {
+  //      case (___________, Expression, ___, __2) => separatedContentExactlyNComputed
+  //      case (___________, Fixed_____, UNB, ___) => SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs")
+  //      case (___________, Fixed_____, max, min) if (max != min) => SDE("occursCountKind='fixed' requires minOccurs to equal maxOccurs (%d != %d)", minOccurs, max)
+  //      case (___________, Fixed_____, max, ___) => separatedContentExactlyN(max)
+  //      case (Never______, Implicit__, UNB, ___) => SDE("separatorSuppressionPolicy='never' with occursCountKind='implicit' required bounded maxOccurs.")
+  //      case (Never______, Implicit__, max, ___) => separatedContentExactlyN(max)
+  //      case (Never______, ock /****/ , ___, __2) => SDE("separatorSuppressionPolicy='never' not allowed in combination with occursCountKind='" + ock + "'.")
+  //      case (Trailing___, Implicit__, UNB, ___) if (!isLastDeclaredRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
+  //      case (Trailing___, Implicit__, UNB, min) => separatedContentWithMinUnbounded
+  //      case (Trailing___, Implicit__, max, min) if min > 0 => separatedContentWithMinAndMax
+  //      case (Trailing___, Implicit__, max, ___) => separatedContentAtMostN
+  //      case (TrailingStr, Implicit__, UNB, ___) if (!isLastDeclaredRequiredElementOfSequence) => SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
+  //      case (TrailingStr, Implicit__, UNB, ___) => separatedContentWithMinUnboundedWithoutTrailingEmpties // we're depending on optionalEmptyPart failing on empty content.
+  //      case (TrailingStr, Implicit__, max, ___) => separatedContentAtMostNWithoutTrailingEmpties
+  //      case (Always_____, Implicit__, UNB, ___) => separatedContentWithMinUnboundedWithoutTrailingEmpties
+  //      case (Always_____, Implicit__, max, ___) => separatedContentAtMostNWithoutTrailingEmpties
+  //      case (Always_____, Parsed____, ___, __2) => separatedContentZeroToUnbounded
+  //      case (Always_____, StopValue_, ___, __2) => separatedContentZeroToUnbounded
+  //      case (policy /**/ , ock /****/ , max, __2) => SDE("separatorSuppressionPolicy='" + policy + "' not allowed with occursCountKind='" + ock + "'.")
+  //    }
+  //    res
+  //  }
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
index bcbbc8629..351ac8648 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/ModelGroupGrammarMixin.scala
@@ -40,10 +40,6 @@ trait ModelGroupGrammarMixin
 
   private lazy val groupRightFraming = prod("groupRightFraming") { TrailingSkipRegion(this) }
 
-  // I believe we can have the same grammar rules whether we're directly inside a complex type, or
-  // we're nested inside another group as a term.
-  final lazy val asChildOfComplexType = termContentBody
-
   final override lazy val termContentBody = prod("termContentBody") {
     dfdlStatementEvaluations ~ groupLeftFraming ~ _content ~ groupRightFraming
   }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/SequenceGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/SequenceGrammarMixin.scala
index 724b29cbd..f83bac1a0 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/SequenceGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/SequenceGrammarMixin.scala
@@ -17,36 +17,113 @@
 
 package org.apache.daffodil.grammar
 import org.apache.daffodil.schema.annotation.props.gen._
-import org.apache.daffodil.grammar.primitives.SequenceCombinator
-import org.apache.daffodil.dsom.SequenceTermBase
-import org.apache.daffodil.grammar.primitives.LayeredSequence
+import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.grammar.primitives._
+import org.apache.daffodil.grammar.primitives.OrderedSequence
 
 trait SequenceGrammarMixin extends GrammarMixin { self: SequenceTermBase =>
 
   final override lazy val groupContent = prod("groupContent") {
-    if (isLayered) layeredSequenceContent
-    else {
-      self.sequenceKind match {
-        case SequenceKind.Ordered => orderedSequenceContent
-        case SequenceKind.Unordered => subsetError("Unordered sequences are not supported.") // unorderedSequenceContent
-      }
+    if (isLayered) layerContent
+    else sequenceContent
+  }
+
+  final lazy val sequenceContent = {
+    import columnConstants._
+    self.sequenceKind match {
+      case Ordered__ => orderedSequence
+      case Unordered => subsetError("Unordered sequences are not supported.") // unorderedSequenceContent
     }
   }
 
-  private lazy val layeredSequenceContent = {
+  private lazy val layerContent = {
     schemaDefinitionUnless(groupMembers.length == 1, "Layered sequence can have only 1 child term. %s were found: %s", groupMembers.length,
       groupMembers.mkString(", "))
     val term = groupMembers(0)
     schemaDefinitionWhen(term.isArray, "Layered sequence body cannot be an array.")
-    val termGram = term.termContentBody
-    LayeredSequence(this, termGram)
+    LayeredSequence(this, new ScalarOrderedRequiredSequenceChild(this, term, 1))
+  }
+
+  private lazy val seqChildren = (groupMembers zip Stream.from(1)).map {
+    case (gm, i) =>
+      sequenceChild(gm, i)
+  }
+
+  private lazy val orderedSequence = {
+    val res = new OrderedSequence(this, seqChildren)
+    res
   }
 
-  private lazy val orderedSequenceContent = prod("sequenceContent") {
-    SequenceCombinator(this, terms)
+  /**
+   * Constants to make the lookup tables below more readable without using fragile whitespace
+   */
+  private object columnConstants {
+    val UNB = -1 // UNBOUNDED
+    val ZER = 0
+    val ONE = 1
+
+    val Sep__ = true
+    val NoSep = false
+
+    val Ordered__ = SequenceKind.Ordered
+    val Unordered = SequenceKind.Unordered
+
+    val Never______ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.Never
+    val Trailing___ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmpty
+    val TrailingStr: SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.TrailingEmptyStrict
+    val Always_____ : SeparatorSuppressionPolicy = SeparatorSuppressionPolicy.AnyEmpty
+
+    val StopValue_ = OccursCountKind.StopValue
+    val Implicit__ = OccursCountKind.Implicit
+    val Parsed____ = OccursCountKind.Parsed
+    val Fixed_____ = OccursCountKind.Fixed
+    val Expression = OccursCountKind.Expression
+
+    type EB = ElementBase
+    type MG = ModelGroup
+    type SG = SequenceTermBase
+    type CG = ChoiceTermBase
   }
 
-  protected lazy val terms = groupMembers.map { _.asTermInSequence }
+  /**
+   * Produces the right kind of SequenceChild object for this particular child
+   * for the role it must play within this sequence's behavior.
+   *
+   * A SequenceChild object is effectively generator for part of the Sequence's parse/unparse
+   * algorithm. For arrays these SequenceChild objects enable processing exactly one array instance at
+   * a time, orchestrated by the surrounding sequence's processor.
+   */
+  private def sequenceChild(child: Term, groupIndex: Int): SequenceChild = {
+    import columnConstants._
+    val (max, min, ock) = child match {
+      case e: EB => (e.maxOccurs, e.minOccurs, e.occursCountKind)
+      case _ => (1, 1, null)
+    }
+    val ssp = separatorSuppressionPolicy
+    val res = (child, sequenceKind, hasSeparator, ssp, ock, min, max) match {
+      case (e: EB, Ordered__, _____, ___________, _________, ONE, ONE) => new ScalarOrderedRequiredSequenceChild(this, e, groupIndex)
+      case (e: EB, _________, _____, ___________, StopValue_, ___, __2) => e.subsetError("dfdl:occursCountKind 'stopValue' is not supported.")
+      case (_____, Unordered, ____2, ___________, __________, ___, __2) => this.subsetError("Unordered sequences are not supported.")
+      case (e: EB, Ordered__, _____, ___________, Fixed_____, ___, UNB) => e.SDE("occursCountKind='fixed' not allowed with unbounded maxOccurs")
+      case (e: EB, Ordered__, _____, ___________, Fixed_____, min, `min`) => new RepOrderedExactlyNSequenceChild(this, e, groupIndex, min)
+      case (e: EB, Ordered__, _____, ___________, Fixed_____, min, max) => { Assert.invariant(min != max); e.SDE("occursCountKind='fixed' requires minOccurs and maxOccurs to be equal (%d != %d)", min, max) }
+      case (e: EB, Ordered__, _____, ___________, Expression, ___, __2) => new RepOrderedExactlyTotalOccursCountSequenceChild(this, e, groupIndex)
+      case (e: EB, Ordered__, _____, Never______, Implicit__, ___, UNB) => e.SDE("separatorSuppressionPolicy='never' with occursCountKind='implicit' requires bounded maxOccurs.")
+      case (e: EB, Ordered__, _____, Never______, Implicit__, ___, max) => new RepOrderedExactlyNSequenceChild(this, e, groupIndex, max)
+      case (e: EB, Ordered__, _____, Never______, ock /****/ , ___, __2) => e.SDE("separatorSuppressionPolicy='never' not allowed in combination with occursCountKind='" + ock + "'.")
+      case (e: EB, Ordered__, _____, Trailing___, Implicit__, ___, UNB) if (!e.isLastDeclaredRequiredElementOfSequence) => e.SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
+      case (e: EB, Ordered__, _____, Trailing___, Implicit__, min, max) => new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex, "MinMaxTrailingLax")
+      case (e: EB, Ordered__, _____, TrailingStr, Implicit__, ___, UNB) if (!e.isLastDeclaredRequiredElementOfSequence) => e.SDE("occursCountKind='implicit' with unbounded maxOccurs only allowed for last element of a sequence")
+      case (e: EB, Ordered__, _____, TrailingStr, Implicit__, ___, max) => new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex, "MinUnboundedTrailingStrict", min, -2)
+      case (e: EB, Ordered__, _____, Always_____, Implicit__, ___, max) => new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex, "MinMaxAlways")
+      case (e: EB, Ordered__, _____, Always_____, Parsed____, ___, __2) => new RepOrderedWithMinMaxSequenceChild(this, e, groupIndex, "Unbounded")
+      case (m: MG, Ordered__, _____, ___________, __________, ___, __2) => new ScalarOrderedRequiredSequenceChild(this, m, groupIndex)
+      case (_____, _________, ____2, policy /**/ , ock /**/ , ___, __2) => child.SDE("separatorSuppressionPolicy='" + policy + "' not allowed with occursCountKind='" + ock + "'.")
+    }
+    res
+  }
 
   /**
    * These are static properties even though the delimiters can have runtime-computed values.
@@ -65,5 +142,19 @@ trait SequenceGrammarMixin extends GrammarMixin { self: SequenceTermBase =>
   }
 
   final lazy val hasSeparator = separatorParseEv.isKnownNonEmpty
+
+  lazy val sequenceSeparator = prod("separator", hasSeparator) {
+    //
+    // TODO: (JIRA DFDL-1400) The separators may be in a different encoding than the terms
+    // that they separate.
+    //
+    // So we must allow for a change of encoding (which may also imply a change
+    // of bit order)
+    //
+    // However, this isn't the same as just plopping down a bitOrderChange ~ encodingChange, since
+    // those examine prior peer, and what we want to scrutinize is the prior term being separated.
+    //
+    delimMTA ~ SequenceSeparator(this)
+  }
 }
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
index 5d086cf8e..6df7bdb19 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/TermGrammarMixin.scala
@@ -16,17 +16,12 @@
  */
 
 package org.apache.daffodil.grammar
-import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.dsom.DFDLNewVariableInstance
-import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.dsom.Term
-import org.apache.daffodil.grammar.primitives.OptionalInfixSep
-import org.apache.daffodil.grammar.primitives.Nada
-import org.apache.daffodil.grammar.primitives.MandatoryTextAlignment
-import org.apache.daffodil.grammar.primitives.Separator
+import org.apache.daffodil.grammar.primitives._
 
 /////////////////////////////////////////////////////////////////
-// Groups System
+// Common to all Terms (Elements and ModelGroups
 /////////////////////////////////////////////////////////////////
 
 trait TermGrammarMixin
@@ -41,8 +36,8 @@ trait TermGrammarMixin
     st.isInstanceOf[DFDLNewVariableInstance]
   }.asInstanceOf[Seq[DFDLNewVariableInstance]]
 
-  private lazy val newVarStarts = newVars.map { _.gram }
-  private lazy val newVarEnds = newVars.map { _.endGram }
+  private lazy val newVarStarts = newVars.map { _.gram(self) }
+  private lazy val newVarEnds = newVars.map { _.endGram(self) }
 
   protected lazy val hasEncoding = optionEncodingRaw.isDefined
 
@@ -55,46 +50,24 @@ trait TermGrammarMixin
     newVarEnds.fold(mt) { _ ~ _ }
   }
 
-  // I am not sure we need to distinguish these two.
-  final lazy val asTermInSequence = prod("asTermInSequence") {
-    separatedForSequencePosition(termContentBody)
-  }
-
   /**
    * overridden in LocalElementGrammarMixin
    */
   lazy val asTermInChoice = termContentBody
 
-  /**
-   * separator combinators - detect cases where no separator applies.
-   * Note that repeating elements are excluded because they have to
-   * managed their own separatedForArrayPosition inside the repetition.
-   */
-  protected final def separatedForArrayPosition(bodyArg: => Gram): Gram = {
-    val body = bodyArg
-    val (isElementWithNoRep, isRepeatingElement) = body.context match {
-      case e: ElementBase => (!e.isRepresented, !e.isScalar)
-      case other => (false, false)
-    }
-    Assert.usage(isRepeatingElement)
-    Assert.invariant(!isElementWithNoRep) //inputValueCalc not allowed on arrays in DFDL v1.0
-    val res = prefixSep ~ infixSepRule ~ body ~ postfixSep
-    res
-  }
-
-  protected final def separatedForSequencePosition(bodyArg: => Gram): Gram = {
-    val body = bodyArg
-    val (isElementWithNoRep, isRepeatingElement) = body.context match {
-      case e: ElementBase => (!e.isRepresented, !e.isScalar)
-      case other => (false, false)
-    }
-    if (isElementWithNoRep) body // no separators for things that have no representation in the data stream
-    else if (isRepeatingElement) body
-    else {
-      val res = prefixSep ~ infixSepRule ~ body ~ postfixSep
-      res
-    }
-  }
+  //  protected final def separatedForSequencePosition(bodyArg: => Gram): Gram = {
+  //    val body = bodyArg
+  //    val (isElementWithNoRep, isRepeatingElement) = body.context match {
+  //      case e: ElementBase => (!e.isRepresented, !e.isScalar)
+  //      case other => (false, false)
+  //    }
+  //    if (isElementWithNoRep) body // no separators for things that have no representation in the data stream
+  //    else if (isRepeatingElement) body
+  //    else {
+  //      val res = prefixSep ~ infixSepRule ~ body ~ postfixSep
+  //      res
+  //    }
+  //  }
 
   // public for unit testing use.
   final lazy val Some(es) = {
@@ -128,46 +101,31 @@ trait TermGrammarMixin
     nearestEnclosingSequence
   }
 
-  private def hasES = nearestEnclosingSequence != None
-  private def ignoreES = inChoiceBeforeNearestEnclosingSequence == true
+  protected final def hasES = nearestEnclosingSequence != None
+  protected final def ignoreES = inChoiceBeforeNearestEnclosingSequence == true
 
-  private lazy val separatorItself = prod("separator", !ignoreES && hasES) {
-    //
-    // TODO: (JIRA DFDL-1400) The separators may be in a different encoding than the terms
-    // that they separate.
-    //
-    // So we must allow for a change of encoding (which may also imply a change
-    // of bit order)
-    //
-    // However, this isn't the same as just plopping down a bitOrderChange ~ encodingChange, since
-    // those examine prior peer, and what we want to scrutinize is the prior term being separated.
-    //
-    delimMTA ~ Separator(es, self)
-  }
+  //  private lazy val sepRule = separatorItself
 
-  private lazy val sepRule = separatorItself
+  //  private lazy val prefixSep = prod("prefixSep", !ignoreES && hasES && es.hasPrefixSep) {
+  //    sepRule
+  //  }
 
-  private lazy val prefixSep = prod("prefixSep", !ignoreES && hasES && es.hasPrefixSep) {
-    sepRule
-  }
-
-  private lazy val postfixSep = prod("postfixSep", !ignoreES && hasES && es.hasPostfixSep) { sepRule }
-  private lazy val infixSep = prod("infixSep", !ignoreES && hasES && es.hasInfixSep) { sepRule }
+  //  private lazy val postfixSep = prod("postfixSep", !ignoreES && hasES && es.hasPostfixSep) { sepRule }
+  // private lazy val infixSep = prod("infixSep", !ignoreES && hasES && es.hasInfixSep) { separatorItself }
 
-  private lazy val isStaticallyFirst = {
-    es.hasInfixSep &&
-      this.positionInNearestEnclosingSequence == 1 &&
-      isScalar &&
-      !hasPriorRequiredSiblings
-  }
-
-  private lazy val infixSepRule = prod("infixSepRule", !ignoreES && hasES && es.hasInfixSep) {
-    if (isStaticallyFirst) Nada(this) // we're first, no infix sep.
-    else if (hasPriorRequiredSiblings) infixSep // always in this case
-    else if (positionInNearestEnclosingSequence > 1 || !isScalar) {
-      new OptionalInfixSep(this, infixSep)
-    } else Assert.invariantFailed("infixSepRule didn't understand what to lay down as grammar for this situation: " + this)
-  }
+  //  private lazy val isStaticallyFirst = {
+  //    es.hasInfixSep &&
+  //      this.positionInNearestEnclosingSequence == 1 &&
+  //      isScalar &&
+  //      !hasPriorRequiredSiblings
+  //  }
+  //
+  //  private lazy val infixSepRule = prod("infixSepRule", !ignoreES && hasES && es.hasInfixSep) {
+  //    if (hasPriorRequiredSiblings) infixSep // always in this case
+  //    else if (positionInNearestEnclosingSequence > 1 || !isScalar) {
+  //      new OptionalInfixSep(this, separatorItself)
+  //    } else Assert.invariantFailed("infixSepRule didn't understand what to lay down as grammar for this situation: " + this)
+  //  }
 
   /**
    * Mandatory text alignment or mta
@@ -178,14 +136,14 @@ trait TermGrammarMixin
    * in the property environment shouldn't get you an MTA region. It has
    * to be textual.
    */
-  protected lazy val mtaBase = prod("mandatoryTextAlignment", hasEncoding) {
+  protected final lazy val mtaBase = prod("mandatoryTextAlignment", hasEncoding) {
     MandatoryTextAlignment(this, knownEncodingAlignmentInBits, false)
   }
 
   /**
    * Mandatory text alignment for delimiters
    */
-  protected lazy val delimMTA = prod("delimMTA",
+  protected final lazy val delimMTA = prod("delimMTA",
     {
       hasDelimiters
     }) {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
new file mode 100644
index 000000000..842a98ed1
--- /dev/null
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ChoiceCombinator.scala
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.grammar.primitives
+
+import org.apache.daffodil.grammar.Terminal
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.processors.parsers._
+import org.apache.daffodil.processors.unparsers._
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.cookers.ChoiceBranchKeyCooker
+import org.apache.daffodil.api.WarnID
+import org.apache.daffodil.equality._
+
+/*
+ * The purpose of the ChoiceCombinator (and the parsers it creates) is to
+ * determine which branch to go down. In the parser case, for non-direct
+ * dispatch, we just rely on backtracking here.
+ *
+ * For direct dispatch, we create a disapatch-branch key map
+ * which is used to determine which branch to parse at runtime.
+ *
+ * In the unparser case, we know which element we got from the infoset, but we
+ * need to determine which branch of the choice to take at runtime. This
+ * unparser uses a Map to make the determination based on the element seen.
+ */
+case class ChoiceCombinator(ch: ChoiceTermBase, rawAlternatives: Seq[Gram])
+  extends Terminal(ch, !rawAlternatives.isEmpty) {
+
+  private lazy val alternatives = rawAlternatives.filterNot(_.isEmpty)
+
+  private lazy val parsers = alternatives.map { _.parser }.filterNot { _.isEmpty }
+
+  override def isEmpty = super.isEmpty || alternatives.isEmpty
+
+  lazy val parser: Parser = {
+    if (!ch.isDirectDispatch) {
+      new ChoiceParser(ch.termRuntimeData, parsers)
+    } else {
+      val dispatchBranchKeyValueTuples = alternatives.flatMap { alt =>
+        val keyTerm = alt.context.asInstanceOf[Term]
+        val cbk = keyTerm.choiceBranchKey
+        // Note that this behaves differently than the specification, since
+        // this accepts a space separated list of keys
+        val cbks = ChoiceBranchKeyCooker.convertConstant(cbk, keyTerm.runtimeData, forUnparse = false)
+        cbks.map { (_, alt) }
+      }
+
+      // check for duplicate dfdl:choiceBranchKeys
+      val groupedByKey = dispatchBranchKeyValueTuples.groupBy(_._1)
+      groupedByKey.foreach {
+        case (k, kvs) =>
+          if (kvs.length > 1) {
+            SDE("dfdl:choiceBranchKey value (%s) is not unique across all branches of a direct dispatch choice. Offending branches are:\n%s",
+              k, kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
+          }
+      }
+
+      val dispatchBranchKeyMap = dispatchBranchKeyValueTuples.toMap.mapValues(_.parser)
+      val serializableMap = dispatchBranchKeyMap.map(identity)
+
+      new ChoiceDispatchCombinatorParser(ch.termRuntimeData, ch.choiceDispatchKeyEv, serializableMap)
+    }
+  }
+
+  override lazy val unparser: Unparser = {
+    if (!ch.isHidden) {
+      val eventRDMap = ch.choiceBranchMap
+      val eventUnparserMap = eventRDMap.flatMap {
+        case (cbe, rd) =>
+          // if we don't find a matching RD for a term that's probably
+          // because the term is an empty sequence or empty choice (which do happen
+          // and we even have tests for them). Since those can never be chosen by
+          // means of an element event, they don't appear in the map.
+          val altGram = alternatives.find { alt =>
+            val crd = alt.context.runtimeData
+            val found = crd =:= rd
+            found
+          }
+          altGram.map { ag => (cbe, ag.unparser) }
+      }
+      val mapValues = eventUnparserMap.map { case (k, v) => v }.toSeq.filterNot(_.isEmpty)
+      if (mapValues.isEmpty)
+        new NadaUnparser(null)
+      else
+        new ChoiceCombinatorUnparser(ch.modelGroupRuntimeData, eventUnparserMap)
+    } else {
+      // Choices inside a hidden group ref are slightly different because we
+      // will never see events for any of the branches. Instead, we will just
+      // always pick the branch in which every thing is defaultble or OVC. It
+      // is a warning if more than one of those branches exist. It is an SDE if
+      // such a branch does not exist, which is detected elsewhere
+
+      // this call is necessary since it will throw an SDE if no choice branch
+      // was defaultable
+      ch.childrenInHiddenGroupNotDefaultableOrOVC
+      val defaultableBranches = ch.groupMembers.filter { _.childrenInHiddenGroupNotDefaultableOrOVC.length == 0 }
+      Assert.invariant(defaultableBranches.length > 0)
+      if (defaultableBranches.length > 1) {
+        SDW(WarnID.ChoiceInsideHiddenGroup, "xs:choice inside a hidden group has unparse ambiguity: multiple branches exist with all children either defaulable or have the dfdl:outputValueCalc property set. The first branch will be chosen during unparse. Defaultable branches are:\n%s",
+          defaultableBranches.mkString("\n"))
+      }
+      val defaultableBranchRD = defaultableBranches(0).runtimeData
+      val defaultableBranchUnparser = alternatives.find(_.context.runtimeData =:= defaultableBranchRD).get.unparser
+      new HiddenChoiceCombinatorUnparser(ch.modelGroupRuntimeData, defaultableBranchUnparser)
+    }
+  }
+}
+
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ComplexTypeCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ComplexTypeCombinator.scala
new file mode 100644
index 000000000..5ff78e603
--- /dev/null
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ComplexTypeCombinator.scala
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.grammar.primitives
+
+import org.apache.daffodil.grammar.Terminal
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.processors.parsers.{ Parser => DaffodilParser }
+import org.apache.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
+import org.apache.daffodil.processors.parsers._
+import org.apache.daffodil.processors.unparsers._
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.util.Misc
+
+case class ComplexTypeCombinator(ct: ComplexTypeBase, body: Gram) extends Terminal(ct.elementDecl, !body.isEmpty) {
+
+  override def isEmpty = body.isEmpty
+
+  private lazy val p = body.parser
+  private lazy val u = body.unparser
+
+  override def toString() =
+    "<" + Misc.getNameFromClass(this) + ">" +
+      body.toString() +
+      "</" + Misc.getNameFromClass(this) + ">"
+
+  override lazy val parser: DaffodilParser =
+    if (p.isEmpty)
+      p
+    else
+      new ComplexTypeParser(ct.runtimeData, p)
+
+  override lazy val unparser: DaffodilUnparser =
+    if (u.isEmpty)
+      u
+    else
+      new ComplexTypeUnparser(ct.runtimeData, u)
+}
+
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/DelimiterAndEscapeRelated.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/DelimiterAndEscapeRelated.scala
new file mode 100644
index 000000000..55ce02519
--- /dev/null
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/DelimiterAndEscapeRelated.scala
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.grammar.primitives
+
+import org.apache.daffodil.grammar.Terminal
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.processors.parsers.{ Parser => DaffodilParser }
+import org.apache.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
+import org.apache.daffodil.processors.parsers._
+import org.apache.daffodil.processors.unparsers._
+import org.apache.daffodil.grammar.Gram
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.util.Maybe._
+import org.apache.daffodil.util.Misc
+import org.apache.daffodil.xml.XMLUtils
+
+case class DelimiterStackCombinatorSequence(sq: SequenceTermBase, body: Gram) extends Terminal(sq, !body.isEmpty) {
+  lazy val pInit = if (sq.initiatorParseEv.isKnownNonEmpty) One(sq.initiatorParseEv) else Nope
+  lazy val pSep = if (sq.separatorParseEv.isKnownNonEmpty) One(sq.separatorParseEv) else Nope
+  lazy val pTerm = if (sq.terminatorParseEv.isKnownNonEmpty) One(sq.terminatorParseEv) else Nope
+
+  lazy val uInit = if (sq.initiatorParseEv.isKnownNonEmpty) One(sq.initiatorUnparseEv) else Nope
+  lazy val uSep = if (sq.separatorParseEv.isKnownNonEmpty) One(sq.separatorUnparseEv) else Nope
+  lazy val uTerm = if (sq.terminatorParseEv.isKnownNonEmpty) One(sq.terminatorUnparseEv) else Nope
+
+  lazy val parser: DaffodilParser = new DelimiterStackParser((pInit.toList ++ pSep.toList ++ pTerm.toList).toArray, sq.runtimeData, body.parser)
+
+  override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, uSep, uTerm, sq.termRuntimeData, body.unparser)
+}
+
+case class DelimiterStackCombinatorChoice(ch: ChoiceTermBase, body: Gram) extends Terminal(ch, !body.isEmpty) {
+  lazy val pInit = if (ch.initiatorParseEv.isKnownNonEmpty) One(ch.initiatorParseEv) else Nope
+  lazy val pTerm = if (ch.terminatorParseEv.isKnownNonEmpty) One(ch.terminatorParseEv) else Nope
+
+  lazy val uInit = if (ch.initiatorParseEv.isKnownNonEmpty) One(ch.initiatorUnparseEv) else Nope
+  lazy val uTerm = if (ch.terminatorParseEv.isKnownNonEmpty) One(ch.terminatorUnparseEv) else Nope
+
+  lazy val parser: DaffodilParser = new DelimiterStackParser((pInit.toList ++ pTerm.toList).toArray, ch.runtimeData, body.parser)
+
+  override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, None, uTerm, ch.termRuntimeData, body.unparser)
+}
+
+case class DelimiterStackCombinatorElement(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
+  lazy val pInit = if (e.initiatorParseEv.isKnownNonEmpty) One(e.initiatorParseEv) else Nope
+  lazy val pTerm = if (e.terminatorParseEv.isKnownNonEmpty) One(e.terminatorParseEv) else Nope
+
+  lazy val uInit = if (e.initiatorParseEv.isKnownNonEmpty) One(e.initiatorUnparseEv) else Nope
+  lazy val uTerm = if (e.terminatorParseEv.isKnownNonEmpty) One(e.terminatorUnparseEv) else Nope
+
+  lazy val delims = (pInit.toList ++ pTerm.toList)
+
+  override def toString() = {
+    val delimAttrib = delims.map { _.toString }.map { XMLUtils.escape(_).toString() }.mkString(" ")
+    "<" + Misc.getNameFromClass(this) + " delims='" + delimAttrib + "'>" +
+      body.toString() +
+      "</" + Misc.getNameFromClass(this) + ">"
+  }
+  lazy val parser: DaffodilParser = {
+    val p = body.parser
+    if (p.isEmpty) p
+    else new DelimiterStackParser(delims.toArray, e.termRuntimeData, p)
+  }
+
+  override lazy val unparser: DaffodilUnparser = {
+    val u = body.unparser
+    if (u.isEmpty) u
+    else new DelimiterStackUnparser(uInit, None, uTerm, e.termRuntimeData, u)
+  }
+}
+
+case class DynamicEscapeSchemeCombinatorElement(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
+
+  val schemeParseOpt = e.optionEscapeScheme.map { _.escapeSchemeParseEv }
+  val schemeUnparseOpt = e.optionEscapeScheme.map { _.escapeSchemeUnparseEv }
+
+  Assert.invariant(schemeParseOpt.isDefined && !schemeParseOpt.get.isConstant)
+  Assert.invariant(schemeUnparseOpt.isDefined && !schemeUnparseOpt.get.isConstant)
+
+  lazy val parser: DaffodilParser = {
+    val p = body.parser
+    if (p.isEmpty) p
+    else new DynamicEscapeSchemeParser(schemeParseOpt.get, e.termRuntimeData, p)
+  }
+
+  override lazy val unparser: DaffodilUnparser = {
+    val u = body.unparser
+    if (u.isEmpty) u
+    else new DynamicEscapeSchemeUnparser(schemeUnparseOpt.get, e.termRuntimeData, u)
+  }
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
index 7dcc7b1fa..f72d3e36d 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/ElementCombinator.scala
@@ -21,7 +21,6 @@ import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.equality.TypeEqual
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.grammar.Gram
-import org.apache.daffodil.grammar.HasNoUnparser
 import org.apache.daffodil.grammar.NamedGram
 import org.apache.daffodil.grammar.Terminal
 import org.apache.daffodil.processors.parsers.ChoiceElementParser
@@ -43,14 +42,12 @@ import org.apache.daffodil.processors.unparsers.ElementUnparserNoRep
 import org.apache.daffodil.processors.unparsers.ElementUnspecifiedLengthUnparser
 import org.apache.daffodil.processors.unparsers.ElementUnusedUnparser
 import org.apache.daffodil.processors.unparsers.LeftCenteredPaddingUnparser
-import org.apache.daffodil.processors.unparsers.NilLiteralCharacterUnparser
 import org.apache.daffodil.processors.unparsers.OVCRetryUnparser
 import org.apache.daffodil.processors.unparsers.OnlyPaddingUnparser
 import org.apache.daffodil.processors.unparsers.RightCenteredPaddingUnparser
 import org.apache.daffodil.processors.unparsers.RightFillUnparser
 import org.apache.daffodil.processors.unparsers.Unparser
 import org.apache.daffodil.schema.annotation.props.gen.LengthKind
-import org.apache.daffodil.schema.annotation.props.gen.NilKind
 import org.apache.daffodil.schema.annotation.props.gen.Representation
 import org.apache.daffodil.schema.annotation.props.gen.TestKind
 import org.apache.daffodil.util.Maybe
@@ -97,7 +94,7 @@ class ElementCombinator(context: ElementBase,
     subComb.parser
   }
 
-  private lazy val uSetVars = context.setVariableStatements.map(_.gram.unparser).toArray
+  private lazy val uSetVars = context.setVariableStatements.map(_.gram(context).unparser).toArray
 
   private lazy val eBeforeUnparser: Maybe[Unparser] =
     if (eBeforeContent.isEmpty) Maybe.Nope
@@ -167,22 +164,22 @@ case class OnlyPadding(ctxt: ElementBase)
   }
 }
 
-case class NilLiteralCharacter(ctxt: ElementBase)
-  extends Terminal(ctxt, ctxt.maybeUnparseTargetLengthInBitsEv.isDefined &&
-    ctxt.isNillable && ctxt.nilKind == NilKind.LiteralCharacter)
-  with Padded {
-
-  override def parser = new NadaParser(ctxt.erd)
-
-  private lazy val nilLitCharacter = ctxt.cookedNilValuesForUnparse.head(0)
-
-  override lazy val unparser: Unparser =
-    new NilLiteralCharacterUnparser(ctxt.erd,
-      ctxt.maybeUnparseTargetLengthInBitsEv.get,
-      ctxt.maybeLengthEv,
-      ctxt.maybeCharsetEv,
-      nilLitCharacter)
-}
+//case class NilLiteralCharacter(ctxt: ElementBase)
+//  extends Terminal(ctxt, ctxt.maybeUnparseTargetLengthInBitsEv.isDefined &&
+//    ctxt.isNillable && ctxt.nilKind == NilKind.LiteralCharacter)
+//  with Padded {
+//
+//  override def parser = new NadaParser(ctxt.erd)
+//
+//  private lazy val nilLitCharacter = ctxt.cookedNilValuesForUnparse.head(0)
+//
+//  override lazy val unparser: Unparser =
+//    new NilLiteralCharacterUnparser(ctxt.erd,
+//      ctxt.maybeUnparseTargetLengthInBitsEv.get,
+//      ctxt.maybeLengthEv,
+//      ctxt.maybeCharsetEv,
+//      nilLitCharacter)
+//}
 
 case class RightCenteredPadding(ctxt: ElementBase)
   extends Terminal(ctxt, ctxt.shouldAddPadding)
@@ -390,7 +387,7 @@ class ElementParseAndUnspecifiedLength(context: ElementBase, eBeforeGram: Gram,
 }
 
 class ChoiceElementCombinator(context: ElementBase, eGramBefore: Gram, eGram: Gram, eAfterGram: Gram)
-  extends ElementCombinatorBase(context, eGramBefore, eGram, eAfterGram) with HasNoUnparser {
+  extends ElementCombinatorBase(context, eGramBefore, eGram, eAfterGram) {
 
   lazy val parser: Parser = new ChoiceElementParser(
     context.erd,
@@ -404,6 +401,8 @@ class ChoiceElementCombinator(context: ElementBase, eGramBefore: Gram, eGram: Gr
     eParser,
     eAfterParser)
 
+  lazy val unparser = hasNoUnparser
+
 }
 
 abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eGram: Gram, eGramAfter: Gram)
@@ -431,50 +430,53 @@ abstract class ElementCombinatorBase(context: ElementBase, eGramBefore: Gram, eG
     if (pd.size == 0) {
       Maybe.Nope
     } else {
-      Maybe(pd(0).gram.parser)
+      pd(0).gram(context).maybeParser
     }
   }
-  lazy val patAssert = context.assertStatements.filter(_.testKind == TestKind.Pattern).map(_.gram.parser).toArray
-  lazy val pSetVar = context.setVariableStatements.map(_.gram.parser).toArray
+  lazy val patAssert = context.assertStatements.filter(_.testKind == TestKind.Pattern).map(_.gram(context).parser).toArray
+  lazy val pSetVar = context.setVariableStatements.map(_.gram(context).parser).toArray
   lazy val testDiscrim = {
     val td = context.discriminatorStatements.filter(_.testKind == TestKind.Expression)
     Assert.invariant(td.size <= 1)
     if (td.size == 0) {
       Maybe.Nope
     } else {
-      Maybe(td(0).gram.parser)
+      td(0).gram(context).maybeParser
     }
   }
-  lazy val testAssert = context.assertStatements.filter(_.testKind == TestKind.Expression).map(_.gram.parser).toArray
+  lazy val testAssert = context.assertStatements.filter(_.testKind == TestKind.Expression).map(_.gram(context).parser).toArray
 
-  lazy val eBeforeParser: Maybe[Parser] =
-    if (eGramBefore.isEmpty) Maybe.Nope
-    else Maybe(eGramBefore.parser)
+  lazy val eBeforeParser: Maybe[Parser] = eGramBefore.maybeParser
 
-  lazy val eParser: Maybe[Parser] =
-    if (eGram.isEmpty) Maybe.Nope
-    else Maybe(eGram.parser)
+  lazy val eParser: Maybe[Parser] = eGram.maybeParser
 
-  lazy val eAfterParser: Maybe[Parser] =
-    if (eGramAfter.isEmpty) Maybe.Nope
-    else Maybe(eGramAfter.parser)
+  lazy val eAfterParser: Maybe[Parser] = eGramAfter.maybeParser
 
   def parser: Parser
 
-  lazy val uSetVar = context.setVariableStatements.map(_.gram.unparser).toArray
+  lazy val uSetVar = context.setVariableStatements.map(_.gram(context).unparser).toArray
 
-  lazy val eBeforeUnparser: Maybe[Unparser] =
-    if (eGramBefore.isEmpty) Maybe.Nope
-    else Maybe(eGramBefore.unparser)
+  lazy val eBeforeUnparser: Maybe[Unparser] = eGramBefore.maybeUnparser
 
-  lazy val eUnparser: Maybe[Unparser] =
-    if (eGram.isEmpty) Maybe.Nope
-    else Maybe(eGram.unparser)
+  lazy val eUnparser: Maybe[Unparser] = eGram.maybeUnparser
 
-  lazy val eAfterUnparser: Maybe[Unparser] =
-    if (eGramAfter.isEmpty) Maybe.Nope
-    else Maybe(eGramAfter.unparser)
+  lazy val eAfterUnparser: Maybe[Unparser] = eGramAfter.maybeUnparser
 
   def unparser: Unparser
 
 }
+
+//
+//case class ArrayCombinator(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
+//  override def toString() = "<Array>" + body.toString + "</Array>"
+//
+//  lazy val parser: Parser = new ArrayCombinatorParser(e.elementRuntimeData, body.parser)
+//  override lazy val unparser: Unparser = new ArrayCombinatorUnparser(e.elementRuntimeData, body.unparser)
+//}
+//
+//case class OptionalCombinator(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
+//
+//  override def toString() = "<Optional>" + body.toString + "</Optional>"
+//  lazy val parser: Parser = new OptionalCombinatorParser(e.elementRuntimeData, body.parser)
+//  override lazy val unparser: Unparser = new OptionalCombinatorUnparser(e.elementRuntimeData, body.unparser)
+//}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/LayeredSequence.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/LayeredSequence.scala
index 0e355b1e1..0e0bdd1b2 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/LayeredSequence.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/LayeredSequence.scala
@@ -21,23 +21,29 @@ import org.apache.daffodil.grammar.Terminal
 import org.apache.daffodil.dsom._
 import org.apache.daffodil.processors.parsers.{ Parser => DaffodilParser }
 import org.apache.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
-import org.apache.daffodil.grammar.Gram
 import org.apache.daffodil.util.Misc
 import org.apache.daffodil.processors.parsers.LayeredSequenceParser
 import org.apache.daffodil.processors.unparsers.LayeredSequenceUnparser
+import org.apache.daffodil.processors.parsers.ScalarOrderedRequiredUnseparatedSequenceChildParser
+import org.apache.daffodil.processors.unparsers.ScalarOrderedRequiredUnseparatedSequenceChildUnparser
 
-case class LayeredSequence(sq: SequenceTermBase, bodyTerm: Gram)
+case class LayeredSequence(sq: SequenceTermBase, bodyTerm: SequenceChild)
   extends Terminal(sq, true) {
 
+  private val srd = sq.sequenceRuntimeData
+  private val trd = bodyTerm.trd
+
   override def toString() =
     "<" + Misc.getNameFromClass(this) + ">" +
       bodyTerm.toString() +
       "</" + Misc.getNameFromClass(this) + ">"
 
   override lazy val parser: DaffodilParser =
-    new LayeredSequenceParser(sq.termRuntimeData, sq.maybeLayerTransformerEv.get, bodyTerm.parser)
+    new LayeredSequenceParser(srd, sq.maybeLayerTransformerEv.get,
+      new ScalarOrderedRequiredUnseparatedSequenceChildParser(bodyTerm.parser, srd, trd))
 
   override lazy val unparser: DaffodilUnparser = {
-    new LayeredSequenceUnparser(sq.modelGroupRuntimeData, sq.maybeLayerTransformerEv.get, bodyTerm.unparser)
+    new LayeredSequenceUnparser(sq.sequenceRuntimeData, sq.maybeLayerTransformerEv.get,
+      new ScalarOrderedRequiredUnseparatedSequenceChildUnparser(bodyTerm.unparser, srd, trd))
   }
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Nada.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Nada.scala
index 4e0f1dbef..272163d7f 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Nada.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Nada.scala
@@ -14,17 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.daffodil.grammar.primitives
-
-import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.dsom.Term
-import org.apache.daffodil.grammar.HasNoUnparser
-import org.apache.daffodil.processors.parsers.NadaParser
-
-case class Nada(sc: Term) extends Terminal(sc, true) with HasNoUnparser {
-  // cannot optimize this out! It is used as an alternative to things
-  // with the intention of "find this and this, or find nothing"
-
-  override lazy val parser = new NadaParser(sc.runtimeData)
-}
+//
+//package org.apache.daffodil.grammar.primitives
+//
+//import org.apache.daffodil.grammar.Terminal
+//import org.apache.daffodil.dsom.Term
+//import org.apache.daffodil.processors.parsers.NadaParser
+//
+//case class Nada(sc: Term) extends Terminal(sc, true) {
+//  // cannot optimize this out! It is used as an alternative to things
+//  // with the intention of "find this and this, or find nothing"
+//
+//  override lazy val parser = new NadaParser(sc.runtimeData)
+//
+//  override lazy val unparser = hasNoUnparser
+//}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/NilEmptyCombinators.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/NilEmptyCombinators.scala
index aff3c48d3..972dce328 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/NilEmptyCombinators.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/NilEmptyCombinators.scala
@@ -20,35 +20,12 @@ package org.apache.daffodil.grammar.primitives
 import org.apache.daffodil.grammar.Gram
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.dsom.ElementBase
-import org.apache.daffodil.processors.parsers.SimpleNilOrEmptyOrValueParser
-import org.apache.daffodil.processors.unparsers.SimpleNilOrEmptyOrValueUnparser
 import org.apache.daffodil.processors.parsers.SimpleNilOrValueParser
 import org.apache.daffodil.processors.unparsers.SimpleNilOrValueUnparser
-import org.apache.daffodil.processors.parsers.SimpleEmptyOrValueParser
-import org.apache.daffodil.processors.unparsers.SimpleEmptyOrValueUnparser
 import org.apache.daffodil.processors.parsers.ComplexNilOrContentParser
 import org.apache.daffodil.processors.unparsers.ComplexNilOrContentUnparser
 import org.apache.daffodil.grammar.Terminal
 
-case class SimpleNilOrEmptyOrValue(ctxt: ElementBase, nilGram: Gram, emptyGram: Gram, valueGram: Gram) extends Terminal(ctxt, true) {
-  Assert.invariant(!nilGram.isEmpty)
-  Assert.invariant(!emptyGram.isEmpty)
-  Assert.invariant(!valueGram.isEmpty)
-
-  lazy val nilParser = nilGram.parser
-  lazy val emptyParser = emptyGram.parser
-  lazy val valueParser = valueGram.parser
-
-  lazy val nilUnparser = nilGram.unparser
-  lazy val emptyUnparser = emptyGram.unparser
-  lazy val valueUnparser = valueGram.unparser
-
-  override lazy val parser = SimpleNilOrEmptyOrValueParser(ctxt.erd, nilParser, emptyParser, valueParser)
-
-  override lazy val unparser = SimpleNilOrEmptyOrValueUnparser(ctxt.erd, nilUnparser, emptyUnparser, valueUnparser)
-
-}
-
 case class SimpleNilOrValue(ctxt: ElementBase, nilGram: Gram, valueGram: Gram) extends Terminal(ctxt, true) {
   Assert.invariant(!nilGram.isEmpty)
   Assert.invariant(!valueGram.isEmpty)
@@ -65,22 +42,6 @@ case class SimpleNilOrValue(ctxt: ElementBase, nilGram: Gram, valueGram: Gram) e
 
 }
 
-case class SimpleEmptyOrValue(ctxt: ElementBase, emptyGram: Gram, valueGram: Gram) extends Terminal(ctxt, true) {
-  Assert.invariant(!emptyGram.isEmpty)
-  Assert.invariant(!valueGram.isEmpty)
-
-  lazy val emptyParser = emptyGram.parser
-  lazy val valueParser = valueGram.parser
-
-  lazy val emptyUnparser = emptyGram.unparser
-  lazy val valueUnparser = valueGram.unparser
-
-  override lazy val parser = SimpleEmptyOrValueParser(ctxt.erd, emptyParser, valueParser)
-
-  override lazy val unparser = SimpleEmptyOrValueUnparser(ctxt.erd, emptyUnparser, valueUnparser)
-
-}
-
 case class ComplexNilOrContent(ctxt: ElementBase, nilGram: Gram, contentGram: Gram) extends Terminal(ctxt, true) {
   Assert.invariant(!nilGram.isEmpty)
   Assert.invariant(!contentGram.isEmpty)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Padded.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Padded.scala
index af961572d..972fa4a03 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Padded.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Padded.scala
@@ -34,7 +34,7 @@ import org.apache.daffodil.schema.annotation.props.gen.YesNo
 import org.apache.daffodil.util.MaybeChar
 
 trait PaddingInfoMixin {
-  def eBase: ElementBase
+  protected def eBase: ElementBase
 
   /**
    * parsingPadChar is the pad character for parsing
@@ -128,6 +128,6 @@ trait PaddingInfoMixin {
 }
 
 trait Padded extends PaddingInfoMixin { self: Gram =>
-  override final def eBase = self.context.asInstanceOf[ElementBase]
+  override final protected def eBase = self.context.asInstanceOf[ElementBase]
 
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Primitives.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Primitives.scala
index 7f784f695..1babfcae3 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Primitives.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/Primitives.scala
@@ -19,7 +19,6 @@ package org.apache.daffodil.grammar.primitives
 
 import org.apache.daffodil.dsom._
 import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.grammar.HasNoUnparser
 
 abstract class Primitive(e: Term, guard: Boolean = false)
   extends Terminal(e, guard) {
@@ -30,25 +29,9 @@ abstract class Primitive(e: Term, guard: Boolean = false)
  * For stubbing out primitives that are placeholders
  */
 abstract class UnimplementedPrimitive(e: Term, guard: Boolean = false)
-  extends Primitive(e, guard)
-  with HasNoUnparser {
+  extends Primitive(e, guard) {
   override final lazy val parser = hasNoParser
+  override final lazy val unparser = hasNoUnparser
 }
 
-// base stub classes
-
-// case class NoValue(e: GlobalElementDecl, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
-
-case class SaveInputStream(e: ElementBase, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
-
-case class SetEmptyInputStream(e: ElementBase, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
-
-case class RestoreInputStream(e: ElementBase, guard: Boolean = true) extends UnimplementedPrimitive(e, guard)
-
-case class NotStopValue(e: ElementBase with LocalElementMixin) extends UnimplementedPrimitive(e, e.hasStopValue)
-
-case class StopValue(e: ElementBase with LocalElementMixin) extends UnimplementedPrimitive(e, e.hasStopValue)
-
-case class TheDefaultValue(e: ElementBase) extends UnimplementedPrimitive(e, e.isDefaultable)
-
 case class UnicodeByteOrderMark(e: Root) extends UnimplementedPrimitive(e, false)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDelimiters.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDelimiters.scala
index f876bb25c..25a857e53 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDelimiters.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesDelimiters.scala
@@ -157,8 +157,10 @@ case class Initiator(e: Term) extends DelimiterText(e, e) {
   Assert.invariant(e.hasInitiator)
   val delimiterType: DelimiterTextType.Type = DelimiterTextType.Initiator
 }
-case class Separator(s: SequenceTermBase, t: Term) extends DelimiterText(s, t) {
-  Assert.invariant(s.hasSeparator)
+//case class ArrayElementSeparator(s: SequenceTermBase, t: Term) extends DelimiterText(s, t, s.hasSeparator) {
+//  val delimiterType: DelimiterTextType.Type = DelimiterTextType.Separator
+//}
+case class SequenceSeparator(s: SequenceTermBase) extends DelimiterText(s, s, s.hasSeparator) {
   val delimiterType: DelimiterTextType.Type = DelimiterTextType.Separator
 }
 case class Terminator(e: Term) extends DelimiterText(e, e) {
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesElementKinds.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesElementKinds.scala
deleted file mode 100644
index 549c4a05e..000000000
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesElementKinds.scala
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.daffodil.grammar.primitives
-
-import org.apache.daffodil.grammar.Terminal
-import org.apache.daffodil.dsom._
-import org.apache.daffodil.processors.parsers.{ Parser => DaffodilParser }
-import org.apache.daffodil.processors.unparsers.{ Unparser => DaffodilUnparser }
-import org.apache.daffodil.grammar.Gram
-import org.apache.daffodil.processors.parsers.ComplexTypeParser
-import org.apache.daffodil.processors.parsers.SequenceCombinatorParser
-import org.apache.daffodil.processors.parsers.ChoiceCombinatorParser
-import org.apache.daffodil.processors.parsers.ChoiceDispatchCombinatorParser
-import org.apache.daffodil.processors.parsers.ArrayCombinatorParser
-import org.apache.daffodil.processors.parsers.OptionalCombinatorParser
-import org.apache.daffodil.processors.unparsers.ComplexTypeUnparser
-import org.apache.daffodil.processors.unparsers.SequenceCombinatorUnparser
-import org.apache.daffodil.processors.unparsers.ChoiceCombinatorUnparser
-import org.apache.daffodil.processors.unparsers.HiddenChoiceCombinatorUnparser
-import org.apache.daffodil.processors.parsers.DelimiterStackParser
-import org.apache.daffodil.processors.parsers.DynamicEscapeSchemeParser
-import org.apache.daffodil.processors.unparsers.DynamicEscapeSchemeUnparser
-import org.apache.daffodil.processors.unparsers.Unparser
-import org.apache.daffodil.processors.unparsers.ArrayCombinatorUnparser
-import org.apache.daffodil.processors.unparsers.OptionalCombinatorUnparser
-import org.apache.daffodil.processors.unparsers.DelimiterStackUnparser
-import org.apache.daffodil.grammar.EmptyGram
-import org.apache.daffodil.equality._;
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.util.Maybe._
-import org.apache.daffodil.cookers.ChoiceBranchKeyCooker
-import org.apache.daffodil.api.WarnID
-import org.apache.daffodil.util.Misc
-import org.apache.daffodil.xml.XMLUtils
-
-object ENoWarn3 { EqualitySuppressUnusedImportWarning() }
-
-case class DelimiterStackCombinatorSequence(sq: SequenceTermBase, body: Gram) extends Terminal(sq, !body.isEmpty) {
-  lazy val pInit = if (sq.initiatorParseEv.isKnownNonEmpty) One(sq.initiatorParseEv) else Nope
-  lazy val pSep = if (sq.separatorParseEv.isKnownNonEmpty) One(sq.separatorParseEv) else Nope
-  lazy val pTerm = if (sq.terminatorParseEv.isKnownNonEmpty) One(sq.terminatorParseEv) else Nope
-
-  lazy val uInit = if (sq.initiatorParseEv.isKnownNonEmpty) One(sq.initiatorUnparseEv) else Nope
-  lazy val uSep = if (sq.separatorParseEv.isKnownNonEmpty) One(sq.separatorUnparseEv) else Nope
-  lazy val uTerm = if (sq.terminatorParseEv.isKnownNonEmpty) One(sq.terminatorUnparseEv) else Nope
-
-  lazy val parser: DaffodilParser = new DelimiterStackParser((pInit.toList ++ pSep.toList ++ pTerm.toList).toArray, sq.runtimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, uSep, uTerm, sq.termRuntimeData, body.unparser)
-}
-
-case class DelimiterStackCombinatorChoice(ch: ChoiceTermBase, body: Gram) extends Terminal(ch, !body.isEmpty) {
-  lazy val pInit = if (ch.initiatorParseEv.isKnownNonEmpty) One(ch.initiatorParseEv) else Nope
-  lazy val pTerm = if (ch.terminatorParseEv.isKnownNonEmpty) One(ch.terminatorParseEv) else Nope
-
-  lazy val uInit = if (ch.initiatorParseEv.isKnownNonEmpty) One(ch.initiatorUnparseEv) else Nope
-  lazy val uTerm = if (ch.terminatorParseEv.isKnownNonEmpty) One(ch.terminatorUnparseEv) else Nope
-
-  lazy val parser: DaffodilParser = new DelimiterStackParser((pInit.toList ++ pTerm.toList).toArray, ch.runtimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, None, uTerm, ch.termRuntimeData, body.unparser)
-}
-
-case class DelimiterStackCombinatorElement(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
-  lazy val pInit = if (e.initiatorParseEv.isKnownNonEmpty) One(e.initiatorParseEv) else Nope
-  lazy val pTerm = if (e.terminatorParseEv.isKnownNonEmpty) One(e.terminatorParseEv) else Nope
-
-  lazy val uInit = if (e.initiatorParseEv.isKnownNonEmpty) One(e.initiatorUnparseEv) else Nope
-  lazy val uTerm = if (e.terminatorParseEv.isKnownNonEmpty) One(e.terminatorUnparseEv) else Nope
-
-  lazy val delims = (pInit.toList ++ pTerm.toList)
-
-  override def toString() = {
-    val delimAttrib = delims.map { _.toString }.map { XMLUtils.escape(_).toString() }.mkString(" ")
-    "<" + Misc.getNameFromClass(this) + " delims='" + delimAttrib + "'>" +
-      body.toString() +
-      "</" + Misc.getNameFromClass(this) + ">"
-  }
-  lazy val parser: DaffodilParser = new DelimiterStackParser(delims.toArray, e.termRuntimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser = new DelimiterStackUnparser(uInit, None, uTerm, e.termRuntimeData, body.unparser)
-}
-
-case class DynamicEscapeSchemeCombinatorElement(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
-
-  val schemeParseOpt = e.optionEscapeScheme.map { _.escapeSchemeParseEv }
-  val schemeUnparseOpt = e.optionEscapeScheme.map { _.escapeSchemeUnparseEv }
-
-  Assert.invariant(schemeParseOpt.isDefined && !schemeParseOpt.get.isConstant)
-  Assert.invariant(schemeUnparseOpt.isDefined && !schemeUnparseOpt.get.isConstant)
-
-  lazy val parser: DaffodilParser = new DynamicEscapeSchemeParser(schemeParseOpt.get, e.termRuntimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser = new DynamicEscapeSchemeUnparser(schemeUnparseOpt.get, e.termRuntimeData, body.unparser)
-}
-
-case class ComplexTypeCombinator(ct: ComplexTypeBase, body: Gram) extends Terminal(ct.elementDecl, !body.isEmpty) {
-
-  override def isEmpty = body.isEmpty
-
-  override def toString() =
-    "<" + Misc.getNameFromClass(this) + ">" +
-      body.toString() +
-      "</" + Misc.getNameFromClass(this) + ">"
-
-  lazy val parser: DaffodilParser = new ComplexTypeParser(ct.runtimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser =
-    new ComplexTypeUnparser(ct.runtimeData, body.unparser)
-}
-
-case class SequenceCombinator(sq: SequenceTermBase, rawTerms: Seq[Gram])
-  extends Terminal(sq, true) {
-
-  override lazy val isEmpty = {
-    val rt = rawTerms
-    val frt = rt.filterNot { _.isEmpty }
-    val res = frt.isEmpty
-    res
-  }
-
-  override def toString() =
-    "<" + Misc.getNameFromClass(this) + ">" +
-      terms.map { _.toString() }.mkString +
-      "</" + Misc.getNameFromClass(this) + ">"
-
-  private val mt: Gram = EmptyGram
-  lazy val body = rawTerms.foldRight(mt) { _ ~ _ }
-
-  lazy val terms = rawTerms.filterNot { _.isEmpty }
-
-  lazy val unparsers = terms.map { term =>
-    term.unparser
-  }.toVector
-
-  lazy val parser: DaffodilParser = new SequenceCombinatorParser(sq.termRuntimeData, body.parser)
-
-  override lazy val unparser: DaffodilUnparser = {
-    sq.checkHiddenSequenceIsDefaultableOrOVC
-    new SequenceCombinatorUnparser(sq.modelGroupRuntimeData, unparsers)
-  }
-}
-
-case class UnorderedSequenceCombinator(s: Sequence, terms: Seq[Gram])
-  extends UnimplementedPrimitive(s, false) {
-  // stub for now. These are not implemented currently.
-}
-
-case class ArrayCombinator(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
-  override def toString() = "<Array>" + body.toString + "</Array>"
-
-  lazy val parser: DaffodilParser = new ArrayCombinatorParser(e.elementRuntimeData, body.parser)
-  override lazy val unparser: Unparser = new ArrayCombinatorUnparser(e.elementRuntimeData, body.unparser)
-}
-
-case class OptionalCombinator(e: ElementBase, body: Gram) extends Terminal(e, !body.isEmpty) {
-
-  override def toString() = "<Optional>" + body.toString + "</Optional>"
-  lazy val parser: DaffodilParser = new OptionalCombinatorParser(e.elementRuntimeData, body.parser)
-  override lazy val unparser: Unparser = new OptionalCombinatorUnparser(e.elementRuntimeData, body.unparser)
-}
-
-/*
- * The purpose of the ChoiceCombinator (and the parsers it creates) is to
- * determine which branch to go down. In the parser case, for non-direct
- * dispatch, we just rely on the AltCompParser behavior to handle the
- * backtracking. For direct dispatch, we create a disapatch-branch key map
- * which is used to determine which branch to parse at runtime.
- *
- * In the unparser case, we know which element we got from the infoset, but we
- * need to determine which branch of the choice to take at runtime. This
- * unparser uses a Map to make the determination based on the element seen.
- */
-case class ChoiceCombinator(ch: ChoiceTermBase, alternatives: Seq[Gram]) extends Terminal(ch, !alternatives.isEmpty) {
-  lazy val parser: DaffodilParser = {
-    if (!ch.isDirectDispatch) {
-      val folded = alternatives.map { gf => gf }.foldRight(EmptyGram.asInstanceOf[Gram]) { _ | _ }
-      new ChoiceCombinatorParser(ch.termRuntimeData, folded.parser)
-    } else {
-      val dispatchBranchKeyValueTuples = alternatives.flatMap { alt =>
-        val keyTerm = alt.context.asInstanceOf[Term]
-        val cbk = keyTerm.choiceBranchKey
-        // Note that this behaves differently than the specification, since
-        // this accepts a space separated list of keys
-        val cbks = ChoiceBranchKeyCooker.convertConstant(cbk, keyTerm.runtimeData, forUnparse = false)
-        cbks.map { (_, alt) }
-      }
-
-      // check for duplicate dfdl:choiceBranchKeys
-      val groupedByKey = dispatchBranchKeyValueTuples.groupBy(_._1)
-      groupedByKey.foreach {
-        case (k, kvs) =>
-          if (kvs.length > 1) {
-            SDE("dfdl:choiceBranchKey value (%s) is not unique across all branches of a direct dispatch choice. Offending branches are:\n%s",
-              k, kvs.map(_._2.context.runtimeData).map(rd => rd.diagnosticDebugName + " " + rd.schemaFileLocation.locationDescription).mkString("- ", "\n- ", ""))
-          }
-      }
-
-      val dispatchBranchKeyMap = dispatchBranchKeyValueTuples.toMap.mapValues(_.parser)
-      val serializableMap = dispatchBranchKeyMap.map(identity)
-
-      new ChoiceDispatchCombinatorParser(ch.termRuntimeData, ch.choiceDispatchKeyEv, serializableMap)
-    }
-  }
-
-  override lazy val unparser: DaffodilUnparser = {
-    if (!ch.isHidden) {
-      val eventRDMap = ch.choiceBranchMap
-      val eventUnparserMap = eventRDMap.mapValues { rd =>
-        alternatives.find(_.context.runtimeData =:= rd).get.unparser
-      }
-
-      // The following line is required because mapValues() creates a "view" of
-      // the map, which is not serializable. map()ing this "view" with the
-      // identity forces evaluation of the "view", creating a map that is
-      // serializable and can be safely passed to a parser. See SI-7005 for
-      // discussions about this issue.
-      val serializableMap = eventUnparserMap.map(identity)
-
-      new ChoiceCombinatorUnparser(ch.modelGroupRuntimeData, serializableMap)
-    } else {
-      // Choices inside a hidden group ref are slightly different because we
-      // will never see events for any of the branches. Instead, we will just
-      // always pick the branch in which every thing is defaultble or OVC. It
-      // is a warning if more than one of those branches exist. It is an SDE if
-      // such a branch does not exist, which is detected elsewhere
-
-      // this call is necessary since it will throw an SDE if no choice branch
-      // was defaultable
-      ch.childrenInHiddenGroupNotDefaultableOrOVC
-      val defaultableBranches = ch.groupMembers.filter { _.childrenInHiddenGroupNotDefaultableOrOVC.length == 0 }
-      Assert.invariant(defaultableBranches.length > 0)
-      if (defaultableBranches.length > 1) {
-        SDW(WarnID.ChoiceInsideHiddenGroup, "xs:choice inside a hidden group has unparse ambiguity: multiple branches exist with all children either defaulable or have the dfdl:outputValueCalc property set. The first branch will be chosen during unparse. Defaultable branches are:\n%s",
-          defaultableBranches.mkString("\n"))
-      }
-      val defaultableBranchRD = defaultableBranches(0).runtimeData
-      val defaultableBranchUnparser = alternatives.find(_.context.runtimeData =:= defaultableBranchRD).get.unparser
-      new HiddenChoiceCombinatorUnparser(ch.modelGroupRuntimeData, defaultableBranchUnparser)
-    }
-  }
-}
-
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
index a810d0850..83a1f1653 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesExpressions.scala
@@ -38,7 +38,6 @@ import org.apache.daffodil.processors.unparsers.SetVariableUnparser
 import org.apache.daffodil.processors.unparsers.NewVariableInstanceEndUnparser
 import org.apache.daffodil.processors.unparsers.NewVariableInstanceStartUnparser
 import org.apache.daffodil.compiler.ForParser
-import org.apache.daffodil.processors.unparsers.NadaUnparser
 import org.apache.daffodil.schema.annotation.props.PropertyLookupResult
 import org.apache.daffodil.schema.annotation.props.Found
 import org.apache.daffodil.dsom.ExpressionCompilers
@@ -73,7 +72,7 @@ abstract class AssertBase(decl: AnnotatedSchemaComponent,
 
   lazy val parser: DaffodilParser = new AssertExpressionEvaluationParser(msg, discrim, decl.runtimeData, expr)
 
-  override def unparser: DaffodilUnparser = new NadaUnparser(decl.runtimeData) // Assert.invariantFailed("should not request unparser for asserts/discriminators")
+  override def unparser: DaffodilUnparser = hasNoUnparser
 
 }
 
@@ -108,8 +107,8 @@ case class InitiatedContent(
     "initiatedContent") {
 }
 
-case class SetVariable(decl: AnnotatedSchemaComponent, stmt: DFDLSetVariable)
-  extends ExpressionEvaluatorBase(decl) {
+case class SetVariable(stmt: DFDLSetVariable)
+  extends ExpressionEvaluatorBase(stmt.context) {
 
   val baseName = "SetVariable[" + stmt.varQName.local + "]"
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesLengthKind.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesLengthKind.scala
index f2e66dd36..683f03575 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesLengthKind.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesLengthKind.scala
@@ -335,7 +335,7 @@ case class LiteralNilDelimitedEndOfData(eb: ElementBase)
 
 case class PrefixLength(e: ElementBase) extends UnimplementedPrimitive(e, e.lengthKind == LengthKind.Prefixed)
 
-class OptionalInfixSep(term: Term, sep: => Gram, guard: Boolean = true) extends Terminal(term, guard) {
+class OptionalInfixSep(term: Term, sep: Gram) extends Terminal(term, !sep.isEmpty) {
 
   lazy val parser: DaffodilParser = new OptionalInfixSepParser(term.termRuntimeData, sep.parser)
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesNil.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesNil.scala
index 52ed6dab8..49b3df8ac 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesNil.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesNil.scala
@@ -23,7 +23,8 @@ import org.apache.daffodil.processors.parsers.LiteralValueNilOfSpecifiedLengthPa
 import org.apache.daffodil.processors.parsers.LiteralCharacterNilOfSpecifiedLengthParser
 import org.apache.daffodil.processors.unparsers.LiteralValueNilOfSpecifiedLengthUnparser
 import org.apache.daffodil.schema.annotation.props.gen.LengthKind
-import org.apache.daffodil.processors.unparsers.NadaUnparser
+import org.apache.daffodil.processors.unparsers.NilLiteralCharacterUnparser
+import org.apache.daffodil.processors.unparsers.Unparser
 
 case class LiteralValueNilOfSpecifiedLength(e: ElementBase)
   extends Terminal(e, true)
@@ -49,7 +50,14 @@ case class LiteralCharacterNilOfSpecifiedLength(e: ElementBase)
     e.ignoreCaseBool,
     e.elementRuntimeData)
 
-  override lazy val unparser = new NadaUnparser(e.termRuntimeData)
+  private lazy val nilLitCharacter = e.cookedNilValuesForUnparse.head(0)
+
+  override lazy val unparser: Unparser =
+    new NilLiteralCharacterUnparser(e.erd,
+      e.maybeUnparseTargetLengthInBitsEv.get,
+      e.maybeLengthEv,
+      e.maybeCharsetEv,
+      nilLitCharacter)
 }
 
 case class LogicalNilValue(e: ElementBase) extends UnimplementedPrimitive(e, e.isNillable)
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
index 44f94b494..9667edb5e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/PrimitivesUnorderedSequence.scala
@@ -23,9 +23,9 @@ import org.apache.daffodil.dsom.ElementBase
 import org.apache.daffodil.dsom.Term
 import org.apache.daffodil.dsom.SequenceTermBase
 import org.apache.daffodil.grammar.UnaryGram
-import org.apache.daffodil.grammar.HasNoUnparser
 import org.apache.daffodil.processors.ElementRuntimeData
 import org.apache.daffodil.processors.parsers.Parser
+import org.apache.daffodil.processors.unparsers.Unparser
 
 object UnorderedSequence {
   def apply(context: Term, eGram: => Gram) = {
@@ -41,7 +41,7 @@ object UnorderedSequence {
 }
 
 class UnorderedSequence private (context: SequenceTermBase, eGramArg: => Gram) // private to force use of the object as factory
-  extends UnaryGram(context, eGramArg) with HasNoUnparser {
+  extends UnaryGram(context, eGramArg) {
 
   lazy val eGram = eGramArg // once only
 
@@ -76,5 +76,6 @@ class UnorderedSequence private (context: SequenceTermBase, eGramArg: => Gram) /
     }
 
   lazy val parser: Parser = ??? // new UnorderedSequenceParser(context.modelGroupRuntimeData, sortOrder, scalarMembers, uoSeqParser)
+  lazy val unparser: Unparser = ???
 
 }
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/RepPrims.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/RepPrims.scala
index 1c8ad613f..a16a714e1 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/RepPrims.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/RepPrims.scala
@@ -18,117 +18,103 @@
 package org.apache.daffodil.grammar.primitives
 
 import org.apache.daffodil.grammar._
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.Implicits._;
 import org.apache.daffodil.dsom._
-import RepPrims._
-import org.apache.daffodil.processors.parsers.RepAtMostTotalNParser
-import org.apache.daffodil.processors.parsers.RepExactlyTotalNParser
-import org.apache.daffodil.processors.parsers.RepUnboundedParser
-import org.apache.daffodil.processors.parsers.OccursCountExpressionParser
-import org.apache.daffodil.processors.parsers.RepExactlyNParser
-import org.apache.daffodil.processors.parsers.RepAtMostOccursCountParser
-import org.apache.daffodil.processors.parsers.RepExactlyTotalOccursCountParser
-import org.apache.daffodil.processors.unparsers.RepExactlyNUnparser
-import org.apache.daffodil.processors.unparsers.RepAtMostOccursCountUnparser
-import org.apache.daffodil.processors.unparsers.RepUnboundedUnparser
-import org.apache.daffodil.processors.unparsers.RepExactlyTotalNUnparser
-import org.apache.daffodil.processors.unparsers.RepAtMostTotalNUnparser
-import org.apache.daffodil.equality._;
 import org.apache.daffodil.processors.unparsers.NadaUnparser
+import org.apache.daffodil.processors.parsers.OccursCountExpressionParser
 
-object INoWarn6 { ImplicitsSuppressUnusedImportWarning() }
-
-object ENoWarn6 { EqualitySuppressUnusedImportWarning() }
-
-object RepPrims {
-  abstract class RepPrim(context: ElementBase, n: Long, r: => Gram)
-    extends UnaryGram(context, r) {
-    Assert.invariant(n > 0)
-    lazy val rd = context.elementRuntimeData
-    lazy val intN = n.toInt
-    lazy val rParser = r.parser
-    lazy val rUnparser = r.unparser
-
-  }
-
-  abstract class Rep3Arg(f: (ElementBase, Long, => Gram) => Gram) {
-    def apply(context: ElementBase, n: Long, rr: => Gram) = {
-      lazy val r = rr
-      if (n == 0 || r.isEmpty) EmptyGram
-      else f(context, n, r)
-    }
-  }
-
-  abstract class Rep2Arg(f: (ElementBase, => Gram) => Gram) {
-    def apply(context: ElementBase, r: => Gram) = {
-      lazy val rr = r
-      if (rr.isEmpty) EmptyGram
-      else f(context, r)
-    }
-  }
-
-  class RepExactlyNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
-
-    // Since this is Exactly N, there is no new point of uncertainty considerations here.
-    override lazy val parser = new RepExactlyNParser(n, r.parser, context.elementRuntimeData)
-    override lazy val unparser = new RepExactlyNUnparser(n, r.unparser, context.elementRuntimeData)
-
-  }
-
-  class RepAtMostTotalNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
-
-    override lazy val parser = new RepAtMostTotalNParser(n, r.parser, context.elementRuntimeData)
-    override lazy val unparser = new RepAtMostTotalNUnparser(n, r.unparser, context.elementRuntimeData)
-
-  }
-
-  class RepExactlyTotalNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
-
-    override lazy val parser = new RepExactlyTotalNParser(n, r.parser, context.elementRuntimeData)
-    override lazy val unparser = new RepExactlyTotalNUnparser(n, r.unparser, context.elementRuntimeData)
-
-  }
-
-  class RepUnboundedPrim(e: ElementBase, r: => Gram) extends RepPrim(e, 1, r) {
-
-    override lazy val parser = new RepUnboundedParser(e.occursCountKind, r.parser, e.elementRuntimeData)
-    override lazy val unparser = new RepUnboundedUnparser(e.occursCountKind, r.unparser, e.elementRuntimeData)
-  }
-
-  class RepAtMostOccursCountPrim(e: ElementBase, n: Long, r: => Gram)
-    extends RepPrim(e, n, r) {
-
-    override lazy val parser = new RepAtMostOccursCountParser(r.parser, n, e.elementRuntimeData)
-    override lazy val unparser = new RepAtMostOccursCountUnparser(r.unparser, n, e.elementRuntimeData)
-
-  }
-
-  class RepExactlyTotalOccursCountPrim(e: ElementBase, r: => Gram)
-    extends RepPrim(e, 1, r) {
-
-    override lazy val parser = new RepExactlyTotalOccursCountParser(r.parser, e.elementRuntimeData)
-
-    /**
-     * Unparser for this is exactly the same as unbounded case - we output all the occurrences in the infoset.
-     */
-    override lazy val unparser = new RepUnboundedUnparser(e.occursCountKind, r.unparser, e.elementRuntimeData)
-
-  }
-}
-
-object RepExactlyN extends Rep3Arg(new RepExactlyNPrim(_, _, _))
-
-object RepAtMostTotalN extends Rep3Arg(new RepAtMostTotalNPrim(_, _, _))
-
-object RepUnbounded extends Rep2Arg(new RepUnboundedPrim(_, _))
-
-object RepExactlyTotalN extends Rep3Arg(new RepExactlyTotalNPrim(_, _, _))
-
-object RepAtMostOccursCount extends Rep3Arg(new RepAtMostOccursCountPrim(_, _, _))
-
-object RepExactlyTotalOccursCount extends Rep2Arg(new RepExactlyTotalOccursCountPrim(_, _))
-
+//
+//object INoWarn6 { ImplicitsSuppressUnusedImportWarning() }
+//
+//object ENoWarn6 { EqualitySuppressUnusedImportWarning() }
+//
+//object RepPrims {
+//  abstract class RepPrim(context: ElementBase, n: Long, r: => Gram)
+//    extends UnaryGram(context, r) {
+//    Assert.invariant(n > 0)
+//    lazy val rd = context.elementRuntimeData
+//    lazy val intN = n.toInt
+//    lazy val rParser = r.parser
+//    lazy val rUnparser = r.unparser
+//
+//  }
+//
+//  abstract class Rep3Arg(f: (ElementBase, Long, => Gram) => Gram) {
+//    def apply(context: ElementBase, n: Long, rr: => Gram) = {
+//      lazy val r = rr
+//      if (n == 0 || r.isEmpty) EmptyGram
+//      else f(context, n, r)
+//    }
+//  }
+//
+//  abstract class Rep2Arg(f: (ElementBase, => Gram) => Gram) {
+//    def apply(context: ElementBase, r: => Gram) = {
+//      lazy val rr = r
+//      if (rr.isEmpty) EmptyGram
+//      else f(context, r)
+//    }
+//  }
+//
+//  class RepExactlyNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
+//
+//    // Since this is Exactly N, there is no new point of uncertainty considerations here.
+//    override lazy val parser = new RepExactlyNParser(n, r.parser, context.elementRuntimeData)
+//    override lazy val unparser = new RepExactlyNUnparser(n, r.unparser, context.elementRuntimeData)
+//
+//  }
+//
+//  class RepAtMostTotalNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
+//
+//    override lazy val parser = new RepAtMostTotalNParser(n, r.parser, context.elementRuntimeData)
+//    override lazy val unparser = new RepAtMostTotalNUnparser(n, r.unparser, context.elementRuntimeData)
+//
+//  }
+//
+//  class RepExactlyTotalNPrim(context: ElementBase, n: Long, r: => Gram) extends RepPrim(context, n, r) {
+//
+//    override lazy val parser = new RepExactlyTotalNParser(n, r.parser, context.elementRuntimeData)
+//    override lazy val unparser = new RepExactlyTotalNUnparser(n, r.unparser, context.elementRuntimeData)
+//
+//  }
+//
+//  class RepUnboundedPrim(e: ElementBase, r: => Gram) extends RepPrim(e, 1, r) {
+//
+//    override lazy val parser = new RepUnboundedParser(e.occursCountKind, r.parser, e.elementRuntimeData)
+//    override lazy val unparser = new RepUnboundedUnparser(e.occursCountKind, r.unparser, e.elementRuntimeData)
+//  }
+//
+//  class RepAtMostOccursCountPrim(e: ElementBase, n: Long, r: => Gram)
+//    extends RepPrim(e, n, r) {
+//
+//    override lazy val parser = new RepAtMostOccursCountParser(r.parser, n, e.elementRuntimeData)
+//    override lazy val unparser = new RepAtMostOccursCountUnparser(r.unparser, n, e.elementRuntimeData)
+//
+//  }
+//
+//  class RepExactlyTotalOccursCountPrim(e: ElementBase, r: => Gram)
+//    extends RepPrim(e, 1, r) {
+//
+//    override lazy val parser = new RepExactlyTotalOccursCountParser(r.parser, e.elementRuntimeData)
+//
+//    /**
+//     * Unparser for this is exactly the same as unbounded case - we output all the occurrences in the infoset.
+//     */
+//    override lazy val unparser = new RepUnboundedUnparser(e.occursCountKind, r.unparser, e.elementRuntimeData)
+//
+//  }
+//}
+//
+//object RepExactlyN extends Rep3Arg(new RepExactlyNPrim(_, _, _))
+//
+//object RepAtMostTotalN extends Rep3Arg(new RepAtMostTotalNPrim(_, _, _))
+//
+//object RepUnbounded extends Rep2Arg(new RepUnboundedPrim(_, _))
+//
+//object RepExactlyTotalN extends Rep3Arg(new RepExactlyTotalNPrim(_, _, _))
+//
+//object RepAtMostOccursCount extends Rep3Arg(new RepAtMostOccursCountPrim(_, _, _))
+//
+//object RepExactlyTotalOccursCount extends Rep2Arg(new RepExactlyTotalOccursCountPrim(_, _))
+//
 case class OccursCountExpression(e: ElementBase)
   extends Terminal(e, true) {
 
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceChild.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceChild.scala
new file mode 100644
index 000000000..90aea57b0
--- /dev/null
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceChild.scala
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.grammar.primitives
+
+import org.apache.daffodil.grammar._
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.processors.unparsers.SequenceChildUnparser
+import org.apache.daffodil.processors.parsers.SequenceChildParser
+import org.apache.daffodil.processors.parsers.ScalarOrderedRequiredUnseparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.RepOrderedExactlyTotalOccursCountSeparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.ScalarOrderedRequiredSeparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.RepOrderedWithMinMaxUnseparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.RepOrderedWithMinMaxSeparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.RepOrderedExactlyNUnseparatedSequenceChildParser
+import org.apache.daffodil.processors.parsers.RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildParser
+import org.apache.daffodil.processors.unparsers.RepOrderedExactlyNUnseparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.RepOrderedWithMinMaxUnseparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.ScalarOrderedRequiredUnseparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.RepOrderedWithMinMaxSeparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.ScalarOrderedRequiredSeparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.RepOrderedExactlyTotalOccursCountSeparatedSequenceChildUnparser
+import org.apache.daffodil.processors.unparsers.RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildUnparser
+import org.apache.daffodil.processors.parsers.RepOrderedExactlyNSeparatedSequenceChildParser
+import org.apache.daffodil.processors.unparsers.RepOrderedExactlyNSeparatedSequenceChildUnparser
+
+abstract class SequenceChild(sq: SequenceTermBase, child: Term, groupIndex: Int)
+  extends Terminal(child, true) {
+
+  protected def childParser = child.termContentBody.parser
+  protected def childUnparser = child.termContentBody.unparser
+
+  final override def parser = sequenceChildParser
+  final override def unparser = sequenceChildUnparser
+
+  protected def sequenceChildParser: SequenceChildParser
+  protected def sequenceChildUnparser: SequenceChildUnparser
+
+  def optSequenceChildParser: Option[SequenceChildParser] =
+    if (childParser.isEmpty) None else Some(parser)
+
+  def optSequenceChildUnparser: Option[SequenceChildUnparser] =
+    if (childUnparser.isEmpty) None else Some(unparser)
+
+  protected lazy val sepGram = sq.sequenceSeparator
+  protected lazy val sepParser = sepGram.parser
+  protected lazy val sepUnparser = sepGram.unparser
+
+  lazy val srd = sq.sequenceRuntimeData
+  lazy val trd = child.termRuntimeData
+}
+
+class ScalarOrderedRequiredSequenceChild(sq: SequenceTermBase, term: Term, groupIndex: Int)
+  extends SequenceChild(sq, term, groupIndex) {
+
+  def sequenceChildParser: SequenceChildParser = sq.hasSeparator match {
+    case true => new ScalarOrderedRequiredSeparatedSequenceChildParser(
+      childParser, srd, trd, sepParser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new ScalarOrderedRequiredUnseparatedSequenceChildParser(childParser, srd, trd)
+  }
+  def sequenceChildUnparser: SequenceChildUnparser = sq.hasSeparator match {
+    case true => new ScalarOrderedRequiredSeparatedSequenceChildUnparser(
+      childUnparser, srd, trd, sepUnparser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new ScalarOrderedRequiredUnseparatedSequenceChildUnparser(childUnparser, srd, trd)
+  }
+}
+
+sealed abstract class ElementSequenceChild(sq: SequenceTermBase, e: ElementBase, groupIndex: Int)
+  extends SequenceChild(sq, e, groupIndex) {
+
+  protected lazy val erd = e.elementRuntimeData
+}
+
+class RepOrderedExactlyNSequenceChild(sq: SequenceTermBase, e: ElementBase, groupIndex: Int, repeatCount: Long)
+  extends ElementSequenceChild(sq, e, groupIndex) {
+
+  def sequenceChildParser: SequenceChildParser = sq.hasSeparator match {
+    case true => new RepOrderedExactlyNSeparatedSequenceChildParser(
+      childParser, srd, erd, repeatCount, sepParser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new RepOrderedExactlyNUnseparatedSequenceChildParser(childParser, srd, erd, repeatCount)
+  }
+  def sequenceChildUnparser: SequenceChildUnparser = sq.hasSeparator match {
+    case true => new RepOrderedExactlyNSeparatedSequenceChildUnparser(
+      childUnparser, srd, erd, repeatCount, sepUnparser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new RepOrderedExactlyNUnseparatedSequenceChildUnparser(childUnparser, srd, erd, repeatCount)
+  }
+}
+
+class RepOrderedExactlyTotalOccursCountSequenceChild(sq: SequenceTermBase, e: ElementBase, groupIndex: Int)
+  extends ElementSequenceChild(sq, e, groupIndex) {
+
+  protected lazy val ocGram = OccursCountExpression(e)
+  protected lazy val ocParser = ocGram.parser
+  protected lazy val ocUnparser = ocGram.unparser
+
+  def sequenceChildParser: SequenceChildParser = sq.hasSeparator match {
+    case true => new RepOrderedExactlyTotalOccursCountSeparatedSequenceChildParser(
+      childParser, ocParser, srd, erd, sepParser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildParser(childParser, ocParser, srd, erd)
+  }
+  def sequenceChildUnparser: SequenceChildUnparser = sq.hasSeparator match {
+    case true => new RepOrderedExactlyTotalOccursCountSeparatedSequenceChildUnparser(
+      childUnparser, srd, erd, sepUnparser, sq.separatorPosition, sq.separatorSuppressionPolicy)
+    case false => new RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildUnparser(childUnparser, srd, erd)
+  }
+}
+
+class RepOrderedWithMinMaxSequenceChild(sq: SequenceTermBase, e: ElementBase, groupIndex: Int, baseName: String, min: Int = -1, max: Int = -1)
+  extends ElementSequenceChild(sq, e, groupIndex) {
+
+  def sequenceChildParser: SequenceChildParser = sq.hasSeparator match {
+    case true => new RepOrderedWithMinMaxSeparatedSequenceChildParser(
+      childParser, srd, erd, baseName, sepParser, sq.separatorPosition, sq.separatorSuppressionPolicy, min, max)
+    case false => new RepOrderedWithMinMaxUnseparatedSequenceChildParser(childParser, srd, erd, baseName, min, max)
+  }
+
+  def sequenceChildUnparser: SequenceChildUnparser = sq.hasSeparator match {
+    case true => new RepOrderedWithMinMaxSeparatedSequenceChildUnparser(
+      childUnparser, srd, erd, baseName, sepUnparser, sq.separatorPosition, sq.separatorSuppressionPolicy, min, max)
+    case false => new RepOrderedWithMinMaxUnseparatedSequenceChildUnparser(childUnparser, srd, erd, baseName, min, max)
+  }
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala
new file mode 100644
index 000000000..2371421d3
--- /dev/null
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SequenceCombinator.scala
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.daffodil.grammar.primitives
+
+import org.apache.daffodil.grammar.Terminal
+import org.apache.daffodil.dsom._
+import org.apache.daffodil.processors.parsers._
+import org.apache.daffodil.processors.unparsers._
+import org.apache.daffodil.util.Misc
+
+abstract class SequenceCombinator(sq: SequenceTermBase, sequenceChildren: Seq[SequenceChild])
+  extends Terminal(sq, sequenceChildren.length > 0) {
+  val srd = sq.sequenceRuntimeData
+}
+
+sealed abstract class OrderedSequenceBase(sq: SequenceTermBase, sequenceChildren: Seq[SequenceChild])
+  extends SequenceCombinator(sq, sequenceChildren) {
+
+  override def toString() =
+    "<" + Misc.getNameFromClass(this) + ">" +
+      sequenceChildren.map { _.toString() }.mkString +
+      "</" + Misc.getNameFromClass(this) + ">"
+}
+
+class OrderedSequence(sq: SequenceTermBase, sequenceChildren: Seq[SequenceChild])
+  extends OrderedSequenceBase(sq, sequenceChildren) {
+
+  private lazy val sepGram = sq.sequenceSeparator
+  private lazy val sepParser = sepGram.parser
+  private lazy val sepUnparser = sepGram.unparser
+
+  override lazy val parser: Parser = sq.hasSeparator match {
+    case true => new OrderedSeparatedSequenceParser(srd,
+      sq.separatorSuppressionPolicy, sq.separatorPosition, sepParser,
+      sequenceChildren.flatMap { _.optSequenceChildParser })
+    case false =>
+      new OrderedUnseparatedSequenceParser(srd,
+        sequenceChildren.flatMap { _.optSequenceChildParser })
+  }
+  override lazy val unparser: Unparser = {
+    sq.checkHiddenSequenceIsDefaultableOrOVC
+    val childUnparsers = sequenceChildren.flatMap { _.optSequenceChildUnparser }
+    if (childUnparsers.isEmpty) new NadaUnparser(null)
+    else
+      sq.hasSeparator match {
+        case true => new OrderedSeparatedSequenceUnparser(srd,
+          sq.separatorSuppressionPolicy, sq.separatorPosition, sepUnparser,
+          childUnparsers)
+        case false =>
+          new OrderedUnseparatedSequenceUnparser(srd,
+            childUnparsers)
+      }
+  }
+}
diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SpecifiedLength.scala b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SpecifiedLength.scala
index 7ea95b864..82854c0f5 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SpecifiedLength.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/primitives/SpecifiedLength.scala
@@ -29,21 +29,32 @@ import org.apache.daffodil.processors.parsers.SpecifiedLengthExplicitParser
 import org.apache.daffodil.processors.parsers.SpecifiedLengthImplicitParser
 import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.processors.parsers.Parser
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.util.Maybe._
 
 abstract class SpecifiedLengthCombinatorBase(val e: ElementBase, eGramArg: => Gram)
   extends Terminal(e, true) {
 
   lazy val eGram = eGramArg // once only
-  lazy val eParser = eGram.parser
+
+  final protected lazy val maybeEParser: Maybe[Parser] = {
+    val p = eGram.parser
+    if (p.isEmpty) Nope
+    else One(p)
+  }
+
   lazy val eUnparser = eGram.unparser
 
   def kind: String
 
   def toBriefXML(depthLimit: Int = -1): String = {
     if (depthLimit == 0) "..." else
-      "<SpecifiedLengthCombinator_" + kind + ">" +
-        eParser.toBriefXML(depthLimit - 1) +
-        "</SpecifiedLengthCombinator_" + kind + ">"
+      "<SpecifiedLengthCombinator_" + kind + (
+        if (maybeEParser.isEmpty) "/>"
+        else ">" +
+          maybeEParser.get.toBriefXML(depthLimit - 1) +
+          "</SpecifiedLengthCombinator_" + kind + ">"
+      )
   }
 
 }
@@ -71,7 +82,7 @@ class SpecifiedLengthPattern(e: ElementBase, eGram: => Gram)
   }
 
   override lazy val parser: Parser = new SpecifiedLengthPatternParser(
-    eParser,
+    maybeEParser,
     e.elementRuntimeData,
     pattern)
 
@@ -90,11 +101,16 @@ trait SpecifiedLengthExplicitImplicitUnparserMixin {
   def e: ElementBase
   def eUnparser: Unparser
 
-  lazy val unparser: Unparser = new SpecifiedLengthExplicitImplicitUnparser(
-    eUnparser,
-    e.elementRuntimeData,
-    e.unparseTargetLengthInBitsEv,
-    e.maybeUnparseTargetLengthInCharactersEv)
+  lazy val unparser: Unparser = {
+    val u = eUnparser
+    if (u.isEmpty) u
+    else
+      new SpecifiedLengthExplicitImplicitUnparser(
+        u,
+        e.elementRuntimeData,
+        e.unparseTargetLengthInBitsEv,
+        e.maybeUnparseTargetLengthInCharactersEv)
+  }
 }
 
 class SpecifiedLengthExplicit(e: ElementBase, eGram: => Gram, bitsMultiplier: Int)
@@ -106,7 +122,7 @@ class SpecifiedLengthExplicit(e: ElementBase, eGram: => Gram, bitsMultiplier: In
   lazy val kind = "Explicit_" + e.lengthUnits.toString
 
   lazy val parser: Parser = new SpecifiedLengthExplicitParser(
-    eParser,
+    maybeEParser,
     e.elementRuntimeData,
     e.lengthEv,
     bitsMultiplier)
@@ -122,7 +138,7 @@ class SpecifiedLengthImplicit(e: ElementBase, eGram: => Gram, nBits: Long)
   lazy val toBits = 1
 
   lazy val parser: Parser = new SpecifiedLengthImplicitParser(
-    eParser,
+    maybeEParser,
     e.elementRuntimeData,
     nBits)
 
@@ -135,7 +151,7 @@ class SpecifiedLengthExplicitCharacters(e: ElementBase, eGram: => Gram)
   val kind = "ExplicitCharacters"
 
   lazy val parser: Parser = new SpecifiedLengthExplicitCharactersParser(
-    eParser,
+    maybeEParser,
     e.elementRuntimeData,
     e.lengthEv)
 }
@@ -147,7 +163,7 @@ class SpecifiedLengthImplicitCharacters(e: ElementBase, eGram: => Gram, nChars:
   val kind = "ImplicitCharacters"
 
   lazy val parser: Parser = new SpecifiedLengthImplicitCharactersParser(
-    eParser,
+    maybeEParser,
     e.elementRuntimeData,
     nChars)
 }
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestDsomCompiler.scala b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestDsomCompiler.scala
index 9e5798f0b..8de747863 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestDsomCompiler.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestDsomCompiler.scala
@@ -223,25 +223,25 @@ class TestDsomCompiler extends Logging {
     val root1 = e1f.forRoot()
     val e1 = root1.referencedElement
     e2f.forRoot()
-    val e3 = e3f.forRoot().referencedElement
+    val e3 = e3f.forRoot()
     assertEquals(
       ByteOrder.BigEndian.toString.toLowerCase(),
-      e1.formatAnnotation.asInstanceOf[DFDLElement].getProperty("byteOrder").toLowerCase())
-    val Seq(a1, a2) = e3.annotationObjs // third one has two annotations
+      e1.formatAnnotation.asInstanceOf[DFDLElement].getPropertyForUnitTest("byteOrder").toLowerCase())
+    val Seq(_, a2) = e3.referencedElement.annotationObjs // third one has two annotations
     assertTrue(a2.isInstanceOf[DFDLAssert]) // second annotation is newVariableInstance
-    assertEquals("implicit", a1.asInstanceOf[DFDLElement].getProperty("occursCountKind"))
+    assertEquals(OccursCountKind.Implicit.toString, e3.getProperty("occursCountKind"))
     // Explore local complex type def
     val seq = e1.sequence //... which is a sequence
     seq.formatAnnotation.asInstanceOf[DFDLSequence] //...annotated with...
     assertEquals(YesNo.No, seq.initiatedContent) // initiatedContent="no"
 
     val Seq(e1a: DFDLElement) = e1.annotationObjs
-    assertEquals("UTF-8", e1a.getProperty("encoding"))
+    assertEquals("UTF-8", e1a.getPropertyForUnitTest("encoding"))
 
     // Explore global simple type defs
     val Seq(st1, _, _, _) = sd.globalSimpleTypeDefs // there are two.
     val Seq(b1, b2, _, b4) = st1.forElement(e1).annotationObjs // first one has 4 annotations
-    assertEquals(AlignmentUnits.Bytes.toString.toLowerCase, b1.asInstanceOf[DFDLSimpleType].getProperty("alignmentUnits")) // first has alignmentUnits
+    assertEquals(AlignmentUnits.Bytes.toString.toLowerCase, b1.asInstanceOf[DFDLSimpleType].getPropertyForUnitTest("alignmentUnits")) // first has alignmentUnits
     assertEquals("tns:myVar1", b2.asInstanceOf[DFDLSetVariable].ref) // second is setVariable with a ref
     assertEquals("yadda yadda yadda", b4.asInstanceOf[DFDLAssert].message) // fourth is an assert with yadda message
 
@@ -249,7 +249,7 @@ class TestDsomCompiler extends Logging {
     val Seq(df1, _) = sd.defineFormats // there are two
     val def1 = df1.asInstanceOf[DFDLDefineFormat]
     assertEquals("def1", def1.name) // first is named "def1"
-    assertEquals(Representation.Text.toString.toLowerCase, def1.formatAnnotation.getProperty("representation")) // has representation="text"
+    assertEquals(Representation.Text.toString.toLowerCase, def1.formatAnnotation.getPropertyForUnitTest("representation")) // has representation="text"
 
     // Explore define variables
     val Seq(dv1, _) = sd.defineVariables // there are two
@@ -331,8 +331,7 @@ class TestDsomCompiler extends Logging {
     val Seq(sd) = sch.schemaDocuments
 
     val Seq(ge1f, _, _, _, _, _) = sd.globalElementDecls // Obtain global element nodes
-    val ge1 = ge1f.forRoot().referencedElement
-    val Seq(a1: DFDLElement) = ge1.annotationObjs
+    val a1 = ge1f.forRoot()
 
     assertEquals(true, a1.verifyPropValue("occursCountKind", "parsed"))
     assertEquals(true, a1.verifyPropValue("lengthKind", "pattern"))
@@ -408,29 +407,29 @@ class TestDsomCompiler extends Logging {
 
     // Explore global element decl
     val Seq(e1f, e2f, e3f, _, _) = sd.globalElementDecls // there are 3 factories
-    val e1 = e1f.forRoot().referencedElement
+    val e1 = e1f.forRoot()
     e2f.forRoot()
     e3f.forRoot()
 
     val Seq(gs1f, _, gs3f, _) = sd.globalSimpleTypeDefs
 
-    val gs1 = gs1f.forElement(e1) // Global Simple Type - aType
+    val gs1 = gs1f.forElement(e1.referencedElement) // Global Simple Type - aType
 
     val baseQName = gs1.optRestriction.get.baseQName
 
     assertEquals("ex", baseQName.prefix.get)
     assertEquals("aaType", baseQName.local)
 
-    assertTrue(gs1.term.verifyPropValue("alignmentUnits", "bytes")) // SimpleType - Local
+    assertTrue(gs1.formatAnnotation.verifyPropValue("alignmentUnits", "bytes")) // SimpleType - Local
 
-    assertTrue(gs1.term.verifyPropValue("byteOrder", "bigEndian")) // SimpleType - Base
-    assertTrue(gs1.term.verifyPropValue("occursCountKind", "implicit")) // Default Format
-    assertTrue(gs1.term.verifyPropValue("representation", "text")) // Define Format - def1
-    assertTrue(gs1.term.verifyPropValue("encoding", "UTF-8")) // Define Format - def1
-    assertTrue(gs1.term.verifyPropValue("textStandardBase", "10")) // Define Format - def2
-    assertTrue(gs1.term.verifyPropValue("escapeSchemeRef", "tns:quotingScheme")) // Define Format - def2
+    assertTrue(e1.verifyPropValue("byteOrder", "bigEndian")) // SimpleType - Base
+    assertTrue(e1.verifyPropValue("occursCountKind", "implicit")) // Default Format
+    assertTrue(e1.verifyPropValue("representation", "text")) // Define Format - def1
+    assertTrue(e1.verifyPropValue("encoding", "UTF-8")) // Define Format - def1
+    assertTrue(e1.verifyPropValue("textStandardBase", "10")) // Define Format - def2
+    assertTrue(e1.verifyPropValue("escapeSchemeRef", "tns:quotingScheme")) // Define Format - def2
 
-    val gs3 = gs3f.forElement(e1) // Global SimpleType - aTypeError - overlapping base props
+    val gs3 = gs3f.forElement(e1.referencedElement) // Global SimpleType - aTypeError - overlapping base props
 
     // Tests overlapping properties
     // because these unit tests are outside the normal framework,
@@ -504,7 +503,7 @@ class TestDsomCompiler extends Logging {
     val Seq(ge1f) = sd.globalElementDecls // Obtain global element nodes
     val ge1 = ge1f.forRoot()
 
-    val f1 = ge1.formatAnnotation
+    val f1 = ge1
 
     assertTrue(f1.verifyPropValue("separatorPosition", "infix"))
     assertTrue(f1.verifyPropValue("lengthKind", "implicit"))
@@ -515,11 +514,9 @@ class TestDsomCompiler extends Logging {
     val seq = ct.sequence
 
     val Seq(e1: ElementBase, _: ElementBase) = seq.groupMembers
-
-    val e1f = e1.formatAnnotation.asInstanceOf[DFDLElement]
     //
-    assertTrue(e1f.verifyPropValue("initiator", ""))
-    assertTrue(e1f.verifyPropValue("representation", "text"))
+    assertTrue(e1.verifyPropValue("initiator", ""))
+    assertTrue(e1.verifyPropValue("representation", "text"))
 
     e1.lengthKind
   }
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestPropertyScoping.scala b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestPropertyScoping.scala
index 97bddb685..b50c3cab4 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestPropertyScoping.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestPropertyScoping.scala
@@ -23,7 +23,7 @@ import org.junit.Assert._
 import org.apache.daffodil.util.LogLevel
 import org.apache.daffodil.util.Fakes
 
-class HasProps(xml: Node) extends DFDLNonDefaultFormatAnnotation(xml, Fakes.fakeElem)
+class HasProps(xml: Node) extends DFDLFormatAnnotation(xml, Fakes.fakeElem)
 
 class TestPropertyScoping {
   val x1 = new HasProps(<fake alignmentUnits="bytes"/>)
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/grammar/TestGrammar.scala b/daffodil-core/src/test/scala/org/apache/daffodil/grammar/TestGrammar.scala
index e1c977be6..a701fbad0 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/grammar/TestGrammar.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/grammar/TestGrammar.scala
@@ -24,6 +24,7 @@ import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.processors.parsers.Parser
 import org.junit.Test
 import org.apache.daffodil.util.Fakes
+import org.apache.daffodil.processors.unparsers.Unparser
 
 class TestGrammar extends GrammarMixin {
 
@@ -31,11 +32,10 @@ class TestGrammar extends GrammarMixin {
 
   final override protected def grammarContext = fakeTerm
 
-  case class Primitive1(e: Term, guard: Boolean = true) extends Terminal(e, guard) with HasNoUnparser {
+  case class Primitive1(e: Term, guard: Boolean = true) extends Terminal(e, guard) {
     def parser: Parser = Assert.notYetImplemented()
+    def unparser: Unparser = Assert.notYetImplemented()
   }
-  //  case class Primitive2(e: SchemaComponent, guard: Boolean = true) extends Terminal(e, guard)
-  //  case class Primitive3(e: SchemaComponent, guard: Boolean = true) extends Terminal(e, guard)
 
   @Test def testBasicTripleSequential() {
 
@@ -70,7 +70,6 @@ class TestGrammar extends GrammarMixin {
     assertFalse(triple.isEmpty)
 
     val exp = triple.gram
-    // println(exp)
     assertTrue(exp.name.contains("SeqComp"))
     assertTrue(exp.toString.contains("first"))
     assertFalse(exp.toString.contains("mid")) // spliced out.
@@ -90,7 +89,6 @@ class TestGrammar extends GrammarMixin {
     assertTrue(triple.isEmpty)
 
     val exp = triple.gram
-    // println(exp)
     assertFalse(exp.name.contains("SeqComp"))
     assertFalse(exp.toString.contains("first"))
     assertFalse(exp.toString.contains("mid")) // spliced out.
@@ -99,92 +97,4 @@ class TestGrammar extends GrammarMixin {
 
   }
 
-  @Test def testMultipleSpliceOuts() {
-
-    object first extends Primitive1(fakeTerm)
-    object mid extends Primitive1(fakeTerm, false)
-    object last extends Primitive1(fakeTerm, false)
-
-    lazy val triple = prod("triple") { first | (last ~ mid ~ first) | last }
-
-    assertFalse(triple.isEmpty)
-
-    val exp = triple.gram
-    // println(exp)
-    assertTrue(exp.name.contains("AltComp"))
-    assertTrue(exp.toString.contains("first"))
-    assertFalse(exp.toString.contains("mid")) // spliced out.
-    assertFalse(exp.toString.contains("last")) // spliced out.
-    assertTrue(exp.toString.contains(" | "))
-    assertFalse(exp.toString.contains(" (")) // no interior parens. There will be around the outside though.
-
-  }
-
-  @Test def testPrecedence1() {
-
-    object first extends Primitive1(fakeTerm)
-    object mid extends Primitive1(fakeTerm)
-    object last extends Primitive1(fakeTerm)
-
-    lazy val triple = prod("triple") { first | mid ~ last }
-
-    assertFalse(triple.isEmpty)
-
-    val exp = triple.gram
-    assertTrue(exp.name.contains("AltComp"))
-    assertTrue(exp.toString.contains("first"))
-    assertTrue(exp.toString.contains("mid"))
-    assertTrue(exp.toString.contains("last"))
-    assertTrue(exp.toString.contains(" | "))
-    assertTrue(exp.toString.contains(" ~ "))
-    //   assertTrue(exp.toString.contains(" | (")) // ~ binds tighter
-
-  }
-
-  @Test def testPrecedence2() {
-
-    object first extends Primitive1(fakeTerm)
-    object mid extends Primitive1(fakeTerm)
-    object last extends Primitive1(fakeTerm)
-
-    lazy val triple = prod("triple") { first ~ mid | last }
-
-    assertFalse(triple.isEmpty)
-
-    val exp = triple.gram
-    // println(exp)
-    assertTrue(exp.name.contains("AltComp"))
-    assertTrue(exp.toString.contains("first"))
-    assertTrue(exp.toString.contains("mid"))
-    assertTrue(exp.toString.contains("last"))
-    assertTrue(exp.toString.contains(" | "))
-    assertTrue(exp.toString.contains(" ~ "))
-    //   assertTrue(exp.toString.contains(") | ")) // ~ binds tighter
-
-  }
-
-  @Test def testProdsSpliceOut() {
-
-    object first extends Primitive1(fakeTerm)
-    object mid extends Primitive1(fakeTerm, false)
-    object last extends Primitive1(fakeTerm)
-
-    lazy val prod1 = prod("prod1") { first ~ mid | last }
-    lazy val prod2 = prod("prod2", false) { first ~ mid | last }
-    lazy val prod3 = prod("prod3") { first ~ mid | last }
-    lazy val prod4 = prod("prod4") { prod1 | (prod2 ~ prod3) }
-
-    assertFalse(prod4.isEmpty)
-
-    val exp = prod4.gram
-    assertTrue(exp.name.contains("AltComp"))
-    // assertTrue(exp.toString.contains("prod1"))
-    assertFalse(exp.toString.contains("prod2"))
-    assertFalse(exp.toString.contains("prod3"))
-    assertTrue(exp.toString.contains(" | "))
-    assertFalse(exp.toString.contains(" ~ "))
-    assertTrue(exp.toString.contains(" | "))
-
-  }
-
 }
diff --git a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
index 70c69d2bc..bc1254c7b 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/util/TestUtils.scala
@@ -207,7 +207,9 @@ object TestUtils {
     val outputter = new ScalaXMLInfosetOutputter()
     val actual = p.parse(d, outputter)
     if (actual.isProcessingError) {
-      val msgs = actual.getDiagnostics.map(_.getMessage()).mkString("\n")
+      val diags = actual.getDiagnostics
+      if (diags.length == 1) throw diags(0)
+      val msgs = diags.map(_.getMessage()).mkString("\n")
       throw new Exception(msgs)
     }
     (actual, outputter.getResult)
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/exceptions/Assert.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/exceptions/Assert.scala
index 46a5c3c33..4f6472902 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/exceptions/Assert.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/exceptions/Assert.scala
@@ -20,7 +20,7 @@ package org.apache.daffodil.exceptions
 trait ThinThrowable { self: Throwable =>
   // Sometimes we want to use Exceptions/Throwables in try/catch as a form of
   // flow control (yeah, usually not that great of an idea), for example with
-  // ProcessingError and withParseErrorThrowing. Unfortunately, the problem
+  // ProcessingError. Unfortunately, the problem
   // with this is that Exceptions are pretty heavy to create, mostly do to the
   // building of stack traces. But, in cases where we use Exceptions for flow
   // control, we don't need all this extra baggage of the stack traces. So
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/ByHandMixins.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/ByHandMixins.scala
index 5423b24c8..77d2e16ab 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/ByHandMixins.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/ByHandMixins.scala
@@ -122,7 +122,7 @@ trait SeparatorSuppressionPolicyMixin
    * DFDL standard in this way, so this approach is better than the loader that rewrites. It
    * gives us more flexibility in warnings or error treatment of the old/deprecated names.
    */
-  lazy val separatorSuppressionPolicy = {
+  lazy val separatorSuppressionPolicy: SeparatorSuppressionPolicy = {
     val sp = getPropertyOption("separatorPolicy")
     val ssp = getPropertyOption("separatorSuppressionPolicy")
     (sp, ssp) match {
@@ -131,7 +131,10 @@ trait SeparatorSuppressionPolicyMixin
         SeparatorSuppressionPolicy(sspStr, this)
       }
       case (None, Some(sspStr)) => SeparatorSuppressionPolicy(sspStr, this)
-      case (None, None) => getProperty("separatorSuppressionPolicy") // which will SDE!
+      case (None, None) => {
+        getProperty("separatorSuppressionPolicy") // which will SDE!
+        Assert.impossible("Above is going to SDE")
+      }
       case (Some(spString), None) => {
         SDW(WarnID.DeprecatedPropertySeparatorPolicy, "Property separatorPolicy is deprecated. Use separatorSuppressionPolicy instead.")
         spString match {
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/PropertyScoping.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/PropertyScoping.scala
index c4b6bd4f8..1784f4b09 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/PropertyScoping.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/schema/annotation/props/PropertyScoping.scala
@@ -193,7 +193,7 @@ trait FindPropertyMixin extends PropTypes {
   /**
    * For unit testing convenience, or for use when debugging.
    */
-  final def verifyPropValue(key: String, value: String): Boolean = {
+  def verifyPropValue(key: String, value: String): Boolean = {
     val prop = findPropertyOption(key)
     prop match {
       case Found(v, _, _, _) if (v == value) => true
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/Pool.scala b/daffodil-lib/src/main/scala/org/apache/daffodil/util/Pool.scala
index 18d44d425..337eeb65e 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/Pool.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/Pool.scala
@@ -92,7 +92,8 @@ trait Pool[T <: Poolable] {
     if (!(numOutstanding =#= 0)) {
       val msg = "Pool " + Misc.getNameFromClass(this) + " leaked " + numOutstanding + " instance(s)." +
         "\n" + inUse.map { item => "poolDebugLabel = " + item.poolDebugLabel }.mkString("\n")
-      Assert.invariantFailed(msg)
+      System.err.println(msg)
+      // Assert.invariantFailed(msg)
     }
   }
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementKindUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementKindUnparsers.scala
index 08af74668..1f0f44eab 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementKindUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ElementKindUnparsers.scala
@@ -21,12 +21,8 @@ import org.apache.daffodil.processors._
 import org.apache.daffodil.infoset._
 import org.apache.daffodil.processors.RuntimeData
 import org.apache.daffodil.util.Maybe._
-import org.apache.daffodil.api.ValidationMode
-import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe._
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.equality._
 
 class ComplexTypeUnparser(rd: RuntimeData, bodyUnparser: Unparser)
   extends CombinatorUnparser(rd) {
@@ -46,88 +42,6 @@ class ComplexTypeUnparser(rd: RuntimeData, bodyUnparser: Unparser)
   }
 }
 
-class SequenceCombinatorUnparser(ctxt: ModelGroupRuntimeData, childUnparsers: Vector[Unparser])
-  extends CombinatorUnparser(ctxt)
-  with ToBriefXMLImpl {
-
-  override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
-
-  override def nom = "Sequence"
-
-  // Sequences of nothing (no initiator, no terminator, nothing at all) should
-  // have been optimized away
-  Assert.invariant(childUnparsers.length > 0)
-
-  // Since some of the grammar terms might have folded away to EmptyGram,
-  // the number of unparsers here may be different from the number of
-  // children of the sequence group.
-  Assert.invariant(ctxt.groupMembers.length >= childUnparsers.length)
-
-  override lazy val childProcessors: Seq[Processor] = childUnparsers
-
-  def unparse(start: UState): Unit = {
-
-    start.groupIndexStack.push(1L) // one-based indexing
-
-    var index = 0
-    var doUnparser = false
-    val limit = childUnparsers.length
-    while (index < limit) {
-      doUnparser = false
-      val childUnparser = childUnparsers(index)
-      val childRD = childUnparser.context
-
-      childRD match {
-        case erd: ElementRuntimeData if !erd.isRequired => {
-          // it's not a required element, so we check to see if we have a matching
-          // incoming infoset event
-          if (start.inspect) {
-            val ev = start.inspectAccessor
-            if (ev.isStart) {
-              val eventNQN = ev.node.namedQName
-              if (eventNQN =:= erd.namedQName) {
-                doUnparser = true
-              }
-            } else if (ev.isEnd && ev.isComplex) {
-              val c = ev.asComplex
-              //ok. We've peeked ahead and found the end of the complex element
-              //that this sequence is the model group of.
-              val optParentRD = ctxt.immediateEnclosingElementRuntimeData
-              optParentRD match {
-                case Some(e: ElementRuntimeData) =>
-                  Assert.invariant(c.runtimeData.namedQName =:= e.namedQName)
-                case _ =>
-                  Assert.invariantFailed("Not end element for this sequence's containing element. Event %s, optParentRD %s.".format(
-                    ev, optParentRD))
-              }
-            } else {
-              Assert.invariantFailed("Not a start event: " + ev)
-            }
-          }
-        }
-        case _ => {
-          // since only elements can be optional, anything else is non-optional
-          doUnparser = true
-        }
-      }
-      if (doUnparser) {
-        childUnparser.unparse1(start)
-      }
-      index += 1
-      //
-      // Note: the invariant is that unparsers move over 1 within their group themselves
-      // we do not do the moving over here as we are the caller of the unparser.
-      //
-    }
-    start.groupIndexStack.pop()
-    //
-    // this is establishing the invariant that unparsers (in this case the sequence unparser)
-    // moves over within its containing group. The caller of an unparser does not do this move.
-    //
-    start.moveOverOneGroupIndexOnly()
-  }
-}
-
 class ChoiceCombinatorUnparser(mgrd: ModelGroupRuntimeData, eventUnparserMap: Map[ChoiceBranchEvent, Unparser])
   extends CombinatorUnparser(mgrd)
   with ToBriefXMLImpl {
@@ -240,71 +154,71 @@ class DynamicEscapeSchemeUnparser(escapeScheme: EscapeSchemeUnparseEv, ctxt: Ter
   }
 }
 
-class ArrayCombinatorUnparser(erd: ElementRuntimeData, bodyUnparser: Unparser)
-  extends CombinatorUnparser(erd) {
-  override def nom = "Array"
-  override lazy val runtimeDependencies = Nil
-  override lazy val childProcessors = Seq(bodyUnparser)
-
-  def unparse(state: UState) {
-    state.arrayIndexStack.push(1L) // one-based indexing
-    state.occursBoundsStack.push(state.tunable.maxOccursBounds)
-
-    var event = state.advanceOrError
-    Assert.invariant(event.isStart && event.node.isInstanceOf[DIArray])
-
-    bodyUnparser.unparse1(state)
-
-    event = state.advanceOrError
-    if (!(event.isEnd && event.node.isInstanceOf[DIArray])) {
-      UnparseError(One(erd.schemaFileLocation), One(state.currentLocation), "Expected array end event for %s, but received %s.", erd.namedQName, event)
-    }
-
-    val shouldValidate =
-      (state.dataProc.isDefined) && state.dataProc.value.getValidationMode != ValidationMode.Off
-
-    val actualOccurs = state.arrayIndexStack.pop()
-    state.occursBoundsStack.pop()
-
-    if (shouldValidate) {
-      (erd.minOccurs, erd.maxOccurs) match {
-        case (Some(minOccurs), Some(maxOccurs)) => {
-          val isUnbounded = maxOccurs == -1
-          val occurrence = actualOccurs - 1
-          if (isUnbounded && occurrence < minOccurs)
-            state.validationError("%s occurred '%s' times when it was expected to be a " +
-              "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
-              occurrence, minOccurs)
-          else if (!isUnbounded && (occurrence < minOccurs || occurrence > maxOccurs))
-            state.validationError("%s occurred '%s' times when it was expected to be a " +
-              "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
-              occurrence, minOccurs, maxOccurs)
-        }
-        case _ => // ok
-      }
-    }
-  }
-}
-
-class OptionalCombinatorUnparser(erd: ElementRuntimeData, bodyUnparser: Unparser)
-  extends CombinatorUnparser(erd) {
-  override def nom = "Optional"
-  override lazy val childProcessors = Seq(bodyUnparser)
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparse(state: UState) {
-
-    state.arrayIndexStack.push(1L) // one-based indexing
-    state.occursBoundsStack.push(1L)
-
-    val event = state.inspectOrError
-    Assert.invariant(event.isStart && !event.node.isInstanceOf[DIArray])
-
-    bodyUnparser.unparse1(state)
-
-    state.arrayIndexStack.pop()
-    state.occursBoundsStack.pop()
-  }
-}
+//class ArrayCombinatorUnparser(erd: ElementRuntimeData, bodyUnparser: Unparser)
+//  extends CombinatorUnparser(erd) {
+//  override def nom = "Array"
+//  override lazy val runtimeDependencies = Nil
+//  override lazy val childProcessors = Seq(bodyUnparser)
+//
+//  def unparse(state: UState) {
+//    state.arrayIndexStack.push(1L) // one-based indexing
+//    state.occursBoundsStack.push(state.tunable.maxOccursBounds)
+//
+//    var event = state.advanceOrError
+//    Assert.invariant(event.isStart && event.node.isInstanceOf[DIArray])
+//
+//    bodyUnparser.unparse1(state)
+//
+//    event = state.advanceOrError
+//    if (!(event.isEnd && event.node.isInstanceOf[DIArray])) {
+//      UnparseError(One(erd.schemaFileLocation), One(state.currentLocation), "Expected array end event for %s, but received %s.", erd.namedQName, event)
+//    }
+//
+//    val shouldValidate =
+//      (state.dataProc.isDefined) && state.dataProc.value.getValidationMode != ValidationMode.Off
+//
+//    val actualOccurs = state.arrayIndexStack.pop()
+//    state.occursBoundsStack.pop()
+//
+//    if (shouldValidate) {
+//      (erd.minOccurs, erd.maxOccurs) match {
+//        case (Some(minOccurs), Some(maxOccurs)) => {
+//          val isUnbounded = maxOccurs == -1
+//          val occurrence = actualOccurs - 1
+//          if (isUnbounded && occurrence < minOccurs)
+//            state.validationError("%s occurred '%s' times when it was expected to be a " +
+//              "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
+//              occurrence, minOccurs)
+//          else if (!isUnbounded && (occurrence < minOccurs || occurrence > maxOccurs))
+//            state.validationError("%s occurred '%s' times when it was expected to be a " +
+//              "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
+//              occurrence, minOccurs, maxOccurs)
+//        }
+//        case _ => // ok
+//      }
+//    }
+//  }
+//}
+//
+//class OptionalCombinatorUnparser(erd: ElementRuntimeData, bodyUnparser: Unparser)
+//  extends CombinatorUnparser(erd) {
+//  override def nom = "Optional"
+//  override lazy val childProcessors = Seq(bodyUnparser)
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparse(state: UState) {
+//
+//    state.arrayIndexStack.push(1L) // one-based indexing
+//    state.occursBoundsStack.push(1L)
+//
+//    val event = state.inspectOrError
+//    Assert.invariant(event.isStart && !event.node.isInstanceOf[DIArray])
+//
+//    bodyUnparser.unparse1(state)
+//
+//    state.arrayIndexStack.pop()
+//    state.occursBoundsStack.pop()
+//  }
+//}
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/LayeredSequenceUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/LayeredSequenceUnparser.scala
index ee456e30f..6240fe104 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/LayeredSequenceUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/LayeredSequenceUnparser.scala
@@ -17,13 +17,14 @@
 
 package org.apache.daffodil.processors.unparsers
 
-import org.apache.daffodil.processors.{ LayerTransformerEv, ModelGroupRuntimeData }
+import org.apache.daffodil.processors.LayerTransformerEv
 import org.apache.daffodil.io.DirectOrBufferedDataOutputStream
+import org.apache.daffodil.processors.SequenceRuntimeData
 
-class LayeredSequenceUnparser(ctxt: ModelGroupRuntimeData,
+class LayeredSequenceUnparser(ctxt: SequenceRuntimeData,
   layerTransformerEv: LayerTransformerEv,
-  childUnparser: Unparser)
-  extends SequenceCombinatorUnparser(ctxt, Vector(childUnparser)) {
+  childUnparser: SequenceChildUnparser)
+  extends OrderedUnseparatedSequenceUnparser(ctxt, Seq(childUnparser)) {
 
   override lazy val runtimeDependencies = Seq(layerTransformerEv)
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NadaUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NadaUnparser.scala
index eaca97ca5..64d22ddc0 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NadaUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NadaUnparser.scala
@@ -21,6 +21,9 @@ import org.apache.daffodil.processors.RuntimeData
 
 class NadaUnparser(override val context: RuntimeData)
   extends PrimUnparser {
+  
+  override def isEmpty = true
+  
   override def toString = "Nada"
 
   override lazy val runtimeDependencies = Nil
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NilEmptyCombinatorUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NilEmptyCombinatorUnparsers.scala
index 018a24cad..5481643f0 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NilEmptyCombinatorUnparsers.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/NilEmptyCombinatorUnparsers.scala
@@ -21,12 +21,12 @@ import org.apache.daffodil.processors.ElementRuntimeData
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.util.Maybe
 
-case class SimpleNilOrEmptyOrValueUnparser(ctxt: ElementRuntimeData,
-  nilUnparser: Unparser, emptyUnparser: Unparser, valueUnparser: Unparser) extends CombinatorUnparser(ctxt) {
+case class SimpleNilOrValueUnparser(ctxt: ElementRuntimeData,
+  nilUnparser: Unparser, valueUnparser: Unparser) extends CombinatorUnparser(ctxt) {
 
   override lazy val runtimeDependencies = Nil
 
-  override lazy val childProcessors = Seq(nilUnparser, emptyUnparser, valueUnparser)
+  override lazy val childProcessors = Seq(nilUnparser, valueUnparser)
 
   def unparse(state: UState): Unit = {
     Assert.invariant(Maybe.WithNulls.isDefined(state.currentInfosetNode))
@@ -39,38 +39,6 @@ case class SimpleNilOrEmptyOrValueUnparser(ctxt: ElementRuntimeData,
     // because _isNilled has not been set yet. Rather than having to deal with
     // suspending, only check isNilled for non-OVC elements.
     if (ctxt.outputValueCalcExpr.isEmpty && inode.isNilled) nilUnparser.unparse1(state)
-    else if (inode.isEmpty) emptyUnparser.unparse1(state)
-    else valueUnparser.unparse1(state)
-  }
-}
-
-case class SimpleNilOrValueUnparser(ctxt: ElementRuntimeData,
-  nilUnparser: Unparser, valueUnparser: Unparser) extends CombinatorUnparser(ctxt) {
-
-  override lazy val runtimeDependencies = Nil
-
-  override lazy val childProcessors = Seq(nilUnparser, valueUnparser)
-
-  def unparse(state: UState): Unit = {
-    Assert.invariant(Maybe.WithNulls.isDefined(state.currentInfosetNode))
-    val inode = state.currentInfosetNode.asSimple
-    // see comment above for why this OVC check is necessary
-    if (ctxt.outputValueCalcExpr.isEmpty && inode.isNilled) nilUnparser.unparse1(state)
-    else valueUnparser.unparse1(state)
-  }
-}
-
-case class SimpleEmptyOrValueUnparser(ctxt: ElementRuntimeData,
-  emptyUnparser: Unparser, valueUnparser: Unparser) extends CombinatorUnparser(ctxt) {
-
-  override lazy val runtimeDependencies = Nil
-
-  override lazy val childProcessors = Seq(emptyUnparser, valueUnparser)
-
-  def unparse(state: UState): Unit = {
-    Assert.invariant(Maybe.WithNulls.isDefined(state.currentInfosetNode))
-    val inode = state.currentInfosetNode.asSimple
-    if (inode.isEmpty) emptyUnparser.unparse1(state)
     else valueUnparser.unparse1(state)
   }
 }
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/OptionalInfixSepUnparser.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/OptionalInfixSepUnparser.scala
index 2e8b11d6b..1ddf9f69f 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/OptionalInfixSepUnparser.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/OptionalInfixSepUnparser.scala
@@ -18,10 +18,13 @@
 package org.apache.daffodil.processors.unparsers
 
 import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.exceptions.Assert
 
 class OptionalInfixSepUnparser(contextArg: TermRuntimeData,
   sepUnparser: Unparser)
   extends CombinatorUnparser(contextArg) {
+  
+  Assert.invariant(!sepUnparser.isEmpty)
 
   override lazy val runtimeDependencies = Nil
 
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala
new file mode 100644
index 000000000..29d5e71be
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SeparatedSequenceUnparsers.scala
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.unparsers
+
+import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
+import org.apache.daffodil.schema.annotation.props.gen.SeparatorPosition
+import org.apache.daffodil.processors.SequenceRuntimeData
+import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.TermRuntimeData
+
+trait Separated { self: SequenceChildUnparser =>
+
+  def sep: Unparser
+  def spos: SeparatorPosition
+  def ssp: SeparatorSuppressionPolicy
+
+  val childProcessors = Seq(childUnparser, sep)
+}
+
+class ScalarOrderedRequiredSeparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  trd: TermRuntimeData,
+  val sep: Unparser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy)
+  extends SequenceChildUnparser(childUnparser, srd, trd)
+  with Separated {
+
+  override def unparse(state: UState) = childUnparser.unparse1(state)
+}
+
+class RepOrderedExactlyNSeparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  repeatCount: Long,
+  val sep: Unparser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy,
+  val baseName: String = "ExactlyN")
+  extends SequenceChildUnparser(childUnparser, srd, erd)
+  with Separated
+  with RepUnparser {
+  val minRepeats = 0L
+  val maxRepeats = repeatCount
+}
+
+class RepOrderedExactlyTotalOccursCountSeparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  sep: Unparser,
+  spos: SeparatorPosition,
+  ssp: SeparatorSuppressionPolicy)
+  extends RepOrderedExactlyNSeparatedSequenceChildUnparser(childUnparser,
+    srd, erd,
+    { val ignored = 0; ignored },
+    sep, spos, ssp,
+    "ExactlyTotalOccursCount") {
+  // nothing
+}
+
+/**
+ * Pass nothing, or -1 for min/max to mean use erd.minOccurs/maxOccurs
+ * Pass -2 for max to force unbounded behavior.
+ */
+class RepOrderedWithMinMaxSeparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  val baseName: String,
+  val sep: Unparser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy,
+  min: Long = -1,
+  max: Long = -1) // pass -2 to force unbounded behavior
+  extends SequenceChildUnparser(childUnparser, srd, erd)
+  with Separated
+  with RepUnparser {
+  val minRepeats = if (min == -1) erd.minOccurs else min
+  val maxRepeats = max match {
+    case -1 if (erd.maxOccurs == -1) => Long.MaxValue
+    case -1 => erd.maxOccurs
+    case -2 => Long.MaxValue
+    case _ => max
+  }
+}
+
+class OrderedSeparatedSequenceUnparser(rd: SequenceRuntimeData,
+  ssp: SeparatorSuppressionPolicy,
+  spos: SeparatorPosition,
+  sep: Unparser,
+  childrenArg: Seq[SequenceChildUnparser])
+  extends OrderedSequenceUnparserBase(rd, childrenArg) {
+
+  import SequenceChildUnparser._
+
+  /**
+   * Unparses one iteration of an array/optional element
+   */
+  protected def unparseOne(
+    parserArg: SequenceChildUnparser,
+    trd: TermRuntimeData,
+    state: UState): Unit = {
+
+    val unparser = parserArg.asInstanceOf[SeparatedChildUnparser]
+
+    if ((spos eq SeparatorPosition.Prefix) && trd.isRepresented) {
+      sep.unparse1(state)
+    }
+
+    if ((spos eq SeparatorPosition.Infix) && state.childPos > 1 && trd.isRepresented) {
+      sep.unparse1(state)
+    }
+
+    unparser.unparse1(state)
+
+    if ((spos eq SeparatorPosition.Postfix) && trd.isRepresented) {
+      sep.unparse1(state)
+    }
+  }
+
+}
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparserBases.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparserBases.scala
new file mode 100644
index 000000000..36564eb99
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparserBases.scala
@@ -0,0 +1,311 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.unparsers
+
+import org.apache.daffodil.processors.Evaluatable
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.SequenceRuntimeData
+import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.api.ValidationMode
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.equality._
+import org.apache.daffodil.infoset.DIArray
+import org.apache.daffodil.util.Maybe._
+
+abstract class OrderedSequenceUnparserBase(srd: SequenceRuntimeData, childUnparsers: Seq[SequenceChildUnparser])
+  extends CombinatorUnparser(srd) {
+
+  override def nom = "Sequence"
+
+  override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
+
+  override lazy val childProcessors = childUnparsers
+
+  /**
+   * Unparses one iteration of an array/optional element
+   */
+  protected def unparseOne(
+    unparser: SequenceChildUnparser,
+    trd: TermRuntimeData,
+    state: UState): Unit
+
+  // Sequences of nothing (no initiator, no terminator, nothing at all) should
+  // have been optimized away
+  Assert.invariant(childUnparsers.length > 0)
+
+  // Since some of the grammar terms might have folded away to EmptyGram,
+  // the number of unparsers here may be different from the number of
+  // children of the sequence group.
+  Assert.invariant(srd.groupMembers.length >= childUnparsers.length - 1) // minus 1 for the separator unparser
+
+  /**
+   * Unparses an entire sequence, including both scalar and array/optional children.
+   *
+   * Of note: This is not symmetric with parsing. For parsing there are separate parse() methods
+   * for the separated and unseparated cases. For unparsing we were able to factor this
+   * difference out into this common base method.
+   */
+  protected def unparse(state: UState): Unit = {
+
+    state.groupIndexStack.push(1L) // one-based indexing
+
+    var index = 0
+    var doUnparser = false
+    val limit = childUnparsers.length
+    while (index < limit) {
+      val childUnparser = childUnparsers(index)
+      val trd = childUnparser.trd
+
+      //
+      // Unparsing an ordered sequence depends on the incoming
+      // stream of infoset events matching up with the order that
+      // they are expected as the unparser recurses through the
+      // child term unparsers.
+      //
+      childUnparser match {
+        case rep: RepUnparser => {
+          val erd = rep.erd
+          //
+          // The number of occurrances we unparse is always exactly driven
+          // by the number of infoset events for the repeating/optional element.
+          //
+          // For RepUnparser - array/optional case - in all cases we should get a
+          // startArray event. If we don't then
+          // the element must be entirely optional, so we get no events for it
+          // at all.
+          //
+          if (state.inspect) {
+            val ev = state.inspectAccessor
+            if (ev.isStart && ev.isArray) {
+              val eventNQN = ev.node.namedQName
+              if (eventNQN =:= erd.namedQName) {
+                //
+                // StartArray for this unparser's array element
+                //
+                rep.startArray(state)
+                while ({
+                  doUnparser = shouldDoUnparser(trd, state)
+                  doUnparser
+                }) {
+                  if (state.dataProc.isDefined) state.dataProc.get.beforeRepetition(state, this)
+
+                  unparseOne(rep, erd, state)
+
+                  if (state.dataProc.isDefined) state.dataProc.get.afterRepetition(state, this)
+                }
+                rep.endArray(state)
+              } else {
+                //
+                // start array for some other array. Not this one. So we
+                // don't unparse anything here, and we'll go on to the next
+                // sequence child, which hopefully will be a matching array.
+                //
+                Assert.invariant(erd.minOccurs == 0L)
+              }
+            } else if (ev.isStart) {
+              Assert.invariant(!ev.erd.isArray)
+              //
+              // start of scalar.
+              // That has to be for a different element later in the sequence
+              // since this one has a RepUnparser (i.e., is NOT scalar)
+              val eventNQN = ev.node.namedQName
+              Assert.invariant(eventNQN !=:= erd.namedQName)
+            } else {
+              Assert.invariant(ev.isEnd && ev.isComplex)
+            }
+          } else {
+            // no event (state.inspect returned false)
+            ??? // TODO: does this happen for the root element ??
+
+          }
+        }
+        //
+        case scalarUnparser => {
+          unparseOne(scalarUnparser, trd, state)
+        }
+      }
+      index += 1
+      //
+      // Note: the invariant is that unparsers move over 1 within their group themselves
+      // we do not do the moving over here as we are the caller of the unparser.
+      //
+    }
+
+    state.groupIndexStack.pop()
+    //
+    // this is establishing the invariant that unparsers (in this case the sequence unparser)
+    // moves over within its containing group. The caller of an unparser does not do this move.
+    //
+    state.moveOverOneGroupIndexOnly() // move past this sequence itself, next group child it ITS parent.
+  }
+
+  private def shouldDoUnparser(childRD: TermRuntimeData, state: UState): Boolean = {
+    childRD match {
+      case erd: ElementRuntimeData => {
+        // it's not a required element, so we check to see if we have a matching
+        // incoming infoset event
+        if (state.inspect) {
+          val ev = state.inspectAccessor
+          if (ev.isStart) {
+            val eventNQN = ev.node.namedQName
+            if (eventNQN =:= erd.namedQName) {
+              true
+            } else {
+              false // event not a state for this element
+            }
+          } else if (ev.isEnd && ev.isComplex) {
+            val c = ev.asComplex
+            //ok. We've peeked ahead and found the end of the complex element
+            //that this sequence is the model group of.
+            val optParentRD = srd.immediateEnclosingElementRuntimeData
+            optParentRD match {
+              case Some(e: ElementRuntimeData) => {
+                Assert.invariant(c.runtimeData.namedQName =:= e.namedQName)
+                false
+              }
+              case _ =>
+                Assert.invariantFailed("Not end element for this sequence's containing element. Event %s, optParentRD %s.".format(
+                  ev, optParentRD))
+            }
+          } else {
+            false
+          }
+        } else {
+          // was no element, so no unparse
+          false
+        }
+      }
+      case _ => {
+        // since only elements can be optional, anything else is non-optional
+        true
+      }
+    }
+  }
+}
+object SequenceChildUnparser {
+  type SeparatedChildUnparser = SequenceChildUnparser with Separated
+  type RepSeparatedChildUnparser = SeparatedChildUnparser with RepUnparser
+
+  type UnseparatedChildUnparser = SequenceChildUnparser with Unseparated
+  type RepUnseparatedChildUnparser = UnseparatedChildUnparser with RepUnparser
+}
+
+/**
+ * base for unparsers for the children of sequences.
+ *
+ * There is one sequence child unparser for each child (declared) of the sequence.
+ *
+ * These do not iterate over multiple recurring instances. That iteration happens
+ * in the caller. These unparse only a single occurrence when the child unparser
+ * is for an array/optional element.
+ */
+abstract class SequenceChildUnparser(
+  val childUnparser: Unparser,
+  val srd: SequenceRuntimeData,
+  val trd: TermRuntimeData)
+  extends CombinatorUnparser(srd) {
+
+  override def runtimeDependencies = Nil
+}
+
+/**
+ * Mixin trait for unparsers of array/optional elements.
+ *
+ * The unparse() method unparses exactly one occurrance, does NOT iterate over
+ * all the occurrences.
+ */
+trait RepUnparser { self: SequenceChildUnparser =>
+
+  def childUnparser: Unparser
+  def srd: SequenceRuntimeData
+  def erd: ElementRuntimeData
+  def baseName: String
+  def minRepeats: Long
+  def maxRepeats: Long
+
+  /**
+   * Unparse exactly one occurrence of an array/optional element.
+   *
+   * Iterating for arrays/optionals is done in the caller.
+   */
+  override protected def unparse(state: UState): Unit = {
+    childUnparser.unparse1(state)
+  }
+
+  override lazy val runtimeDependencies = Nil
+
+  override def toString = "Rep" + baseName + "(" + childUnparser.toString + ")"
+
+  override def toBriefXML(depthLimit: Int = -1): String = {
+    if (depthLimit == 0) "..." else
+      "<Rep" + baseName + " name='" + erd.name + "'>" + childUnparser.toBriefXML(depthLimit - 1) +
+        "</Rep" + baseName + ">"
+  }
+
+  /**
+   * Sets up for the start of an array. Pulls an event, which must be a start-array
+   * event.
+   */
+  def startArray(state: UState): Unit = {
+    state.arrayIndexStack.push(1L) // one-based indexing
+    state.occursBoundsStack.push(state.tunable.maxOccursBounds)
+
+    val event = state.advanceOrError
+    Assert.invariant(event.isStart && event.node.isInstanceOf[DIArray])
+  }
+
+  /**
+   * Ends an array. Pulls an event which must be an end-array event.
+   * Validates array dimensions if validation has been requested.
+   */
+  def endArray(state: UState): Unit = {
+
+    val event = state.advanceOrError
+    if (!(event.isEnd && event.node.isInstanceOf[DIArray])) {
+      UnparseError(One(erd.schemaFileLocation), One(state.currentLocation), "Expected array end event for %s, but received %s.", erd.namedQName, event)
+    }
+
+    val actualOccurs = state.arrayIndexStack.pop()
+    state.occursBoundsStack.pop()
+
+    if (state.processorStatus ne Success) return
+
+    val shouldValidate =
+      state.dataProc.isDefined && state.dataProc.value.getValidationMode != ValidationMode.Off
+
+    if (shouldValidate) {
+      val minO = erd.minOccurs
+      val maxO = erd.maxOccurs
+      val isUnbounded = maxO == -1
+      val occurrence = actualOccurs - 1
+
+      if (isUnbounded && occurrence < minO)
+        state.validationError("%s occurred '%s' times when it was expected to be a " +
+          "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
+          occurrence, minO)
+      else if (!isUnbounded && (occurrence < minO || occurrence > maxO))
+        state.validationError("%s occurred '%s' times when it was expected to be a " +
+          "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
+          occurrence, minO, maxO)
+      else {
+        //ok
+      }
+    }
+  }
+}
+
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparsers.scala
new file mode 100644
index 000000000..1579a58fc
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SequenceUnparsers.scala
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//
+//package org.apache.daffodil.processors.unparsers
+//
+//import org.apache.daffodil.processors.Evaluatable
+//import org.apache.daffodil.processors.ElementRuntimeData
+//import org.apache.daffodil.processors.RuntimeData
+//import org.apache.daffodil.processors.Processor
+//import org.apache.daffodil.schema.annotation.props.gen.SeparatorPosition
+//import org.apache.daffodil.exceptions.Assert
+//import org.apache.daffodil.equality._
+//import org.apache.daffodil.processors.SequenceRuntimeData
+//import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
+//import org.apache.daffodil.processors.TermRuntimeData
+//
+//sealed abstract class OrderedSequenceUnparserBase(ctxt: SequenceRuntimeData, childUnparsers: Seq[Unparser])
+//  extends CombinatorUnparser(ctxt) {
+//  override def nom = "Sequence"
+//
+//  // Sequences of nothing (no initiator, no terminator, nothing at all) should
+//  // have been optimized away
+//  Assert.invariant(childUnparsers.length > 0)
+//
+//  // Since some of the grammar terms might have folded away to EmptyGram,
+//  // the number of unparsers here may be different from the number of
+//  // children of the sequence group.
+//  Assert.invariant(ctxt.groupMembers.length >= childUnparsers.length - 1) // minus 1 for the separator unparser
+//
+//  override lazy val childProcessors: Seq[Processor] = childUnparsers
+//
+//  def unparse(start: UState): Unit = {
+//
+//    start.groupIndexStack.push(1L) // one-based indexing
+//    unparseChildren(start)
+//    start.groupIndexStack.pop()
+//    //
+//    // this is establishing the invariant that unparsers (in this case the sequence unparser)
+//    // moves over within its containing group. The caller of an unparser does not do this move.
+//    //
+//    start.moveOverOneGroupIndexOnly()
+//  }
+//
+//  protected def unparseChildren(start: UState): Unit
+//
+//  protected final def shouldDoUnparser(childRD: RuntimeData, start: UState): Boolean = {
+//    childRD match {
+//      case erd: ElementRuntimeData if !erd.isRequired => {
+//        // it's not a required element, so we check to see if we have a matching
+//        // incoming infoset event
+//        if (start.inspect) {
+//          val ev = start.inspectAccessor
+//          if (ev.isStart) {
+//            val eventNQN = ev.node.namedQName
+//            if (eventNQN =:= erd.namedQName) {
+//              true
+//            } else {
+//              false // event not a start for this element
+//            }
+//          } else if (ev.isEnd && ev.isComplex) {
+//            val c = ev.asComplex
+//            //ok. We've peeked ahead and found the end of the complex element
+//            //that this sequence is the model group of.
+//            val optParentRD = ctxt.immediateEnclosingElementRuntimeData
+//            optParentRD match {
+//              case Some(e: ElementRuntimeData) => {
+//                Assert.invariant(c.runtimeData.namedQName =:= e.namedQName)
+//                false
+//              }
+//              case _ =>
+//                Assert.invariantFailed("Not end element for this sequence's containing element. Event %s, optParentRD %s.".format(
+//                  ev, optParentRD))
+//            }
+//          } else {
+//            Assert.invariantFailed("Not a start event: " + ev)
+//          }
+//        } else {
+//          // was no element, so no unparse
+//          false
+//        }
+//      }
+//      case _ => {
+//        // since only elements can be optional, anything else is non-optional
+//        true
+//      }
+//    }
+//  }
+//}
+//
+///*
+// * Temporarily these next two classes are identical, but as we break out the separator
+// * functionality from being built into the child unparsers, these will diverge.
+// * The unseparated variant will stay like it is, but the separated one should be
+// * distinct.
+// */
+//class OrderedUnseparatedSequenceUnparser(ctxt: SequenceRuntimeData, childUnparsers: Seq[SequenceChildUnparser])
+//  extends OrderedSequenceUnparserBase(ctxt, childUnparsers) {
+//
+//  override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
+//
+//  protected def unparseChildren(start: UState): Unit = {
+//
+//    var index = 0
+//    var doUnparser = false
+//    val limit = childUnparsers.length
+//    while (index < limit) {
+//      val childUnparser = childUnparsers(index)
+//      val childRD = childUnparser.context
+//
+//      doUnparser = shouldDoUnparser(childRD, start)
+//
+//      if (doUnparser) {
+//        childUnparser.unparse1(start)
+//      }
+//      index += 1
+//      //
+//      // Note: the invariant is that unparsers move over 1 within their group themselves
+//      // we do not do the moving over here as we are the caller of the unparser.
+//      //
+//    }
+//  }
+//}
+//
+//trait Repeating { self: SequenceChildUnparser => }
+//trait Scalar { self: SequenceChildUnparser => }
+//
+//abstract class SequenceChildUnparser(
+//  val srd: SequenceRuntimeData,
+//  val trd: TermRuntimeData,
+//  override val childProcessors: Seq[Unparser])
+//  extends CombinatorUnparser(trd) {
+//  override def unparse(state: UState): Unit = ???
+//  override def runtimeDependencies = Nil
+//}
+//
+//abstract class UnseparatedSequenceChildUnparser(
+//  srd: SequenceRuntimeData,
+//  trd: TermRuntimeData,
+//  childUnparsers: Seq[Unparser])
+//  extends SequenceChildUnparser(srd, trd, childUnparsers)
+//
+//class ScalarOrderedRequiredUnseparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  trd: TermRuntimeData)
+//  extends UnseparatedSequenceChildUnparser(srd, trd, Seq(childUnparser)) with Scalar
+//
+//class RepOrderedExactlyNUnseparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData)
+//  extends UnseparatedSequenceChildUnparser(srd, erd, Seq(childUnparser)) with Repeating
+//
+//class RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  ocUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData)
+//  extends UnseparatedSequenceChildUnparser(srd, erd, Seq(childUnparser)) with Repeating
+//
+//class RepOrderedWithMinMaxUnseparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData)
+//  extends UnseparatedSequenceChildUnparser(srd, erd, Seq(childUnparser)) with Repeating
+//
+//class OrderedSeparatedSequenceUnparser(ctxt: SequenceRuntimeData,
+//  spos: SeparatorPosition,
+//  sep: Unparser,
+//  childUnparsers: Seq[SequenceChildUnparser])
+//  extends OrderedSequenceUnparserBase(ctxt, childUnparsers :+ sep) {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  protected def unparseChildren(start: UState): Unit = {
+//
+//    var prefixDone = false
+//    var atLeastOneUnparsed = false
+//
+//    var index = 0
+//    var doUnparser = false
+//
+//    val limit = childUnparsers.length
+//    while (index < limit) {
+//      val child = childUnparsers(index)
+//      val trd = child.trd
+//      val childUnparser = child
+//      val childRD = childUnparser.context
+//      doUnparser = shouldDoUnparser(childRD, start)
+//
+//      if (doUnparser) {
+//        // unparse prefix sep if any
+//        if ((spos eq SeparatorPosition.Prefix) && !prefixDone && trd.isRepresented) {
+//          prefixDone = true
+//          sep.unparse1(start)
+//        }
+//        // except for the first position of the group, unparse an infix separator
+//        if (index != 0 && trd.isRequiredScalar && trd.isRepresented) {
+//          sep.unparse1(start)
+//        }
+//        childUnparser.unparse1(start)
+//        if (trd.isRepresented)
+//          atLeastOneUnparsed = true
+//      }
+//      index += 1
+//      if (index == limit && (spos eq SeparatorPosition.Postfix) && atLeastOneUnparsed) {
+//        sep.unparse1(start)
+//      }
+//      //
+//      // Note: the invariant is that unparsers move over 1 within their group themselves
+//      // we do not do the moving over here as we are the caller of the unparser.
+//      //
+//    }
+//  }
+//}
+//
+//abstract class SeparatedSequenceChildUnparser(
+//  srd: SequenceRuntimeData,
+//  trd: TermRuntimeData,
+//  childUnparsers: Seq[Unparser],
+//  val sep: Unparser,
+//  val spos: SeparatorPosition,
+//  val ssp: SeparatorSuppressionPolicy)
+//  extends SequenceChildUnparser(srd, trd, childUnparsers :+ sep)
+//
+//class ScalarOrderedRequiredSeparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  trd: TermRuntimeData,
+//  sep: Unparser,
+//  spos: SeparatorPosition,
+//  ssp: SeparatorSuppressionPolicy)
+//  extends SeparatedSequenceChildUnparser(srd, trd, Seq(childUnparser), sep, spos, ssp) with Scalar
+//
+//class RepOrderedExactlyNSequenceSeparatedChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData,
+//  sep: Unparser,
+//  spos: SeparatorPosition,
+//  ssp: SeparatorSuppressionPolicy)
+//  extends SeparatedSequenceChildUnparser(srd, erd, Seq(childUnparser), sep, spos, ssp) with Repeating
+//
+//class RepOrderedExactlyTotalOccursCountSeparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  ocUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData,
+//  sep: Unparser,
+//  spos: SeparatorPosition,
+//  ssp: SeparatorSuppressionPolicy)
+//  extends SeparatedSequenceChildUnparser(srd, erd, Seq(childUnparser), sep, spos, ssp) with Repeating
+//
+//class RepOrderedWithMinMaxSeparatedSequenceChildUnparser(
+//  childUnparser: Unparser,
+//  srd: SequenceRuntimeData,
+//  erd: ElementRuntimeData,
+//  sep: Unparser,
+//  spos: SeparatorPosition,
+//  ssp: SeparatorSuppressionPolicy)
+//  extends SeparatedSequenceChildUnparser(srd, erd, Seq(childUnparser), sep, spos, ssp) with Repeating
+//
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
index c82c03deb..24f1d73e0 100644
--- a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/SpecifiedLength2.scala
@@ -391,7 +391,7 @@ sealed trait NeedValueAndTargetLengthMixin {
             paddingLengthInBits
           }
           case c: DIComplex =>
-            ???
+            ??? // FIXME: This code was left incomplete - JIRA DAFFODIL-1952
         }
       } else {
         0L // must be delimited, so we don't pad unless there's a minLength, which
diff --git a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala
new file mode 100644
index 000000000..5b66ce1e5
--- /dev/null
+++ b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/UnseparatedSequenceUnparsers.scala
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.unparsers
+
+import org.apache.daffodil.processors.{ SequenceRuntimeData, TermRuntimeData }
+import org.apache.daffodil.processors.ElementRuntimeData
+
+trait Unseparated { self: SequenceChildUnparser =>
+
+  val childProcessors = Seq(childUnparser)
+}
+
+class ScalarOrderedRequiredUnseparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  trd: TermRuntimeData)
+  extends SequenceChildUnparser(childUnparser, srd, trd) with Unseparated {
+
+  override protected def unparse(state: UState) = childUnparser.unparse1(state)
+}
+
+abstract class RepUnseparatedUnparser(
+  childUnparser: Unparser,
+  val minRepeats: Long,
+  val maxRepeats: Long,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  val baseName: String)
+  extends SequenceChildUnparser(childUnparser, srd, erd)
+  with Unseparated with RepUnparser {
+  // nothing
+}
+
+class RepOrderedExactlyNUnseparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  repeatCount: Long,
+  baseName: String = "ExactlyN")
+  extends RepUnseparatedUnparser(childUnparser, 0, repeatCount, srd, erd, baseName)
+  with RepUnparser {
+  // nothing
+}
+
+class RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData)
+  extends RepOrderedExactlyNUnseparatedSequenceChildUnparser(childUnparser,
+    srd, erd,
+    { val ignored = 0; ignored },
+    "ExactlyTotalOccursCount") {
+  // nothing
+}
+
+class RepOrderedWithMinMaxUnseparatedSequenceChildUnparser(
+  childUnparser: Unparser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  baseName: String,
+  min: Long = -1,
+  max: Long = -1) // pass -2 to force unbounded behavior
+  extends RepUnseparatedUnparser(
+    childUnparser,
+    (if (min == -1) erd.minOccurs else min),
+    max match {
+      case -1 if (erd.maxOccurs == -1) => Long.MaxValue
+      case -1 => erd.maxOccurs
+      case -2 => Long.MaxValue
+      case _ => max
+    },
+    srd, erd, baseName) {
+  // nothing
+}
+
+class OrderedUnseparatedSequenceUnparser(rd: SequenceRuntimeData, childUnparsers: Seq[SequenceChildUnparser])
+  extends OrderedSequenceUnparserBase(rd, childUnparsers) {
+
+  /**
+   * Unparses one iteration of an array/optional element
+   */
+  protected def unparseOne(
+    unparser: SequenceChildUnparser,
+    trd: TermRuntimeData,
+    state: UState): Unit = {
+
+    unparser.unparse1(state)
+  }
+
+}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
index a56735ea8..3704a4438 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/debugger/InteractiveDebugger.scala
@@ -196,11 +196,10 @@ class InteractiveDebugger(runner: InteractiveDebuggerRunner, eCompilers: Express
     val interesting = parser match {
       case _: ComplexTypeParser => false
       case _: SeqCompParser => false
-      case _: SequenceCombinatorParser => false
-      case _: AltCompParser => false
       case _: RepParser => false
       case _: OptionalInfixSepParser => false
       case _: ConvertTextCombinatorParser => false
+      case _: CombinatorParser => false
       case _ => true
     }
     interesting
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorBases.scala
index a7db50406..ffc94a109 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorBases.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/ProcessorBases.scala
@@ -32,6 +32,9 @@ object Processor {
   private def ensureCompiled(proc: Processor) {
     if (!proc.isInitialized) {
       proc.isInitialized = true
+      //      proc.childProcessors.foreach { cp =>
+      //        Assert.invariant(!cp.isEmpty)
+      //      }
       proc.runtimeDependencies.foreach { ensureCompiled }
       proc.childProcessors.foreach { ensureCompiled }
     }
@@ -70,6 +73,8 @@ trait Processor
    * interact with the data stream.
    */
   def isPrimitive: Boolean
+
+  def isEmpty: Boolean
 }
 
 /**
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/Runtime.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/Runtime.scala
index 7504f2e54..8238de423 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/Runtime.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/Runtime.scala
@@ -274,8 +274,8 @@ class DataProcessor(val ssrd: SchemaSetRuntimeData)
         state.setFailed(e)
       }
       case us: UnsuppressableException => throw us
-      case x: Throwable =>
-        Assert.invariantFailed("Runtime.scala - Leaked exception: " + x)
+      //      case x: Throwable =>
+      //        Assert.invariantFailed("Runtime.scala - Leaked exception: " + x)
     }
 
     state.dataInputStream.validateFinalStreamState
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
index 7a299982d..67188498b 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/RuntimeData.scala
@@ -55,6 +55,7 @@ import org.apache.daffodil.dpath.NodeInfo.PrimType
 import org.apache.daffodil.util.OKOrError
 import java.util.regex.Matcher
 import org.apache.daffodil.api.DaffodilTunables
+import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
 
 /*
  * NOTE: Any time you add a member to one of these objects, you must modify at least 3 places.
@@ -129,6 +130,9 @@ sealed abstract class TermRuntimeData(
     case _ => false
   }
 
+  def isRequiredScalar: Boolean
+  def isArray: Boolean
+
   /**
    * At some point TermRuntimeData is a ResolvesQNames which requires tunables:
    *
@@ -618,8 +622,9 @@ final class ElementRuntimeData(
   @TransientParam targetNamespaceArg: => NS,
   @TransientParam thisElementsNamespaceArg: => NS,
   @TransientParam optSimpleTypeRuntimeDataArg: => Option[SimpleTypeRuntimeData],
-  @TransientParam minOccursArg: => Option[Int],
-  @TransientParam maxOccursArg: => Option[Int],
+  @TransientParam minOccursArg: => Long,
+  @TransientParam maxOccursArg: => Long,
+  @TransientParam maybeOccursCountKindArg: => Maybe[OccursCountKind],
   @TransientParam nameArg: => String,
   @TransientParam targetNamespacePrefixArg: => String,
   @TransientParam thisElementsNamespacePrefixArg: => String,
@@ -659,6 +664,8 @@ final class ElementRuntimeData(
     maybeCheckByteAndBitOrderEvArg,
     maybeCheckBitOrderAndCharsetEvArg) {
 
+  override def isRequiredScalar = !isArray && isRequired
+
   lazy val parent = parentArg
   lazy val parentTerm = parentTermArg
   lazy val children = childrenArg
@@ -678,12 +685,13 @@ final class ElementRuntimeData(
   lazy val optSimpleTypeRuntimeData = optSimpleTypeRuntimeDataArg
   lazy val minOccurs = minOccursArg
   lazy val maxOccurs = maxOccursArg
+  lazy val maybeOccursCountKind = maybeOccursCountKindArg
   lazy val name = nameArg
   lazy val targetNamespacePrefix = targetNamespacePrefixArg
   lazy val thisElementsNamespacePrefix = thisElementsNamespacePrefixArg
   lazy val isHidden = isHiddenArg
   lazy val isNillable = isNillableArg
-  lazy val isArray = isArrayArg
+  override lazy val isArray = isArrayArg
   lazy val isOptional = isOptionalArg
   lazy val isRequired = isRequiredArg
   lazy val namedQName = namedQNameArg
@@ -715,6 +723,7 @@ final class ElementRuntimeData(
     optSimpleTypeRuntimeData
     minOccurs
     maxOccurs
+    maybeOccursCountKind
     name
     targetNamespacePrefix
     thisElementsNamespacePrefix
@@ -789,6 +798,9 @@ sealed abstract class ModelGroupRuntimeData(
     maybeCheckByteAndBitOrderEvArg,
     maybeCheckBitOrderAndCharsetEvArg) {
 
+  final override def isRequiredScalar = true
+  final override def isArray = false
+
   lazy val variableMap = variableMapArg
   lazy val encInfo = encInfoArg
   lazy val schemaFileLocation = schemaFileLocationArg
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/DelimiterParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/DelimiterParsers.scala
index accad28d0..4cf49cff3 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/DelimiterParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/DelimiterParsers.scala
@@ -158,7 +158,7 @@ class DelimiterTextParser(
           return
         } else {
           // no match and no ES in delims
-          PE(start, "Delimiter not found.")
+          PE(start, "%s '%s' not found.", delimiterType.toString, start.mpstate.delimiters.last.lookingFor)
           return
         }
       }
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
index fecd7d61e..147dac948 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ElementKindParsers.scala
@@ -17,16 +17,8 @@
 
 package org.apache.daffodil.processors.parsers
 
-import org.apache.daffodil.api.ValidationMode
-import org.apache.daffodil.processors.ChoiceDispatchKeyEv
-import org.apache.daffodil.processors.DelimiterParseEv
-import org.apache.daffodil.processors.ElementRuntimeData
-import org.apache.daffodil.processors.EscapeSchemeParseEv
-import org.apache.daffodil.processors.RuntimeData
-import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors._
 import org.apache.daffodil.util.LogLevel
-import org.apache.daffodil.processors.TermRuntimeData
-import org.apache.daffodil.processors.Evaluatable
 
 class ComplexTypeParser(rd: RuntimeData, bodyParser: Parser)
   extends CombinatorParser(rd) {
@@ -120,43 +112,6 @@ class DynamicEscapeSchemeParser(escapeScheme: EscapeSchemeParseEv,
   }
 }
 
-class SequenceCombinatorParser(rd: TermRuntimeData, bodyParser: Parser)
-  extends CombinatorParser(rd) {
-  override def nom = "Sequence"
-
-  override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
-
-  override lazy val childProcessors = Seq(bodyParser)
-
-  def parse(start: PState): Unit = {
-    start.mpstate.groupIndexStack.push(1L) // one-based indexing
-
-    bodyParser.parse1(start)
-
-    start.mpstate.groupIndexStack.pop()
-    start.mpstate.moveOverOneGroupIndexOnly()
-    ()
-  }
-}
-
-/**
- * This is essentially just a wrapper around the bodyParser, which is an
- * AltCompParser. This is only here to maintain symmetry with the unparse side,
- * which has a more complicated unparser that differs from an AltCompUnparser.
- */
-class ChoiceCombinatorParser(rd: TermRuntimeData, bodyParser: Parser)
-  extends CombinatorParser(rd) {
-  override def nom = "Choice"
-
-  override lazy val runtimeDependencies = Nil
-
-  override lazy val childProcessors = Seq(bodyParser)
-
-  def parse(start: PState): Unit = {
-    bodyParser.parse1(start)
-  }
-}
-
 class ChoiceDispatchCombinatorParser(rd: TermRuntimeData, dispatchKeyEv: ChoiceDispatchKeyEv, dispatchBranchKeyMap: Map[String, Parser])
   extends CombinatorParser(rd) {
   override def nom = "ChoiceDispatch"
@@ -197,50 +152,50 @@ class ChoiceDispatchCombinatorParser(rd: TermRuntimeData, dispatchKeyEv: ChoiceD
   }
 }
 
-class ArrayCombinatorParser(erd: ElementRuntimeData, bodyParser: Parser)
-  extends CombinatorParser(erd) {
-  override def nom = "Array"
-  override lazy val childProcessors = Seq(bodyParser)
-
-  override lazy val runtimeDependencies = Nil
-
-  def parse(start: PState): Unit = {
-
-    start.mpstate.arrayIndexStack.push(1L) // one-based indexing
-    start.mpstate.occursBoundsStack.push(start.tunable.maxOccursBounds)
-
-    bodyParser.parse1(start)
-
-    val actualOccurs = start.mpstate.arrayIndexStack.pop()
-    start.mpstate.occursBoundsStack.pop()
-
-    if (start.processorStatus ne Success) return
-
-    val shouldValidate =
-      start.dataProc.isDefined && start.dataProc.value.getValidationMode != ValidationMode.Off
-
-    if (shouldValidate && erd.minOccurs.isDefined && erd.maxOccurs.isDefined) {
-      val minO = erd.minOccurs.get
-      val maxO = erd.maxOccurs.get
-      val isUnbounded = maxO == -1
-      val occurrence = actualOccurs - 1
-
-      if (isUnbounded && occurrence < minO)
-        start.validationError("%s occurred '%s' times when it was expected to be a " +
-          "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
-          occurrence, minO)
-      else if (!isUnbounded && (occurrence < minO || occurrence > maxO))
-        start.validationError("%s occurred '%s' times when it was expected to be a " +
-          "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
-          occurrence, minO, maxO)
-      else {
-        //ok
-      }
-    }
-  }
-}
-
-// This follows the same behavior as Arrays for parsing
-class OptionalCombinatorParser(erd: ElementRuntimeData, bodyParser: Parser) extends ArrayCombinatorParser(erd, bodyParser) {
-  override def nom = "Optional"
-}
+//class ArrayCombinatorParser(erd: ElementRuntimeData, bodyParser: Parser)
+//  extends CombinatorParser(erd) {
+//  override def nom = "Array"
+//  override lazy val childProcessors = Seq(bodyParser)
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def parse(start: PState): Unit = {
+//
+//    start.mpstate.arrayIndexStack.push(1L) // one-based indexing
+//    start.mpstate.occursBoundsStack.push(start.tunable.maxOccursBounds)
+//
+//    bodyParser.parse1(start)
+//
+//    val actualOccurs = start.mpstate.arrayIndexStack.pop()
+//    start.mpstate.occursBoundsStack.pop()
+//
+//    if (start.processorStatus ne Success) return
+//
+//    val shouldValidate =
+//      start.dataProc.isDefined && start.dataProc.value.getValidationMode != ValidationMode.Off
+//
+//    if (shouldValidate && erd.minOccurs.isDefined && erd.maxOccurs.isDefined) {
+//      val minO = erd.minOccurs.get
+//      val maxO = erd.maxOccurs.get
+//      val isUnbounded = maxO == -1
+//      val occurrence = actualOccurs - 1
+//
+//      if (isUnbounded && occurrence < minO)
+//        start.validationError("%s occurred '%s' times when it was expected to be a " +
+//          "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
+//          occurrence, minO)
+//      else if (!isUnbounded && (occurrence < minO || occurrence > maxO))
+//        start.validationError("%s occurred '%s' times when it was expected to be a " +
+//          "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
+//          occurrence, minO, maxO)
+//      else {
+//        //ok
+//      }
+//    }
+//  }
+//}
+//
+//// This follows the same behavior as Arrays for parsing
+//class OptionalCombinatorParser(erd: ElementRuntimeData, bodyParser: Parser) extends ArrayCombinatorParser(erd, bodyParser) {
+//  override def nom = "Optional"
+//}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/LayeredSequenceParser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/LayeredSequenceParser.scala
index f87c550ee..0ba8c9104 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/LayeredSequenceParser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/LayeredSequenceParser.scala
@@ -17,14 +17,14 @@
 
 package org.apache.daffodil.processors.parsers
 
-import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.processors.SequenceRuntimeData
 import org.apache.daffodil.processors.LayerTransformerEv
 import org.apache.daffodil.io.ByteBufferDataInputStream
 
-class LayeredSequenceParser(rd: TermRuntimeData,
+class LayeredSequenceParser(rd: SequenceRuntimeData,
   layerTransformerEv: LayerTransformerEv,
-  bodyParser: Parser)
-  extends SequenceCombinatorParser(rd, bodyParser) {
+  bodyParser: SequenceChildParser)
+  extends OrderedUnseparatedSequenceParser(rd, Seq(bodyParser)) {
   override def nom = "LayeredSequence"
 
   override lazy val runtimeDependencies = Seq(layerTransformerEv)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
index 77ff2a651..4e9766afc 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/NilEmptyCombinatorParsers.scala
@@ -19,14 +19,8 @@ package org.apache.daffodil.processors.parsers
 
 import org.apache.daffodil.processors.TermRuntimeData
 
-case class SimpleNilOrEmptyOrValueParser(ctxt: TermRuntimeData, nilParser: Parser, emptyParser: Parser, valueParser: Parser)
-  extends AltCompParser(ctxt, Seq(nilParser, emptyParser, valueParser))
-
 case class SimpleNilOrValueParser(ctxt: TermRuntimeData, nilParser: Parser, valueParser: Parser)
-  extends AltCompParser(ctxt, Seq(nilParser, valueParser))
-
-case class SimpleEmptyOrValueParser(ctxt: TermRuntimeData, emptyParser: Parser, valueParser: Parser)
-  extends AltCompParser(ctxt, Seq(emptyParser, valueParser))
+  extends ChoiceParser(ctxt, Seq(nilParser, valueParser))
 
 case class ComplexNilOrContentParser(ctxt: TermRuntimeData, emptyParser: Parser, contentParser: Parser)
-  extends AltCompParser(ctxt, Seq(emptyParser, contentParser))
+  extends ChoiceParser(ctxt, Seq(emptyParser, contentParser))
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/OptionalInfixSepParser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/OptionalInfixSepParser.scala
index e9584df95..d37cd51db 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/OptionalInfixSepParser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/OptionalInfixSepParser.scala
@@ -18,10 +18,13 @@
 package org.apache.daffodil.processors.parsers
 
 import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.exceptions.Assert
 
 class OptionalInfixSepParser(ctxt: TermRuntimeData, sepParser: Parser)
   extends CombinatorParser(ctxt) {
 
+  Assert.invariant(!sepParser.isEmpty)
+
   override lazy val nom = "OptionalInfixSep"
   override lazy val childProcessors = Seq(sepParser)
   override lazy val runtimeDependencies = Nil
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
index 333a9ba51..9e8d3765a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
@@ -26,10 +26,8 @@ import org.apache.daffodil.api.DataLocation
 import org.apache.daffodil.api.Diagnostic
 import org.apache.daffodil.exceptions.Assert
 import org.apache.daffodil.infoset.DIComplex
-import org.apache.daffodil.infoset.DIComplexState
 import org.apache.daffodil.infoset.DIElement
 import org.apache.daffodil.infoset.DISimple
-import org.apache.daffodil.infoset.DISimpleState
 import org.apache.daffodil.infoset.Infoset
 import org.apache.daffodil.infoset.InfosetDocument
 import org.apache.daffodil.infoset.InfosetOutputter
@@ -59,6 +57,8 @@ import org.apache.daffodil.util.MaybeULong
 import org.apache.daffodil.util.Misc
 import org.apache.daffodil.util.Pool
 import org.apache.daffodil.util.Poolable
+import org.apache.daffodil.infoset.DIComplexState
+import org.apache.daffodil.infoset.DISimpleState
 
 object MPState {
 
@@ -113,7 +113,11 @@ class MPState private () {
   // stack. Can we get rid of it?
   val childIndexStack = MStackOfLong()
   def moveOverOneElementChildOnly() = childIndexStack.push(childIndexStack.pop + 1)
-  def childPos = childIndexStack.top
+  def childPos = {
+    val res = childIndexStack.top
+    Assert.invariant(res >= 1)
+    res
+  }
 
   val occursBoundsStack = MStackOfLong()
   def updateBoundsHead(ob: Long) = {
@@ -127,6 +131,9 @@ class MPState private () {
 
   val escapeSchemeEVCache = new MStackOfMaybe[EscapeSchemeParserHelper]
 
+  //var wasAnyArrayElementNonZeroLength = false
+  //var wasLastArrayElementZeroLength = true
+
   private def init {
     arrayIndexStack.push(1L)
     groupIndexStack.push(1L)
@@ -328,6 +335,13 @@ object PState {
    */
   class Mark extends Poolable {
 
+    override def toString() = {
+      if (disMark ne null)
+        "Mark(bitPos0b = " + bitPos0b + ")"
+      else
+        "Mark(uninitialized)"
+    }
+
     def bitPos0b = disMark.bitPos0b
 
     val simpleElementState = DISimpleState()
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ParseErrors.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ParseErrors.scala
index 176297523..ce0364819 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ParseErrors.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ParseErrors.scala
@@ -48,12 +48,12 @@ class AssertionFailed(rd: SchemaFileLocation, state: PState, msg: String, detail
   }
 }
 
-class ParseAlternativeFailed(rd: SchemaFileLocation, state: PState, val errors: Seq[Diagnostic])
+class ChoiceBranchFailed(rd: SchemaFileLocation, state: PState, val errors: Seq[Diagnostic])
   extends ParseError(One(rd), One(state.currentLocation), "Alternative failed. Reason(s): %s", errors)
 
-class AltParseFailed(rd: SchemaFileLocation, state: PState,
-                     diags: Seq[Diagnostic])
-  extends ParseError(One(rd), One(state.currentLocation), "All alternatives failed. Reason(s): %s", diags) {
+class EntireChoiceFailed(rd: SchemaFileLocation, state: PState,
+  diags: Seq[Diagnostic])
+  extends ParseError(One(rd), One(state.currentLocation), "All choice alternatives failed. Reason(s): %s", diags) {
 
   override def getLocationsInSchemaFiles: Seq[LocationInSchemaFile] = diags.flatMap { _.getLocationsInSchemaFiles }
 
@@ -84,24 +84,11 @@ class GeneralParseFailure(msg: String) extends Diagnostic(Nope, Nope, Nope, Mayb
 }
 
 /**
- * Mix this into parsers that have deep algorithms that are spread over multiple classes.
+ * Mixin for signaling Schema Definition Errors at runtime.
  *
- * These allow one to bend the rule about parsers not throwing ParseError so that
- * if you are inside a parser, but you are way down a bunch of calls away from the parser itself
- * you can throw, and it will be intercepted and proper behavior (not throwing, but returning
- * a failed status) will result.
- *
- * Use like this:
- * @example {{{
- * withParseErrorThrowing(pstate) { // something enclosing like the parser
- * ...
- *   // calls something which calls something which eventually calls
- *       PE(context, bitOffset % 8 == 0, "must be byte boundary, not bit %s", bitOffset)
- * ...
- * }
- * }}}
+ * Some SDE cannot be detected until runtime. Classes that need to signal them
+ * mixin this trait.
  */
-
 trait DoSDEMixin {
 
   protected final def doSDE(e: Throwable, state: ParseOrUnparseState) = {
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
index 2264ef320..07ba82fef 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/Parser.scala
@@ -48,6 +48,8 @@ import org.apache.daffodil.processors.PrimProcessorNoData
 sealed trait Parser
   extends Processor {
 
+  def isEmpty = false // override in NadaParser
+
   protected def parserName = Misc.getNameFromClass(this)
 
   def PE(pstate: PState, s: String, args: Any*) = {
@@ -127,17 +129,19 @@ trait TextPrimParser
 /**
  * Parser which does "Nada" (Nothing in Spanish)
  *
- * Used for optitonality in some cases, but is usually recognized and
+ * Used for optionality in some cases, but is usually recognized and
  * optimized out.
  */
 final class NadaParser(override val context: RuntimeData)
   extends PrimParserNoData {
   override def runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
 
+  override def isEmpty = true
+
   override def toString = "Nada"
 
   override def parse(start: PState): Unit = {
-    // do nothing
+    Assert.abort("NadaParsers are all supposed to optimize out!")
   }
 }
 
@@ -169,12 +173,12 @@ final class SeqCompParser(context: RuntimeData, val childParsers: Array[Parser])
 
 }
 
-class AltCompParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
+class ChoiceParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
   extends CombinatorParser(ctxt) {
   override lazy val runtimeDependencies = Nil
   override lazy val childProcessors = childParsers
 
-  override def nom = "alt"
+  override def nom = "choice"
 
   def parse(pstate: PState): Unit = {
     var pBefore: PState.Mark = null
@@ -191,7 +195,7 @@ class AltCompParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
         parser = childParsers(i)
         i += 1
         log(LogLevel.Debug, "Trying choice alternative: %s", parser)
-        pBefore = pstate.mark("AltCompParser1")
+        pBefore = pstate.mark("ChoiceParser1")
         try {
           parser.parse1(pstate)
         } catch {
@@ -212,7 +216,7 @@ class AltCompParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
           //
           // capture diagnostics
           //
-          val diag = new ParseAlternativeFailed(context.schemaFileLocation, pstate, pstate.diagnostics)
+          val diag = new ChoiceBranchFailed(context.schemaFileLocation, pstate, pstate.diagnostics)
           diagnostics = diag +: diagnostics
           //
           // check for discriminator evaluated to true.
@@ -223,7 +227,7 @@ class AltCompParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
             // consume this discriminator status result (so it doesn't ripple upward)
             // and return the failed state with all the diagnostics.
             //
-            val allDiags = new AltParseFailed(context.schemaFileLocation, pstate, diagnostics.reverse)
+            val allDiags = new EntireChoiceFailed(context.schemaFileLocation, pstate, diagnostics.reverse)
             pstate.discard(pBefore) // because disc set, we don't unwind side effects on input stream & infoset
             pBefore = null
             pstate.setFailed(allDiags)
@@ -244,9 +248,9 @@ class AltCompParser(ctxt: RuntimeData, val childParsers: Seq[Parser])
       if (returnFlag == false) {
         Assert.invariant(i == limit)
         // Out of alternatives. All of them failed.
-        val allDiags = new AltParseFailed(context.schemaFileLocation, pstate, diagnostics.reverse)
+        val allDiags = new EntireChoiceFailed(context.schemaFileLocation, pstate, diagnostics.reverse)
         pstate.setFailed(allDiags)
-        log(LogLevel.Debug, "All AltParser alternatives failed.")
+        log(LogLevel.Debug, "All Choice alternatives failed.")
       }
 
       pstate.popDiscriminator
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/RepParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/RepParsers.scala
index e012db81e..9e39d2d5a 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/RepParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/RepParsers.scala
@@ -18,345 +18,383 @@
 package org.apache.daffodil.processors.parsers
 
 import java.lang.{ Long => JLong }
-import java.io.StringWriter
-import java.io.PrintWriter
 
-import org.apache.daffodil.dsom.SchemaDefinitionDiagnosticBase
-import org.apache.daffodil.equality.ViewEqual
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.exceptions.UnsuppressableException
-import org.apache.daffodil.processors.ElementRuntimeData
-import org.apache.daffodil.processors.Evaluatable
-import org.apache.daffodil.processors.Failure
-import org.apache.daffodil.processors.Success
-import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
-import org.apache.daffodil.util.LogLevel
+import org.apache.daffodil.processors.{ ElementRuntimeData, Evaluatable }
 import org.apache.daffodil.util.Numbers
-import org.apache.daffodil.processors.TermRuntimeData
 
-abstract class RepParser(n: Long, rParser: Parser, context: ElementRuntimeData, baseName: String)
-  extends CombinatorParser(context) {
-
-  override lazy val runtimeDependencies = Nil
-
-  override lazy val childProcessors = Seq(rParser)
-
-  val intN = n.toInt
-
-  def checkN(pstate: PState, n: Long): Boolean = {
-    if (n > pstate.tunable.maxOccursBounds) {
-      // TODO: how can we go after bigger than max int bytes? We have 64-bit computers
-      // after all....
-      PE(pstate, "Occurs count %s exceeds implementation maximum of %s.", n, pstate.tunable.maxOccursBounds)
-      false
-    } else true
-  }
-
-  final def parse(pstate: PState): Unit = {
-    if (!checkN(pstate, n)) return
-    parseAllRepeats(pstate)
-    // pstate.mpstate.clearDelimitedText
-  }
-
-  protected def parseAllRepeats(pstate: PState): Unit
-
-  override def toString = "Rep" + baseName + "(" + rParser.toString + ")"
-
-  override def toBriefXML(depthLimit: Int = -1): String = {
-    if (depthLimit == 0) "..." else
-      "<Rep" + baseName + " name='" + context.name + "' n='" + n + "'>" + rParser.toBriefXML(depthLimit - 1) +
-        "</Rep" + baseName + ">"
-  }
-}
-
-/**
- * This object is so that we can share the iteration idioms between situations
- * where we know N statically, and where dynamic evaluation computes N.
- *
- * In these cases, there are no new points of uncertainty because computed or
- * otherwise, we know N.
- */
-object Rep {
-  def loopExactlyTotalN(intN: Int, rParser: Parser, pstate: PState, context: TermRuntimeData, iParser: Parser): Unit = {
-    while (pstate.mpstate.arrayPos <= intN) {
-      if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, iParser)
-      rParser.parse1(pstate)
-      if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, iParser)
-      if (pstate.processorStatus ne Success) {
-        return // fail if we don't get them all
-      }
-      pstate.mpstate.moveOverOneArrayIndexOnly
-    }
-  }
-}
-
-class RepExactlyNParser(n: Long, rParser: Parser, context: ElementRuntimeData)
-  extends RepParser(n, rParser, context, "ExactlyN") {
-
-  def parseAllRepeats(pstate: PState): Unit = {
-    var i = 0
-    while (i < intN) {
-      i += 1
-      if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, rParser)
-      rParser.parse1(pstate)
-      if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, rParser)
-      if (pstate.processorStatus ne Success) {
-        val cause = pstate.processorStatus.asInstanceOf[Failure].cause
-        PE(pstate, "Failed to populate %s[%s].  Expected %s item(s). Cause: %s.",
-          context.prefixedName, pstate.mpstate.arrayPos, n,
-          cause) // they all must succeed, otherwise we fail here.
-        return
-      }
-      pstate.mpstate.moveOverOneArrayIndexOnly
-    }
-  }
-}
-
-class RepAtMostTotalNParser(n: Long, rParser: Parser, erd: ElementRuntimeData)
-  extends RepParser(n, rParser, erd, "AtMostTotalN") {
-
-  def parseAllRepeats(initialState: PState): Unit = {
-    var startState: PState.Mark = null
-    var priorState: PState.Mark = null
-    var markLeakCausedByException = false
-
-    try {
-      if (initialState.mpstate.arrayPos <= intN) {
-        startState = initialState.mark("RepAtMostTotalNParser1")
-        priorState = initialState.mark("RepAtMostTotalNParser2")
-        val pstate = initialState
-        var returnFlag = false
-        while (!returnFlag && (pstate.mpstate.arrayPos <= intN)) {
-          // Since each one could fail, each is a new point of uncertainty.
-
-          pstate.pushDiscriminator
-
-          if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
-
-          try {
-            rParser.parse1(pstate)
-          } catch {
-            case sde: SchemaDefinitionDiagnosticBase => {
-              pstate.discard(startState)
-              startState = null
-              priorState = null
-              throw sde
-            }
-          }
-
-          if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
-
-          if (pstate.processorStatus ne Success) {
-            //
-            // Did not succeed
-            //
-            // Was a discriminator set?
-            //
-            if (pstate.discriminator == true) {
-              // we fail the whole RepUnbounded, because there was a discriminator set
-              // before the failure.
-              pstate.reset(startState)
-              startState = null
-              priorState = null
-
-              // no need discard priorState, that is implicitly discarded by resetting the startState
-              returnFlag = true
-            } else {
-              //
-              // backout any element appended as part of this attempt.
-              //
-              pstate.reset(priorState)
-              pstate.discard(startState)
-              priorState = null
-              startState = null
-
-              returnFlag = true // success at prior state.
-            }
-          } else {
-            //
-            // Success
-            //
-            pstate.discard(priorState)
-            priorState = pstate.mark("RepAtMostTotalNParser3")
-            pstate.mpstate.moveOverOneArrayIndexOnly
-            returnFlag = false
-          }
-
-          pstate.popDiscriminator
-        }
-        if (returnFlag == false) {
-          // we exited the loop due to arrayPos hitting the upper limit
-          pstate.discard(priorState)
-          pstate.discard(startState)
-          priorState = null
-          startState = null
-        }
-      }
-    } catch {
-      // Similar try/catch/finally logic for returning marks is also used in
-      // the AltCompParser and RepUnboundedParser. The logic isn't
-      // easily factored out so it is duplicated. Changes made here should also
-      // be made there. Only these parsers deal with taking marks, so this logic
-      // should not be needed elsewhere.
-      case t: Throwable => {
-        if (priorState != null || startState != null) {
-          markLeakCausedByException = true
-          if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && !t.isInstanceOf[UnsuppressableException]) {
-            val stackTrace = new StringWriter()
-            t.printStackTrace(new PrintWriter(stackTrace))
-            Assert.invariantFailed("Exception thrown with mark not returned: " + t + "\nStackTrace:\n" + stackTrace)
-          }
-        }
-        throw t
-      }
-    } finally {
-      var markLeak = false;
-      if (priorState != null) {
-        initialState.discard(priorState)
-        markLeak = true;
-      }
-      if (startState != null) {
-        initialState.discard(startState)
-        markLeak = true;
-      }
-
-      if (markLeak && !markLeakCausedByException) {
-        // likely a logic bug, throw assertion
-        Assert.invariantFailed("mark not returned, likely a logic bug")
-      }
-    }
-  }
-}
-
-class RepExactlyTotalNParser(n: Long, rParser: Parser, context: ElementRuntimeData)
-  extends RepParser(n, rParser, context, "ExactlyTotalN") {
-
-  def parseAllRepeats(pstate: PState): Unit = {
-    Rep.loopExactlyTotalN(intN, rParser, pstate, context, this)
-
-    if (pstate.processorStatus ne Success) {
-      PE(pstate, "Failed to populate %s[%s].  Expected %s item(s).",
-        context.prefixedName, pstate.mpstate.arrayPos, n) // they all must succeed, otherwise we fail here.
-    }
-  }
-}
-
-class RepUnboundedParser(occursCountKind: OccursCountKind.Value, rParser: Parser, erd: ElementRuntimeData)
-  extends RepParser(-1, rParser, erd, "Unbounded") {
-
-  def parseAllRepeats(initialState: PState): Unit = {
-    var startState: PState.Mark = null
-    var priorState: PState.Mark = null
-    var markLeakCausedByException = false
-
-    Assert.invariant(initialState.processorStatus eq Success)
-
-    try {
-      val pstate = initialState
-      startState = initialState.mark("RepUnboundedParser1")
-      priorState = initialState.mark("RepUnboundedParser2")
-      var returnFlag = false
-      while (!returnFlag && (pstate.processorStatus eq Success)) {
-
-        // Every parse is a new point of uncertainty.
-        pstate.pushDiscriminator
-        if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
-
-        try {
-          rParser.parse1(pstate)
-        } catch {
-          case sde: SchemaDefinitionDiagnosticBase => {
-            pstate.discard(startState)
-            startState = null
-            priorState = null
-            throw sde
-          }
-        }
-
-        if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
-        if (pstate.processorStatus ne Success) {
-          //
-          // Did not succeed
-          //
-          // Was a discriminator set?
-          //
-          if (pstate.discriminator == true) {
-            // we fail the whole RepUnbounded, because there was a discriminator set
-            // before the failure.
-            pstate.reset(startState)
-            startState = null
-            priorState = null
-            // no need discard priorState, that is implicitly discarded by resetting the startState
-          } else {
-            //
-            // no discriminator, so suppress the failure. Loop terminated with prior element.
-            //
-
-            log(LogLevel.Debug, "Failure suppressed. This is normal termination of a occursCountKind='parsed' array.")
-            pstate.reset(priorState)
-            pstate.discard(startState)
-            startState = null
-            priorState = null
-          }
-          returnFlag = true
-        } else {
-          // Success
-          // Need to check for forward progress
-          if (pstate.bitPos =#= priorState.bitPos0b) {
-            pstate.discard(priorState) // didn't move, but might have assigned variables, have to undo those.
-            pstate.discard(startState)
-            startState = null
-            priorState = null
-            PE(pstate,
-              "RepUnbounded - No forward progress at byte %s. Attempt to parse %s " +
-                "succeeded but consumed no data.\nPlease re-examine your schema to correct this infinite loop.",
-              pstate.bytePos, erd.diagnosticDebugName)
-            returnFlag = true
-          } else {
-            pstate.discard(priorState)
-            priorState = pstate.mark("RepUnboundedParser3")
-            pstate.mpstate.moveOverOneArrayIndexOnly
-            returnFlag = false
-          }
-        }
-        pstate.popDiscriminator
-      }
-      Assert.invariant(returnFlag == true)
-    } catch {
-      // Similar try/catch/finally logic for returning marks is also used in
-      // the AltCompParser and RepAtMostTotalNParser. The logic isn't
-      // easily factored out so it is duplicated. Changes made here should also
-      // be made there. Only these parsers deal with taking marks, so this logic
-      // should not be needed elsewhere.
-      case t: Throwable => {
-        if (priorState != null || startState != null) {
-          markLeakCausedByException = true
-          if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && !t.isInstanceOf[UnsuppressableException]) {
-            val stackTrace = new StringWriter()
-            t.printStackTrace(new PrintWriter(stackTrace))
-            Assert.invariantFailed("Exception thrown with mark not returned: " + t + "\nStackTrace:\n" + stackTrace)
-          }
-        }
-        throw t
-      }
-    } finally {
-      var markLeak = false;
-      if (priorState != null) {
-        initialState.discard(priorState)
-        markLeak = true;
-      }
-      if (startState != null) {
-        initialState.discard(startState)
-        markLeak = true;
-      }
-
-      if (markLeak && !markLeakCausedByException) {
-        // likely a logic bug, throw assertion
-        Assert.invariantFailed("mark not returned, likely a logic bug")
-      }
-    }
-  }
-}
+//trait RepParser { self: SequenceChildParser =>
+//
+//  def rParser: Parser
+//  def srd: SequenceRuntimeData
+//  def erd: ElementRuntimeData
+//  def baseName: String
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def checkN(pstate: PState, n: Long): Boolean = {
+//    if (n > pstate.tunable.maxOccursBounds) {
+//      PE(pstate, "Occurs count %s exceeds implementation maximum of %s.", n, pstate.tunable.maxOccursBounds)
+//      false
+//    } else true
+//  }
+//
+//  override def toString = "Rep" + baseName + "(" + rParser.toString + ")"
+//
+//  override def toBriefXML(depthLimit: Int = -1): String = {
+//    if (depthLimit == 0) "..." else
+//      "<Rep" + baseName + " name='" + erd.name + "'>" + rParser.toBriefXML(depthLimit - 1) +
+//        "</Rep" + baseName + ">"
+//  }
+//
+//  def startArray(state: PState): Unit = {
+//
+//    state.mpstate.arrayIndexStack.push(1L) // one-based indexing
+//    state.mpstate.occursBoundsStack.push(state.tunable.maxOccursBounds)
+//  }
+//
+//  def endArray(state: PState): Unit = {
+//    val actualOccurs = state.mpstate.arrayIndexStack.pop()
+//    state.mpstate.occursBoundsStack.pop()
+//
+//    if (state.processorStatus ne Success) return
+//
+//    val shouldValidate =
+//      state.dataProc.isDefined && state.dataProc.value.getValidationMode != ValidationMode.Off
+//
+//    if (shouldValidate && erd.minOccurs.isDefined && erd.maxOccurs.isDefined) {
+//      val minO = erd.minOccurs.get
+//      val maxO = erd.maxOccurs.get
+//      val isUnbounded = maxO == -1
+//      val occurrence = actualOccurs - 1
+//
+//      if (isUnbounded && occurrence < minO)
+//        state.validationError("%s occurred '%s' times when it was expected to be a " +
+//          "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
+//          occurrence, minO)
+//      else if (!isUnbounded && (occurrence < minO || occurrence > maxO))
+//        state.validationError("%s occurred '%s' times when it was expected to be a " +
+//          "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
+//          occurrence, minO, maxO)
+//      else {
+//        //ok
+//      }
+//    }
+//  }
+//}
+//
+//abstract class RepUnseparatedParser(
+//  val repeatCount: Long,
+//  val rParser: Parser,
+//  srd: SequenceRuntimeData,
+//  val erd: ElementRuntimeData,
+//  val baseName: String)
+//  extends UnseparatedSequenceChildParser(srd, erd, Seq(rParser)) with RepParser {
+//
+//  override protected def parse(pstate: PState): Unit = {
+//    startArray(pstate)
+//    parseAllRepeats(pstate)
+//    endArray(pstate)
+//  }
+//
+//  protected def parseAllRepeats(pstate: PState): Unit
+//}
+//
+///**
+// * This object is so that we can share the iteration idioms between situations
+// * where we know N statically, and where dynamic evaluation computes N.
+// *
+// * In these cases, there are no new points of uncertainty because computed or
+// * otherwise, we know N.
+// */
+//object Rep {
+//  def loopExactlyTotalN(intN: Long, rParser: Parser, pstate: PState, context: TermRuntimeData, iParser: Parser): Unit = {
+//    while (pstate.mpstate.arrayPos <= intN) {
+//      if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, iParser)
+//      rParser.parse1(pstate)
+//      if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, iParser)
+//      if (pstate.processorStatus ne Success) {
+//        return // fail if we don't get them all
+//      }
+//      pstate.mpstate.moveOverOneArrayIndexOnly
+//    }
+//  }
+//}
+//
+//class RepExactlyNParser(repeatCount: Long, rParser: Parser, sContext: SequenceRuntimeData, eContext: ElementRuntimeData)
+//  extends RepUnseparatedParser(repeatCount, rParser, sContext, eContext, "ExactlyN") {
+//
+//  /**
+//   * Overridden for bounds check.
+//   */
+//  override protected def parse(pstate: PState): Unit = {
+//    if (!checkN(pstate, repeatCount)) return
+//    super.parse(pstate)
+//  }
+//
+//  def parseAllRepeats(pstate: PState): Unit = {
+//    var i = 0
+//    while (i < repeatCount) {
+//      i += 1
+//      if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, rParser)
+//      rParser.parse1(pstate)
+//      if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, rParser)
+//      if (pstate.processorStatus ne Success) {
+//        val cause = pstate.processorStatus.asInstanceOf[Failure].cause
+//        PE(pstate, "Failed to populate %s[%s].  Expected %s item(s). Cause: %s.",
+//          eContext.prefixedName, pstate.mpstate.arrayPos, repeatCount,
+//          cause) // they all must succeed, otherwise we fail here.
+//        return
+//      }
+//      pstate.mpstate.moveOverOneArrayIndexOnly
+//    }
+//  }
+//}
+//
+//class RepAtMostTotalNParser(repeatCount: Long, rParser: Parser, srd: SequenceRuntimeData, erd: ElementRuntimeData)
+//  extends RepUnseparatedParser(repeatCount, rParser, srd, erd, "AtMostTotalN") {
+//
+//  def parseAllRepeats(initialState: PState): Unit = {
+//    var startState: PState.Mark = null
+//    var priorState: PState.Mark = null
+//    var markLeakCausedByException = false
+//
+//    try {
+//      if (initialState.mpstate.arrayPos <= repeatCount) {
+//        startState = initialState.mark("RepAtMostTotalNParser1")
+//        priorState = initialState.mark("RepAtMostTotalNParser2")
+//        val pstate = initialState
+//        var returnFlag = false
+//        while (!returnFlag && (pstate.mpstate.arrayPos <= repeatCount)) {
+//          // Since each one could fail, each is a new point of uncertainty.
+//
+//          pstate.pushDiscriminator
+//
+//          if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
+//
+//          try {
+//            rParser.parse1(pstate)
+//          } catch {
+//            case sde: SchemaDefinitionDiagnosticBase => {
+//              pstate.discard(startState)
+//              startState = null
+//              priorState = null
+//              throw sde
+//            }
+//          }
+//
+//          if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
+//
+//          if (pstate.processorStatus ne Success) {
+//            //
+//            // Did not succeed
+//            //
+//            // Was a discriminator set?
+//            //
+//            if (pstate.discriminator == true) {
+//              // we fail the whole RepUnbounded, because there was a discriminator set
+//              // before the failure.
+//              pstate.reset(startState)
+//              startState = null
+//              priorState = null
+//
+//              // no need discard priorState, that is implicitly discarded by resetting the startState
+//              returnFlag = true
+//            } else {
+//              //
+//              // backout any element appended as part of this attempt.
+//              //
+//              pstate.reset(priorState)
+//              pstate.discard(startState)
+//              priorState = null
+//              startState = null
+//
+//              returnFlag = true // success at prior state.
+//            }
+//          } else {
+//            //
+//            // Success
+//            //
+//            pstate.discard(priorState)
+//            priorState = pstate.mark("RepAtMostTotalNParser3")
+//            pstate.mpstate.moveOverOneArrayIndexOnly
+//            returnFlag = false
+//          }
+//
+//          pstate.popDiscriminator
+//        }
+//        if (returnFlag == false) {
+//          // we exited the loop due to arrayPos hitting the upper limit
+//          pstate.discard(priorState)
+//          pstate.discard(startState)
+//          priorState = null
+//          startState = null
+//        }
+//      }
+//    } catch {
+//      // Similar try/catch/finally logic for returning marks is also used in
+//      // the AltCompParser and RepUnboundedParser. The logic isn't
+//      // easily factored out so it is duplicated. Changes made here should also
+//      // be made there. Only these parsers deal with taking marks, so this logic
+//      // should not be needed elsewhere.
+//      case t: Throwable => {
+//        if (priorState != null || startState != null) {
+//          markLeakCausedByException = true
+//          if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && !t.isInstanceOf[UnsuppressableException]) {
+//            val stackTrace = new StringWriter()
+//            t.printStackTrace(new PrintWriter(stackTrace))
+//            Assert.invariantFailed("Exception thrown with mark not returned: " + t + "\nStackTrace:\repeatCount" + stackTrace)
+//          }
+//        }
+//        throw t
+//      }
+//    } finally {
+//      var markLeak = false;
+//      if (priorState != null) {
+//        initialState.discard(priorState)
+//        markLeak = true;
+//      }
+//      if (startState != null) {
+//        initialState.discard(startState)
+//        markLeak = true;
+//      }
+//
+//      if (markLeak && !markLeakCausedByException) {
+//        // likely a logic bug, throw assertion
+//        Assert.invariantFailed("mark not returned, likely a logic bug")
+//      }
+//    }
+//  }
+//}
+//
+//class RepExactlyTotalNParser(repeatCount: Long, rParser: Parser, srd: SequenceRuntimeData, erd: ElementRuntimeData)
+//  extends RepUnseparatedParser(repeatCount, rParser, srd, erd, "ExactlyTotalN") {
+//
+//  def parseAllRepeats(pstate: PState): Unit = {
+//    Rep.loopExactlyTotalN(repeatCount, rParser, pstate, erd, this)
+//
+//    if (pstate.processorStatus ne Success) {
+//      PE(pstate, "Failed to populate %s[%s].  Expected %s item(s).",
+//        erd.prefixedName, pstate.mpstate.arrayPos, repeatCount) // they all must succeed, otherwise we fail here.
+//    }
+//  }
+//}
+//
+//class RepUnboundedParser(occursCountKind: OccursCountKind.Value, rParser: Parser, srd: SequenceRuntimeData, erd: ElementRuntimeData)
+//  extends RepUnseparatedParser(-1, rParser, srd, erd, "Unbounded") {
+//
+//  def parseAllRepeats(initialState: PState): Unit = {
+//    var startState: PState.Mark = null
+//    var priorState: PState.Mark = null
+//    var markLeakCausedByException = false
+//
+//    Assert.invariant(initialState.processorStatus eq Success)
+//
+//    try {
+//      val pstate = initialState
+//      startState = initialState.mark("RepUnboundedParser1")
+//      priorState = initialState.mark("RepUnboundedParser2")
+//      var returnFlag = false
+//      while (!returnFlag && (pstate.processorStatus eq Success)) {
+//
+//        // Every parse is a new point of uncertainty.
+//        pstate.pushDiscriminator
+//        if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
+//
+//        try {
+//          rParser.parse1(pstate)
+//        } catch {
+//          case sde: SchemaDefinitionDiagnosticBase => {
+//            pstate.discard(startState)
+//            startState = null
+//            priorState = null
+//            throw sde
+//          }
+//        }
+//
+//        if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
+//        if (pstate.processorStatus ne Success) {
+//          //
+//          // Did not succeed
+//          //
+//          // Was a discriminator set?
+//          //
+//          if (pstate.discriminator == true) {
+//            // we fail the whole RepUnbounded, because there was a discriminator set
+//            // before the failure.
+//            pstate.reset(startState)
+//            startState = null
+//            priorState = null
+//            // no need discard priorState, that is implicitly discarded by resetting the startState
+//          } else {
+//            //
+//            // no discriminator, so suppress the failure. Loop terminated with prior element.
+//            //
+//
+//            log(LogLevel.Debug, "Failure suppressed. This is normal termination of a occursCountKind='parsed' array.")
+//            pstate.reset(priorState)
+//            pstate.discard(startState)
+//            startState = null
+//            priorState = null
+//          }
+//          returnFlag = true
+//        } else {
+//          // Success
+//          // Need to check for forward progress
+//          if (pstate.bitPos =#= priorState.bitPos0b) {
+//            pstate.discard(priorState) // didn't move, but might have assigned variables, have to undo those.
+//            pstate.discard(startState)
+//            startState = null
+//            priorState = null
+//            PE(pstate,
+//              "RepUnbounded - No forward progress at byte %s. Attempt to parse %s " +
+//                "succeeded but consumed no data.\nPlease re-examine your schema to correct this infinite loop.",
+//              pstate.bytePos, erd.diagnosticDebugName)
+//            returnFlag = true
+//          } else {
+//            pstate.discard(priorState)
+//            priorState = pstate.mark("RepUnboundedParser3")
+//            pstate.mpstate.moveOverOneArrayIndexOnly
+//            returnFlag = false
+//          }
+//        }
+//        pstate.popDiscriminator
+//      }
+//      Assert.invariant(returnFlag == true)
+//    } catch {
+//      // Similar try/catch/finally logic for returning marks is also used in
+//      // the AltCompParser and RepAtMostTotalNParser. The logic isn't
+//      // easily factored out so it is duplicated. Changes made here should also
+//      // be made there. Only these parsers deal with taking marks, so this logic
+//      // should not be needed elsewhere.
+//      case t: Throwable => {
+//        if (priorState != null || startState != null) {
+//          markLeakCausedByException = true
+//          if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && !t.isInstanceOf[UnsuppressableException]) {
+//            val stackTrace = new StringWriter()
+//            t.printStackTrace(new PrintWriter(stackTrace))
+//            Assert.invariantFailed("Exception thrown with mark not returned: " + t + "\nStackTrace:\n" + stackTrace)
+//          }
+//        }
+//        throw t
+//      }
+//    } finally {
+//      var markLeak = false;
+//      if (priorState != null) {
+//        initialState.discard(priorState)
+//        markLeak = true;
+//      }
+//      if (startState != null) {
+//        initialState.discard(startState)
+//        markLeak = true;
+//      }
+//
+//      if (markLeak && !markLeakCausedByException) {
+//        // likely a logic bug, throw assertion
+//        Assert.invariantFailed("mark not returned, likely a logic bug")
+//      }
+//    }
+//  }
+//}
 
 class OccursCountExpressionParser(occursCountEv: Evaluatable[JLong],
   override val context: ElementRuntimeData)
@@ -365,7 +403,7 @@ class OccursCountExpressionParser(occursCountEv: Evaluatable[JLong],
 
   override lazy val childProcessors = Nil
 
-  def parse(pstate: PState): Unit = {
+  override def parse(pstate: PState): Unit = {
     val oc = occursCountEv.evaluate(pstate)
     val ocLong = Numbers.asLong(oc)
     if (ocLong < 0 ||
@@ -383,29 +421,30 @@ class OccursCountExpressionParser(occursCountEv: Evaluatable[JLong],
   }
 }
 
-class RepAtMostOccursCountParser(rParser: Parser, intN: Long, erd: ElementRuntimeData)
-  extends RepParser(intN, rParser, erd, "AtMostOccursCount") {
-  def parseAllRepeats(pstate: PState): Unit = {
-    // repeat either n times, or occursCount times if that's less than n.
-    val n = math.min(pstate.mpstate.occursBounds, erd.minOccurs.get)
-    Rep.loopExactlyTotalN(intN.toInt, rParser, pstate, erd, this)
-    if (pstate.processorStatus ne Success) {
-      PE(pstate, "Failed to populate %s[%s].  Expected at most %s items.",
-        erd.prefixedName, pstate.mpstate.arrayPos, n) // they all must succeed, otherwise we fail here.
-      return
-    }
-  }
-}
-
-class RepExactlyTotalOccursCountParser(rParser: Parser, erd: ElementRuntimeData)
-  extends RepParser(-1, rParser, erd, "ExactlyTotalOccursCount") {
-  def parseAllRepeats(pstate: PState): Unit = {
-    val ocInt = pstate.mpstate.occursBounds.toInt
-    Rep.loopExactlyTotalN(ocInt, rParser, pstate, erd, this)
-    if (pstate.processorStatus ne Success) {
-      PE(pstate, "Failed to populate %s[%s].  Expected %s item(s).",
-        erd.prefixedName, pstate.mpstate.arrayPos, ocInt) // they all must succeed, otherwise we fail here.
-      return
-    }
-  }
-}
+//class RepAtMostOccursCountParser(rParser: Parser, intN: Long, srd: SequenceRuntimeData, erd: ElementRuntimeData)
+//  extends RepUnseparatedParser(intN, rParser, srd, erd, "AtMostOccursCount") {
+//  def parseAllRepeats(pstate: PState): Unit = {
+//    // repeat either n times, or occursCount times if that's less than n.
+//    val n = math.min(pstate.mpstate.occursBounds, erd.minOccurs.get)
+//    Rep.loopExactlyTotalN(intN.toInt, rParser, pstate, erd, this)
+//    if (pstate.processorStatus ne Success) {
+//      PE(pstate, "Failed to populate %s[%s].  Expected at most %s items.",
+//        erd.prefixedName, pstate.mpstate.arrayPos, n) // they all must succeed, otherwise we fail here.
+//      return
+//    }
+//  }
+//}
+//
+//class RepExactlyTotalOccursCountParser(rParser: Parser, ocParser: Parser, srd: SequenceRuntimeData, erd: ElementRuntimeData)
+//  extends RepUnseparatedParser(-1, rParser, srd, erd, "ExactlyTotalOccursCount") {
+//  def parseAllRepeats(pstate: PState): Unit = {
+//    ocParser.parse1(pstate)
+//    val ocInt = pstate.mpstate.occursBounds.toInt
+//    Rep.loopExactlyTotalN(ocInt, rParser, pstate, erd, this)
+//    if (pstate.processorStatus ne Success) {
+//      PE(pstate, "Failed to populate %s[%s].  Expected %s item(s).",
+//        erd.prefixedName, pstate.mpstate.arrayPos, ocInt) // they all must succeed, otherwise we fail here.
+//      return
+//    }
+//  }
+//}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SeparatedSequenceParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SeparatedSequenceParsers.scala
new file mode 100644
index 000000000..5e1f394ae
--- /dev/null
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SeparatedSequenceParsers.scala
@@ -0,0 +1,549 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.parsers
+
+import org.apache.daffodil.dpath.NodeInfo
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.processors.{ ElementRuntimeData, Failure, ModelGroupRuntimeData, SequenceRuntimeData, Success, TermRuntimeData }
+import org.apache.daffodil.schema.annotation.props.SeparatorSuppressionPolicy
+import org.apache.daffodil.schema.annotation.props.gen.SeparatorPosition
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
+
+trait Separated { self: SequenceChildParser =>
+  import ArrayIndexStatus._
+
+  def sep: Parser
+  def spos: SeparatorPosition
+  def ssp: SeparatorSuppressionPolicy
+
+  val childProcessors = Seq(sep, childParser)
+
+  /**
+   * Tells us if we should remove a successfully parsed zero-length string
+   * or hexBinary from the infoset, because it is optional, so even though
+   * zero length may parse successfully and return an empty string or hexbinary,
+   * the optionality of the element wins out over the empty-string value, and
+   * we don't put the element into the infoset as an array child.
+   */
+  def shouldRemoveZLStringHexBinaryValue(ais: ArrayIndexStatus, erd: ElementRuntimeData): Boolean = {
+    val optional = ais eq Optional
+    val res =
+      if (!optional) false
+      else {
+        val optPT = erd.optPrimType
+        if (optPT.isEmpty) false
+        else {
+          val pt: NodeInfo.PrimType = optPT.get
+          pt match {
+            case NodeInfo.PrimType.String => true
+            case NodeInfo.PrimType.HexBinary => true
+            case _ => false
+          }
+        }
+      }
+    res
+  }
+}
+
+class ScalarOrderedRequiredSeparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  trd: TermRuntimeData,
+  val sep: Parser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy)
+  extends SequenceChildParser(childParser, srd, trd)
+  with Separated {
+
+  override def parse(state: PState) = childParser.parse1(state)
+
+  override def shouldRemoveZLStringHexBinaryValue(ais: ArrayIndexStatus, erd: ElementRuntimeData): Boolean = false
+}
+
+class ExactlyNLoopState(limit: Long) extends ParseLoopState {
+  import ArrayIndexStatus._
+
+  def arrayIndexStatus(parser: SequenceChildParser,
+    pstate: PState,
+    resultOfPriorTry: ParseAttemptStatus) = {
+    val res =
+      if (pstate.processorStatus ne Success)
+        Failed
+      else {
+        resultOfPriorTry match {
+          case _: FailedParseAttemptStatus => ArrayIndexStatus.Failed
+          case _ => {
+            if (pstate.arrayPos <= limit)
+              Required
+            else
+              MaxExceeded
+          }
+        }
+      }
+    res
+  }
+
+  def nextArrayIndex(pstate: PState): Long = {
+    val res = pstate.arrayPos
+    pstate.mpstate.moveOverOneArrayIndexOnly() // advance array position
+    res
+  }
+}
+
+trait OccursCountExactLoopStateMixin { self: SequenceChildParser =>
+
+  def repeatCount: Long
+  val minRepeats = 0L
+  val maxRepeats = repeatCount
+
+  def loopState(pstate: PState): ParseLoopState = {
+    new ExactlyNLoopState(repeatCount)
+  }
+}
+
+class RepOrderedExactlyNSeparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  val repeatCount: Long,
+  val sep: Parser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy,
+  val baseName: String = "ExactlyN")
+  extends SequenceChildParser(childParser, srd, erd)
+  with OccursCountExactLoopStateMixin
+  with Separated
+  with RepParser
+
+trait OccursCountExpressionLoopStateMixin { self: SequenceChildParser =>
+
+  def ocParser: Parser
+
+  def loopState(pstate: PState): ParseLoopState = {
+    ocParser.parse1(pstate)
+    val ocInt = pstate.mpstate.occursBounds.toInt
+    new ExactlyNLoopState(ocInt)
+  }
+}
+
+class RepOrderedExactlyTotalOccursCountSeparatedSequenceChildParser(
+  childParser: Parser,
+  override val ocParser: Parser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  sep: Parser,
+  spos: SeparatorPosition,
+  ssp: SeparatorSuppressionPolicy)
+  extends RepOrderedExactlyNSeparatedSequenceChildParser(childParser,
+    srd, erd,
+    { val ignored = 0; ignored },
+    sep, spos, ssp,
+    "ExactlyTotalOccursCount")
+  with OccursCountExpressionLoopStateMixin {
+
+  override val childProcessors = Seq(ocParser, childParser)
+
+  override def loopState(pstate: PState) = super.loopState(pstate)
+}
+
+trait OccursCountMinMaxLoopStateMixin { self: SequenceChildParser =>
+  def min: Long
+  def max: Long
+  def erd: ElementRuntimeData
+
+  def loopState(pstate: PState): ParseLoopState =
+    new MinMaxLoopState(erd, min, max)
+}
+
+/**
+ * Pass nothing, or -1 for min/max to mean use erd.minOccurs/maxOccurs
+ * Pass -2 for max to force unbounded behavior.
+ */
+class RepOrderedWithMinMaxSeparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  val baseName: String,
+  val sep: Parser,
+  val spos: SeparatorPosition,
+  val ssp: SeparatorSuppressionPolicy,
+  val min: Long = -1,
+  val max: Long = -1) // pass -2 to force unbounded behavior
+  extends SequenceChildParser(childParser, srd, erd)
+  with Separated
+  with RepParser
+  with OccursCountMinMaxLoopStateMixin
+
+class MinMaxLoopState(erd: ElementRuntimeData, min: Long, max: Long) extends ParseLoopState {
+  import ArrayIndexStatus._
+
+  Assert.invariant(erd.maybeOccursCountKind.isDefined)
+  lazy val minRepeats = if (min == -1) erd.minOccurs else min
+  lazy val maxRepeats = max match {
+    case -1 if (erd.maxOccurs == -1) => Long.MaxValue
+    case -1 => if (erd.maybeOccursCountKind.get eq OccursCountKind.Parsed) Long.MaxValue else erd.maxOccurs
+    case -2 => Long.MaxValue
+    case _ => max
+  }
+
+  def arrayIndexStatus(
+    parser: SequenceChildParser,
+    pstate: PState,
+    resultOfPriorTry: ParseAttemptStatus): ArrayIndexStatus = {
+    import ParseAttemptStatus._
+
+    if (pstate.processorStatus ne Success)
+      return Failed
+    resultOfPriorTry match {
+      case Success_EndOfArray => return Done
+      case _: SuccessParseAttemptStatus => // ok
+      case Uninitialized => // ok
+      case FailedEntireArray => return Failed
+      case FailedWithDiscriminatorSet => return Failed
+      case FailedSpeculativeParse => Assert.invariantFailed("Should already be handled.")
+    }
+    if (pstate.arrayPos <= minRepeats)
+      return Required
+    if (pstate.arrayPos <= maxRepeats)
+      return Optional
+    MaxExceeded
+  }
+
+  def nextArrayIndex(pstate: PState): Long = {
+    val res = pstate.arrayPos
+    pstate.mpstate.moveOverOneArrayIndexOnly() // advance array position
+    res
+  }
+}
+
+class OrderedSeparatedSequenceParser(rd: SequenceRuntimeData,
+  ssp: SeparatorSuppressionPolicy,
+  spos: SeparatorPosition,
+  sep: Parser,
+  childrenArg: Seq[SequenceChildParser])
+  extends OrderedSequenceParserBase(rd, childrenArg) {
+
+  override lazy val childProcessors = sep +: childrenArg
+
+  import SequenceChildParser._
+  //
+  // TODO: It would be good to get rid of this downcast
+  // leaving it for now, as the split into Separated/Unseparated might get
+  // recombined if upon implementing the separated interations it seems
+  // like they don't need to be independent code.
+  //
+  private val children = childrenArg.map { _.asInstanceOf[SeparatedChildParser] }
+
+  private def failedSeparator(pstate: PState, priorState: PState.Mark, kind: String): Unit = {
+    val cause = pstate.processorStatus.asInstanceOf[Failure].cause
+    PE(pstate, "Failed to parse %s separator. Cause: %s.", kind, cause)
+  }
+
+  private def failedZeroLengthWithAnyEmpty(pstate: PState, priorState: PState.Mark) = {
+    PE(pstate, "Failed to parse sequence child. Cause: zero-length data, but dfdl:separatorSuppressionPolicy is 'anyEmpty'.")
+    ParseAttemptStatus.FailedSpeculativeParse
+  }
+
+  /**
+   * Parses one iteration of an array/optional element, and returns
+   * * MaybeBoolean.One(true) - indicates the child parse was zero length
+   * * MaybeBoolean.One(false) - indicates the child parse was not zero length or failed
+   * * MaybeBoolean.Nope - indicates that the array loop should terminate. If due to discriminator failure
+   * the pstate will indicate failure. If due to speculative parsing finding the end of the array
+   * then pstate will indicate success.
+   */
+  protected def parseOne(
+    parserArg: SequenceChildParser,
+    trd: TermRuntimeData,
+    pstate: PState,
+    priorState: PState.Mark,
+    maybeStartState: Maybe[PState.Mark],
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+
+    val parser = parserArg.asInstanceOf[SeparatedChildParser]
+
+    val isFixedOccurs = maybeStartState.isEmpty
+    val isVariableOccurs = !isFixedOccurs
+
+    val finalStatus: ParseAttemptStatus = {
+      // parse prefix sep if any
+      val prefixSepSuccessful =
+        if ((spos eq SeparatorPosition.Prefix) && trd.isRepresented) {
+          sep.parse1(pstate)
+          pstate.processorStatus eq Success
+        } else
+          true
+
+      if (!prefixSepSuccessful) {
+        failedSeparator(pstate, priorState, "prefix")
+        processFailedChildParseResults(pstate, priorState, maybeStartState)
+      } else {
+        // except for the first position of the group, parse an infix separator
+
+        val infixSepSuccessful =
+          if ((spos eq SeparatorPosition.Infix) && pstate.mpstate.childPos > 1 && trd.isRepresented) {
+            sep.parse1(pstate)
+            pstate.processorStatus eq Success
+          } else
+            true
+
+        if (!infixSepSuccessful) {
+          failedSeparator(pstate, priorState, "infix")
+          processFailedChildParseResults(pstate, priorState, maybeStartState)
+        } else {
+          //
+          // now we parse the child
+          //
+          val prevBitPos = pstate.bitPos0b
+
+          if (isVariableOccurs) {
+            pstate.pushDiscriminator
+          }
+
+          if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
+
+          parser.parse1(pstate)
+
+          if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
+
+          val childSuccessful = pstate.processorStatus eq Success
+
+          val res: ParseAttemptStatus = {
+            if (!childSuccessful) {
+              processFailedChildParseResults(pstate, priorState, maybeStartState)
+            } else {
+              Assert.invariant(childSuccessful)
+              val zl = prevBitPos == pstate.bitPos0b
+              // parse postfix sep if any
+              val postfixSepSuccessful =
+                if ((spos eq SeparatorPosition.Postfix) && trd.isRepresented) {
+                  sep.parse1(pstate)
+                  pstate.processorStatus eq Success
+                } else
+                  true
+
+              if (!postfixSepSuccessful) {
+                failedSeparator(pstate, priorState, "postfix")
+                processFailedChildParseResults(pstate, priorState, maybeStartState)
+              } else {
+
+                processSuccessfulChildParseResults(trd, parser, zl, pstate, priorState, maybeStartState, ais)
+
+              } // end if postfix success/fail
+            } // child if child success/fail
+          }
+          if (isVariableOccurs) {
+            pstate.popDiscriminator
+          }
+          res
+        } // end if infix
+      } // end if prefix
+    }
+    finalStatus
+  }
+
+  private def processSuccessfulChildParseResults(
+    trd: TermRuntimeData,
+    parser: SeparatedChildParser,
+    zl: Boolean,
+    pstate: PState,
+    priorState: PState.Mark,
+    maybeStartState: Maybe[PState.Mark],
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+
+    val isFixedOccurs = maybeStartState.isEmpty
+    val isVariableOccurs = !isFixedOccurs
+
+    //
+    // Total success with this array child.
+    //
+    if (isFixedOccurs) {
+      //
+      // successful parse of required element. Zero length or not.
+      // note that anyEmpty doesn't apply in this case, and
+      // that property should be ignored.
+      //
+      ParseAttemptStatus.Success_LengthUndetermined
+    } else {
+      Assert.invariant(isVariableOccurs)
+      val startState = maybeStartState.get
+      processSuccessfulVariableOccursChildParseResults(trd, parser, zl, pstate, priorState, startState, ais)
+    }
+  }
+
+  private def processSuccessfulVariableOccursChildParseResults(trd: TermRuntimeData,
+    parser: SeparatedChildParser,
+    zl: Boolean,
+    pstate: PState,
+    priorState: PState.Mark,
+    startState: PState.Mark,
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+    import ArrayIndexStatus._
+
+    //
+    // Now we have to analyze the cases where ZL matters and needs special handling
+    //
+    val result: ParseAttemptStatus = ais match {
+      case Required => processSuccessfulRequiredVariableOccursChildParseResults(zl, pstate, priorState, startState)
+      case Optional => processSuccessfulOptionalVariableOccursChildParseResults(trd, parser, zl, pstate, priorState, startState, ais)
+    }
+    result
+
+  }
+
+  private def processSuccessfulRequiredVariableOccursChildParseResults(
+    zl: Boolean,
+    pstate: PState,
+    priorState: PState.Mark,
+    startState: PState.Mark): ParseAttemptStatus = {
+    //
+    // It's required. If it is empty, then that has to work as a value
+    // for us (so must work as empty string value, or empty hexBinary.
+    // we don't need to check for that here, as it is detected elsewhere.
+    //
+    // But we still have to check if we're allowed to have any empty
+    // at all according to separatorSuppressionPolicy
+    //
+    // FIXME: verify this. It might be that ssp is ignored for required
+    // elements
+    //
+    if (zl) {
+      if (ssp eq SeparatorSuppressionPolicy.AnyEmpty) {
+        //
+        // Not allowed to have any empty at all
+        //
+        failedZeroLengthWithAnyEmpty(pstate, priorState)
+      } else {
+        ParseAttemptStatus.Success_ZeroLength // ZL, successful parse of required element
+      }
+    } else {
+      ParseAttemptStatus.Success_NotZeroLength // not ZL, successful parse of required element
+    }
+  }
+
+  private def processSuccessfulOptionalVariableOccursChildParseResults(trd: TermRuntimeData,
+    parser: SeparatedChildParser,
+    zl: Boolean,
+    pstate: PState,
+    priorState: PState.Mark,
+    startState: PState.Mark,
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+    if (zl) {
+      val shouldRemoveZLElement =
+        trd match {
+          case erd: ElementRuntimeData =>
+            parser.shouldRemoveZLStringHexBinaryValue(ais, erd)
+          case mgd: ModelGroupRuntimeData =>
+            false
+        }
+      if (shouldRemoveZLElement) {
+        // it's an optional element, type is string/hexBinary and length is zero
+        // so we don't want to add it to the infoset
+        //
+        // However, we do want to keep trying to parse more, as they could be trailing separators.
+        // that are to be tolerated.
+        //
+        // So we don't backtrack here. We just remove the accumulated element.
+        //
+        pstate.reset(priorState) // no need discard priorState, that is implicitly discarded by resetting the startState
+        pstate.discard(startState) // finished with array, so cleanup
+        ParseAttemptStatus.Success_EndOfArray // success, but we're at end of array. not zero length (in this case with success at prior element)
+      } else {
+        //
+        // optional, zero length, successful parse, not a string/hexBinary
+        //
+        // This can happen if you parse a choice group that
+        // has all its branches entirely optional
+        //
+        if (ssp eq SeparatorSuppressionPolicy.AnyEmpty) {
+          //
+          // Not allowed to have any empty at all
+          //
+          failedZeroLengthWithAnyEmpty(pstate, priorState)
+        } else {
+          ParseAttemptStatus.Success_ZeroLength // ZL, successful parse of optional 'thing'
+        }
+      }
+    } else {
+      ParseAttemptStatus.Success_NotZeroLength // not ZL, successful optional element parse
+    }
+  }
+
+  protected def parse(pstate: PState): Unit = {
+
+    var scpIndex = 0
+    pstate.mpstate.groupIndexStack.push(1L) // one-based indexing
+
+    val limit = children.length
+
+    var wasLastChildZeroLength = false
+
+    while ((scpIndex < limit) && (pstate.processorStatus eq Success)) {
+      val child = children(scpIndex)
+      child match {
+        case parser: RepSeparatedChildParser => {
+
+          val loopState = parser.loopState(pstate)
+
+          // push new array context for array/optional
+          parser.startArray(pstate)
+
+          val maybeStartState =
+            if (parser.erd.maxOccurs != parser.erd.minOccurs)
+              Maybe(pstate.mark("startState in OrderedSeparatedSequenceParser"))
+            else
+              Maybe.Nope
+
+          var ais: ArrayIndexStatus = null
+
+          var resultOfTry: ParseAttemptStatus = ParseAttemptStatus.Uninitialized
+
+          while ({
+            ais = loopState.arrayIndexStatus(parser, pstate, resultOfTry)
+            ais.isInstanceOf[GoArrayIndexStatus]
+          }) {
+
+            resultOfTry =
+              tryParseDetectMarkLeaks(parser, pstate, maybeStartState, ais.asInstanceOf[GoArrayIndexStatus])
+
+            wasLastChildZeroLength = resultOfTry eq ParseAttemptStatus.Success_ZeroLength
+
+            loopState.nextArrayIndex(pstate)
+          } // end while for each repeat
+
+          parser.endArray(pstate)
+        } // end match case RepParser
+
+        case scalarParser => {
+          val resultOfTry = tryParseDetectMarkLeaks(scalarParser, pstate, Maybe.Nope, ArrayIndexStatus.Required)
+
+          wasLastChildZeroLength = resultOfTry eq ParseAttemptStatus.Success_ZeroLength
+        } // end match case scalar parser
+      } // end match
+      scpIndex += 1
+    } // end while for each sequence child parser
+
+    if ((pstate.processorStatus eq Success) && wasLastChildZeroLength) {
+      Assert.invariant(ssp eq SeparatorSuppressionPolicy.TrailingEmptyStrict)
+      PE(pstate, "Empty trailing elements are not allowed when dfdl:separatorSuppressionPolicy='trailingEmptyStrict'")
+    }
+    pstate.mpstate.groupIndexStack.pop()
+    pstate.mpstate.moveOverOneGroupIndexOnly()
+    ()
+  }
+}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
new file mode 100644
index 000000000..896581c85
--- /dev/null
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SequenceParserBases.scala
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.parsers
+
+import org.apache.daffodil.processors.Evaluatable
+import java.io.PrintWriter
+import org.apache.daffodil.exceptions.UnsuppressableException
+import java.io.StringWriter
+import org.apache.daffodil.dsom.SchemaDefinitionDiagnosticBase
+import org.apache.daffodil.exceptions.Assert
+import org.apache.daffodil.processors.Success
+import org.apache.daffodil.processors.SequenceRuntimeData
+import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.processors.TermRuntimeData
+import org.apache.daffodil.util.Maybe
+import org.apache.daffodil.api.ValidationMode
+import org.apache.daffodil.processors.Failure
+
+sealed trait ArrayIndexStatus
+sealed trait GoArrayIndexStatus extends ArrayIndexStatus
+sealed trait StopArrayIndexStatus extends ArrayIndexStatus
+
+object ArrayIndexStatus {
+
+  /**
+   * Indicates the array element is between 0 and minOccurs in position
+   * so is required. However, for occursCountKind 'parsed' or 'stopValue'
+   * this is never returned, as the min/max bounds are only advisory
+   * for validation purposes in that case.
+   */
+  case object Required extends GoArrayIndexStatus
+
+  /**
+   * Indicates that the array element index is between minOccurs and maxOccurs
+   * so isOptional, or that occursCountKind is 'parsed' or 'stopValue' so
+   * all elements are optional.
+   */
+  case object Optional extends GoArrayIndexStatus
+
+  /**
+   * Indicates that maxOccurs bound has been reached and the occursCountKind
+   * is such that one must stop after that many. (Not unbounded behavior)
+   *
+   * If this status is returned then one should NOT attempt to parse an
+   * additional array element.
+   */
+  case object MaxExceeded extends StopArrayIndexStatus
+
+  /**
+   * Indicates that pstate status is failed, that is, we
+   * are unable to continue parsing.
+   */
+  case object Failed extends StopArrayIndexStatus
+
+  /**
+   * Indicates that we are done iterating, and should stop parsing more
+   * array. Used to indicate that the end of the array was identified
+   * by speculative parsing.
+   */
+  case object Done extends StopArrayIndexStatus
+}
+
+sealed trait ParseAttemptStatus
+sealed trait SuccessParseAttemptStatus extends ParseAttemptStatus
+sealed trait FailedParseAttemptStatus extends ParseAttemptStatus
+
+object ParseAttemptStatus {
+
+  case object Uninitialized extends ParseAttemptStatus
+
+  case object Success_ZeroLength extends SuccessParseAttemptStatus
+
+  case object Success_NotZeroLength extends SuccessParseAttemptStatus
+
+  case object Success_LengthUndetermined extends SuccessParseAttemptStatus
+
+  case object Success_EndOfArray extends SuccessParseAttemptStatus
+
+  case object FailedWithDiscriminatorSet extends FailedParseAttemptStatus
+
+  case object FailedSpeculativeParse extends FailedParseAttemptStatus
+
+  case object FailedEntireArray extends FailedParseAttemptStatus
+
+}
+
+/**
+ * TODO: Performance Stack allocate these objects (stack will be in PState to avoid
+ * overhead of another threadlocal)
+ */
+trait ParseLoopState {
+
+  /**
+   * Tells us whether to attempt another array element at the current index,
+   * and how we should interpret the existence of an element
+   * or empty/zero-length based on the array index.
+   */
+  def arrayIndexStatus(parser: SequenceChildParser, state: PState,
+    resultOfPriorTry: ParseAttemptStatus): ArrayIndexStatus
+
+  /**
+   * Must advance array position for arrays, plus any
+   * incrementation.
+   */
+  def nextArrayIndex(state: PState): Long
+
+}
+
+abstract class OrderedSequenceParserBase(rd: SequenceRuntimeData, childParsers: Seq[Parser])
+  extends CombinatorParser(rd) {
+  override def nom = "Sequence"
+
+  override lazy val runtimeDependencies: Seq[Evaluatable[AnyRef]] = Nil
+
+  override lazy val childProcessors = childParsers
+
+  /**
+   * Parses one iteration of an array/optional element, and returns
+   * * MaybeBoolean.One(true) - indicates the child parse was zero length
+   * * MaybeBoolean.One(false) - indicates the child parse was not zero length or failed
+   * * MaybeBoolean.Nope - indicates that the array loop should terminate due to discriminator failure. in which case the pstate will indicate failure.
+   */
+  protected def parseOne(
+    parser: SequenceChildParser,
+    trd: TermRuntimeData,
+    pstate: PState,
+    priorState: PState.Mark,
+    maybeStartState: Maybe[PState.Mark],
+    ais: GoArrayIndexStatus): ParseAttemptStatus
+
+  final protected def tryParseDetectMarkLeaks(
+    parser: SequenceChildParser,
+    pstate: PState,
+    maybeStartState: Maybe[PState.Mark],
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+
+    var markLeakCausedByException = false
+    var priorState: PState.Mark = null
+    val result =
+      try {
+        priorState = pstate.mark("OrderedSeparatedSequence_beforeSeparator")
+        val res = parseOne(parser, parser.trd, pstate, priorState, maybeStartState, ais)
+        priorState = null
+        res
+      } catch {
+        // Similar try/catch/finally logic for returning marks is also used in
+        // the AltCompParser and RepUnboundedParser. The logic isn't
+        // easily factored out so it is duplicated. Changes made here should also
+        // be made there. Only these parsers deal with taking marks, so this logic
+        // should not be needed elsewhere.
+        case t: Throwable => {
+          if (priorState != null) {
+            markLeakCausedByException = true
+            if (!t.isInstanceOf[SchemaDefinitionDiagnosticBase] && !t.isInstanceOf[UnsuppressableException]) {
+              val stackTrace = new StringWriter()
+              t.printStackTrace(new PrintWriter(stackTrace))
+              Assert.invariantFailed("Exception thrown with mark not returned: " + t + "\nStackTrace:\n" + stackTrace)
+            }
+          }
+          throw t
+        }
+      } finally {
+        var markLeak = false;
+        if (priorState != null) {
+          pstate.discard(priorState)
+          markLeak = true;
+        }
+        if (markLeak && !markLeakCausedByException) {
+          // likely a logic bug, throw assertion
+          Assert.invariantFailed("mark not returned, likely a logic bug")
+        }
+      } // end try/catch/finally
+    result
+  }
+
+  protected def failedChild(pstate: PState, priorState: PState.Mark): ParseAttemptStatus = {
+    val cause = pstate.processorStatus.asInstanceOf[Failure].cause
+    PE(pstate, "Failed to parse sequence child. Cause: %s.", cause)
+    ParseAttemptStatus.FailedSpeculativeParse
+  }
+
+  protected def processFailedChildParseResults(
+    pstate: PState,
+    priorState: PState.Mark,
+    maybeStartState: Maybe[PState.Mark]): ParseAttemptStatus = {
+    val isFixedOccurs = maybeStartState.isEmpty
+    val isVariableOccurs = !isFixedOccurs
+    //
+    // we failed to parse a child
+    //
+    // failedChild(pstate, priorState) // discards prior state
+    if (isFixedOccurs) {
+      //
+      // in fixed occurs, there are no points of uncertainty for the
+      // individual elements, so a failure is a failure of the whole loop.
+      // And any discriminator that has been set is the discriminator
+      // of some surrounding scope.
+      //
+      ParseAttemptStatus.FailedEntireArray
+    } else {
+      Assert.invariant(isVariableOccurs)
+      val startState = maybeStartState.get
+      //
+      // variable occurs case, there is a PoU per array element
+      //
+      // Parsing the array element may be a deep recursive walk
+      // somewhere in there a discriminator may be set indicating
+      // this array element is known to exist
+      //
+      // Hence, if discriminator is set, we fail the whole array
+      // because the discriminator says this array element *is here*, but
+      // the parse failed, so it isn't.
+      //
+      if (pstate.discriminator == true) {
+        pstate.discard(priorState) // deallocate
+        pstate.reset(startState)
+        ParseAttemptStatus.FailedWithDiscriminatorSet
+      } else {
+        Assert.invariant(pstate.discriminator == false)
+        //
+        // discriminator false, variable occurs case, we failed the element, but that
+        // just means we back out to prior element.
+        //
+        pstate.reset(priorState)
+        pstate.discard(startState) // finished with array, so cleanup
+        ParseAttemptStatus.Success_EndOfArray // not zero length (in this case with success at prior element)
+      }
+    } // end if fixed/variable
+  }
+}
+
+object SequenceChildParser {
+  type SeparatedChildParser = SequenceChildParser with Separated
+  type RepSeparatedChildParser = SeparatedChildParser with RepParser
+
+  type UnseparatedChildParser = SequenceChildParser with Unseparated
+  type RepUnseparatedChildParser = UnseparatedChildParser with RepParser
+}
+
+abstract class SequenceChildParser(
+  val childParser: Parser,
+  val srd: SequenceRuntimeData,
+  val trd: TermRuntimeData)
+  extends CombinatorParser(srd) {
+
+  override def runtimeDependencies = Nil
+
+}
+
+trait RepParser { self: SequenceChildParser =>
+
+  def childParser: Parser
+  def srd: SequenceRuntimeData
+  def erd: ElementRuntimeData
+  def baseName: String
+
+  override protected def parse(pstate: PState): Unit = {
+    childParser.parse1(pstate)
+    if (pstate.processorStatus ne Success) {
+      val cause = pstate.processorStatus.asInstanceOf[Failure].cause
+      PE(pstate, "Failed to populate %s[%s]. Cause: %s.",
+        erd.prefixedName, pstate.mpstate.arrayPos, cause) // they all must succeed, otherwise we fail here.
+      return
+    }
+  }
+
+  def loopState(state: PState): ParseLoopState
+
+  override lazy val runtimeDependencies = Nil
+
+  def checkN(pstate: PState, n: Long): Boolean = {
+    if (n > pstate.tunable.maxOccursBounds) {
+      PE(pstate, "Occurs count %s exceeds implementation maximum of %s.", n, pstate.tunable.maxOccursBounds)
+      false
+    } else true
+  }
+
+  override def toString = "Rep" + baseName + "(" + childParser.toString + ")"
+
+  override def toBriefXML(depthLimit: Int = -1): String = {
+    if (depthLimit == 0) "..." else
+      "<Rep" + baseName + " name='" + erd.name + "'>" + childParser.toBriefXML(depthLimit - 1) +
+        "</Rep" + baseName + ">"
+  }
+
+  def startArray(state: PState): Unit = {
+
+    state.mpstate.arrayIndexStack.push(1L) // one-based indexing
+    state.mpstate.occursBoundsStack.push(state.tunable.maxOccursBounds)
+  }
+
+  def endArray(state: PState): Unit = {
+    val actualOccurs = state.mpstate.arrayIndexStack.pop()
+    state.mpstate.occursBoundsStack.pop()
+
+    if (state.processorStatus ne Success) return
+
+    val shouldValidate =
+      state.dataProc.isDefined && state.dataProc.value.getValidationMode != ValidationMode.Off
+
+    if (shouldValidate) {
+      val minO = erd.minOccurs
+      val maxO = erd.maxOccurs
+      val isUnbounded = maxO == -1
+      val occurrence = actualOccurs - 1
+
+      if (isUnbounded && occurrence < minO)
+        state.validationError("%s occurred '%s' times when it was expected to be a " +
+          "minimum of '%s' and a maximum of 'UNBOUNDED' times.", erd.diagnosticDebugName,
+          occurrence, minO)
+      else if (!isUnbounded && (occurrence < minO || occurrence > maxO))
+        state.validationError("%s occurred '%s' times when it was expected to be a " +
+          "minimum of '%s' and a maximum of '%s' times.", erd.diagnosticDebugName,
+          occurrence, minO, maxO)
+      else {
+        //ok
+      }
+    }
+  }
+}
+
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
index 4c305d203..b1f213028 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/SpecifiedLengthParsers.scala
@@ -28,15 +28,16 @@ import org.apache.daffodil.util.MaybeULong
 import passera.unsigned.ULong
 import org.apache.daffodil.equality._
 import java.lang.{ Long => JLong }
+import org.apache.daffodil.util.Maybe
 
-sealed abstract class SpecifiedLengthParserBase(eParser: Parser,
+sealed abstract class SpecifiedLengthParserBase(maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData)
   extends CombinatorParser(erd)
   with CaptureParsingValueLength {
 
   override lazy val runtimeDependencies = Nil
 
-  override lazy val childProcessors = Seq(eParser)
+  override lazy val childProcessors = maybeEParser.toSeq
 
   override def charsetEv = Assert.invariantFailed("Specified Length parsers should not capture value length using the charset")
 
@@ -56,7 +57,8 @@ sealed abstract class SpecifiedLengthParserBase(eParser: Parser,
 
     val startingBitPos0b = dis.bitPos0b
     val isLimitOk: Boolean = dis.withBitLengthLimit(nBits) {
-      eParser.parse1(pState)
+      if (maybeEParser.isDefined) 
+        maybeEParser.get.parse1(pState)
     }
     if (!isLimitOk) {
       val availBits = if (dis.remainingBits.isDefined) dis.remainingBits.get.toString else "(unknown)"
@@ -86,10 +88,10 @@ sealed abstract class SpecifiedLengthParserBase(eParser: Parser,
 }
 
 class SpecifiedLengthPatternParser(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData,
   pattern: java.util.regex.Pattern)
-  extends SpecifiedLengthParserBase(eParser, erd) {
+  extends SpecifiedLengthParserBase(maybeEParser, erd) {
 
   object withMatcher extends OnStack[Matcher](pattern.matcher(""))
 
@@ -112,11 +114,11 @@ class SpecifiedLengthPatternParser(
 }
 
 class SpecifiedLengthExplicitParser(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData,
   lengthEv: Evaluatable[JLong],
   toBits: Int)
-  extends SpecifiedLengthParserBase(eParser, erd) {
+  extends SpecifiedLengthParserBase(maybeEParser, erd) {
 
   final override def getBitLength(s: PState): MaybeULong = {
     val nBytesAsAny = lengthEv.evaluate(s)
@@ -126,10 +128,10 @@ class SpecifiedLengthExplicitParser(
 }
 
 class SpecifiedLengthImplicitParser(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData,
   nBits: Long)
-  extends SpecifiedLengthParserBase(eParser, erd) {
+  extends SpecifiedLengthParserBase(maybeEParser, erd) {
 
   final override def getBitLength(s: PState): MaybeULong = MaybeULong(nBits)
 }
@@ -148,9 +150,9 @@ class SpecifiedLengthImplicitParser(
  * characters, as we're going to recursively descend and parse it into the complex structure.
  */
 sealed abstract class SpecifiedLengthCharactersParserBase(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData)
-  extends SpecifiedLengthParserBase(eParser, erd) {
+  extends SpecifiedLengthParserBase(maybeEParser, erd) {
 
   private def maybeBitPosAfterNChars(start: PState, nChars: Long): MaybeULong = {
     val dis = start.dataInputStream
@@ -191,20 +193,20 @@ sealed abstract class SpecifiedLengthCharactersParserBase(
 }
 
 final class SpecifiedLengthImplicitCharactersParser(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData,
   nChars: Long)
-  extends SpecifiedLengthCharactersParserBase(eParser, erd) {
+  extends SpecifiedLengthCharactersParserBase(maybeEParser, erd) {
 
   override def getCharLength(s: PState) = nChars
 
 }
 
 final class SpecifiedLengthExplicitCharactersParser(
-  eParser: Parser,
+  maybeEParser: Maybe[Parser],
   erd: ElementRuntimeData,
   lengthEv: Evaluatable[JLong])
-  extends SpecifiedLengthCharactersParserBase(eParser, erd) {
+  extends SpecifiedLengthCharactersParserBase(maybeEParser, erd) {
 
   def getCharLength(s: PState): Long = {
     val nChars = lengthEv.evaluate(s)
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/StringLengthParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/StringLengthParsers.scala
index e6a259046..380467f21 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/StringLengthParsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/StringLengthParsers.scala
@@ -27,7 +27,7 @@ import org.apache.daffodil.processors.CharsetEv
 /**
  * Specifically designed to be used inside one of the SpecifiedLength parsers.
  * override
- * This grabs a string as long as it coverride an get, depending on the SpecifiedLength context
+ * This grabs a string as long as it can get, depending on the SpecifiedLength context
  * to constrain how much it can get.
  */
 final class StringOfSpecifiedLengthParser(
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/UnseparatedSequenceParsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/UnseparatedSequenceParsers.scala
new file mode 100644
index 000000000..131fb65ee
--- /dev/null
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/UnseparatedSequenceParsers.scala
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.processors.parsers
+
+import org.apache.daffodil.processors.{ SequenceRuntimeData, Success, TermRuntimeData }
+import org.apache.daffodil.processors.ElementRuntimeData
+import org.apache.daffodil.util.Maybe
+
+trait Unseparated { self: SequenceChildParser =>
+
+  val childProcessors = Seq(childParser)
+}
+
+class ScalarOrderedRequiredUnseparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  trd: TermRuntimeData)
+  extends SequenceChildParser(childParser, srd, trd) with Unseparated {
+
+  override protected def parse(state: PState) = childParser.parse1(state)
+}
+
+abstract class RepUnseparatedParser(
+  childParser: Parser,
+  val min: Long,
+  val max: Long,
+  srd: SequenceRuntimeData,
+  val erd: ElementRuntimeData,
+  val baseName: String)
+  extends SequenceChildParser(childParser, srd, erd) with Unseparated with RepParser {
+}
+
+class RepOrderedExactlyNUnseparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  val repeatCount: Long,
+  baseName: String = "ExactlyN")
+  extends RepUnseparatedParser(childParser, 0, repeatCount, srd, erd, baseName)
+  with RepParser
+  with OccursCountExactLoopStateMixin
+
+class RepOrderedExactlyTotalOccursCountUnseparatedSequenceChildParser(
+  childParser: Parser,
+  override val ocParser: Parser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData)
+  extends RepOrderedExactlyNUnseparatedSequenceChildParser(childParser,
+    srd, erd,
+    { val ignored = 0; ignored },
+    "ExactlyTotalOccursCount")
+  with OccursCountExpressionLoopStateMixin {
+
+  override val childProcessors = Seq(ocParser, childParser)
+
+  override def loopState(pstate: PState) = super.loopState(pstate)
+}
+
+class RepOrderedWithMinMaxUnseparatedSequenceChildParser(
+  childParser: Parser,
+  srd: SequenceRuntimeData,
+  erd: ElementRuntimeData,
+  baseName: String,
+  min: Long = -1,
+  max: Long = -1) // pass -2 to force unbounded behavior
+  extends RepUnseparatedParser(
+    childParser,
+    min, max,
+    srd, erd, baseName)
+  with OccursCountMinMaxLoopStateMixin
+
+class OrderedUnseparatedSequenceParser(rd: SequenceRuntimeData, childParsersArg: Seq[SequenceChildParser])
+  extends OrderedSequenceParserBase(rd, childParsersArg) {
+
+  import SequenceChildParser._
+
+  /**
+   * Parses one iteration of an array/optional element, and returns
+   * * MaybeBoolean.One(true) - indicates the child parse was zero length
+   * * MaybeBoolean.One(false) - indicates the child parse was not zero length or failed
+   * * MaybeBoolean.Nope - indicates that the array loop should terminate due to discriminator failure. in which case the pstate will indicate failure.
+   */
+  protected def parseOne(
+    parserArg: SequenceChildParser,
+    trd: TermRuntimeData,
+    pstate: PState,
+    priorState: PState.Mark,
+    maybeStartState: Maybe[PState.Mark],
+    ais: GoArrayIndexStatus): ParseAttemptStatus = {
+
+    val parser = parserArg.asInstanceOf[UnseparatedChildParser]
+
+    val isFixedOccurs = maybeStartState.isEmpty
+    val isVariableOccurs = !isFixedOccurs
+
+    if (isVariableOccurs) {
+      pstate.pushDiscriminator
+    }
+
+    if (pstate.dataProc.isDefined) pstate.dataProc.get.beforeRepetition(pstate, this)
+
+    parser.parse1(pstate)
+
+    if (pstate.dataProc.isDefined) pstate.dataProc.get.afterRepetition(pstate, this)
+
+    val childSuccessful = pstate.processorStatus eq Success
+
+    val res: ParseAttemptStatus = {
+      if (!childSuccessful)
+        processFailedChildParseResults(pstate, priorState, maybeStartState)
+      else
+        ParseAttemptStatus.Success_LengthUndetermined
+
+    }
+    if (isVariableOccurs) {
+      pstate.popDiscriminator
+    }
+    res
+  }
+
+  //
+  // TODO: It would be good to get rid of this downcast
+  // leaving it for now, as the split into Separated/Unseparated might get
+  // recombined if upon implementing the separated interations it seems
+  // like they don't need to be independent code.
+  //
+  private val children = childParsersArg.map { _.asInstanceOf[UnseparatedChildParser] }
+
+  protected def parse(pstate: PState): Unit = {
+
+    var scpIndex = 0
+    pstate.mpstate.groupIndexStack.push(1L) // one-based indexing
+
+    val limit = children.length
+
+    while ((scpIndex < limit) && (pstate.processorStatus eq Success)) {
+      val child = children(scpIndex)
+      child match {
+        case parser: RepUnseparatedChildParser => {
+
+          val loopState = parser.loopState(pstate)
+
+          // push new array context for array/optional
+          parser.startArray(pstate)
+
+          var ais: ArrayIndexStatus = null
+
+          var resultOfTry: ParseAttemptStatus = ParseAttemptStatus.Uninitialized
+
+          val maybeStartState =
+            if (parser.erd.maxOccurs != parser.erd.minOccurs)
+              Maybe(pstate.mark("startState in OrderedUnseparatedSequenceParser"))
+            else
+              Maybe.Nope
+
+          while ({
+            ais = loopState.arrayIndexStatus(parser, pstate, resultOfTry)
+            ais.isInstanceOf[GoArrayIndexStatus]
+          }) {
+
+            resultOfTry = tryParseDetectMarkLeaks(parser, pstate, maybeStartState, ais.asInstanceOf[GoArrayIndexStatus])
+
+            loopState.nextArrayIndex(pstate)
+          } // end while for each repeat
+
+          parser.endArray(pstate)
+        } // end match case RepParser
+
+        case scalarParser => {
+          tryParseDetectMarkLeaks(scalarParser, pstate, Maybe.Nope, ArrayIndexStatus.Required)
+        } // end match case scalar parser
+      } // end match
+      scpIndex += 1
+    } // end while for each sequence child parser
+
+    pstate.mpstate.groupIndexStack.pop()
+    pstate.mpstate.moveOverOneGroupIndexOnly()
+    ()
+  }
+}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/RepUnparsers.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/RepUnparsers.scala
index 3effbd577..1e4c789a5 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/RepUnparsers.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/RepUnparsers.scala
@@ -14,146 +14,146 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package org.apache.daffodil.processors.unparsers
-
-import org.apache.daffodil.exceptions.Assert
-import org.apache.daffodil.processors.ElementRuntimeData
-import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
-import org.apache.daffodil.processors.TermRuntimeData
-
-abstract class RepUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData, baseName: String)
-  extends CombinatorUnparser(context) {
-
-  override lazy val childProcessors = Seq(rUnparser)
-
-  val intN = n.toInt
-
-  def checkN(ustate: UState, n: Long) {
-    if (n > ustate.tunable.maxOccursBounds) {
-      // TODO: how can we go after bigger than max int bytes? We have 64-bit computers
-      // after all....
-      UE(ustate, "Occurs count %s exceeds implementation maximum of %s.", n, ustate.tunable.maxOccursBounds)
-    }
-  }
-
-  final def unparse(ustate: UState): Unit = {
-    checkN(ustate, n)
-    unparseAllRepeats(ustate)
-  }
-
-  protected def unparseAllRepeats(ustate: UState): Unit
-
-  override def toString = "Rep" + baseName + "(" + rUnparser.toString + ")"
-
-  override def toBriefXML(depthLimit: Int = -1): String = {
-    if (depthLimit == 0) "..." else
-      "<Rep" + baseName + ">" + rUnparser.toBriefXML(depthLimit - 1) +
-        "</Rep" + baseName + ">"
-  }
-}
-
-/**
- * This object is so that we can share the iteration idioms between situations
- * where we know N statically, and where dynamic evaluation computes N.
- *
- * In these cases, there are no new points of uncertainty because computed or
- * otherwise, we know N.
- */
-object Rep {
-  def loopExactlyTotalN(intN: Int, rUnparser: Unparser, ustate: UState, context: TermRuntimeData, iParser: Unparser): Unit = {
-    while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
-      // Debugger.beforeRepetition(ustate, iParser)
-      rUnparser.unparse1(ustate)
-      // Debugger.afterRepetition(ustate, iParser)
-      ustate.moveOverOneArrayIndexOnly
-    }
-  }
-}
-
-class RepExactlyNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
-  extends RepUnparser(n, rUnparser, context, "ExactlyN") {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparseAllRepeats(ustate: UState) {
-    1 to intN foreach { _ =>
-      {
-        // Debugger.beforeRepetition(pResult, this)
-        rUnparser.unparse1(ustate)
-        // Debugger.afterRepetition(pResult, this)
-        ustate.moveOverOneArrayIndexOnly
-      }
-    }
-  }
-}
-
-class RepAtMostTotalNUnparser(n: Long, rUnparser: Unparser, erd: ElementRuntimeData)
-  extends RepUnparser(n, rUnparser, erd, "AtMostTotalN") {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparseAllRepeats(ustate: UState): Unit = {
-    while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
-      // Debugger.beforeRepetition(ustate, this)
-      rUnparser.unparse1(ustate)
-      if (ustate.isInspectArrayEnd) return
-      // Debugger.afterRepetition(ustate, this)
-      ustate.moveOverOneArrayIndexOnly
-    }
-  }
-}
-
-class RepExactlyTotalNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
-  extends RepUnparser(n, rUnparser, context, "ExactlyTotalN") {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparseAllRepeats(ustate: UState) {
-    Rep.loopExactlyTotalN(intN, rUnparser, ustate, context, this)
-  }
-}
-
-/**
- * This is a bit tricky: Since we are streaming the elements to the unparser,
- * it will receive a DIArray element, but the length of that DIArray element at the
- * time it is received, will probably be 0 because the child elements (not even
- * one of them) will have been pulled yet. So we cannot just measure how big the array
- * is and iterate that many times. Rather, we have to iterate until we come to
- * an End(DIArray) event.
- *
- * This requires the ability to look ahead into the input stream by 1, see if it
- * is EndArray, and if so consume it and end the iteration.
- */
-class RepUnboundedUnparser(occursCountKind: OccursCountKind.Value, rUnparser: Unparser, erd: ElementRuntimeData)
-  extends RepUnparser(-1, rUnparser, erd, "Unbounded") {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparseAllRepeats(ustate: UState) {
-    Assert.invariant(ustate.currentInfosetNodeMaybe.isDefined)
-    while (!ustate.isInspectArrayEnd) {
-      // Debugger.beforeRepetition(ustate, this)
-      rUnparser.unparse1(ustate)
-      // Debugger.afterRepetition(ustate, this)
-      ustate.moveOverOneArrayIndexOnly
-    }
-  }
-}
-
-//
-// Unparsers don't evaluate or use expression values. The number of occurrences is the number
-// in the infoset (augmented up to minOccurs with default values when appropriate)
-//
-
-class RepAtMostOccursCountUnparser(rUnparser: Unparser, intN: Long, erd: ElementRuntimeData)
-  extends RepUnparser(intN, rUnparser, erd, "AtMostOccursCount") {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparseAllRepeats(ustate: UState) {
-    // repeat either n times, or occursCount times if that's less than n.
-    // val n = math.min(ustate.occursBounds, erd.minOccurs.get)
-    Rep.loopExactlyTotalN(intN.toInt, rUnparser, ustate, erd, this)
-  }
-}
+//
+//package org.apache.daffodil.processors.unparsers
+//
+//import org.apache.daffodil.exceptions.Assert
+//import org.apache.daffodil.processors.ElementRuntimeData
+//import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind
+//import org.apache.daffodil.processors.TermRuntimeData
+//
+//abstract class RepUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData, baseName: String)
+//  extends CombinatorUnparser(context) {
+//
+//  override lazy val childProcessors = Seq(rUnparser)
+//
+//  val intN = n.toInt
+//
+//  def checkN(ustate: UState, n: Long) {
+//    if (n > ustate.tunable.maxOccursBounds) {
+//      // TODO: how can we go after bigger than max int bytes? We have 64-bit computers
+//      // after all....
+//      UE(ustate, "Occurs count %s exceeds implementation maximum of %s.", n, ustate.tunable.maxOccursBounds)
+//    }
+//  }
+//
+//  final def unparse(ustate: UState): Unit = {
+//    checkN(ustate, n)
+//    unparseAllRepeats(ustate)
+//  }
+//
+//  protected def unparseAllRepeats(ustate: UState): Unit
+//
+//  override def toString = "Rep" + baseName + "(" + rUnparser.toString + ")"
+//
+//  override def toBriefXML(depthLimit: Int = -1): String = {
+//    if (depthLimit == 0) "..." else
+//      "<Rep" + baseName + ">" + rUnparser.toBriefXML(depthLimit - 1) +
+//        "</Rep" + baseName + ">"
+//  }
+//}
+//
+///**
+// * This object is so that we can share the iteration idioms between situations
+// * where we know N statically, and where dynamic evaluation computes N.
+// *
+// * In these cases, there are no new points of uncertainty because computed or
+// * otherwise, we know N.
+// */
+//object Rep {
+//  def loopExactlyTotalN(intN: Int, rUnparser: Unparser, ustate: UState, context: TermRuntimeData, iParser: Unparser): Unit = {
+//    while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
+//      // Debugger.beforeRepetition(ustate, iParser)
+//      rUnparser.unparse1(ustate)
+//      // Debugger.afterRepetition(ustate, iParser)
+//      ustate.moveOverOneArrayIndexOnly
+//    }
+//  }
+//}
+//
+//class RepExactlyNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
+//  extends RepUnparser(n, rUnparser, context, "ExactlyN") {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparseAllRepeats(ustate: UState) {
+//    1 to intN foreach { _ =>
+//      {
+//        // Debugger.beforeRepetition(pResult, this)
+//        rUnparser.unparse1(ustate)
+//        // Debugger.afterRepetition(pResult, this)
+//        ustate.moveOverOneArrayIndexOnly
+//      }
+//    }
+//  }
+//}
+//
+//class RepAtMostTotalNUnparser(n: Long, rUnparser: Unparser, erd: ElementRuntimeData)
+//  extends RepUnparser(n, rUnparser, erd, "AtMostTotalN") {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparseAllRepeats(ustate: UState): Unit = {
+//    while (ustate.arrayPos <= intN && !ustate.isInspectArrayEnd) {
+//      // Debugger.beforeRepetition(ustate, this)
+//      rUnparser.unparse1(ustate)
+//      if (ustate.isInspectArrayEnd) return
+//      // Debugger.afterRepetition(ustate, this)
+//      ustate.moveOverOneArrayIndexOnly
+//    }
+//  }
+//}
+//
+//class RepExactlyTotalNUnparser(n: Long, rUnparser: Unparser, context: ElementRuntimeData)
+//  extends RepUnparser(n, rUnparser, context, "ExactlyTotalN") {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparseAllRepeats(ustate: UState) {
+//    Rep.loopExactlyTotalN(intN, rUnparser, ustate, context, this)
+//  }
+//}
+//
+///**
+// * This is a bit tricky: Since we are streaming the elements to the unparser,
+// * it will receive a DIArray element, but the length of that DIArray element at the
+// * time it is received, will probably be 0 because the child elements (not even
+// * one of them) will have been pulled yet. So we cannot just measure how big the array
+// * is and iterate that many times. Rather, we have to iterate until we come to
+// * an End(DIArray) event.
+// *
+// * This requires the ability to look ahead into the input stream by 1, see if it
+// * is EndArray, and if so consume it and end the iteration.
+// */
+//class RepUnboundedUnparser(occursCountKind: OccursCountKind.Value, rUnparser: Unparser, erd: ElementRuntimeData)
+//  extends RepUnparser(-1, rUnparser, erd, "Unbounded") {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparseAllRepeats(ustate: UState) {
+//    Assert.invariant(ustate.currentInfosetNodeMaybe.isDefined)
+//    while (!ustate.isInspectArrayEnd) {
+//      // Debugger.beforeRepetition(ustate, this)
+//      rUnparser.unparse1(ustate)
+//      // Debugger.afterRepetition(ustate, this)
+//      ustate.moveOverOneArrayIndexOnly
+//    }
+//  }
+//}
+//
+////
+//// Unparsers don't evaluate or use expression values. The number of occurrences is the number
+//// in the infoset (augmented up to minOccurs with default values when appropriate)
+////
+//
+//class RepAtMostOccursCountUnparser(rUnparser: Unparser, intN: Long, erd: ElementRuntimeData)
+//  extends RepUnparser(intN, rUnparser, erd, "AtMostOccursCount") {
+//
+//  override lazy val runtimeDependencies = Nil
+//
+//  def unparseAllRepeats(ustate: UState) {
+//    // repeat either n times, or occursCount times if that's less than n.
+//    // val n = math.min(ustate.occursBounds, erd.minOccurs.get)
+//    Rep.loopExactlyTotalN(intN.toInt, rUnparser, ustate, erd, this)
+//  }
+//}
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UnparseError.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UnparseError.scala
index 52cd22801..04e0ebaa4 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UnparseError.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UnparseError.scala
@@ -23,6 +23,7 @@ import org.apache.daffodil.util.Maybe._
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe._
 import org.apache.daffodil.exceptions.SchemaFileLocation
+import org.apache.daffodil.exceptions.Assert
 
 class UnparseAlternativeFailed(rd: TermRuntimeData, loc: DataLocation, val errors: Seq[Diagnostic])
   extends UnparseError(One(rd.schemaFileLocation), One(loc), Maybe.Nope, Maybe("Alternative failed. Reason(s): %s"), errors)
@@ -30,7 +31,7 @@ class UnparseAlternativeFailed(rd: TermRuntimeData, loc: DataLocation, val error
 object UnparseError {
   def apply(rd: Maybe[SchemaFileLocation], loc: Maybe[DataLocation], formatString: String, args: Any*) = {
     val ue = new UnparseError(rd, loc, Maybe.Nope, Maybe(formatString), args: _*)
-    throw ue
+    Assert.toss(ue)
   }
 }
 
diff --git a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/Unparser.scala b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/Unparser.scala
index 9442a67e2..ce7e29ef6 100644
--- a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/Unparser.scala
+++ b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/Unparser.scala
@@ -140,25 +140,6 @@ trait SuspendableUnparser
   }
 }
 
-// No-op, in case an optimization lets one of these sneak thru.
-// TODO: make this fail, and test optimizer sufficiently to know these
-// do NOT get through.
-final class EmptyGramUnparser(override val context: TermRuntimeData = null) extends PrimUnparserNoData {
-
-  override lazy val runtimeDependencies = Nil
-
-  def unparse(ustate: UState) {
-    Assert.invariantFailed("EmptyGramUnparsers are all supposed to optimize out!")
-  }
-
-  override def isEmpty = true
-
-  override lazy val childProcessors = Nil
-
-  override def toBriefXML(depthLimit: Int = -1) = "<empty/>"
-  override def toString = toBriefXML()
-}
-
 final class ErrorUnparser(override val context: TermRuntimeData = null) extends PrimUnparserNoData {
 
   override lazy val runtimeDependencies = Nil
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/alignment_bytes_12.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/alignment_bytes_12.dfdl.xsd
index bf2224925..fe474ef88 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/alignment_bytes_12.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/alignment_bytes_12.dfdl.xsd
@@ -31,7 +31,7 @@
 					alignment="1" alignmentUnits="bytes" fillByte="f" leadingSkip="0" trailingSkip="0"
 					ignoreCase="no" lengthKind="implicit" sequenceKind="ordered"
 					initiatedContent="no"	textPadKind="none" textTrimKind="none" textStringJustification="left"
-					occursCountKind="implicit"	
+					occursCountKind="implicit" separatorSuppressionPolicy="trailingEmpty"
                     bitOrder="mostSignificantBitFirst"
 					/>
 				
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpabol101.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpabol101.dfdl.xsd
index 7f9712e60..21ec46b35 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpabol101.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpabol101.dfdl.xsd
@@ -26,7 +26,7 @@
 			<dfdl:format initiator="" terminator="" leadingSkip="0" trailingSkip="0" truncateSpecifiedLengthString="no"  separator="" textBidi="no" floating="no"   ref="booleanText" escapeSchemeRef=""
 				encodingErrorPolicy="replace" encoding="utf-8" byteOrder="bigEndian" lengthUnits="characters"
 				textOutputMinLength="1" alignment="1" alignmentUnits="bytes"
-				fillByte="f"  occursCountKind="implicit" ignoreCase="no"
+				fillByte="f"  occursCountKind="implicit" ignoreCase="no" separatorSuppressionPolicy="trailingEmpty"
 				lengthKind="delimited" sequenceKind="ordered" initiatedContent="no" />
 
 		</xs:appinfo>
@@ -135,4 +135,4 @@
 	
 	
 
-</xs:schema>
\ No newline at end of file
+</xs:schema>
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaflsaln101_01.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaflsaln101_01.dfdl.xsd
index da2cfdf32..cf8adb062 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaflsaln101_01.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaflsaln101_01.dfdl.xsd
@@ -29,7 +29,7 @@
 	<xs:annotation>
 		<xs:appinfo source="http://www.ogf.org/dfdl/">
 			<dfdl:format representation="text" 
-            initiator="" terminator="" leadingSkip="0" trailingSkip="0" 
+      initiator="" terminator="" leadingSkip="0" trailingSkip="0" separatorSuppressionPolicy="trailingEmpty"
 			truncateSpecifiedLengthString="no"  separator="" textBidi="no" floating="no"   
 			ref="rdefFormat" encoding="ascii" byteOrder="bigEndian" bitOrder="mostSignificantBitFirst"
 			ignoreCase="yes" textStringJustification="left" occursCountKind="implicit"
@@ -142,4 +142,4 @@
 			</xs:sequence>
 		</xs:complexType>
 	</xs:element>
-</xs:schema>
\ No newline at end of file
+</xs:schema>
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc121_01.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc121_01.dfdl.xsd
index 16206cd32..6a145acb4 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc121_01.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc121_01.dfdl.xsd
@@ -30,7 +30,7 @@
 		
 		<xs:appinfo source="http://www.ogf.org/dfdl/">
 			<dfdl:format initiator="" terminator="" leadingSkip="0" trailingSkip="0" truncateSpecifiedLengthString="no"  separator="" textBidi="no" floating="no"    encodingErrorPolicy="replace" encoding="utf-8" 
-				lengthUnits="bytes" lengthKind="implicit"
+				lengthUnits="bytes" lengthKind="implicit" separatorSuppressionPolicy="trailingEmpty"
 				initiatedContent="no" sequenceKind="ordered" 
 				alignment="1" alignmentUnits="bytes" fillByte="f" occursCountKind="implicit"
 				bitOrder="mostSignificantBitFirst" representation="binary"
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_01.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_01.dfdl.xsd
index 1af10b609..e1d2674a5 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_01.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_01.dfdl.xsd
@@ -33,7 +33,7 @@
 	<xs:annotation>
 		<xs:appinfo source="http://www.ogf.org/dfdl/">
 			<dfdl:format initiator="" terminator="" leadingSkip="0" trailingSkip="0" 
-				truncateSpecifiedLengthString="no"  separator=""  
+				truncateSpecifiedLengthString="no"  separator="" separatorSuppressionPolicy="trailingEmpty"
 				textBidi="no" floating="no"   encodingErrorPolicy="replace" encoding="ASCII" representation="text"
 				byteOrder="bigEndian" bitOrder="mostSignificantBitFirst" initiatedContent="no" sequenceKind="ordered"
 				ignoreCase="no" textPadKind="none" textTrimKind="none"
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_02.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_02.dfdl.xsd
index e26cb24ce..ad226be2c 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_02.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/dpaspc83_02.dfdl.xsd
@@ -28,7 +28,7 @@
 	<xs:annotation>
 		<xs:appinfo source="http://www.ogf.org/dfdl/">
 			<dfdl:format initiator="" terminator="" leadingSkip="0" trailingSkip="0" 
-				truncateSpecifiedLengthString="no"  separator=""  
+				truncateSpecifiedLengthString="no"  separator=""  separatorSuppressionPolicy="trailingEmpty"
 				textBidi="no" floating="no"   encodingErrorPolicy="replace" 
 				encoding="ASCII" representation="text"
 				byteOrder="bigEndian" bitOrder="mostSignificantBitFirst" 
diff --git a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/syntax_entities_6.dfdl.xsd b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/syntax_entities_6.dfdl.xsd
index d70eb87ca..d63fa83fc 100644
--- a/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/syntax_entities_6.dfdl.xsd
+++ b/daffodil-test-ibm1/src/test/resources/test-suite/ibm-contributed/syntax_entities_6.dfdl.xsd
@@ -36,7 +36,7 @@
 		<xs:appinfo source="http://www.ogf.org/dfdl/">
 			<dfdl:defineFormat name="rdefFormat">
 				<dfdl:format initiatedContent="no" initiator="" terminator="" leadingSkip="0" trailingSkip="0" separator="" textBidi="no" floating="no"   encoding="ascii" byteOrder="bigEndian" bitOrder="mostSignificantBitFirst"
-					ignoreCase="no" outputNewLine="%LF;"
+					ignoreCase="no" outputNewLine="%LF;" separatorSuppressionPolicy="trailingEmpty"
 					truncateSpecifiedLengthString="no" decimalSigned="yes" alignment="1"
 					alignmentUnits="bytes" fillByte="1" occursCountKind="implicit" escapeSchemeRef=""
 					representation="text" lengthUnits="characters" lengthKind="explicit" 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section00/general/testUnparserBitOrderOVC.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section00/general/testUnparserBitOrderOVC.tdml
index 20d069ee6..62a86a73c 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section00/general/testUnparserBitOrderOVC.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section00/general/testUnparserBitOrderOVC.tdml
@@ -34,7 +34,8 @@
   
   <tdml:defineSchema name="s1" elementFormDefault="unqualified">
 
-    <dfdl:format textPadKind='none' lengthKind="explicit" lengthUnits="bits" alignmentUnits="bits" alignment="1"
+    <dfdl:format ref="ex:GeneralFormat"
+      textPadKind='none' lengthKind="explicit" lengthUnits="bits" alignmentUnits="bits" alignment="1"
       fillByte="%#rFF;" representation="binary" binaryNumberRep="binary"
       leadingSkip="0" trailingSkip="0" initiator="" terminator="" separator="" 
       ignoreCase="yes" sequenceKind="ordered" encoding="ascii" initiatedContent="no"
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section02/validation_errors/Validation.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section02/validation_errors/Validation.tdml
index f07dba258..a20fb216a 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section02/validation_errors/Validation.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section02/validation_errors/Validation.tdml
@@ -1684,7 +1684,7 @@
 
       <!-- Final Error -->
       <tdml:error>Parse Error</tdml:error>
-      <tdml:error>All alternatives failed</tdml:error>
+      <tdml:error>All choice alternatives failed</tdml:error>
       
       <!-- Failure for branch1:e2  -->
       <tdml:error>Parse Error</tdml:error>
@@ -1694,7 +1694,7 @@
       <tdml:error>Parse Error</tdml:error>
       <!-- <tdml:error>Alternative failed. Reason(s): List(Parse Error: Init('C') - branch2: Delimiter not found!</tdml:error> -->
       <tdml:error>branch2</tdml:error>
-      <tdml:error>Delimiter not found</tdml:error>
+      <tdml:error>Initiator 'C' not found</tdml:error>
     </tdml:errors>
 
     <tdml:validationErrors>
@@ -1743,7 +1743,7 @@
 
       <!-- Final Error -->
       <tdml:error>Parse Error</tdml:error>
-      <tdml:error>All alternatives failed</tdml:error>
+      <tdml:error>All choice alternatives failed</tdml:error>
       
       <!-- Failure for branch1:e2  -->
       <tdml:error>Parse Error</tdml:error>
@@ -1752,7 +1752,7 @@
       <!-- Failure for branch2  -->
       <tdml:error>Parse Error</tdml:error>
       <tdml:error>branch2</tdml:error>
-      <tdml:error>Delimiter not found</tdml:error>
+      <tdml:error>Initiator 'C' not found</tdml:error>
     </tdml:errors>
   
   </tdml:parserTestCase>
@@ -1771,7 +1771,7 @@
 
       <!-- Final Error -->
       <tdml:error>Parse Error</tdml:error>
-      <tdml:error>All alternatives failed</tdml:error>
+      <tdml:error>All choice alternatives failed</tdml:error>
       
       <!-- Failure for branch1:e2  -->
       <tdml:error>Parse Error</tdml:error>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section05/facets/Facets.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section05/facets/Facets.tdml
index ce972b355..15bd89b2b 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section05/facets/Facets.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section05/facets/Facets.tdml
@@ -5288,7 +5288,7 @@
     model="enumSchema" description="Section 5 - Facets - enumeration - DFDL-5-054R">
     <tdml:document>crow</tdml:document>
       <tdml:errors>
-        <tdml:error>All alternatives failed</tdml:error>
+        <tdml:error>All choice alternatives failed</tdml:error>
       </tdml:errors>
   </tdml:parserTestCase>
   
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section07/discriminators/discriminator.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section07/discriminators/discriminator.tdml
index b26670016..d4b4fbeb4 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section07/discriminators/discriminator.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section07/discriminators/discriminator.tdml
@@ -466,7 +466,7 @@
     <tdml:document><![CDATA[1234]]></tdml:document>
     <tdml:errors>
       <tdml:error>Parse Error</tdml:error>
-      <tdml:error>All Alternatives Failed</tdml:error>
+      <tdml:error>All Choice Alternatives Failed</tdml:error>
       <tdml:error>Insufficient</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
index 563456622..0725c1381 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section07/escapeScheme/escapeScenarios.tdml
@@ -76,7 +76,7 @@
       <dfdlInfoset>
         <e_infix>
           <x>foo</x>
-	  <y>bar</y>
+	      <y>bar</y>
 	</e_infix>
       </dfdlInfoset>
     </infoset>
@@ -150,8 +150,8 @@
       <dfdlInfoset>
         <e_infix>
           <x>foo</x>
-	  <y></y>
-	</e_infix>
+          <!-- should not be a element y with emptystring contents here since y is optional. -->
+	   </e_infix>
       </dfdlInfoset>
     </infoset>
   </parserTestCase>
@@ -576,6 +576,7 @@
     <document>foo$$$//$;</document>
     <errors>
       <error>Parse Error</error>
+      <error>Terminator '$;' not found</error>
     </errors>
   </parserTestCase>
 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section14/occursCountKind/ockImplicit.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section14/occursCountKind/ockImplicit.tdml
index af19cfacd..e24e8591d 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section14/occursCountKind/ockImplicit.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section14/occursCountKind/ockImplicit.tdml
@@ -575,7 +575,7 @@
       <tdml:error>Failed to populate</tdml:error>
       <tdml:error>ex:a[2]</tdml:error>
       <tdml:error>Expected 2 item(s)</tdml:error>
-      <tdml:error>Delimiter not found</tdml:error>
+      <tdml:error>Separator ',' not found</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
   
@@ -610,7 +610,7 @@
       <tdml:error>Failed to populate</tdml:error>
       <tdml:error>ex:a[2]</tdml:error>
       <tdml:error>Expected 2 item(s)</tdml:error>
-      <tdml:error>Delimiter not found</tdml:error>
+      <tdml:error>Initiator ')' not found</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
 
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
index 7d3da3c4d..b2658887c 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section15/choice_groups/choice.tdml
@@ -301,7 +301,7 @@
       <tdml:error>float</tdml:error>
       <tdml:error>int</tdml:error>
       <tdml:error>Alternative failed</tdml:error>
-      <tdml:error>All alternatives failed</tdml:error>
+      <tdml:error>All choice alternatives failed</tdml:error>
 
     </tdml:errors>
 
@@ -1913,7 +1913,7 @@ it sure is/
     <tdml:document><![CDATA[1;5;foo]]></tdml:document>
     <tdml:errors>
       <tdml:error>Parse Error</tdml:error>
-      <tdml:error>All alternatives failed</tdml:error>
+      <tdml:error>All choice alternatives failed</tdml:error>
       <tdml:error>Choice dispatch branch failed</tdml:error>
     </tdml:errors>
   </tdml:parserTestCase>
diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/ArrayOptionalElem.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/ArrayOptionalElem.tdml
index 3bda61aa7..efcb729a1 100644
--- a/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/ArrayOptionalElem.tdml
+++ b/daffodil-test/src/test/resources/org/apache/daffodil/section16/array_optional_elem/ArrayOptionalElem.tdml
@@ -632,4 +632,179 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
+  <tdml:defineSchema name="array-ockImplicitSeparators.dfdl.xsd" elementFormDefault="unqualified">
+
+    <dfdl:format lengthKind="delimited" lengthUnits="bytes"
+      ref="ex:GeneralFormat" encoding="UTF-8" separator="" initiator="" terminator=""
+      occursCountKind="implicit" ignoreCase="no" textNumberRep="standard"
+      representation="text" />
+
+    <xs:element name="te_root">
+      <xs:complexType>
+        <xs:sequence dfdl:separator=":"
+          dfdl:separatorPosition="infix" dfdl:separatorSuppressionPolicy="trailingEmpty"
+          dfdl:sequenceKind="ordered">
+          <xs:element name="a" type="xs:int"/>
+          <xs:element name="b" type="xs:int" minOccurs="0" />
+          <xs:element name="c" type="xs:int" minOccurs="0" />
+          <xs:element name="d" type="xs:int" minOccurs="0" />
+          <xs:element name="e" type="xs:int" minOccurs="0" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="ts_root">
+      <xs:complexType>
+        <xs:sequence dfdl:separator=":"
+          dfdl:separatorPosition="infix" dfdl:separatorSuppressionPolicy="trailingEmptyStrict"
+          dfdl:sequenceKind="ordered">
+          <xs:element name="a" type="xs:int"/>
+          <xs:element name="b" type="xs:int" minOccurs="0" />
+          <xs:element name="c" type="xs:int" minOccurs="0" />
+          <xs:element name="d" type="xs:int" minOccurs="0" />
+          <xs:element name="e" type="xs:int" minOccurs="0" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="occursCountKindImplicitSeparators01" root="te_root"
+    model="array-ockImplicitSeparators.dfdl.xsd"
+    roundTrip="false">
+
+    <tdml:document><![CDATA[1::3::5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:te_root>
+          <a>1</a>
+          <c>3</c>
+          <e>5</e>
+        </ex:te_root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="occursCountKindImplicitSeparators02" root="te_root"
+    model="array-ockImplicitSeparators.dfdl.xsd"
+    roundTrip="false">
+
+    <tdml:document><![CDATA[:2:3::5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:te_root>
+          <b>2</b>
+          <c>3</c>
+          <e>5</e>
+        </ex:te_root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="occursCountKindImplicitSeparators03" root="te_root"
+    model="array-ockImplicitSeparators.dfdl.xsd"
+    roundTrip="true">
+
+    <tdml:document><![CDATA[1:2:3::]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:te_root>
+          <a>1</a>
+          <b>2</b>
+          <c>3</c>
+        </ex:te_root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:unparserTestCase name="occursCountKindImplicitSeparatorsUnparser" root="te_root"
+    model="array-ockImplicitSeparators.dfdl.xsd"
+    roundTrip="false">
+
+    <tdml:document><![CDATA[1:3:5]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:te_root>
+          <a>1</a>
+          <c>3</c>
+          <e>5</e>
+        </ex:te_root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:unparserTestCase>
+
+  <tdml:parserTestCase name="occursCountKindImplicitSeparators04" root="ts_root"
+    model="array-ockImplicitSeparators.dfdl.xsd"
+    roundTrip="true">
+
+    <tdml:document><![CDATA[1:2:3::]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:ts_root>
+          <a>1</a>
+          <b>2</b>
+          <c>3</c>
+        </ex:ts_root>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+
+  <tdml:defineSchema name="nestedSequence" elementFormDefault="unqualified">
+
+    <dfdl:format lengthKind="delimited" lengthUnits="bytes"
+      ref="ex:GeneralFormat" encoding="UTF-8" separator="" initiator="" terminator=""
+      occursCountKind="implicit" ignoreCase="no" textNumberRep="standard"
+      representation="text" />
+
+    <dfdl:defineFormat name="listFormat">
+      <dfdl:format separator="||%NL;" separatorPosition="postfix" separatorSuppressionPolicy="trailingEmptyStrict"/>
+    </dfdl:defineFormat>
+
+    <dfdl:defineFormat name="parameters">
+      <dfdl:format sequenceKind="ordered" separator="|" separatorPosition="prefix" separatorSuppressionPolicy="trailingEmptyStrict"/>
+    </dfdl:defineFormat>
+
+    <xs:complexType name="listObject">
+      <xs:sequence dfdl:ref="parameters">
+        <xs:element name="param1" type="xs:string"/>
+        <xs:element name="param2" type="xs:string"/>
+        <xs:element name="param3" type="xs:string" minOccurs="0" maxOccurs="4" dfdl:occursCountKind="implicit"/>
+      </xs:sequence>
+    </xs:complexType>
+
+    <xs:element name="nestedRoot">
+      <xs:complexType>
+        <xs:sequence dfdl:ref="listFormat">
+          <xs:element name="list1" type="listObject" dfdl:initiator="BEGIN"/>
+          <xs:element name="list2" type="listObject" dfdl:initiator="BEGIN"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+  </tdml:defineSchema>
+
+  <tdml:parserTestCase name="occursCountKindImplicitSeparators05" root="nestedRoot"
+    model="nestedSequence"
+    roundTrip="true">
+
+    <tdml:document><![CDATA[BEGIN|this|is||
+BEGIN|of|nested||
+]]></tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <ex:nestedRoot>
+          <list1>
+            <param1>this</param1>
+            <param2>is</param2>
+          </list1>
+          <list2>
+            <param1>of</param1>
+            <param2>nested</param2>
+          </list2>
+        </ex:nestedRoot>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
 </tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestArrayOptionalElem.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestArrayOptionalElem.scala
index b2ec15725..315fa1c46 100644
--- a/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestArrayOptionalElem.scala
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/section16/array_optional_elem/TestArrayOptionalElem.scala
@@ -70,4 +70,11 @@ class TestArrayOptionalElem {
   @Test def test_arrays_16_01() { runner1.runOneTest("arrays_16_01") }
 
   @Test def test_backtrack1Text() = { rBack.runOneTest("backtrack1Text") }
+
+  @Test def test_occursCountKindImplicitSeparators01() { runner.runOneTest("occursCountKindImplicitSeparators01") }
+  @Test def test_occursCountKindImplicitSeparators02() { runner.runOneTest("occursCountKindImplicitSeparators02") }
+  @Test def test_occursCountKindImplicitSeparators03() { runner.runOneTest("occursCountKindImplicitSeparators03") }
+  @Test def test_occursCountKindImplicitSeparators04() { runner.runOneTest("occursCountKindImplicitSeparators04") }
+  @Test def test_occursCountKindImplicitSeparators05() { runner.trace.runOneTest("occursCountKindImplicitSeparators05") }
+  @Test def test_occursCountKindImplicitSeparatorsUnparser() { runner.runOneTest("occursCountKindImplicitSeparatorsUnparser") }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services