stevedlawrence commented on a change in pull request #259: Incremental progress
on schema compilation space/speed issue.
URL: https://github.com/apache/incubator-daffodil/pull/259#discussion_r301558946
##########
File path:
daffodil-core/src/main/scala/org/apache/daffodil/dpath/Expression.scala
##########
@@ -943,155 +950,148 @@ sealed abstract class StepExpression(val step: String,
val pred: Option[Predicat
NodeInfo.ArrayIndex
}
+}
+
+sealed abstract class DownStepExpression(s: String, predArg:
Option[PredicateExpression])
+ extends StepExpression(s, predArg) {
+
override lazy val inherentType: NodeInfo.Kind = {
if (!isLastStep) NodeInfo.Complex
else {
- if (stepElement.optPrimType.isDefined) {
+ if (stepElements.head.optPrimType.isDefined) {
// simple type, so
- val pt = stepElement.optPrimType.get
+ val pt = stepElements.head.optPrimType.get
pt
} else {
NodeInfo.Complex
}
}
}
-
}
-//TODO: Is ".[i]" ever a valid expression in DFDL?
+// TODO: Is ".[i]" ever a valid expression in DFDL?
+// Yes. For an array element, .[1] is the first occurrence.
-case class Self(predArg: Option[PredicateExpression]) extends
StepExpression(null, predArg) {
+sealed abstract class SelfStepExpression(s: String, predArg:
Option[PredicateExpression])
+ extends DownStepExpression(s, predArg) {
override lazy val compiledDPath = new CompiledDPath(SelfMove +: conversions)
-
override def text = "."
- override lazy val stepElement: DPathElementCompileInfo =
- priorStep.map { _.stepElement }.getOrElse {
- // no prior step, so we're the first step
- this.compileInfo.elementCompileInfo.getOrElse {
- relPathErr()
+ override lazy val stepElements = stepElementDefs
+
+ protected def stepElementDefs: Seq[DPathElementCompileInfo] = {
+ if (this.isFirstStep) {
+ val ecs = compileInfo.elementCompileInfos
+ if (ecs.isEmpty) {
+ SDE("There is no enclosing element")
}
- }
+ ecs
+ } else
+ priorStep.get match {
+ case priorUp: UpStepExpression => SDE("Path '../.' is not allowed.")
+ case priorDown: DownStepExpression => priorDown.stepElements
+ case x => Assert.invariantFailed("Not recognized: " + x)
+ }
+ }
}
+case class Self(predArg: Option[PredicateExpression])
+ extends SelfStepExpression(null, predArg)
+
/**
* Different from Self in that it verifies the qName (s) is
* the name of the context.
*/
case class Self2(s: String, predArg: Option[PredicateExpression])
- extends StepExpression(s, predArg) {
+ extends SelfStepExpression(s, predArg) {
requiredEvaluations(stepQName)
- override lazy val compiledDPath = new CompiledDPath(SelfMove +: conversions)
+ override def stepElementDefs: Seq[DPathElementCompileInfo] = {
+ val cis = super.stepElementDefs
+ cis.map { ci =>
+ if (!ci.namedQName.matches(stepQName))
+ ci.noMatchError(stepQName)
+ ci
+ }
+ }
+}
- override def text = "."
+sealed abstract class UpStepExpression(s: String, predArg:
Option[PredicateExpression])
+ extends StepExpression(s, predArg) {
+
+ override def text = ".." // + "{" + stepElement.path + "}"
- override lazy val stepElement: DPathElementCompileInfo = {
- val ci = priorStep.map { _.stepElement }.getOrElse {
- // no prior step, so we're the first step
- this.compileInfo.elementCompileInfo.getOrElse {
- relPathErr()
+ // Combination of lazy val and a protected def is an idiom
+ // that enables a lazy calculation to call super.
+ //
+ final override lazy val stepElements = stepElementsDef
+
+ protected def stepElementsDef: Seq[DPathElementCompileInfo] = {
+ val res =
+ if (isFirstStep) {
+ Assert.invariant(!isAbsolutePath)
+ //
+ // This looks like 2 hops up, but it is really 1 hop up.
+ // first position ourselves on nearest enclosing element, or self if
+ // we are an element.
+ // Then take the enclosing elements to get ".." upward move.
+ //
+ val p = compileInfo.elementCompileInfos.flatMap {
_.enclosingElementCompileInfos }
+ p.headOption.getOrElse { relPathErr() }
+ p
+ } else {
+ val ps = priorStep.get
+ ps.stepElements.flatMap { _.enclosingElementCompileInfos }
}
- }
- if (!ci.namedQName.matches(stepQName))
- ci.noMatchError(stepQName)
- ci
+ res
}
+ override lazy val inherentType: NodeInfo.Kind = NodeInfo.Complex
}
-case class Up(predArg: Option[PredicateExpression]) extends
StepExpression(null, predArg) {
+case class Up(predArg: Option[PredicateExpression]) extends
UpStepExpression(null, predArg) {
+
override lazy val compiledDPath = {
- if (isLastStep && stepElement.isArray && targetType == NodeInfo.Array) {
+ if (isLastStep && stepElements.forall { _.isArray } && targetType ==
NodeInfo.Array) {
new CompiledDPath(UpMoveArray)
} else {
new CompiledDPath(UpMove)
}
}
- override def text = ".." // + "{" + stepElement.path + "}"
-
- override lazy val stepElement: DPathElementCompileInfo = {
- if (isFirstStep) {
- Assert.invariant(!isAbsolutePath)
- val sc = this.compileInfo
- // if we are some component inside an element then we
- // need to get the element surrounding first, then go up one.
- val e = sc.elementCompileInfo
- val e1 = e.getOrElse {
- SDE("No enclosing element.")
- }
- val e2 = e1.enclosingElementCompileInfo
- val e3 = e2.getOrElse {
- relPathErr()
- }
- e3
- } else {
- // not first, so
- val ps = priorStep
- val ps2 = ps.map { _.stepElement }
- val ps3 = ps2.getOrElse {
- relPathErr()
- }
- val ps4 = ps3.enclosingElementCompileInfo
- val ps5 = ps4.getOrElse {
- relPathErr()
- }
- ps5
- }
- }
}
/**
* Different from Up in that it verifies the qName (s) is the
* name of the parent node.
*/
case class Up2(s: String, predArg: Option[PredicateExpression])
- extends StepExpression(s, predArg) {
+ extends UpStepExpression(s, predArg) {
+
override lazy val compiledDPath = new CompiledDPath(UpMove)
requiredEvaluations(stepQName)
override def text = ".." // + "{" + stepElement.path + "}"
- override lazy val stepElement: DPathElementCompileInfo = {
- val ci = if (isFirstStep) {
- Assert.invariant(!isAbsolutePath)
- val sc = this.compileInfo
- // if we are some component inside an element then we
- // need to get the element surrounding first, then go up one.
- val e = sc.elementCompileInfo
- val e1 = e.getOrElse {
- SDE("No enclosing element.")
- }
- val e2 = e1.enclosingElementCompileInfo
- val e3 = e2.getOrElse {
- relPathErr()
- }
- e3
- } else {
- // not first, so
- val ps = priorStep
- val ps2 = ps.map { _.stepElement }
- val ps3 = ps2.getOrElse {
- relPathErr()
- }
- val ps4 = ps3.enclosingElementCompileInfo
- val ps5 = ps4.getOrElse {
- relPathErr()
- }
- ps5
+ override protected def stepElementsDef: Seq[DPathElementCompileInfo] = {
+ val p = super.stepElementsDef
+ val res = p.filter { ci => ci.namedQName.matches(stepQName) }
+ if (res.isEmpty) {
+ p.headOption.foreach { _.noMatchError(stepQName) } // error about first
one.
+ }
+ if (res.length > 1) {
+ // There are multiple upward paths to elements of the same name.
+ // So long as they all ultimately type-check, this is ok.
Review comment:
This logic looks slightly differeent than Self2, but I think it's actually
checking the same thing. Can the logic here be made the same?
----------------------------------------------------------------
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]
With regards,
Apache Git Services