stevedlawrence commented on a change in pull request #369:
URL: https://github.com/apache/incubator-daffodil/pull/369#discussion_r413217592



##########
File path: 
daffodil-core/src/main/scala/org/apache/daffodil/dsom/DFDLStatementMixin.scala
##########
@@ -202,6 +208,13 @@ trait ProvidesDFDLStatementMixin extends ThrowsSDE with 
HasTermCheck { self: Ann
     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)

Review comment:
       Might be helpful to print the variables that are duplicated rather than 
all of the names.

##########
File path: 
daffodil-core/src/main/scala/org/apache/daffodil/grammar/HasStatementsGrammarMixin.scala
##########
@@ -18,10 +18,11 @@
 package org.apache.daffodil.grammar
 
 import org.apache.daffodil.dsom.Term
+import org.apache.daffodil.dsom.DFDLNewVariableInstance
 
 trait HasStatementsGrammarMixin extends GrammarMixin { self: Term =>
 
-  private lazy val statementGrams = statements.map { _.gram(self) }
+  private lazy val statementGrams = statements.filter{ st => 
!st.isInstanceOf[DFDLNewVariableInstance] }.map { _.gram(self) }

Review comment:
       Why filter out NVE? I guess because NVE isn't a dfdlStatementEvaluation 
and is handled differently? Maybe this variable (or trait) needs a different 
name or maybe we just move this into the dfdlStatementEvaluations variable? 
Nothing else outside of this trait uses statementGrams. What are the other 
DFDLStatements? Does it make sense to collect those that we explictly want 
rather than filtering out NVE?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -53,28 +54,54 @@ 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 Variable(
+  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 Variable(state, value, rd, defaultValueExpr, priorState, priorValue)

Review comment:
       Does this add any extra value over what a case class provides? Cases 
class I think should generate the same thing, right?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -129,54 +156,66 @@ 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[Variable]])
   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[Variable]
+        stack.push(variab)
+        (vrd.globalQName, stack)
+    }: _*))
 
   override def toString(): String = {
     "VariableMap(" + vTable.mkString(" | ") + ")"
   }
 
+  def copy(): VariableMap = {
+    val table = Map[GlobalQName, MStackOf[Variable]]()
+    vTable.foreach { case (k: GlobalQName, s: MStackOf[Variable]) => {
+      val newStack= new MStackOf[Variable]()
+      newStack.push(s.top.copy())
+      table(k) = newStack
+    }}
+
+    new VariableMap(table)
+  }
+
   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
+    val optStack = optVrd.flatMap { vrd => vTable.get(vrd.globalQName) }
+    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 {
+    val optStack = vTable.get(qName)
+    optStack match {
       case None => None // no such variable.
-      case Some(lists) => {
-        val flatLists = lists.flatten
-        Assert.invariant(flatLists.length > 0)
-        val varObj = flatLists.head
+      case Some(stack) => {
+        val varObj = stack.top
         Some(varObj.rd)
       }
     }
   }
 
-  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)
+  def getVaraibleStack(qName: GlobalQName): Option[MStackOf[Variable]] = {

Review comment:
       Or find() and getVariableRUntimeData() could use this.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -129,54 +156,66 @@ 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[Variable]])
   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[Variable]
+        stack.push(variab)
+        (vrd.globalQName, stack)
+    }: _*))
 
   override def toString(): String = {
     "VariableMap(" + vTable.mkString(" | ") + ")"
   }
 
+  def copy(): VariableMap = {

Review comment:
       I think this needs a comment that this isn't really a copy. It looks 
like it's creating a new variable map where each stack just contains the top of 
the existing stack. Is this intentional?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -185,96 +224,84 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
    * shows that the variable has been read (state VariableRead), when the 
variable hadn't
    * previously been read yet.
    */
-  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)

Review comment:
       Should we use the new getVariableStack()? Or should that function be 
removed? Not sure if anything uses it.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -129,54 +156,66 @@ 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[Variable]])
   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[Variable]
+        stack.push(variab)
+        (vrd.globalQName, stack)
+    }: _*))
 
   override def toString(): String = {
     "VariableMap(" + vTable.mkString(" | ") + ")"
   }
 
+  def copy(): VariableMap = {
+    val table = Map[GlobalQName, MStackOf[Variable]]()
+    vTable.foreach { case (k: GlobalQName, s: MStackOf[Variable]) => {
+      val newStack= new MStackOf[Variable]()
+      newStack.push(s.top.copy())
+      table(k) = newStack
+    }}
+
+    new VariableMap(table)
+  }
+
   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
