stevedlawrence commented on code in PR #1070:
URL: https://github.com/apache/daffodil/pull/1070#discussion_r1299973051


##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/TypeCalculator.scala:
##########
@@ -21,135 +21,98 @@ import scala.collection.immutable.HashMap
 import scala.collection.immutable.HashSet
 
 import org.apache.daffodil.lib.exceptions.Assert
-import org.apache.daffodil.lib.util.Delay
 import org.apache.daffodil.lib.util.Maybe
-import org.apache.daffodil.lib.util.Maybe.One
 import org.apache.daffodil.lib.util.Numbers
-import org.apache.daffodil.lib.xml.QNameBase
-import org.apache.daffodil.runtime1.dpath.DState
 import org.apache.daffodil.runtime1.dpath.NodeInfo
-import org.apache.daffodil.runtime1.dsom.CompiledExpression
 import org.apache.daffodil.runtime1.infoset.DataValue
 import org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitive
 import 
org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitiveNullable
 import org.apache.daffodil.runtime1.processors.parsers.PState
 import org.apache.daffodil.runtime1.processors.parsers.ParseError
 import org.apache.daffodil.runtime1.processors.unparsers.UState
 
+/**
+ * A TypeCalculator is a class that performs some kind of conversion from one 
DataValuePrimitive
+ * to another DataValuePrimitive. In general, this is done to convert a parsed 
physical
+ * representation from the data to a logical representation that goes in the 
infoset, and
+ * reverse on unparse. The former is done in the inputTypeCalc functions, and 
the latter in the
+ * outputTypeCalc functions. Generally the conversion is to a different type 
(e.g. xs:int to
+ * xs:string), but this need not be the case.

Review Comment:
   I think we support all int-like types to string. I think only decimal, 
float, double, calendar, etc. are not supported. We do also support string to 
string using the identity type calculator, which was untested but I did add a 
test for. I'm not sure what the use case for this was, but the logic was there.



##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/TypeCalculator.scala:
##########
@@ -223,159 +186,34 @@ class KeysetValueTypeCalculatorUnordered(
   override def inputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = {
+  ): Either[Error, DataValuePrimitiveNullable] = {
     valueMap.get(x) match {
-      case Some(a) => (a, Maybe.Nope)
-      case None =>
-        (DataValue.NoValue, One(s"Value ${x} not found in enumeration 
dfdlx:repValues"))
+      case Some(a) => Right(a)
+      case None => Left(s"Value ${x} not found in enumeration dfdlx:repValues")
     }
   }
 
   override def outputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = {
+  ): Either[Error, DataValuePrimitiveNullable] = {
     unparseMap.get(x) match {
-      case Some(v) => (v, Maybe.Nope)
-      case None => (DataValue.NoValue, One(s"Value ${x} not found in 
enumeration"))
+      case Some(v) => Right(v)
+      case None => Left(s"Value ${x} not found in enumeration")
     }
   }
 
 }
 
-class ExpressionTypeCalculator(
-  private val maybeInputTypeCalcDelay: 
Delay[Maybe[CompiledExpression[AnyRef]]],
-  private val maybeOutputTypeCalcDelay: 
Delay[Maybe[CompiledExpression[AnyRef]]],
-  srcType: NodeInfo.Kind,
-  dstType: NodeInfo.Kind,
-) extends TypeCalculator(srcType, dstType) {
-
-  /*
-   * objects with Delay arguments for functional programming construction of
-   * cyclic graphs, need a way to force the delays, resulting in an ordinary
-   * (though cyclic) data structure.
-   */
-  final override def initialize(): Unit = {
-    super.initialize()
-    maybeInputTypeCalc
-    maybeOutputTypeCalc
-  }
-
-  override def supportsParse = maybeInputTypeCalc.isDefined
-  override def supportsUnparse = maybeOutputTypeCalc.isDefined
-
-  /*
-   * Compiling DPath expressions may need to evaluate typeCalculators in order 
to lookup their srcType and dstType.
-   * To prevent circular dependencies, this means that the underlying 
expressions must be lazy.
-   *
-   * Since these fields must be lazy, we cannot use them to determine 
supportsParse or supportUnparse
-   */
-  lazy val maybeInputTypeCalc = maybeInputTypeCalcDelay.value
-  lazy val maybeOutputTypeCalc = maybeOutputTypeCalcDelay.value
-
-  // The class TypeValueCalc will verify that supports(Un)Parse is true when 
nessasary
-  // Therefore, if we ever call the below functions, we know that the relevent 
Maybe object is defined.
-
-  // TODO, pass x into DPath state
-
+class IdentityTypeCalculator(srcType: NodeInfo.Kind) extends 
TypeCalculator(srcType, srcType) {
   override def inputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) =
-    Assert.invariantFailed(
-      "inputTypeCalc not implemented on ExpressionTypeCalculator. Call the 
more specialized forms directly",
-    )
+  ): Either[Error, DataValuePrimitiveNullable] = Right(x)
   override def outputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) =
