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

jadams pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new dd49907  Restructured the Variables API and implemented 
newVariableInstance
dd49907 is described below

commit dd499070049b91fb8d859ac8d44fd0be088c985c
Author: Josh Adams <[email protected]>
AuthorDate: Mon Apr 13 09:34:26 2020 -0400

    Restructured the Variables API and implemented newVariableInstance
    
    Primary changes are located within VariableMap, which now is a map of
    name->Stack[Variable] instead of name->List(List(Variable)). Also
    necessitated some housekeeping code in the PState for to handle
    instances of backtracking after modifying a Variable.
    
    DAFFODIL-341
---
 .../apache/daffodil/dsom/DFDLDefineVariable.scala  |  72 ++-
 .../apache/daffodil/dsom/DFDLStatementMixin.scala  |  31 +-
 .../scala/org/apache/daffodil/dsom/GroupDef.scala  |   2 +-
 .../grammar/HasStatementsGrammarMixin.scala        |  18 +-
 .../daffodil/grammar/ModelGroupGrammarMixin.scala  |   6 +-
 .../apache/daffodil/grammar/TermGrammarMixin.scala |   4 +-
 .../org/apache/daffodil/grammar/VariableMap.scala  |   4 +-
 .../grammar/primitives/PrimitivesExpressions.scala |   8 +-
 .../daffodil/dsom/TestExternalVariablesNew.scala   |   2 +-
 .../externalvars/TestExternalVariablesLoader.scala |  12 +-
 .../scala/org/apache/daffodil/util/MStack.scala    |   5 +-
 .../unparsers/ExpressionEvaluatingUnparsers.scala  |  15 +-
 .../org/apache/daffodil/dpath/DPathRuntime.scala   |   8 +-
 .../externalvars/ExternalVariablesLoader.scala     |   4 +-
 .../apache/daffodil/processors/DataProcessor.scala |  48 +-
 .../apache/daffodil/processors/RuntimeData.scala   |   4 +-
 .../apache/daffodil/processors/VariableMap1.scala  | 301 +++++++------
 .../parsers/ExpressionEvaluatingParsers.scala      |  16 +-
 .../parsers/InitiatedContentParsers.scala          |   4 +-
 .../daffodil/processors/parsers/PState.scala       |  48 +-
 .../daffodil/processors/parsers/Parser.scala       |   4 +-
 .../processors/parsers/SequenceParserBases.scala   |   4 +-
 .../daffodil/processors/unparsers/UState.scala     |  16 +-
 .../daffodil/section07/variables/variables.tdml    | 493 ++++++++++++++++++++-
 .../daffodil/section07/variables/variables_01.tdml |   7 +-
 .../section07/variables/TestVariables.scala        |  11 +
 26 files changed, 880 insertions(+), 267 deletions(-)

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 3a7d713..8931995 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
@@ -27,7 +27,7 @@ import org.apache.daffodil.xml.GlobalQName
 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.{ SetVariable, 
NewVariableInstanceStart, NewVariableInstanceEnd }
 import org.apache.daffodil.schema.annotation.props.Found
 import org.apache.daffodil.exceptions.Assert
 
@@ -100,7 +100,7 @@ abstract class VariableReference(node: Node, decl: 
AnnotatedSchemaComponent)
   final lazy val ref = getAttributeRequired("ref")
   final lazy val varQName = resolveQName(ref)
 
-  final def variableRuntimeData = defv.runtimeData
+  def variableRuntimeData = defv.runtimeData
 
   final lazy val defv = decl.schemaSet.getDefineVariable(varQName).getOrElse(
     this.schemaDefinitionError("Variable definition not found: %s", ref))
@@ -109,13 +109,73 @@ abstract class VariableReference(node: Node, decl: 
AnnotatedSchemaComponent)
 final class DFDLNewVariableInstance(node: Node, decl: AnnotatedSchemaComponent)
   extends VariableReference(node, decl) // with 