+    val optStack = optVrd.flatMap { vrd => vTable.get(vrd.globalQName) }
+    val variab = {
+      if (optStack.isDefined)
+        Some(optStack.get.top)
+      else
+        None

Review comment:
       Can this just instead just do something like:
   ```scala
   val optStack = vTable.get(qName)
   val variab = ...
   ```
   If this uses getVariableRuntimeData it's going to do two looks up. Once in 
getVariableRuntimeData to find the right stack and get the rd of the the thing 
on the top, then another to find that stack that getVariableRuntimeData already 
found.
   
   And then getVariableRuntimeData can just be
   ```
   val optVariable = find(qName)
   if (optVariable.isDefined) Some(optVariable.get.rd) else None
   ```

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -185,96 +224,84 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
    * shows that the variable has been read (state VariableRead), when the 
variable hadn't
    * previously been read yet.
    */
-  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

Review comment:
       Not sure this comment adds anything. I'd just remove it.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -340,6 +393,6 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
       }
       case x => Assert.invariantFailed("variables data structure not as 
expected. Should not be " + x)
     }
-  }
+  }*/

Review comment:
       Remove comment.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -185,96 +224,84 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
    * shows that the variable has been read (state VariableRead), when the 
variable hadn't
    * previously been read yet.
    */
-  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.
    */
-  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)
+    if (stack.isDefined) {
+      stack.get.push(vrd.newVariableInstance)
+    }

Review comment:
       In what case is stack not defined? Should this be an Assertion that 
stack.isDefined instead

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
##########
@@ -158,25 +158,24 @@ 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 = {
+    log(LogLevel.Debug, "This is %s", toString) // important. Don't toString 
unless we have to log.
+    if (start.processorStatus.isInstanceOf[Failure]) return
+    // val vmap = start.variableMap
+    start.newVariableInstance(context)

Review comment:
       In what case does the previous processor fail but we continue to call 
this one? If we do need thi heck, can you remove the return, so it's something 
like
   ```
   if (start.processorStatus == Success) {
     start.newVariableInstance(contxt)
   }
   ```
   
   Can you remove the comment.
   
   

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
##########
@@ -166,6 +168,9 @@ final class PState private (
   private val discriminatorStack = MStackOfBoolean()
   discriminatorStack.push(false)
 
+  private val changedVariablesStack = new 
MStackOf[mutable.MutableList[GlobalQName]]()

Review comment:
       Can you add a comment describing what this is? It's not clear what a 
stack of lists of qnames is used for.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/ExpressionEvaluatingParsers.scala
##########
@@ -158,25 +158,24 @@ 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 = {
+    log(LogLevel.Debug, "This is %s", toString) // important. Don't toString 
unless we have to log.

Review comment:
       Remove ebug message. I don't think this provides anything extra.

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/parsers/PState.scala
##########
@@ -265,17 +276,29 @@ 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
+  }

Review comment:
       Do we also need to modify changedVariableStack if a variable is read, 
since that does change the state of the variable? Or is resetting that handled 
somehow else?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -185,96 +224,84 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
    * shows that the variable has been read (state VariableRead), when the 
variable hadn't
    * previously been read yet.
    */
-  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.
    */
-  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)
+    if (stack.isDefined) {
+      stack.get.push(vrd.newVariableInstance)
+    }
+  }
+
+  def removeVariableInstance(vrd: VariableRuntimeData) {
+    val varQName = vrd.globalQName
+    val stack = vTable.get(varQName)
+    if (stack.isDefined) {
+      stack.get.pop
+    }

Review comment:
       Same here, should there be an assertion that stack is defined?

##########
File path: 
daffodil-runtime1/src/main/scala/org/apache/daffodil/processors/VariableMap1.scala
##########
@@ -185,96 +224,84 @@ class VariableMap private(vTable: Map[GlobalQName, 
List[List[Variable]]])
    * shows that the variable has been read (state VariableRead), when the 
variable hadn't
    * previously been read yet.
    */
-  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.
    */
-  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)
+    if (stack.isDefined) {
+      stack.get.push(vrd.newVariableInstance)
+    }
+  }
+
+  def removeVariableInstance(vrd: VariableRuntimeData) {
+    val varQName = vrd.globalQName
+    val stack = vTable.get(varQName)
+    if (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 (gqn, stack) if (stack.top.rd.external) => {
+        stack.top.rd.globalQName
       }
-      case (_, Nil) => Assert.invariantFailed("empty tier")
+      case (_, stack) if (stack.isEmpty) => Assert.invariantFailed("empty 
tier")

Review comment:
       Does stack.top throw an exception if the stack is empty? Seems like it's 
never possible to hit this case.




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

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


Reply via email to