-    Assert.invariantFailed(
-      "outputTypeCalc not implemented on ExpressionTypeCalculator. Call the 
more specialized forms directly",
-    )
-
-  override def inputTypeCalcParse(
-    state: PState,
-    context: RuntimeData,
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): DataValuePrimitiveNullable = {
-    val dstate = state.dState
-    val oldRepValue = dstate.repValue
-    val oldLogicalValue = dstate.logicalValue
-    dstate.repValue = x
-    dstate.logicalValue = DataValue.NoValue
-
-    val ans = Maybe(maybeInputTypeCalc.get.evaluate(state))
-
-    dstate.repValue = oldRepValue
-    dstate.logicalValue = oldLogicalValue
-    if (ans.isDefined) {
-      DataValue.unsafeFromAnyRef(ans.get)
-    } else {
-      DataValue.NoValue;
-    }
-  }
-  override def outputTypeCalcUnparse(
-    state: UState,
-    context: RuntimeData,
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): DataValuePrimitiveNullable = {
-    val dstate = state.dState
-    val oldRepValue = dstate.repValue
-    val oldLogicalValue = dstate.logicalValue
-    dstate.repValue = DataValue.NoValue
-    dstate.logicalValue = x
-
-    val ans = maybeOutputTypeCalc.get.evaluate(state)
-
-    dstate.repValue = oldRepValue
-    dstate.logicalValue = oldLogicalValue
-    DataValue.unsafeFromAnyRef(ans)
-  }
-
-  override def inputTypeCalcRun(
-    dstate: DState,
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): Unit = {
-    val oldRepValue = dstate.repValue
-    val oldLogicalValue = dstate.logicalValue
-    dstate.repValue = x
-    dstate.logicalValue = DataValue.NoValue
-
-    maybeInputTypeCalc.get.run(dstate)
-
-    dstate.repValue = oldRepValue
-    dstate.logicalValue = oldLogicalValue
-
-  }
-  override def outputTypeCalcRun(
-    dstate: DState,
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): Unit = {
-    val oldRepValue = dstate.repValue
-    val oldLogicalValue = dstate.logicalValue
-    dstate.repValue = DataValue.NoValue
-    dstate.logicalValue = x
-
-    maybeOutputTypeCalc.get.run(dstate)
-
-    dstate.repValue = oldRepValue
-    dstate.logicalValue = oldLogicalValue
-  }
-}
-
-class IdentifyTypeCalculator(srcType: NodeInfo.Kind) extends 
TypeCalculator(srcType, srcType) {
-  override def inputTypeCalc(
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = (x, Maybe.Nope)
-  override def outputTypeCalc(
-    x: DataValuePrimitive,
-    xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = (x, Maybe.Nope)
+  ): Either[Error, DataValuePrimitiveNullable] = Right(x)
 }
 
 class UnionTypeCalculator(

Review Comment:
   > Do we allow simple type unions as a repType or can one combine enums with 
a rep type using unions?
   
   We allow a simple type to be a union of other simple types which can have 
repTypes defined. We only have one tdml test for it (and a couple of unit 
tests) so I'm not sure how heavily it's tested or used, but it seems to work.
   
   Daffodil also supports unions in general (see union.tdml), so maybe it was 
added for that reason?
   
   If we think this repType unions is unnecessary I can remove it to, I think 
it would be pretty straightforward and I'd rather do it now while it's all 
fresh in my memory. But I can do it in a separate PR if that's preferred.



##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/TypeCalculator.scala:
##########
@@ -21,135 +21,98 @@ import scala.collection.immutable.HashMap
 import scala.collection.immutable.HashSet
 
 import org.apache.daffodil.lib.exceptions.Assert
-import org.apache.daffodil.lib.util.Delay
 import org.apache.daffodil.lib.util.Maybe
-import org.apache.daffodil.lib.util.Maybe.One
 import org.apache.daffodil.lib.util.Numbers
-import org.apache.daffodil.lib.xml.QNameBase
-import org.apache.daffodil.runtime1.dpath.DState
 import org.apache.daffodil.runtime1.dpath.NodeInfo
-import org.apache.daffodil.runtime1.dsom.CompiledExpression
 import org.apache.daffodil.runtime1.infoset.DataValue
 import org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitive
 import 
org.apache.daffodil.runtime1.infoset.DataValue.DataValuePrimitiveNullable
 import org.apache.daffodil.runtime1.processors.parsers.PState
 import org.apache.daffodil.runtime1.processors.parsers.ParseError
 import org.apache.daffodil.runtime1.processors.unparsers.UState
 
+/**
+ * A TypeCalculator is a class that performs some kind of conversion from one 
DataValuePrimitive
+ * to another DataValuePrimitive. In general, this is done to convert a parsed 
physical
+ * representation from the data to a logical representation that goes in the 
infoset, and
+ * reverse on unparse. The former is done in the inputTypeCalc functions, and 
the latter in the
+ * outputTypeCalc functions. Generally the conversion is to a different type 
(e.g. xs:int to
+ * xs:string), but this need not be the case.
+ *
+ * The different TypeCalculator implementations allow for different 
conversions based on
+ * different properties provided in the schema (e.g. repType, enumerations, 
unions, repValues,

Review Comment:
   You can have two different simple types with enumerations/repValues defined 
that are unioned by another simple type. Here's a test we have of this:
   
   
https://github.com/apache/daffodil/blob/main/daffodil-test/src/test/resources/org/apache/daffodil/extensions/type_calc/inputTypeCalc.tdml#L66-L80
   
   I'm not sure if we actually use unions with repValues in any schemas, but I 
do see use a xs:unions.



##########
daffodil-runtime1/src/main/scala/org/apache/daffodil/runtime1/processors/TypeCalculator.scala:
##########
@@ -223,159 +186,34 @@ class KeysetValueTypeCalculatorUnordered(
   override def inputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = {
+  ): Either[Error, DataValuePrimitiveNullable] = {
     valueMap.get(x) match {
-      case Some(a) => (a, Maybe.Nope)
-      case None =>
-        (DataValue.NoValue, One(s"Value ${x} not found in enumeration 
dfdlx:repValues"))
+      case Some(a) => Right(a)
+      case None => Left(s"Value ${x} not found in enumeration dfdlx:repValues")
     }
   }
 
   override def outputTypeCalc(
     x: DataValuePrimitive,
     xType: NodeInfo.Kind,
-  ): (DataValuePrimitiveNullable, Maybe[Error]) = {
+  ): Either[Error, DataValuePrimitiveNullable] = {
     unparseMap.get(x) match {
-      case Some(v) => (v, Maybe.Nope)
-      case None => (DataValue.NoValue, One(s"Value ${x} not found in 
enumeration"))
+      case Some(v) => Right(v)
+      case None => Left(s"Value ${x} not found in enumeration")
     }
   }
 
 }
 
-class ExpressionTypeCalculator(
-  private val maybeInputTypeCalcDelay: 
Delay[Maybe[CompiledExpression[AnyRef]]],
-  private val maybeOutputTypeCalcDelay: 
Delay[Maybe[CompiledExpression[AnyRef]]],
-  srcType: NodeInfo.Kind,
-  dstType: NodeInfo.Kind,
-) extends TypeCalculator(srcType, dstType) {
-
-  /*
-   * objects with Delay arguments for functional programming construction of
-   * cyclic graphs, need a way to force the delays, resulting in an ordinary
-   * (though cyclic) data structure.
-   */
-  final override def initialize(): Unit = {
-    super.initialize()
-    maybeInputTypeCalc
-    maybeOutputTypeCalc
-  }
-
-  override def supportsParse = maybeInputTypeCalc.isDefined
-  override def supportsUnparse = maybeOutputTypeCalc.isDefined
-
-  /*
-   * Compiling DPath expressions may need to evaluate typeCalculators in order 
to lookup their srcType and dstType.
-   * To prevent circular dependencies, this means that the underlying 
expressions must be lazy.
-   *
-   * Since these fields must be lazy, we cannot use them to determine 
supportsParse or supportUnparse
-   */
-  lazy val maybeInputTypeCalc = maybeInputTypeCalcDelay.value
-  lazy val maybeOutputTypeCalc = maybeOutputTypeCalcDelay.value
-
-  // The class TypeValueCalc will verify that supports(Un)Parse is true when 
nessasary
-  // Therefore, if we ever call the below functions, we know that the relevent 
Maybe object is defined.
-
-  // TODO, pass x into DPath state
-
+class IdentityTypeCalculator(srcType: NodeInfo.Kind) extends 
TypeCalculator(srcType, srcType) {

Review Comment:
   It was previously used when the repType and simpleType bases were the same, 
for example both are xs:string. I'm not actually sure if int to int works, but 
I can add a test for it.
   
   I'm also not sure the use case for it, but I was thinking maybe it might be 
useful to use the same rep type for multiple logical types. E.g.
   
   ```xml
   <simpleType name="rep" dfdl:properties="...">
     <restriction base="xs:int" />
   </simpleType>
   
   <simplType name="intToString" dfdl:repType="rep">
     <restriction base="xs:string">
       <enumeration value="1" dfdl:repValues="1" />
       ...
     </restriction>
   </simpleType>
   
   <simplType name="intToInt" dfdl:repType="rep">
     <restriction base="xs:int">
       <enumeration value="1" dfdl:repValues="1" />
       ...
     </restriction>
   </simpleType>
   ```
   I'm not sure if this is actually useful, or even if it works, but I assumed 
there was a reason for the identity transform.



-- 
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.

To unsubscribe, e-mail: [email protected]

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

Reply via email to