NewVariableInstance_AnnotationMixin
   {
-  lazy val defaultValue = getAttributeOption("defaultValue")
+  // newVariableInstance only allowed within group ref, sequence, or choice
+  decl match {
+    case gr: GroupRef =>
+    case grl: GroupDefLike =>
+    case _ => decl.SDE("newVariableInstance may only be used on group 
reference, sequence or choice")
+  }
+
+  private lazy val attrValue = getAttributeOption("value")
+
+  private lazy val <dfdl:setVariable>{ eltChildren @ _* }</dfdl:setVariable> = 
node
+
+  private lazy val eltValue = eltChildren.text.trim
+
+  private lazy val defaultValueAsAttribute = getAttributeOption("defaultValue")
+  private lazy val defaultValueAsElement = node.child.text.trim
+
+  final lazy val defaultValue = (defaultValueAsAttribute, 
defaultValueAsElement) match {
+    case (None, "") => defv.defaultValue
+    case (None, str) => Some(str)
+    case (Some(str), "") => Some(str)
+    case (Some(str), v) => schemaDefinitionError("Default value of variable 
was supplied both as attribute and element value: %s", node.toString)
+  }
+
+  final lazy val value = (attrValue, eltValue) match {
+    case (None, v) if (v != "") => v
+    case (Some(v), "") => v
+    case (Some(v), ev) if (ev != "") => decl.SDE("Cannot have both a value 
attribute and an element value: %s", node)
+    case (None, "") => decl.SDE("Must have either a value attribute or an 
element value: %s", node)
+  }
 
-  def gram(term: Term): Gram = Assert.nyi("dfdl:newVariableInstance")
-  def endGram(term: Term): Gram = Assert.nyi("dfdl:newVariableInstance")
+  lazy val maybeDefaultValueExpr = {
+    val compilationTargetType = defv.primType
+    val qn = this.qNameForProperty("defaultValue", XMLUtils.dafintURI)
+    val defaultValExpr = defaultValue.map { e =>
+      ExpressionCompilers.AnyRef.compileProperty(qn, compilationTargetType, 
Found(e, this.dpathCompileInfo, "defaultValue", false), this, dpathCompileInfo)
+    }
+
+    Maybe.toMaybe(defaultValExpr)
+  }
+
+  /* Need to override variableRuntimeData so that defaultValues
+   * are read from newVariableInstance instead of the original
+   * variable definition. Also allows diagnostic messages to
+   * point to this location instead of the original definition
+   */
+  final override lazy val variableRuntimeData = {
+    val vrd = new VariableRuntimeData(
+      this.schemaFileLocation,
+      this.diagnosticDebugName,
+      this.path,
+      this.namespaces,
+      defv.external,
+      maybeDefaultValueExpr,
+      defv.typeQName,
+      defv.namedQName.asInstanceOf[GlobalQName],
+      defv.primType,
+      this.tunable.unqualifiedPathStepPolicy)
+    vrd
+  }
 
-  lazy val newVariableInstance = defv.newVariableInstance
+  final def gram(term: Term) = LV('gram) {
+    NewVariableInstanceStart(decl, this)
+  }.value
 
+  final def endGram(term: Term) = LV('endGram) {
+    NewVariableInstanceEnd(decl, this)
+  }.value
 }
 
 final class DFDLSetVariable(node: Node, decl: AnnotatedSchemaComponent)
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 38fff9c..7393a83 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
@@ -59,8 +59,11 @@ trait ResolvesDFDLStatementMixin
           ReferencedElementInfos.None
       }
       case nv: DFDLNewVariableInstance => {
-        // nv.defaultValueExpr.contentReferencedElementInfos
-        s.notYetImplemented("dfdl:newVariableInstance")
+        val mdv = nv.maybeDefaultValueExpr
+        if (mdv.isDefined)
+          f(mdv.get)
+        else
+          ReferencedElementInfos.None
       }
       case _ => ReferencedElementInfos.None
     }
@@ -177,9 +180,22 @@ trait ProvidesDFDLStatementMixin extends ThrowsSDE with 
HasTermCheck { self: Ann
     checkDistinctVariableNames(combinedSvs)
   }
 
+  private lazy val patternAsserts: Seq[DFDLAssert] = combinedAsserts.filter{ 
st => st.testKind == TestKind.Pattern }
+  private lazy val nonPatternAsserts: Seq[DFDLAssert] = 
combinedAsserts.filter{ st => st.testKind != TestKind.Pattern }
+
+  private lazy val patternDiscrims: Seq[DFDLDiscriminator] = 
combinedDiscrims.filter{ st => st.testKind == TestKind.Pattern }
+  private lazy val nonPatternDiscrims: Seq[DFDLDiscriminator] = 
combinedDiscrims.filter{ st => st.testKind != TestKind.Pattern }
+
+  final lazy val patternStatements: Seq[DFDLStatement] = patternAsserts ++ 
patternDiscrims
+
+  final lazy val lowPriorityStatements: Seq[DFDLStatement] = 
setVariableStatements ++ nonPatternAsserts ++ nonPatternDiscrims
+
   final protected lazy val localStatements = this.annotationObjs.collect { 
case st: DFDLStatement => st }
 
-  private lazy val localNewVariableInstanceStatements = 
localStatements.collect { case nve: DFDLNewVariableInstance => nve }
+  private lazy val localNewVariableInstanceStatements = {
+    val nvis = localStatements.collect { case nve: DFDLNewVariableInstance => 
nve }
+    checkDistinctNewVariableInstances(nvis)
+  }
 
   final protected lazy val (localDiscriminatorStatements, 
localAssertStatements) = {
     val discrims = localStatements.collect { case disc: DFDLDiscriminator => 
disc }
@@ -198,10 +214,17 @@ trait ProvidesDFDLStatementMixin extends ThrowsSDE with 
HasTermCheck { self: Ann
   private def checkDistinctVariableNames(svs: Seq[DFDLSetVariable]) = {
     val names = svs.map { _.defv.globalQName }
     val areAllDistinct = names.distinct.size == names.size
-    schemaDefinitionUnless(areAllDistinct, "Variable names must all be 
distinct at the same location: %s", names)
+    schemaDefinitionUnless(areAllDistinct, "Variables referenced by 
setVariable must all be distinct at the same location: %s", names.distinct)
     svs
   }
 
+  private def checkDistinctNewVariableInstances(nvis: 
Seq[DFDLNewVariableInstance]) = {
+    val names = nvis.map { _.defv.globalQName }
+    val areAllDistinct = names.distinct.size == names.size
+    schemaDefinitionUnless(areAllDistinct, "Variables referenced by 
newVariableInstances must all be distinct within the same scope: %s", 
names.distinct)
+    nvis
+  }
+
   final protected lazy val localSetVariableStatements = {
     val svs = localStatements.collect { case sv: DFDLSetVariable => sv }
     checkDistinctVariableNames(svs)
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala 
b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
index d686925..28f38a7 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/dsom/GroupDef.scala
@@ -92,7 +92,7 @@ trait GroupDefLike
 }
 
 /**
- * Global Group Defs carry annotations that are combined wiht those
+ * Global Group Defs carry annotations that are combined with those
  * of the corresponding group reference that refers to them.
  *
  * These are not carried on the xs:group element itself, but the xs:sequence
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 463c998..cbe559a 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
@@ -18,13 +18,23 @@
 package org.apache.daffodil.grammar
 
 import org.apache.daffodil.dsom.Term
+import org.apache.daffodil.dsom.{ DFDLNewVariableInstance, DFDLDiscriminator, 
DFDLAssert }
+import org.apache.daffodil.schema.annotation.props.gen.TestKind
 
 trait HasStatementsGrammarMixin extends GrammarMixin { self: Term =>
 
-  private lazy val statementGrams = statements.map { _.gram(self) }
-  // TODO: statements (but specifically not newVariableInstance) can appear on 
simple type definitions as well as terms.
+  // Includes setVariable as well as assert/discriminator statements that
+  // are not testKind="pattern"
+  private lazy val lowPriorityStatementGrams = lowPriorityStatements.map { 
_.gram(self) }
 
-  final lazy val dfdlStatementEvaluations = prod("dfdlStatementEvaluations", 
statementGrams.length > 0) {
-    statementGrams.fold(mt) { _ ~ _ }
+  final lazy val dfdlLowPriorityStatementEvaluations = 
prod("dfdlStatementEvaluations", lowPriorityStatementGrams.length > 0) {
+    lowPriorityStatementGrams.fold(mt) { _ ~ _ }
+  }
+
+  // assert/discriminator statements with testKind="pattern"
+  private lazy val patternStatementGrams = patternStatements.map { 
_.gram(self) }
+
+  final lazy val dfdlPatternStatementEvaluations = 
prod("dfdlPatternStatementEvaluations", patternStatementGrams.length > 0) {
+    patternStatementGrams.fold(mt) { _ ~ _ }
   }
 }
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 5ad9835..89790b2f 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
@@ -42,7 +42,11 @@ trait ModelGroupGrammarMixin
   private lazy val groupRightFraming = prod("groupRightFraming") { 
TrailingSkipRegion(this) }
 
   final override lazy val termContentBody = prod("termContentBody") {
-    dfdlStatementEvaluations ~ groupLeftFraming ~ 
groupContentWithInitiatorTerminator ~ groupRightFraming
+    // See 9.5 Evaluation Order for Statement Annotations
+    dfdlPatternStatementEvaluations ~ // Assert and Discriminator statements 
with testKind="pattern"
+    dfdlScopeBegin ~ // newVariableInstance
+    dfdlLowPriorityStatementEvaluations ~ // setVariable and the rest of the 
Assert and Discriminator statements
+    groupLeftFraming ~ groupContentWithInitiatorTerminator ~ groupRightFraming 
~ dfdlScopeEnd
   }
 
   private lazy val groupContentWithInitiatorTerminator = 
prod("groupContentWithInitiatorTerminator") {
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 9c50459..ea318e3 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
@@ -35,9 +35,7 @@ trait TermGrammarMixin
 
   def termContentBody: Gram
 
-  private lazy val newVars = this.annotationObjs.filter { st =>
-    st.isInstanceOf[DFDLNewVariableInstance]
-  }.asInstanceOf[Seq[DFDLNewVariableInstance]]
+  private lazy val newVars = this.newVariableInstanceStatements
 
   private lazy val newVarStarts = newVars.map { _.gram(self) }
   private lazy val newVarEnds = newVars.map { _.endGram(self) }
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala 
b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
index 3ced2e9..262252e 100644
--- a/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
+++ b/daffodil-core/src/main/scala/org/apache/daffodil/grammar/VariableMap.scala
@@ -31,8 +31,6 @@ object VariableMapFactory {
   }
 
   def setExternalVariables(currentVMap: VariableMap, bindings: Seq[Binding], 
referringContext: ThrowsSDE) = {
-    var newVMap = currentVMap
-    bindings.foreach(b => newVMap = newVMap.setExtVariable(b.varQName, 
b.varValue, referringContext))
-    newVMap
+    bindings.foreach(b => currentVMap.setExtVariable(b.varQName, b.varValue, 
referringContext))
   }
 }
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 42e1413..2a5efc7 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
@@ -180,15 +180,15 @@ abstract class NewVariableInstanceBase(decl: 
AnnotatedSchemaComponent, stmt: DFD
 case class NewVariableInstanceStart(decl: AnnotatedSchemaComponent, stmt: 
DFDLNewVariableInstance)
   extends NewVariableInstanceBase(decl, stmt) {
 
-  lazy val parser: DaffodilParser = new 
NewVariableInstanceStartParser(decl.runtimeData)
-  override lazy val unparser: DaffodilUnparser = new 
NewVariableInstanceStartUnparser(decl.runtimeData)
+  lazy val parser: DaffodilParser = new 
NewVariableInstanceStartParser(stmt.variableRuntimeData)
+  override lazy val unparser: DaffodilUnparser = new 
NewVariableInstanceStartUnparser(stmt.variableRuntimeData)
 }
 
 case class NewVariableInstanceEnd(decl: AnnotatedSchemaComponent, stmt: 
DFDLNewVariableInstance)
   extends NewVariableInstanceBase(decl, stmt) {
 
-  lazy val parser: DaffodilParser = new 
NewVariableInstanceEndParser(decl.runtimeData)
-  override lazy val unparser: DaffodilUnparser = new 
NewVariableInstanceEndUnparser(decl.runtimeData)
+  lazy val parser: DaffodilParser = new 
NewVariableInstanceEndParser(stmt.variableRuntimeData)
+  override lazy val unparser: DaffodilUnparser = new 
NewVariableInstanceEndUnparser(stmt.variableRuntimeData)
 }
 
 /**
diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
 
b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
index c252674..95e1620 100644
--- 
a/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
+++ 
b/daffodil-core/src/test/scala/org/apache/daffodil/dsom/TestExternalVariablesNew.scala
@@ -142,7 +142,7 @@ class TestExternalVariablesNew {
       case Success(None) => fail("Did not find " + keyToFind + " in the 
VariableMap.")
       case Success(Some(variab)) => {
         // Found var1 but is the value correct?
-        
assertTrue(variab.toString.contains("Variable(VariableDefined,DataValue(" + 
expectedValue + ")"))
+        
assertTrue(variab.toString.contains("VariableInstance(VariableDefined,DataValue("
 + expectedValue + ")"))
       }
     }
   }
diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
 
b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
index 6939e91..3446a89 100644
--- 
a/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
+++ 
b/daffodil-core/src/test/scala/org/apache/daffodil/externalvars/TestExternalVariablesLoader.scala
@@ -89,11 +89,11 @@ class TestExternalVariablesLoader extends Logging {
     val (sd, sset) = generateSD(topLevelAnnotations)
     val initialVMap = sset.variableMap
 
-    val lst_v_no_default = initialVMap.getVariableBindings(v_no_default)
-    val lst_v_with_default = initialVMap.getVariableBindings(v_with_default)
+    val stk_v_no_default = initialVMap.getVariableBindings(v_no_default)
+    val stk_v_with_default = initialVMap.getVariableBindings(v_with_default)
 
-    val var_v_no_default = lst_v_no_default.head.head
-    val var_v_with_default = lst_v_with_default.head.head
+    val var_v_no_default = stk_v_no_default.top
+    val var_v_with_default = stk_v_with_default.top
 
     val v_no_default_vrd = var_v_no_default.rd
     val v_with_default_vrd = var_v_with_default.rd
@@ -110,9 +110,9 @@ class TestExternalVariablesLoader extends Logging {
 
     // Verify that the external variables override the previous values
     // in the VariableMap
-    val (value1, _) = vmap.readVariable(v_no_default_vrd, Fakes.fakeElem)
+    val value1 = vmap.readVariable(v_no_default_vrd, Fakes.fakeElem)
     assertEquals(1, value1.getAnyRef)
-    val (value2, _) = vmap.readVariable(v_with_default_vrd, Fakes.fakeElem)
+    val value2 = vmap.readVariable(v_with_default_vrd, Fakes.fakeElem)
     assertEquals(2, value2.getAnyRef)
   }
 
diff --git a/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala 
b/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
index 37463e0..99afdc9 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/util/MStack.scala
@@ -128,7 +128,8 @@ final class MStackOfMaybe[T <: AnyRef] {
  * an object reference or null, and call Maybe(thing) explicitly outside the
  * iteration. Maybe(null) is Nope, and Maybe(thing) is One(thing) if thing is 
not null.
  */
-final class MStackOf[T <: AnyRef] {
+final class MStackOf[T <: AnyRef]
+  extends Serializable {
 
   override def toString = delegate.toString
 
@@ -176,7 +177,7 @@ object MStackOfAnyRef {
  * things.
  */
 protected abstract class MStack[@specialized T] private[util] (
-  arrayAllocator: (Int) => Array[T], nullValue: T) {
+  arrayAllocator: (Int) => Array[T], nullValue: T) extends Serializable {
 
   private var index = 0
   private var table: Array[T] = null
diff --git 
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
 
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
index 7906e77..8ef915b 100644
--- 
a/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
+++ 
b/daffodil-runtime1-unparser/src/main/scala/org/apache/daffodil/processors/unparsers/ExpressionEvaluatingUnparsers.scala
@@ -42,10 +42,7 @@ final class SetVariableSuspendableExpression(
   extends SuspendableExpression {
 
   override protected def processExpressionResult(ustate: UState, v: 
DataValuePrimitive) {
-    val newVMap =
       ustate.variableMap.setVariable(rd, v, referencingContext, ustate)
-
-    ustate.setVariables(newVMap)
   }
 
   override protected def maybeKnownLengthInBits(ustate: UState) = MaybeULong(0)
@@ -90,10 +87,8 @@ class NewVariableInstanceStartUnparser(override val context: 
RuntimeData)
 
   override lazy val childProcessors = Vector()
 
-  context.notYetImplemented("newVariableInstance")
-
-  override def unparse(ustate: UState) = {
-    context.notYetImplemented("newVariableInstance")
+  override def unparse(state: UState) = {
+    state.newVariableInstance(context.asInstanceOf[VariableRuntimeData])
   }
 }
 
@@ -104,10 +99,8 @@ class NewVariableInstanceEndUnparser(override val context: 
RuntimeData)
 
   override lazy val childProcessors = Vector()
 
-  context.notYetImplemented("newVariableInstance")
-
-  override def unparse(ustate: UState) = {
-    context.notYetImplemented("newVariableInstance")
+  override def unparse(state: UState) = {
+    state.removeVariableInstance(context.asInstanceOf[VariableRuntimeData])
   }
 }
 
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
index d904c1c..07cc4d0 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DPathRuntime.scala
@@ -41,6 +41,7 @@ import org.apache.daffodil.processors.ParseOrUnparseState
 import org.apache.daffodil.processors.ProcessingError
 import org.apache.daffodil.processors.VariableException
 import org.apache.daffodil.processors.VariableRuntimeData
+import org.apache.daffodil.processors.parsers.PState
 import org.apache.daffodil.util.Misc
 
 class CompiledDPath(val ops: RecipeOp*) extends Serializable {
@@ -180,9 +181,10 @@ case class VRef(vrd: VariableRuntimeData, context: 
ThrowsSDE)
 
   override def run(dstate: DState) {
     Assert.invariant(dstate.vmap != null)
-    val (res, newVMap) = dstate.vmap.readVariable(vrd, context)
-    dstate.setVMap(newVMap)
-    dstate.setCurrentValue(res)
+    if (dstate.parseOrUnparseState.isDefined)
+      if (dstate.parseOrUnparseState.get.isInstanceOf[PState])
+        dstate.parseOrUnparseState.get.asInstanceOf[PState].variableRead(vrd)
+    dstate.setCurrentValue(dstate.vmap.readVariable(vrd, context))
   }
 
   override def toXML = toXML("$" + vrd.globalQName.toPrettyString)
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
index 162d2fc..4f551ca 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/externalvars/ExternalVariablesLoader.scala
@@ -47,8 +47,8 @@ object ExternalVariablesLoader {
 
   def loadVariables(bindings: Seq[Binding], referringContext: ThrowsSDE, vmap: 
VariableMap): VariableMap = {
     Assert.usage(referringContext != null, "loadVariables expects 
'referringContext' to not be null!")
-    val finalVMap = VariableUtils.setExternalVariables(vmap, bindings, 
referringContext)
-    finalVMap
+    VariableUtils.setExternalVariables(vmap, bindings, referringContext)
+    vmap
   }
 
   // The following are methods that retrieve and transform variables into 
Seq[Binding]
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
index 187df57..ca0c534 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/DataProcessor.scala
@@ -186,10 +186,8 @@ class DataProcessor private (
     tunables:DaffodilTunables,
     compilerExternalVars: Queue[Binding] = Queue.empty,
     validationMode: ValidationMode.Type = ValidationMode.Off) =
-    this(ssrd, tunables,
-      ExternalVariablesLoader.loadVariables(compilerExternalVars, ssrd, 
ssrd.originalVariables),
-      false, None, validationMode,
-      compilerExternalVars)
+    this(ssrd, tunables, 
ExternalVariablesLoader.loadVariables(compilerExternalVars, ssrd, 
ssrd.originalVariables),
+      false, None, validationMode, compilerExternalVars)
 
   private def copy(
     ssrd: SchemaSetRuntimeData = ssrd,
@@ -260,48 +258,46 @@ class DataProcessor private (
     copy(areDebugging = flag, tunables = newTunables)
   }
 
-  private def newVariableMap(extVars: Map[String, String]): (VariableMap, 
Queue[Binding]) = {
+  private def loadExternalVariables(extVars: Map[String, String]): 
Queue[Binding] = {
     val bindings = ExternalVariablesLoader.mapToBindings(extVars)
     val newVars = externalVars ++ bindings
-    val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd, 
variableMap)
-    (newMap, newVars)
+    ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+    newVars
   }
 
-  private def newVariableMap(extVars: File): (VariableMap, Queue[Binding])  = {
+  private def loadExternalVariables(extVars: File): Queue[Binding]  = {
     val bindings = ExternalVariablesLoader.fileToBindings(extVars)
     val newVars = externalVars ++ bindings
-    val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd, 
variableMap)
-    (newMap, newVars)
+    ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+    newVars
   }
 
-  private def newVariableMap(bindings: Seq[Binding]): (VariableMap, 
Queue[Binding])  = {
+  private def loadExternalVariables(bindings: Seq[Binding]): Queue[Binding]  = 
{
     val newVars = externalVars ++ bindings
-    val newMap = ExternalVariablesLoader.loadVariables(bindings, ssrd, 
variableMap)
-    (newMap, newVars)
+    ExternalVariablesLoader.loadVariables(bindings, ssrd, variableMap)
+    newVars
   }
 
   @deprecated("Use withExternalVariables.", "2.6.0")
   def setExternalVariables(extVars: Map[String, String]): Unit = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    variableMap = newMap
+    val newBindings = loadExternalVariables(extVars)
     externalVars = newBindings
   }
 
   def withExternalVariables(extVars: Map[String, String]): DataProcessor = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    copy(variableMap = newMap, externalVars = newBindings)
+    val newBindings = loadExternalVariables(extVars)
+    copy(externalVars = newBindings)
   }
 
   @deprecated("Use withExternalVariables.", "2.6.0")
   def setExternalVariables(extVars: File): Unit = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    variableMap = newMap
+    val  newBindings = loadExternalVariables(extVars)
     externalVars = newBindings
   }
 
   def withExternalVariables(extVars: File): DataProcessor = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    copy(variableMap = newMap, externalVars = newBindings)
+    val newBindings = loadExternalVariables(extVars)
+    copy(externalVars = newBindings)
   }
 
   /**
@@ -313,21 +309,19 @@ class DataProcessor private (
    */
   @deprecated("Use withExternalVariables.", "2.6.0")
   def setExternalVariables(extVars: File, tunable: DaffodilTunables): Unit = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    variableMap = newMap
+    val newBindings = loadExternalVariables(extVars)
     externalVars = newBindings
   }
 
   @deprecated("Use withExternalVariables.", "2.6.0")
   def setExternalVariables(extVars: Seq[Binding]): Unit = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    variableMap = newMap
+    val newBindings = loadExternalVariables(extVars)
     externalVars = newBindings
   }
 
   def withExternalVariables(extVars: Seq[Binding]): DataProcessor = {
-    val (newMap, newBindings) = newVariableMap(extVars)
-    copy(variableMap = newMap, externalVars = newBindings)
+    val newBindings = loadExternalVariables(extVars)
+    copy(externalVars = newBindings)
   }
 
   @deprecated("Use withTunables.", "2.6.0")
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 22484c6..33c0ebc 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
@@ -984,7 +984,7 @@ final class VariableRuntimeData(
     if (!maybeDefaultValueExpr.isDefined) VariableUndefined
     else VariableDefined
 
-  private lazy val maybeValue: DataValuePrimitiveNullable =
+  private lazy val value: DataValuePrimitiveNullable =
     if (maybeDefaultValueExpr.isEmpty) DataValue.NoValue
     else {
       val defaultValueExpr = maybeDefaultValueExpr.get
@@ -994,6 +994,6 @@ final class VariableRuntimeData(
       }
     }
 
-  def newVariableInstance: Variable = Variable(state, maybeValue, this, 
maybeDefaultValueExpr)
+  def newVariableInstance: VariableInstance = VariableInstance(state, value, 
this, maybeDefaultValueExpr)
 
 }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
index 3cdfce3..441fed6 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
@@ -28,9 +28,10 @@ import 
org.apache.daffodil.infoset.DataValue.DataValuePrimitiveNullable
 import org.apache.daffodil.infoset.RetryableException
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe.Nope
+import org.apache.daffodil.util.MStackOf
 import org.apache.daffodil.xml.{GlobalQName, UnspecifiedNamespace, NamedQName, 
QNameBase, RefQName}
 
-import scala.collection.immutable
+import scala.collection.mutable.Map
 
 sealed abstract class VariableState extends Serializable
 
@@ -53,22 +54,49 @@ case object VariableRead extends VariableState
 case object VariableInProcess extends VariableState
 
 /**
- * Core tuple of a pure functional "state" for variables.
+ * Core tuple of a state for variables.
  */
-case class Variable(state: VariableState, value: DataValuePrimitiveNullable, 
rd: VariableRuntimeData, defaultValueExpr: Maybe[CompiledExpression[AnyRef]]) 
extends Serializable
+case class VariableInstance(
+  var state: VariableState,
+  var value: DataValuePrimitiveNullable,
+  rd: VariableRuntimeData,
+  defaultValueExpr: Maybe[CompiledExpression[AnyRef]],
+  var priorState: VariableState = VariableUndefined,
+  var priorValue: DataValuePrimitiveNullable = DataValue.NoValue)
+  extends Serializable {
+
+  def copy(
+    state: VariableState = state,
+    value: DataValuePrimitiveNullable = value,
+    rd: VariableRuntimeData = rd,
+    defaultValueExpr: Maybe[CompiledExpression[AnyRef]] = defaultValueExpr,
+    priorState: VariableState = priorState,
+    priorValue: DataValuePrimitiveNullable = priorValue) =
+      new VariableInstance(state, value, rd, defaultValueExpr, priorState, 
priorValue)
+
+  def setState(s: VariableState) = {
+    this.priorState = this.state
+    this.state = s
+  }
+
+  def setValue(v: DataValuePrimitiveNullable) = {
+    this.priorValue = this.value
+    this.value = v
+  }
+
+  def reset() = {
+    Assert.invariant(this.state != VariableUndefined)
+    this.value = this.priorValue
+    this.state = this.priorState
+    this.priorValue = DataValue.NoValue
+    this.priorState = VariableUndefined
+  }
+}
 
 object VariableUtils {
 
   def setExternalVariables(currentVMap: VariableMap, bindings: Seq[Binding], 
referringContext: ThrowsSDE) = {
-    var newVMap = currentVMap
-    bindings.foreach { b =>
-      val vm = newVMap
-      val vqn = b.varQName
-      val vv = b.varValue
-      val res = vm.setExtVariable(vqn, vv, referringContext)
-      newVMap = res
-    }
-    newVMap
+    bindings.foreach { b => currentVMap.setExtVariable(b.varQName, b.varValue, 
referringContext) }
   }
 
   def convert(v: String, rd: VariableRuntimeData): DataValuePrimitive =
@@ -114,8 +142,6 @@ final class VariableBox(initialVMap: VariableMap) {
 }
 
 /**
- * Pure functional data structure for implementing DFDL's variables.
- *
  * Key concepts: DFDL variables are single-assignment. Once they have been 
set, they may not be set again.
  * Furthermore, they have default values, and if the default value has been 
read, then they may not
  * subsequently be set.
@@ -127,152 +153,139 @@ final class VariableBox(initialVMap: VariableMap) {
  * The DPath implementation must be made to implement the
  * no-set-after-default-value-has-been-read behavior. This requires that 
reading the variables causes a state transition.
  */
-class VariableMap private(vTable: Map[GlobalQName, List[List[Variable]]])
+class VariableMap private(vTable: Map[GlobalQName, MStackOf[VariableInstance]])
   extends Serializable {
 
   def this(topLevelVRDs: Seq[VariableRuntimeData] = Nil) =
-    this(topLevelVRDs.map {
+    this(Map(topLevelVRDs.map {
       vrd =>
         val variab = vrd.newVariableInstance
-        (vrd.globalQName, List(List(variab)))
-    }.toMap)
+        val stack = new MStackOf[VariableInstance]
+        stack.push(variab)
+        (vrd.globalQName, stack)
+    }: _*))
 
   override def toString(): String = {
     "VariableMap(" + vTable.mkString(" | ") + ")"
   }
 
-  def find(qName: GlobalQName): Option[Variable] = {
-    val optVrd = getVariableRuntimeData(qName)
-    val optLists = optVrd.flatMap { vrd => vTable.get(vrd.globalQName) }
-    val variab = optLists.flatMap { lists => lists.flatMap {
-      _.toStream
-    }.headOption
+  /**
+   * Returns a full, deep copy of the current VariableMap. This is needed as
+   * VariableInstances are mutable and cannot safely be shared across threads
+   */
+  def copy(): VariableMap = {
+    val table = Map[GlobalQName, MStackOf[VariableInstance]]()
+    vTable.foreach { case (k: GlobalQName, s: MStackOf[VariableInstance]) => {
+      val newStack= new MStackOf[VariableInstance]()
+
+      // toList provides a list in LIFO order, so we need to reverse it to
+      // maintain order
+      s.toList.reverse.foreach { case v: VariableInstance => 
newStack.push(v.copy()) }
+      table(k) = newStack
+    }}
+
+    new VariableMap(table)
+  }
+
+  def find(qName: GlobalQName): Option[VariableInstance] = {
+    val optStack = vTable.get(qName)
+    val variab = {
+      if (optStack.isDefined)
+        Some(optStack.get.top)
+      else
+        None
     }
     variab
   }
 
   def getVariableRuntimeData(qName: GlobalQName): Option[VariableRuntimeData] 
= {
-    val optLists = vTable.get(qName)
-    optLists match {
-      case None => None // no such variable.
-      case Some(lists) => {
-        val flatLists = lists.flatten
-        Assert.invariant(flatLists.length > 0)
-        val varObj = flatLists.head
-        Some(varObj.rd)
-      }
-    }
+    val optVariable = find(qName)
+    if (optVariable.isDefined) Some(optVariable.get.rd) else None
   }
 
   lazy val context = Assert.invariantFailed("unused.")
 
-  private def mkVMapForNewVariable(newVar: Variable, firstTier: 
List[Variable], enclosingScopes: List[List[Variable]]) = {
-    val newMap = vTable + ((newVar.rd.globalQName, (newVar :: firstTier) :: 
enclosingScopes))
-    new VariableMap(newMap)
-  }
-
   /**
    * For testing mostly.
    */
-  def getVariableBindings(qn: GlobalQName): List[List[Variable]] = {
+  def getVariableBindings(qn: GlobalQName): MStackOf[VariableInstance] = {
     vTable.get(qn).get
   }
 
   /**
-   * Returns the value of a variable, constructing also a modified variable 
map which
-   * shows that the variable has been read (state VariableRead), when the 
variable hadn't
-   * previously been read yet.
+   * Returns the value of a variable and sets the state of the variable to be
+   * VariableRead.
    */
-  def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE): 
(DataValuePrimitive, VariableMap) = {
+  def readVariable(vrd: VariableRuntimeData, referringContext: ThrowsSDE): 
DataValuePrimitive = {
     val referringContext: VariableRuntimeData = vrd
     val varQName = vrd.globalQName
-    val lists = vTable.get(varQName)
-    lists match {
-
-      case Some(firstTier :: enclosingScopes) =>
-        firstTier match {
-
-          case Variable(VariableRead, v, ctxt, _) :: rest if (v.isDefined) => 
(v.getNonNullable, this)
-
-          case Variable(st, v, ctxt, defExpr) :: rest if ((v.isDefined) && (st 
== VariableDefined || st == VariableSet)) => {
-            val newVar = Variable(VariableRead, v, ctxt, defExpr)
-            val vmap = mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
-            val converted = v.getNonNullable // already converted
-            (converted, vmap)
-          }
-
-          case _ => {
-            // Fix DFDL-766
-            throw new VariableHasNoValue(varQName, referringContext)
-            // Runtime error:
-            // referringContext.SDE(msg, varQName)
-          }
+    val stack = vTable.get(varQName)
+    if (stack.isDefined) {
+      val variable = stack.get.top
+      variable.state match {
+        case VariableRead if (variable.value.isDefined) => 
variable.value.getNonNullable
+        case VariableDefined | VariableSet if (variable.value.isDefined) => {
+          variable.setState(VariableRead)
+          variable.value.getNonNullable
         }
-
-      case Some(Nil) => Assert.invariantFailed()
-
-      case None => {
-        // Compile time error:
-        referringContext.SDE("Variable map (compilation): unknown variable 
%s", varQName)
+        case _ => throw new VariableHasNoValue(varQName, referringContext)
       }
-    }
+    } else
+      referringContext.SDE("Variable map (compilation): unknown variable %s", 
varQName) // Fix DFDL-766
   }
 
   /**
-   * Assigns a variable, returning a new VariableMap which shows the state of 
the variable.
+   * Assigns a variable and sets the variables state to VariableSet
    */
-  def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive, 
referringContext: ThrowsSDE, pstate: ParseOrUnparseState): VariableMap = {
+  def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive, 
referringContext: ThrowsSDE, pstate: ParseOrUnparseState) = {
     val varQName = vrd.globalQName
+    val stack = vTable.get(varQName)
+    if (stack.isDefined) {
+      val variable = stack.get.top
+      variable.state match {
+        case VariableSet => {
+          referringContext.SDE("Cannot set variable %s twice. State was: %s. 
Existing value: %s",
+          variable.rd.globalQName, VariableSet, variable.value)
+        }
 
-    vTable.get(varQName) match {
-
-      case None => referringContext.schemaDefinitionError("unknown variable 
%s", varQName)
-
-      // There should always be a list with at least one tier in it (the 
global tier).
-      case x @ Some(firstTier :: enclosingScopes) => {
-        firstTier match {
-
-          case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            val newVar = Variable(VariableSet, 
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
-            mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
-          }
-
-          case Variable(VariableUndefined, DataValue.NoValue, ctxt, 
defaultExpr) :: rest => {
-            val newVar = Variable(VariableSet, 
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
-            mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
-          }
-
-          case Variable(VariableSet, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            referringContext.SDE("Cannot set variable %s twice. State was: %s. 
Existing value: %s", ctxt.globalQName, VariableSet, v)
-          }
-
-          case Variable(VariableRead, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            // referringContext.SDE
-            pstate.SDW("Cannot set variable %s after reading the default 
value. State was: %s. Existing value: %s", ctxt.globalQName, VariableSet, v)
-            val newVar = Variable(VariableSet, 
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
-            mkVMapForNewVariable(newVar, firstTier, enclosingScopes)
-          }
+        case VariableRead => {
+          referringContext.SDE("Cannot set variable %s after reading the 
default value. State was: %s. Existing value: %s",
+          variable.rd.globalQName, VariableSet, variable.value)
+        }
 
-          case _ => Assert.invariantFailed("variable map internal list 
structure not as expected: " + x)
+        case _ => {
+          variable.setValue(VariableUtils.convert(newValue.getAnyRef.toString, 
variable.rd))
+          variable.setState(VariableSet)
         }
       }
-      case x => Assert.invariantFailed("variables data structure not as 
expected. Should not be " + x)
     }
   }
 
+  /**
+   * Creates a new instance of a variable
+   */
+  def newVariableInstance(vrd: VariableRuntimeData) = {
+    val varQName = vrd.globalQName
+    val stack = vTable.get(varQName)
+    Assert.invariant(stack.isDefined)
+    stack.get.push(vrd.newVariableInstance)
+  }
+
+  def removeVariableInstance(vrd: VariableRuntimeData) {
+    val varQName = vrd.globalQName
+    val stack = vTable.get(varQName)
+    Assert.invariant(stack.isDefined)
+    stack.get.pop
+  }
+
+
   private lazy val externalVarGlobalQNames: Seq[GlobalQName] =
-    vTable.map { pair => pair match {
-      case (gqn, firstTier :: _) => firstTier match {
-        case (variable@Variable(_, v, ctxt, _)) :: _ if (ctxt.external) => 
variable.rd.globalQName
-      }
-      case (_, Nil) => Assert.invariantFailed("empty tier")
-    }
-    }.toSeq
+    vTable.map { case (_, stack) if (stack.top.rd.external) => 
stack.top.rd.globalQName }.toSeq
 
   /**
-   * Assigns a variable, returning a new VariableMap which shows the state of 
the variable.
+   * Assigns an external variable and sets the variables state to VariableSet
    */
-  def setExtVariable(bindingQName: RefQName, newValue: DataValuePrimitive, 
referringContext: ThrowsSDE): VariableMap = {
+  def setExtVariable(bindingQName: RefQName, newValue: DataValuePrimitive, 
referringContext: ThrowsSDE) = {
     val varQName: RefQName =
       if (bindingQName.namespace == UnspecifiedNamespace) {
         // The external variable binding was hoping to get away with not 
specifying a namespace.
@@ -293,51 +306,37 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
       } else {
         bindingQName
       }
-    val optMapTiers = vTable.get(varQName.toGlobalQName)
-    optMapTiers match {
-      case None => referringContext.schemaDefinitionError("unknown variable 
%s", varQName)
-      // There should always be a list with at least one tier in it (the 
global tier).
-      case x @ Some(firstTier :: enclosingScopes) => {
-        firstTier match {
-
-          case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if 
(v.isDefined && ctxt.external) => {
-            val newVar = Variable(VariableDefined, 
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
-            val newFirstTier = newVar :: rest
-            mkVMapForNewVariable(newVar, newFirstTier, enclosingScopes)
-          }
-          case Variable(VariableDefined, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            referringContext.SDE("Cannot set variable %s externally. State 
was: %s. Existing value: %s.", ctxt.globalQName, VariableDefined, v)
-            // this // Unaltered VMap
-          }
 
-          case Variable(VariableUndefined, DataValue.NoValue, ctxt, 
defaultExpr) :: rest if ctxt.external => {
-            val newVar = Variable(VariableDefined, 
VariableUtils.convert(newValue.getAnyRef.toString, ctxt), ctxt, defaultExpr)
-            val newFirstTier = newVar :: rest
-            mkVMapForNewVariable(newVar, newFirstTier, enclosingScopes)
-          }
+    val stack = vTable.get(varQName.toGlobalQName)
+    if (!stack.isDefined)
+      referringContext.schemaDefinitionError("unknown variable %s", varQName)
+    else {
+      val variable = stack.get.top
+      variable.state match {
+        case VariableDefined if (!variable.rd.external) => {
+          referringContext.SDE("Cannot set variable %s externally. State was: 
%s. Existing value: %s.",
+            variable.rd.globalQName, VariableDefined, variable.value)
+        }
 
-          case Variable(VariableUndefined, DataValue.NoValue, ctxt, 
defaultExpr) :: rest => {
-            referringContext.SDE("Cannot set variable %s externally. State 
was: %s.", ctxt.globalQName, VariableUndefined)
-            // this // Unaltered VMap
-          }
+        case VariableUndefined if (!variable.rd.external) => {
+          referringContext.SDE("Cannot set variable %s externally. State was: 
%s.", variable.rd.globalQName, VariableUndefined)
+        }
 
-          case Variable(VariableSet, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            // Shouldn't this be an impossible case? External variables should 
be defined before parsing.
-            // Parsing is the only point at which Set can be called?
-            referringContext.SDE("Cannot externally set variable %s twice. 
State was: %s. Existing value: %s", ctxt.globalQName, VariableSet, v)
-            // this // Unaltered VMap
-          }
+        case VariableSet => {
+          referringContext.SDE("Cannot externally set variable %s twice. State 
was: %s. Existing value: %s",
+            variable.rd.globalQName, VariableSet, variable.value)
+        }
 
-          case Variable(VariableRead, v, ctxt, defaultExpr) :: rest if 
(v.isDefined) => {
-            referringContext.SDE("Cannot externally set variable %s after 
reading the default value. State was: %s. Existing value: %s", 
ctxt.globalQName, VariableSet, v)
-            // this // Unaltered VMap
-          }
+        case VariableRead => {
+          referringContext.SDE("Cannot externally set variable %s after 
reading the default value. State was: %s. Existing value: %s",
+            variable.rd.globalQName, VariableSet, variable.value)
+        }
 
-          case _ => Assert.invariantFailed("variable map internal list 
structure not as expected: " + x)
+        case _ => {
+          variable.setValue(VariableUtils.convert(newValue.getAnyRef.toString, 
variable.rd))
+          variable.setState(VariableDefined)
         }
       }
-      case x => Assert.invariantFailed("variables data structure not as 
expected. Should not be " + x)
     }
   }
-
 }
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
index 6e1c5b7..47ff959 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
@@ -153,25 +153,21 @@ class SetVariableParser(expr: CompiledExpression[AnyRef], 
decl: VariableRuntimeD
   }
 }
 
-class NewVariableInstanceStartParser(
-  override val context: RuntimeData)
+class NewVariableInstanceStartParser(override val context: VariableRuntimeData)
   extends PrimParser {
-  context.notYetImplemented("newVariableInstance")
   override lazy val runtimeDependencies = Vector()
 
-  def parse(pstate: PState) = {
-    context.notYetImplemented("newVariableInstance")
+  def parse(start: PState): Unit = {
+    start.newVariableInstance(context)
   }
 }
 
-class NewVariableInstanceEndParser(
-  override val context: RuntimeData)
+class NewVariableInstanceEndParser(override val context: VariableRuntimeData)
   extends PrimParser {
-  context.notYetImplemented("newVariableInstance")
   override lazy val runtimeDependencies = Vector()
 
-  def parse(pstate: PState) = {
-    context.notYetImplemented("newVariableInstance")
+  def parse(start: PState) = {
+    start.removeVariableInstance(context)
   }
 }
 
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
index 7cb5502..578bbe1 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/InitiatedContentParsers.scala
@@ -49,9 +49,9 @@ final class 
InitiatedContentDiscrimChoiceAndIndexGreaterThanMinParser(
 
   def parse(start: PState): Unit = {
     val top = start.discriminator
-    start.popDiscriminator // pop array discriminator off of stack
+    start.popPointOfUncertainty // pop array discriminator off of stack and 
any changed variables
     start.setDiscriminator(true) // set the choice discriminator
-    start.pushDiscriminator
+    start.pushPointOfUncertainty
     start.setDiscriminator(top) // restore the array discriminator to top of 
stack
     if (start.arrayPos > min)
       start.setDiscriminator(true) // set array discriminator only if
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 e780bbe..ea85fca 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
@@ -55,6 +55,7 @@ import org.apache.daffodil.util.MStackOfBoolean
 import org.apache.daffodil.util.MStackOfInt
 import org.apache.daffodil.util.MStackOfLong
 import org.apache.daffodil.util.MStackOfMaybe
+import org.apache.daffodil.util.MStackOf
 import org.apache.daffodil.util.Maybe
 import org.apache.daffodil.util.Maybe.Nope
 import org.apache.daffodil.util.Maybe.One
@@ -66,6 +67,7 @@ import org.apache.daffodil.infoset.DISimpleState
 import org.apache.daffodil.exceptions.UnsuppressableException
 import org.apache.daffodil.exceptions.Abort
 import org.apache.daffodil.infoset.DataValue.DataValuePrimitive
+import org.apache.daffodil.xml.GlobalQName
 
 object MPState {
 
@@ -166,6 +168,14 @@ final class PState private (
   private val discriminatorStack = MStackOfBoolean()
   discriminatorStack.push(false)
 
+  /**
+   * This stack tracks variables that have changed within the current point of
+   * uncertainty. This tracking is necessary to revert changes made to 
variables
+   * when the parser needs to backtrack.
+   */
+  private val changedVariablesStack = new 
MStackOf[mutable.MutableList[GlobalQName]]()
+  changedVariablesStack.push(mutable.MutableList[GlobalQName]())
+
   override def dataStream = One(dataInputStream)
 
   def saveDelimitedParseResult(result: Maybe[dfa.ParseResult]) {
@@ -205,6 +215,11 @@ final class PState private (
     m.restoreInto(this)
     m.clear()
     markPool.returnToPool(m)
+    changedVariablesStack.top.foreach { v => {
+      val variable = variableMap.find(v)
+      if (variable.isDefined)
+        variable.get.reset
+    }}
   }
 
   def discard(m: PState.Mark) {
@@ -265,17 +280,37 @@ final class PState private (
   }
 
   def setVariable(vrd: VariableRuntimeData, newValue: DataValuePrimitive, 
referringContext: VariableRuntimeData, pstate: PState) {
-    this.setVariableMap(variableMap.setVariable(vrd, newValue, 
referringContext, pstate))
+    variableMap.setVariable(vrd, newValue, referringContext, pstate)
+    changedVariablesStack.top += vrd.globalQName
+  }
+
+  /**
+   * Note that this function does not actually read the variable, it is used
+   * just to track that the variable was read in case we need to backtrack.
+   */
+  def variableRead(vrd: VariableRuntimeData) {
+    changedVariablesStack.top += vrd.globalQName
+  }
+
+  def newVariableInstance(vrd: VariableRuntimeData) {
+    variableMap.newVariableInstance(vrd)
+    changedVariablesStack.top += vrd.globalQName
   }
 
-  def pushDiscriminator {
+  def removeVariableInstance(vrd: VariableRuntimeData) {
+    variableMap.removeVariableInstance(vrd)
+  }
+
+  def pushPointOfUncertainty {
     // threadCheck()
     discriminatorStack.push(false)
+    changedVariablesStack.push(mutable.MutableList[GlobalQName]())
   }
 
-  def popDiscriminator {
+  def popPointOfUncertainty {
     // threadCheck()
     discriminatorStack.pop
+    changedVariablesStack.pop
   }
 
   def setDiscriminator(disc: Boolean) {
@@ -501,7 +536,12 @@ object PState {
     output: InfosetOutputter,
     dataProc: DFDL.DataProcessor): PState = {
 
-    val variables = dataProc.variableMap
+    /**
+     * This is a full deep copy as variableMap is mutable. Reusing
+     * dataProc.VariableMap without a copy would not be thread safe.
+     */
+    val variables = dataProc.variableMap.copy
+
     val diagnostics = Nil
     val mutablePState = MPState()
     val tunables = dataProc.getTunables()
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 208de71..2797403 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
@@ -196,7 +196,7 @@ class ChoiceParser(
     var markLeakCausedByException = false;
 
     try {
-      pstate.pushDiscriminator
+      pstate.pushPointOfUncertainty
       var diagnostics: Seq[Diagnostic] = Nil
       var i = 0
       var parser: Parser = null
@@ -273,7 +273,7 @@ class ChoiceParser(
         log(LogLevel.Debug, "All Choice alternatives failed.")
       }
 
-      pstate.popDiscriminator
+      pstate.popPointOfUncertainty
 
       // This is only used for unordered sequences. If we hit a failure after 
parsing a discriminator,
       // we need to pass this discriminator out of the make shift ChoiceParser 
and back to the
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
index 91bc796..2d692e8 100644
--- 
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
@@ -279,7 +279,7 @@ abstract class SequenceParserBase(
     try {
       checkN(pstate, parser) // check if occursIndex exceeds tunable limit.
 
-      if (hasPoU) pstate.pushDiscriminator
+      if (hasPoU) pstate.pushPointOfUncertainty
       val priorPos = pstate.bitPos0b
 
       resultOfTry = parser.parseOne(pstate, roStatus)
@@ -289,7 +289,7 @@ abstract class SequenceParserBase(
       val currentPos = pstate.bitPos0b
 
       val wasDiscriminatorSet = pstate.discriminator
-      if (hasPoU) pstate.popDiscriminator
+      if (hasPoU) pstate.popPointOfUncertainty
       //
       // Now we handle the result of the parse attempt.
       //
diff --git 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
index 8e6d1cd..2c63c88 100644
--- 
a/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
+++ 
b/daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/unparsers/UState.scala
@@ -56,6 +56,7 @@ import org.apache.daffodil.processors.TermRuntimeData
 import org.apache.daffodil.processors.UnparseResult
 import org.apache.daffodil.processors.VariableBox
 import org.apache.daffodil.processors.VariableMap
+import org.apache.daffodil.processors.VariableRuntimeData
 import org.apache.daffodil.processors.charset.BitsCharset
 import org.apache.daffodil.processors.charset.BitsCharsetDecoder
 import org.apache.daffodil.processors.charset.BitsCharsetEncoder
@@ -343,6 +344,14 @@ abstract class UState(
   def regexMatchBitPositionBuffer: LongBuffer = Assert.usageError("Not to be 
used.")
 
   def documentElement: DIDocument
+
+  def newVariableInstance(vrd: VariableRuntimeData) {
+    variableMap.newVariableInstance(vrd)
+  }
+
+  def removeVariableInstance(vrd: VariableRuntimeData) {
+    variableMap.removeVariableInstance(vrd)
+  }
 }
 
 /**
@@ -678,7 +687,12 @@ object UState {
     inputter: InfosetInputter): UStateMain = {
     Assert.invariant(inputter.isInitialized)
 
-    val variables = dataProc.variableMap
+    /**
+     * This is a full deep copy as variableMap is mutable. Reusing
+     * dataProc.VariableMap without a copy would not be thread safe.
+     */
+    val variables = dataProc.variableMap.copy
+
     val diagnostics = Nil
     val newState = new UStateMain(inputter, variables, diagnostics, 
dataProc.asInstanceOf[DataProcessor], out,
       new mutable.Queue[Suspension], dataProc.getTunables()) // null means no 
prior UState
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
index 969e7cf..185871a 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables.tdml
@@ -29,6 +29,10 @@
       defaultValue="42" />
     <dfdl:defineVariable name="myVar1" type="xs:int"
       defaultValue="6" />
+    <dfdl:defineVariable name="myVar2" type="xs:int"
+      defaultValue="16" />
+    <dfdl:defineVariable name="myVar3" type="xs:int"
+      defaultValue="26" />
     <xs:include 
schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
     <dfdl:format ref="ex:GeneralFormat" />
     <xs:element name="c">
@@ -97,7 +101,7 @@
       </xs:complexType>
     </xs:element>
 
-    <xs:element name="a">
+    <xs:element name="nvi1">
       <xs:complexType>
         <xs:sequence>
           <xs:annotation>
@@ -106,7 +110,299 @@
                 defaultValue="7" />
             </xs:appinfo>
           </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi2">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="7" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+          <xs:element name="nest1">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:newVariableInstance ref="ex:myVar1"
+                      defaultValue="8" />
+                  </xs:appinfo>
+                </xs:annotation>
+                <xs:element name="b" type="xsd:int"
+                  dfdl:inputValueCalc="{ $ex:myVar1 }" />
+                <xs:element name="nest2">
+                  <xs:complexType>
+                    <xs:sequence>
+                      <xs:annotation>
+                        <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                          <dfdl:newVariableInstance ref="ex:myVar1"
+                            defaultValue="9" />
+                        </xs:appinfo>
+                      </xs:annotation>
+                      <xs:element name="c" type="xsd:int"
+                        dfdl:inputValueCalc="{ $ex:myVar1 }" />
+                    </xs:sequence>
+                  </xs:complexType>
+                </xs:element>
+                <xs:element name="d" type="xsd:int"
+                  dfdl:inputValueCalc="{ $ex:myVar1 }" />
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="e" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi3">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="7" />
+              <dfdl:newVariableInstance ref="ex:myVar2"
+                defaultValue="17" />
+              <dfdl:newVariableInstance ref="ex:myVar3"
+                defaultValue="27" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
           <xs:element name="b" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar2 }" />
+          <xs:element name="c" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar3 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi4">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="7">8</dfdl:newVariableInstance>
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi5">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="7" />
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="8" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi6">
+      <xs:annotation>
+        <xs:appinfo source="http://www.ogf.org/dfdl/";>
+          <dfdl:newVariableInstance ref="ex:myVar1"
+            defaultValue="7" />
+        </xs:appinfo>
+      </xs:annotation>
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi7">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <dfdl:defineVariable name="s1" type="xs:string" 
defaultValue="global_default1" />
+    <dfdl:defineVariable name="s2" type="xs:string" 
defaultValue="global_default2" />
+    <xs:element name="nvi8">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:setVariable ref="ex:s1">{ "global_set1" 
}</dfdl:setVariable>
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="nest1">
+            <xs:complexType>
+              <xs:sequence>
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:newVariableInstance ref="ex:s1"
+                      defaultValue="nest1_nvi1_default1" />
+                    <dfdl:newVariableInstance ref="ex:s2"
+                      defaultValue="nest1_nvi1_default2" />
+                    <dfdl:setVariable ref="ex:s1">{ "nest1_nvi1_set1" 
}</dfdl:setVariable>
+                  </xs:appinfo>
+                </xs:annotation>
+                <xs:element name="nest2">
+                  <xs:complexType>
+                    <xs:sequence>
+                      <xs:annotation>
+                        <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                          <dfdl:newVariableInstance ref="ex:s1"
+                            defaultValue="nest2_nvi2_default1" />
+                          <dfdl:newVariableInstance ref="ex:s2"
+                            defaultValue="nest2_nvi2_default2" />
+                        </xs:appinfo>
+                      </xs:annotation>
+                      <xs:element name="choice">
+                        <xs:complexType>
+                          <xs:choice>
+                            <xs:element name="c1_s2" type="xs:string" 
dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                                  <dfdl:setVariable ref="ex:s1">{ "c1_s1_set1" 
}</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq 
"fail" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                            <xs:element name="c2_s2" type="xs:string" 
dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                                  <dfdl:setVariable ref="ex:s1">{ "c2_s1_set1" 
}</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq 
"fail" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                            <xs:element name="c3_s2" type="xs:string" 
dfdl:lengthKind="delimited">
+                              <xs:annotation>
+                                <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                                  <dfdl:setVariable ref="ex:s1">{ "c3_s1_set1" 
}</dfdl:setVariable>
+                                  <dfdl:assert><![CDATA[{ xs:string(.) eq 
"nest2_nvi2_default2" }]]></dfdl:assert>
+                                </xs:appinfo>
+                              </xs:annotation>
+                            </xs:element>
+                          </xs:choice>
+                        </xs:complexType>
+                      </xs:element>
+                      <xs:element name="d" type="xs:string"
+                        dfdl:inputValueCalc="{ $ex:s1 }" />
+                      <xs:element name="e" type="xs:string"
+                        dfdl:inputValueCalc="{ $ex:s2 }" />
+                    </xs:sequence>
+                  </xs:complexType>
+                </xs:element>
+                <xs:element name="f" type="xs:string"
+                  dfdl:inputValueCalc="{ $ex:s1 }">
+                </xs:element>
+                <xs:element name="g" type="xs:string"
+                  dfdl:inputValueCalc="{ $ex:s2 }" />
+              </xs:sequence>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="h" type="xs:string"
+            dfdl:inputValueCalc="{ $ex:s1 }" />
+          <xs:element name="i" type="xs:string"
+            dfdl:inputValueCalc="{ $ex:s2 }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi9">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="original" type="xsd:int"
+            dfdl:inputValueCalc="{ $ex:myVar1 }" />
+          <xs:element name="choice">
+            <xs:complexType>
+              <xs:choice>
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:newVariableInstance ref="ex:myVar1"
+                      defaultValue="7" />
+                  </xs:appinfo>
+                </xs:annotation>
+                <xs:element name="new" type="xsd:int" 
dfdl:lengthKind="explicit" dfdl:length="1">
+                  <xs:annotation>
+                    <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                      <dfdl:assert><![CDATA[{ xs:int(.) eq $ex:myVar1 
}]]></dfdl:assert>
+                    </xs:appinfo>
+                  </xs:annotation>
+                </xs:element>
+              </xs:choice>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi10">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="before">
+            <xs:complexType>
+              <xs:group ref="ex:g5">
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:setVariable ref="ex:v1_no_default">{ "January" 
}</dfdl:setVariable>
+                  </xs:appinfo>
+                </xs:annotation>
+              </xs:group>
+            </xs:complexType>
+          </xs:element>
+          <xs:element name="after">
+            <xs:complexType>
+              <xs:group ref="ex:g5">
+                <xs:annotation>
+                  <xs:appinfo source="http://www.ogf.org/dfdl/";>
+                    <dfdl:newVariableInstance ref="ex:v1_no_default" />
+                    <dfdl:setVariable ref="ex:v1_no_default">{ "February" 
}</dfdl:setVariable>
+                  </xs:appinfo>
+                </xs:annotation>
+              </xs:group>
+            </xs:complexType>
+          </xs:element>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="nvi11">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:annotation>
+            <xs:appinfo source="http://www.ogf.org/dfdl/";>
+              <dfdl:newVariableInstance ref="ex:myVar1"
+                defaultValue="10" />
+              <dfdl:assert testKind="pattern" testPattern="\d"
+                message="Assertion failed for pattern \d" />
+            </xs:appinfo>
+          </xs:annotation>
+          <xs:element name="a" type="xsd:int"
             dfdl:inputValueCalc="{ $ex:myVar1 }" />
         </xs:sequence>
       </xs:complexType>
@@ -348,9 +644,9 @@
 
     <tdml:errors>
       <tdml:error>Schema Definition Error</tdml:error>
-      <tdml:error>variable</tdml:error>
-      <tdml:error>twice</tdml:error>
-      <tdml:error>v_no_default</tdml:error>
+      <tdml:error>Variables</tdml:error>
+      <tdml:error>distinct</tdml:error>
+      <tdml:error>same location</tdml:error>
     </tdml:errors>
 
   </tdml:parserTestCase>
@@ -363,9 +659,9 @@
 
     <tdml:errors>
       <tdml:error>Schema Definition Error</tdml:error>
-      <tdml:error>variable</tdml:error>
-      <tdml:error>twice</tdml:error>
-      <tdml:error>v_no_default</tdml:error>
+      <tdml:error>Variables</tdml:error>
+      <tdml:error>distinct</tdml:error>
+      <tdml:error>same location</tdml:error>
     </tdml:errors>
 
   </tdml:parserTestCase>
@@ -384,20 +680,193 @@
 
   </tdml:parserTestCase>
 
-  <tdml:parserTestCase name="varInstance" root="a"
-    model="v">
+  <tdml:parserTestCase name="varInstance_01" root="nvi1"
+    model="v"
+    description="Simple demonstration of newVariableInstance">
+
+    <tdml:document />
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi1 xmlns="http://example.com";>
+          <a xsi:type="xsd:int">7</a>
+        </nvi1>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_02" root="nvi2"
+    model="v"
+    description="Demonstrates that newVariableInstance correctly goes into and 
out of scope">
+
+    <tdml:document />
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi2 xmlns="http://example.com";>
+          <a xsi:type="xsd:int">7</a>
+          <nest1>
+            <b xsi:type="xsd:int">8</b>
+            <nest2>
+              <c xsi:type="xsd:int">9</c>
+            </nest2>
+            <d xsi:type="xsd:int">8</d>
+          </nest1>
+          <e xsi:type="xsd:int">7</e>
+        </nvi2>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_03" root="nvi3"
+    model="v"
+    description="Demonstrates using multiple newVariableInstance statements 
within a sequence">
 
     <tdml:document />
 
     <tdml:infoset>
       <tdml:dfdlInfoset>
-        <a xmlns="http://example.com";>
-          <b xsi:type="xsd:int">7</b>
-        </a>
+        <nvi3 xmlns="http://example.com";>
+          <a xsi:type="xsd:int">7</a>
+          <b xsi:type="xsd:int">17</b>
+          <c xsi:type="xsd:int">27</c>
+        </nvi3>
       </tdml:dfdlInfoset>
     </tdml:infoset>
   </tdml:parserTestCase>
 
+  <tdml:parserTestCase name="varInstance_04" root="nvi4"
+    model="v"
+    description="SDE due to default value being set both as attribute and 
element value">
+
+    <tdml:document />
+
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>value of variable was supplied both as attribute and element 
value</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_05" root="nvi5"
+    model="v"
+    description="SDE due to multiple newVariableInstance calls within the same 
scope to the same variable">
+
+    <tdml:document />
+
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>newVariableInstances must all be distinct within the same 
scope</tdml:error>
+      <tdml:error>ex:myVar1</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_06" root="nvi6"
+    model="v"
+    description="SDE due to newVariableInstance being declared on an element 
instead of group reference, sequence or choice">
+
+    <tdml:document />
+
+    <tdml:errors>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>newVariableInstance may only be used on group reference, 
sequence or choice</tdml:error>
+    </tdml:errors>
+
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_07" root="nvi7"
+    model="v"
+    description="Demonstrates that newVariableInstance inherits default value 
from original definition">
+
+    <tdml:document />
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi7 xmlns="http://example.com";>
+          <a xsi:type="xsd:int">6</a>
+        </nvi7>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_08" root="nvi8"
+    model="v"
+    description="Demonstrates that newVariableInstance correctly goes into and 
out of scope with backtracking">
+
+    <tdml:document><![CDATA[nest2_nvi2_default2]]></tdml:document>
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi8 xmlns="http://example.com";>
+          <nest1>
+            <nest2>
+              <choice>
+                <c3_s2>nest2_nvi2_default2</c3_s2>
+              </choice>
+              <d>c3_s1_set1</d>
+              <e>nest2_nvi2_default2</e>
+            </nest2>
+            <f>nest1_nvi1_set1</f>
+            <g>nest1_nvi1_default2</g>
+          </nest1>
+          <h>global_set1</h>
+          <i>global_default2</i>
+        </nvi8>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_09" root="nvi9"
+    model="v"
+    description="Simple demonstration of newVariableInstance on a choice">
+
+    <tdml:document><![CDATA[7]]></tdml:document>
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi9 xmlns="http://example.com";>
+          <original xsi:type="xsd:int">6</original>
+          <choice>
+            <new xsi:type="xsd:int">7</new>
+          </choice>
+        </nvi9>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_10" root="nvi10"
+    model="v" description="setVariable on a group reference - DFDL-7-121R">
+    <tdml:document><![CDATA[2324]]></tdml:document>
+
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <nvi10 xmlns="http://example.com";>
+          <before>
+            <month>January</month>
+            <day>23</day>
+          </before>
+          <after>
+            <month>February</month>
+            <day>24</day>
+          </after>
+        </nvi10>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <tdml:parserTestCase name="varInstance_11" root="nvi11"
+    model="v"
+    description="Demonstrates correct statement order is observed with 
newVariableInstance">
+
+    <tdml:document />
+
+    <tdml:errors>
+      <tdml:error>Parse Error</tdml:error>
+      <tdml:error>Assertion failed: Assertion failed for pattern 
\d</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
   <tdml:parserTestCase name="varAsSeparator" root="e1"
     model="v"
     description="variable referenced as a separator; the defineVariable 
annotation occurs after the element declaration that references it - 
DFDL-7-107R">
diff --git 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
index 4466831..6d636c6 100644
--- 
a/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
+++ 
b/daffodil-test/src/test/resources/org/apache/daffodil/section07/variables/variables_01.tdml
@@ -77,9 +77,10 @@
     <tdml:document />
 
     <tdml:errors>
-      <tdml:error>variable</tdml:error>
-      <tdml:error>twice</tdml:error>
-      <tdml:error>v_no_default</tdml:error>
+      <tdml:error>Schema Definition Error</tdml:error>
+      <tdml:error>Variables</tdml:error>
+      <tdml:error>distinct</tdml:error>
+      <tdml:error>same location</tdml:error>
     </tdml:errors>
 
   </tdml:parserTestCase>
diff --git 
a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
 
b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
index 30fabf2..4dbefa5 100644
--- 
a/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
+++ 
b/daffodil-test/src/test/scala/org/apache/daffodil/section07/variables/TestVariables.scala
@@ -48,6 +48,17 @@ class TestVariables {
   @Test def test_multiSetErr() { runner.runOneTest("multiSetErr") }
   // DFDL-1443 & DFDL-1448
   // @Test def test_setAfterReadErr() { runner.runOneTest("setAfterReadErr") }
+  @Test def test_varInstance_01() { runner.runOneTest("varInstance_01") }
+  @Test def test_varInstance_02() { runner.runOneTest("varInstance_02") }
+  @Test def test_varInstance_03() { runner.runOneTest("varInstance_03") }
+  @Test def test_varInstance_04() { runner.runOneTest("varInstance_04") }
+  @Test def test_varInstance_05() { runner.runOneTest("varInstance_05") }
+  @Test def test_varInstance_06() { runner.runOneTest("varInstance_06") }
+  @Test def test_varInstance_07() { runner.runOneTest("varInstance_07") }
+  @Test def test_varInstance_08() { runner.runOneTest("varInstance_08") }
+  @Test def test_varInstance_09() { runner.runOneTest("varInstance_09") }
+  @Test def test_varInstance_10() { runner.runOneTest("varInstance_10") }
+  @Test def test_varInstance_11() { runner.runOneTest("varInstance_11") }
   @Test def test_setVarChoice() { runner.runOneTest("setVarChoice") }
   @Test def test_unparse_setVarChoice() { 
runner.runOneTest("unparse_setVarChoice") }
   @Test def test_setVarOnSeqAndElemRef() { 
runner.runOneTest("setVarOnSeqAndElemRef") }

Reply via email to