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


##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -744,21 +744,41 @@ abstract class PathExpression() extends Expression {
    * about an array such as its current count.
    */
   lazy val isPathToOneWholeArray: Boolean = {
-    if (steps == Nil) false // root is never an array
+    isNotRootOrSelf &&
+    steps.last.isArray &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  /**
+   * Path to a single array element without a [N] predicate qualifying it
+   * or to an optional element.
+   *
+   * That is, the kind of path expression used to access information
+   * about an array or optional such as its current count.
+   */
+  lazy val isPathToOneWholeArrayOrOptional: Boolean = {
+    isNotRootOrSelf &&
+    (steps.last.isArray || steps.last.isOptional) &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  private lazy val isNotRootOrSelf: Boolean = {
     if (steps == Nil) false // root is never an array
     else if (isSelf) false // self path is never an array
-    else
-      steps.last.isArray &&
-      !steps.last.pred.isDefined && // last cannot have a [N] pred
-      steps.dropRight(1).forall {
-        // for all the prior steps
-        // if they mention an array element, there
-        // must be a [N] predicate.
-        step =>
-          if (step.isInstanceOf[Up]) true
-          else if (step.isArray) step.pred.isDefined
-          else true
-      }
+    else true
+  }
+
+  private lazy val priorStepsHaveIndexingPredicates: Boolean = {
+    steps.last.pred.isEmpty && // last cannot have a [N] pred

Review Comment:
   Based on the name of this val, it feels like this `last.pred.isEmpty` check 
shouldn't be done here since. For example, you could want to ask if 
"priorStepsHaveIndxingPredicates" for a path not used in fn-count. In fact, the 
answer to that question should always be true for all path expressions, right? 
For all path expressions, all init steps must have a predicate if they are 
arrays/optional.
   
   So maybe the last.pred.isEmpty check wants to be somewhere else, and all 
path expressions need something like
   
   ```
   SDEUnless(priorStepsHaveIndexingPredicates)
   ```



##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -744,21 +744,41 @@ abstract class PathExpression() extends Expression {
    * about an array such as its current count.
    */
   lazy val isPathToOneWholeArray: Boolean = {
-    if (steps == Nil) false // root is never an array
+    isNotRootOrSelf &&
+    steps.last.isArray &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  /**
+   * Path to a single array element without a [N] predicate qualifying it
+   * or to an optional element.
+   *
+   * That is, the kind of path expression used to access information
+   * about an array or optional such as its current count.
+   */
+  lazy val isPathToOneWholeArrayOrOptional: Boolean = {
+    isNotRootOrSelf &&
+    (steps.last.isArray || steps.last.isOptional) &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  private lazy val isNotRootOrSelf: Boolean = {
     if (steps == Nil) false // root is never an array
     else if (isSelf) false // self path is never an array
-    else
-      steps.last.isArray &&
-      !steps.last.pred.isDefined && // last cannot have a [N] pred
-      steps.dropRight(1).forall {
-        // for all the prior steps
-        // if they mention an array element, there
-        // must be a [N] predicate.
-        step =>
-          if (step.isInstanceOf[Up]) true
-          else if (step.isArray) step.pred.isDefined
-          else true
-      }
+    else true
+  }
+
+  private lazy val priorStepsHaveIndexingPredicates: Boolean = {
+    steps.last.pred.isEmpty && // last cannot have a [N] pred
+    steps.dropRight(1).forall {

Review Comment:
   I believe this is the same as `steps.init.forall { ...`. Related, I wonder 
if using `initSteps` instead of `priorSteps` might be more clear, since "init" 
means a specific thing, at least in the scala world?



##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -744,21 +744,41 @@ abstract class PathExpression() extends Expression {
    * about an array such as its current count.
    */
   lazy val isPathToOneWholeArray: Boolean = {
-    if (steps == Nil) false // root is never an array
+    isNotRootOrSelf &&
+    steps.last.isArray &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  /**
+   * Path to a single array element without a [N] predicate qualifying it
+   * or to an optional element.
+   *
+   * That is, the kind of path expression used to access information
+   * about an array or optional such as its current count.
+   */
+  lazy val isPathToOneWholeArrayOrOptional: Boolean = {
+    isNotRootOrSelf &&
+    (steps.last.isArray || steps.last.isOptional) &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  private lazy val isNotRootOrSelf: Boolean = {
     if (steps == Nil) false // root is never an array
     else if (isSelf) false // self path is never an array
-    else
-      steps.last.isArray &&
-      !steps.last.pred.isDefined && // last cannot have a [N] pred
-      steps.dropRight(1).forall {
-        // for all the prior steps
-        // if they mention an array element, there
-        // must be a [N] predicate.
-        step =>
-          if (step.isInstanceOf[Up]) true
-          else if (step.isArray) step.pred.isDefined
-          else true
-      }
+    else true
+  }
+
+  private lazy val priorStepsHaveIndexingPredicates: Boolean = {
+    steps.last.pred.isEmpty && // last cannot have a [N] pred
+    steps.dropRight(1).forall {
+      // for all the prior steps
+      // if they mention an array element, there
+      // must be a [N] predicate.
+      step =>
+        if (step.isInstanceOf[Up]) true

Review Comment:
   What about `Up2`, `Self`, and `Self2`? Do we need exceptions for those other 
kinds steps?



##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -969,16 +989,27 @@ sealed abstract class StepExpression(val step: String, 
val pred: Option[Predicat
   }
 
   final lazy val isArray: Boolean = {
-    val (arrays, scalars) = stepElements.partition { _.isArray }
-    (arrays.length, scalars.length) match {
+    checkAmbiguousPath
+    stepElements.exists(_.isArray)
+  }
+
+  final lazy val isOptional: Boolean = {
+    checkAmbiguousPath
+    stepElements.exists(_.isOptional)
+  }

Review Comment:
   These feel potentially confusing. I feel like our definition is usually an 
element is either an array OR an optional, but never both. But with step 
expressions, because of ambiguities, it's possible for isArray and isOptional 
to both be true. That seems reasonable considering the way dpath works and 
supports this ambiguity, but it might be worth adding a comment.



##########
daffodil-test/src/test/resources/org/apache/daffodil/section23/dfdl_functions/Functions.tdml:
##########
@@ -5150,6 +5147,51 @@
       </xs:complexType>
     </xs:element>
 
+    <xs:element name="count07">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" minOccurs="2" maxOccurs="2" 
dfdl:representation="binary" />
+          <xs:element name="count" type="xs:int" dfdl:representation="binary" 
dfdl:inputValueCalc="{ fn:count(../ex:int) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="count08">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" dfdl:representation="binary" />
+          <xs:element name="count" type="xs:int" dfdl:representation="binary" 
dfdl:inputValueCalc="{ fn:count(../ex:int) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="count09">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" minOccurs="0" maxOccurs="1" 
dfdl:representation="binary" />
+          <xs:element name="count" type="xs:int" dfdl:representation="binary" 
dfdl:inputValueCalc="{ fn:count(../ex:int) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="count10">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" dfdl:representation="binary" />
+          <xs:element name="count" type="xs:int" dfdl:representation="binary" 
dfdl:outputValueCalc="{ fn:count(../ex:int) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
+
+    <xs:element name="count11">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="int" type="xs:int" dfdl:representation="binary" />
+          <xs:element name="count" type="xs:int" dfdl:representation="binary" 
dfdl:occursCountKind="expression" dfdl:occursCount="{ fn:count(../ex:int) }" />
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>

Review Comment:
   Can you add a test that has a conditional inside the fn:count expression? 
Maybe some with good or bad ambiguities and mixing arrays and optionals? e.g.
   ```xquery
   fn:count(if (../foo) then ../someArray else ../someOptional)
   ```



##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -744,21 +744,41 @@ abstract class PathExpression() extends Expression {
    * about an array such as its current count.
    */
   lazy val isPathToOneWholeArray: Boolean = {
-    if (steps == Nil) false // root is never an array
+    isNotRootOrSelf &&
+    steps.last.isArray &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  /**
+   * Path to a single array element without a [N] predicate qualifying it
+   * or to an optional element.
+   *
+   * That is, the kind of path expression used to access information
+   * about an array or optional such as its current count.
+   */
+  lazy val isPathToOneWholeArrayOrOptional: Boolean = {
+    isNotRootOrSelf &&
+    (steps.last.isArray || steps.last.isOptional) &&
+    priorStepsHaveIndexingPredicates
+  }
+
+  private lazy val isNotRootOrSelf: Boolean = {
     if (steps == Nil) false // root is never an array
     else if (isSelf) false // self path is never an array
-    else
-      steps.last.isArray &&
-      !steps.last.pred.isDefined && // last cannot have a [N] pred
-      steps.dropRight(1).forall {
-        // for all the prior steps
-        // if they mention an array element, there
-        // must be a [N] predicate.
-        step =>
-          if (step.isInstanceOf[Up]) true
-          else if (step.isArray) step.pred.isDefined
-          else true
-      }
+    else true
+  }
+
+  private lazy val priorStepsHaveIndexingPredicates: Boolean = {
+    steps.last.pred.isEmpty && // last cannot have a [N] pred
+    steps.dropRight(1).forall {
+      // for all the prior steps
+      // if they mention an array element, there
+      // must be a [N] predicate.
+      step =>
+        if (step.isInstanceOf[Up]) true
+        else if (step.isArray) step.pred.isDefined // no special case to check 
for optional

Review Comment:
   Optional elements do not need a predicate? I thought DPath treated them like 
arrays so they must have a `[1]` predicate?



##########
daffodil-core/src/main/scala/org/apache/daffodil/core/dpath/Expression.scala:
##########
@@ -2295,6 +2295,18 @@ abstract class FunctionCallBase(
     res
   }
 
+  final def checkArgArray(): Unit = {
+    lazy val isArray = expressions.head match {
+      case rpe: RelativePathExpression => rpe.steps.last.isArray
+      case rpe: RootPathExpression => rpe.steps.last.isArray
+      case _ => true

Review Comment:
   I don't think my original comment is addressed? Or are you saying there is a 
check somewhere else within the type system that makes sure things work as 
expected? Are there tests for this?



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