blob: 2fe900ee0a06dddd07db1c4e6821fb3375d0560d [file] [log] [blame]
/* Copyright (c) 2012-2015 Tresys Technology, LLC. All rights reserved.
*
* Developed by: Tresys Technology, LLC
* http://www.tresys.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal with
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimers.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the names of Tresys Technology, nor the names of its contributors
* may be used to endorse or promote products derived from this Software
* without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
* SOFTWARE.
*/
package edu.illinois.ncsa.daffodil.dpath
import edu.illinois.ncsa.daffodil.dsom.oolag.OOLAG.OOLAGHost
import edu.illinois.ncsa.daffodil.exceptions._
import edu.illinois.ncsa.daffodil.dsom._
import scala.xml.NamespaceBinding
import edu.illinois.ncsa.daffodil.xml._
import edu.illinois.ncsa.daffodil.processors._
import edu.illinois.ncsa.daffodil.xml.RefQName
import scala.util.{ Success, Failure }
import edu.illinois.ncsa.daffodil.dsom.RelativePathPastRootError
import edu.illinois.ncsa.daffodil.equality._
import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInt }
/**
* Root class of the type hierarchy for the AST nodes used when we
* compile a DPath Expression.
*
* All the work in 'compiling' the DPath expressions happens on methods
* of these objects.
*
* This is the OOLAG pattern again.
*/
abstract class Expression extends OOLAGHost
with ImplementsThrowsOrSavesSDE {
/**
* Use several calls instead of one, because then OOLAG will try them
* all separately, and perhaps if you're lucky report more errors.
*/
requiredEvaluations(parent)
requiredEvaluations(targetType)
requiredEvaluations(inherentType)
requiredEvaluations(isTypeCorrect)
requiredEvaluations(compiledDPath_)
/**
* Override where we traverse/access elements.
*/
def leafContentLengthReferencedElements = ReferencedElementInfos.None
def leafValueLengthReferencedElements = ReferencedElementInfos.None
def contentReferencedElementInfos: Set[DPathElementCompileInfo] = {
val clds = children
val cldsreis: Set[DPathElementCompileInfo] =
clds.foldLeft(ReferencedElementInfos.None) {
(s, item) =>
{
val ireis = item.contentReferencedElementInfos
s.union(ireis)
}
}
val res = cldsreis ++
leafContentLengthReferencedElements
res
}
def valueReferencedElementInfos: Set[DPathElementCompileInfo] = {
val clds = children
val cldsreis: Set[DPathElementCompileInfo] =
clds.foldLeft(ReferencedElementInfos.None) {
(s, item) =>
{
val ireis = item.valueReferencedElementInfos
s.union(ireis)
}
}
val res = cldsreis ++
leafValueLengthReferencedElements
res
}
// This split allows overrides of this lazy val to still reuse this
// (super.isTypeCorrect doesn't work inside an overridden lazy val.)
lazy val isTypeCorrect: Boolean = checkTypeCorrectness
//
// override for other checking beyond what is needed to do conversions
//
protected def checkTypeCorrectness = true
def text: String
def hasReferenceTo(elem: DPathElementCompileInfo): Boolean = {
children.exists(_.hasReferenceTo(elem))
}
/**
* TODO: get more precise line and column information for
* pointing at sub-regions of large DPath expressions
*
* We're parsing them, so we should have access to specific locations
* within the expression.
*/
lazy val schemaFileLocation = compileInfo.schemaFileLocation
lazy val conversions = {
val inh = inherentType
val tt = targetType
val res = Conversion.conversionOps(inh, tt, this)
res
}
private def compiledDPath_ = LV('compiledDPath) { compiledDPath }
def compiledDPath: CompiledDPath
final lazy val parentOpt = Option(parent)
final lazy val parent: Expression = {
if (!this.hasOOLAGRootSetup)
Assert.invariantFailed("not setup")
this match {
case w: WholeExpression => null
case _ => this.oolagContext.asInstanceOf[Expression]
}
}
lazy val compileInfo: DPathCompileInfo = // override in WholeExpression
parent.compileInfo
final lazy val enclosingElementCompileInfo: Option[DPathElementCompileInfo] =
compileInfo.enclosingElementCompileInfo
final lazy val rootElement: DPathElementCompileInfo = {
enclosingElementCompileInfo.map {
_.rootElement
}.getOrElse {
compileInfo.elementCompileInfo.getOrElse {
Assert.invariantFailed("root doesn't have compile info")
}
}
}
lazy val namespaces: NamespaceBinding = parent.namespaces
def children: Seq[Expression]
def setContextsForChildren(context: OOLAGHost = this) {
children.foreach {
c =>
c.setOOLAGContext(context);
c.setContextsForChildren(c);
}
}
/**
* The target type is defined for simple types. It gives the type that
* the expression must return. It is the type that the context is expecting
* or in attribute-grammar terms, the inherited type.
* <p>
* For inputValueCalc, this is the type of the element carrying that property.
* <p>
* For setVariable and newVariableInstance, it is the type of the variable.
* <p>
* For dfdl:length and dfdl:occursCount it is UnsignedInt
* <p>
* For the test properties of assert/discriminator it is Boolean
* <p>
* For the following properties, when their value is an expression
* the xsdTargetType is NonEmptyString
* byteOrder,
* encoding,
* outputNewLine,
* escapeCharacter,
* escapeEscapeCharacter,
* initiator,
* terminator,
* separator,
* textStandardDecimalSeparator,
* textStandardGroupingSeparator,
* textStandardExponentRep,
* binaryFloatRep,
* textBooleanFalseRep,
* textBooleanTrueRep
*/
lazy val targetType: NodeInfo.Kind = {
// this will only be called for expressions that have a parent
Assert.usage(parentOpt.isDefined)
val p = parentOpt.get
val res = p.targetTypeForSubexpression(this)
res
}
def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind
/**
* The inherent type is the bottom-up type. That is, the type that
* is determined by examining the expression, or in attribute-grammar speak,
* the synthetic type.
*
* For "foo" it is string, for
* 5.0 it is double, and for 5 it is integer. For an identifier such
* as b in /a/b, it is the simple type of the element declaration for b.
*
* The inherent type can be a complex type, as in the foo in /foo/bar.
* That is why this is not the same system of type-kind values as the
* xsdTargetType
*/
def inherentType: NodeInfo.Kind
def resolveRef(qnameString: String) = {
QName.resolveRef(qnameString, namespaces).recover {
case _: Throwable =>
SDE("The prefix of '%s' has no corresponding namespace definition.", qnameString)
}.get
}
}
abstract class ExpressionLists(val lst: List[Expression])
extends Expression {
override lazy val children = lst
}
trait BinaryExpMixin { self: ExpressionLists =>
Assert.invariant(children.length == 2)
def left = children(0)
def right = children(1)
def op: String
def text = left.text + " " + op + " " + right
}
trait BooleanExpression extends BinaryExpMixin {
self: ExpressionLists =>
override lazy val compiledDPath = {
val leftDPath = left.compiledDPath
val rightDPath = right.compiledDPath
val c = conversions
val res = new CompiledDPath(BooleanOp(op, leftDPath, rightDPath) +: c)
res
}
override def targetTypeForSubexpression(subexp: Expression): NodeInfo.Kind = NodeInfo.Boolean
override lazy val inherentType: NodeInfo.Kind = NodeInfo.Boolean
}
case class ComparisonExpression(op: String, adds: List[Expression])
extends ExpressionLists(adds) with BooleanExpression {
lazy val compareOp: CompareOpBase = {
import NodeInfo.PrimType._
import NodeInfo.ArrayIndex
(op, convergedArgType) match {
case ("<", _) => subsetError("Unsupported operation '%s'. Use 'lt' instead.", op)
case (">", _) => subsetError("Unsupported operation '%s'. Use 'gt' instead.", op)
case ("<=", _) => subsetError("Unsupported operation '%s'. Use 'le' instead.", op)
case (">=", _) => subsetError("Unsupported operation '%s'. Use 'ge' instead.", op)
case ("=", _) => subsetError("Unsupported operation '%s'. Use 'eq' instead.", op)
case ("!=", _) => subsetError("Unsupported operation '%s'. Use 'ne' instead.", op)
case ("eq", _) => EQ_Compare
case ("ne", _) => NE_Compare
case ("lt", Boolean) => LT_Boolean
case ("gt", Boolean) => GT_Boolean
case ("le", Boolean) => LE_Boolean
case ("ge", Boolean) => GE_Boolean
case ("lt", Date) => LT_Date
case ("gt", Date) => GT_Date
case ("le", Date) => LE_Date
case ("ge", Date) => GE_Date
case ("lt", Time) => LT_Time
case ("gt", Time) => GT_Time
case ("le", Time) => LE_Time
case ("ge", Time) => GE_Time
case ("lt", DateTime) => LT_DateTime
case ("gt", DateTime) => GT_DateTime
case ("le", DateTime) => LE_DateTime
case ("ge", DateTime) => GE_DateTime
case ("lt", String) => LT_String
case ("gt", String) => GT_String
case ("le", String) => LE_String
case ("ge", String) => GE_String
case ("lt", Decimal) => LT_Decimal
case ("gt", Decimal) => GT_Decimal
case ("le", Decimal) => LE_Decimal
case ("ge", Decimal) => GE_Decimal
case ("lt", Integer) => LT_Integer
case ("gt", Integer) => GT_Integer
case ("le", Integer) => LE_Integer
case ("ge", Integer) => GE_Integer
case ("lt", NonNegativeInteger) => LT_NonNegativeInteger
case ("gt", NonNegativeInteger) => GT_NonNegativeInteger
case ("le", NonNegativeInteger) => LE_NonNegativeInteger
case ("ge", NonNegativeInteger) => GE_NonNegativeInteger
case ("lt", UnsignedLong) => LT_UnsignedLong
case ("gt", UnsignedLong) => GT_UnsignedLong
case ("le", UnsignedLong) => LE_UnsignedLong
case ("ge", UnsignedLong) => GE_UnsignedLong
case ("lt", Long) => LT_Long
case ("gt", Long) => GT_Long
case ("le", Long) => LE_Long
case ("ge", Long) => GE_Long
case ("lt", UnsignedInt) => LT_UnsignedInt
case ("gt", UnsignedInt) => GT_UnsignedInt
case ("le", UnsignedInt) => LE_UnsignedInt
case ("ge", UnsignedInt) => GE_UnsignedInt
case ("lt", ArrayIndex) => LT_UnsignedInt
case ("gt", ArrayIndex) => GT_UnsignedInt
case ("le", ArrayIndex) => LE_UnsignedInt
case ("ge", ArrayIndex) => GE_UnsignedInt
case ("lt", Int) => LT_Int
case ("gt", Int) => GT_Int
case ("le", Int) => LE_Int
case ("ge", Int) => GE_Int
case ("lt", UnsignedShort) => LT_UnsignedShort
case ("gt", UnsignedShort) => GT_UnsignedShort
case ("le", UnsignedShort) => LE_UnsignedShort
case ("ge", UnsignedShort) => GE_UnsignedShort
case ("lt", Short) => LT_Short
case ("gt", Short) => GT_Short
case ("le", Short) => LE_Short
case ("ge", Short) => GE_Short
case ("lt", UnsignedByte) => LT_UnsignedByte
case ("gt", UnsignedByte) => GT_UnsignedByte
case ("le", UnsignedByte) => LE_UnsignedByte
case ("ge", UnsignedByte) => GE_UnsignedByte
case ("lt", Byte) => LT_Byte
case ("gt", Byte) => GT_Byte
case ("le", Byte) => LE_Byte
case ("ge", Byte) => GE_Byte
case ("lt", Float) => LT_Float
case ("gt", Float) => GT_Float
case ("le", Float) => LE_Float
case ("ge", Float) => GE_Float
case ("lt", Double) => LT_Double
case ("gt", Double) => GT_Double
case ("le", Double) => LE_Double
case ("ge", Double) => GE_Double
case _ => subsetError("Unsupported operation '%s' on type %s.", op, convergedArgType)
}
}
override lazy val compiledDPath = {
val leftDPath = left.compiledDPath
val rightDPath = right.compiledDPath
val c = conversions
val res = new CompiledDPath(CompareOperator(compareOp, leftDPath, rightDPath) +: c)
res
}
override def targetTypeForSubexpression(child: Expression): NodeInfo.Kind = convergedArgType
lazy val convergedArgType = (left.inherentType, right.inherentType) match {
// String => Numeric conversions are not allowed for comparison Ops.
//
// See http://www.w3.org/TR/xpath20/#mapping
//
case (left: NodeInfo.String.Kind, right: NodeInfo.String.Kind) =>
NodeInfo.String
case (left: NodeInfo.Numeric.Kind, right: NodeInfo.Numeric.Kind) =>
NodeInfoUtils.generalizeArgTypesForComparisonOp(op, left, right)
case (left: NodeInfo.Date.Kind, right: NodeInfo.Date.Kind) =>
NodeInfo.Date
case (left: NodeInfo.Time.Kind, right: NodeInfo.Time.Kind) =>
NodeInfo.Time
case (left: NodeInfo.DateTime.Kind, right: NodeInfo.DateTime.Kind) =>
NodeInfo.DateTime
case (left: NodeInfo.Boolean.Kind, right: NodeInfo.Boolean.Kind) =>
NodeInfo.Boolean
case (l, r) =>
SDE("Cannot compare %s with %s for operator '%s'", l, r, op)
}
}
trait NumericExpression extends BinaryExpMixin {
self: ExpressionLists =>
lazy val numericOp: NumericOp = {
import NodeInfo._
(op, convergedArgType) match {
case ("+", Decimal) => PlusDecimal
case ("-", Decimal) => MinusDecimal
case ("*", Decimal) => TimesDecimal
case ("div", Decimal) => DivDecimal
case ("idiv", Decimal) => IDivDecimal
case ("mod", Decimal) => ModDecimal
case ("+", Integer) => PlusInteger
case ("-", Integer) => MinusInteger
case ("*", Integer) => TimesInteger
case ("div", Integer) => DivInteger
case ("idiv", Integer) => IDivInteger
case ("mod", Integer) => ModInteger
case ("+", NonNegativeInteger) => PlusNonNegativeInteger
case ("-", NonNegativeInteger) => MinusNonNegativeInteger
case ("*", NonNegativeInteger) => TimesNonNegativeInteger
case ("div", NonNegativeInteger) => DivNonNegativeInteger
case ("idiv", NonNegativeInteger) => IDivNonNegativeInteger
case ("mod", NonNegativeInteger) => ModNonNegativeInteger
case ("+", UnsignedLong) => PlusUnsignedLong
case ("-", UnsignedLong) => MinusUnsignedLong
case ("*", UnsignedLong) => TimesUnsignedLong
case ("div", UnsignedLong) => DivUnsignedLong
case ("idiv", UnsignedLong) => IDivUnsignedLong
case ("mod", UnsignedLong) => ModUnsignedLong
case ("+", Long) => PlusLong
case ("-", Long) => MinusLong
case ("*", Long) => TimesLong
case ("div", Long) => DivLong
case ("idiv", Long) => IDivLong
case ("mod", Long) => ModLong
case ("+", UnsignedInt) => PlusUnsignedInt
case ("-", UnsignedInt) => MinusUnsignedInt
case ("*", UnsignedInt) => TimesUnsignedInt
case ("div", UnsignedInt) => DivUnsignedInt
case ("idiv", UnsignedInt) => IDivUnsignedInt
case ("mod", UnsignedInt) => ModUnsignedInt
case ("+", ArrayIndex) => PlusUnsignedInt
case ("-", ArrayIndex) => MinusUnsignedInt
case ("*", ArrayIndex) => TimesUnsignedInt
case ("div", ArrayIndex) => DivUnsignedInt
case ("idiv", ArrayIndex) => IDivUnsignedInt
case ("mod", ArrayIndex) => ModUnsignedInt
case ("+", Int) => PlusInt
case ("-", Int) => MinusInt
case ("*", Int) => TimesInt
case ("div", Int) => DivInt
case ("idiv", Int) => IDivInt
case ("mod", Int) => ModInt
case ("+", UnsignedShort) => PlusUnsignedShort
case ("-", UnsignedShort) => MinusUnsignedShort
case ("*", UnsignedShort) => TimesUnsignedShort
case ("div", UnsignedShort) => DivUnsignedShort
case ("idiv", UnsignedShort) => IDivUnsignedShort
case ("mod", UnsignedShort) => ModUnsignedShort
case ("+", Short) => PlusShort
case ("-", Short) => MinusShort
case ("*", Short) => TimesShort
case ("div", Short) => DivShort
case ("idiv", Short) => IDivShort
case ("mod", Short) => ModShort
case ("+", UnsignedByte) => PlusUnsignedByte
case ("-", UnsignedByte) => MinusUnsignedByte
case ("*", UnsignedByte) => TimesUnsignedByte
case ("div", UnsignedByte) => DivUnsignedByte
case ("idiv", UnsignedByte) => IDivUnsignedByte
case ("mod", UnsignedByte) => ModUnsignedByte
case ("+", Byte) => PlusByte
case ("-", Byte) => MinusByte
case ("*", Byte) => TimesByte
case ("div", Byte) => DivByte
case ("idiv", Byte) => IDivByte
case ("mod", Byte) => ModByte
case ("+", Float) => PlusFloat
case ("-", Float) => MinusFloat
case ("*", Float) => TimesFloat
case ("div", Float) => DivFloat
case ("idiv", Float) => IDivFloat
case ("mod", Float) => ModFloat
case ("+", Double) => PlusDouble
case ("-", Double) => MinusDouble
case ("*", Double) => TimesDouble
case ("div", Double) => DivDouble
case ("idiv", Double) => IDivDouble
case ("mod", Double) => ModDouble
case _ => subsetError("Unsupported operation '%s' on type %s.", op, convergedArgType)
}
}
override lazy val compiledDPath = {
val leftDPath = left.compiledDPath
val rightDPath = right.compiledDPath
val c = conversions
new CompiledDPath(NumericOperator(numericOp, leftDPath, rightDPath) +: c)
}
/**
* The DPath operator, such as, "+", or "idiv"
*/
def op: String
override def inherentType: NodeInfo.Kind = convergedResult
override def targetTypeForSubexpression(child: Expression): NodeInfo.Kind = convergedArgType
lazy val (convergedArgType, convergedResult) = (left.inherentType, right.inherentType) match {
case (left: NodeInfo.Numeric.Kind, right: NodeInfo.Numeric.Kind) =>
NodeInfoUtils.generalizeArgAndResultTypesForNumericOp(op, left, right)
case (left: NodeInfo.Numeric.Kind, r) =>
SDE("Right operand for operator '%s' must have numeric type. Type was: %s.", op, r)
case (l, r: NodeInfo.Numeric.Kind) =>
SDE("Left operand for operator '%s' must have numeric type. Type was: %s.", op, l)
case (l, r) =>
SDE("Operands for operator '%s' must have numeric type. Types were: %s and %s.", op, l, r)
}
}
/**
* A whole expression
*/
case class WholeExpression(
nodeInfoKind: NodeInfo.Kind,
ifor: Expression,
nsBindingForPrefixResolution: NamespaceBinding,
ci: DPathCompileInfo)
extends Expression {
def init() {
this.setOOLAGContext(null) // we are the root.
this.setContextsForChildren()
}
override lazy val namespaces = nsBindingForPrefixResolution
override def text = ifor.text
override lazy val targetType = nodeInfoKind
override def targetTypeForSubexpression(subExpr: Expression): NodeInfo.Kind = {
//
// Note: the subExpr might not be exactly our ifor child expression
// because if ifor was a function call, then the resulting function
// object will have been created and given this as its parent pointer.
//
Assert.invariant(subExpr == ifor | ifor.isInstanceOf[FunctionCallExpression])
targetType
}
override lazy val inherentType = ifor.inherentType
override lazy val compileInfo = ci
override lazy val compiledDPath = ifor.compiledDPath
override lazy val children = List(ifor)
}
case class IfExpression(ifthenelse: List[Expression])
extends ExpressionLists(ifthenelse) {
override lazy val compiledDPath = new CompiledDPath(IF(predicate.compiledDPath,
thenPart.compiledDPath, elsePart.compiledDPath))
override def text = "if (" + predicate.text + ") then " + thenPart.text + " else " + elsePart.text
lazy val List(predicate, thenPart, elsePart) = ifthenelse
val op = "if"
override def targetTypeForSubexpression(subexp: Expression) = {
Assert.invariant(children.contains(subexp))
if (subexp == predicate)
NodeInfo.Boolean
else
this.targetType
}
override lazy val inherentType = {
// we need the type which is the generalization of the thenPart and
// elsePart inherent types, but it's an error if they have no generalization.
//
NodeInfoUtils.generalizeIfThenElse(thenPart, elsePart)
}
}
case class OrExpression(ands: List[Expression])
extends ExpressionLists(ands) with BooleanExpression {
val op = "or"
}
case class AndExpression(comps: List[Expression])
extends ExpressionLists(comps) with BooleanExpression {
val op = "and"
}
case class AdditiveExpression(op: String, mults: List[Expression])
extends ExpressionLists(mults) with NumericExpression {
}
case class MultiplicativeExpression(op: String, unarys: List[Expression])
extends ExpressionLists(unarys) with NumericExpression
case class UnaryExpression(op: String, exp: Expression)
extends ExpressionLists(List(exp)) {
override def text = "( " + op + " (" + exp.text + "))"
override def inherentType: NodeInfo.Kind = exp.inherentType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind =
if (op == "+") targetType
else {
exp.inherentType // negate the inherent type, then we'll convert to whatever the target wants.
}
override lazy val compiledDPath =
if (op == "+") {
// no conversions because we passed on target type to subexpression
exp.compiledDPath
} else {
new CompiledDPath(NegateOp(exp.compiledDPath) +: conversions)
}
}
abstract class PathExpression()
extends Expression {
def steps: Seq[StepExpression]
override def text = steps.map { _.text }.mkString("/")
override lazy val inherentType: NodeInfo.Kind = steps.last.inherentType
override def targetTypeForSubexpression(subexp: Expression) = {
parent.targetTypeForSubexpression(this)
}
/**
* Path to a single array element without a [N] predicate qualifying it.
*
* That is, the kind of path expression used to access information
* about an array such as its current count.
*/
lazy val isPathToOneWholeArray: Boolean = {
if (steps == Nil) false // root 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
}
}
}
case class RootPathExpression(relPath: Option[RelativePathExpression])
extends PathExpression() {
override def text = "/" + super.text
lazy val steps = relPath.map { _.steps }.getOrElse(Nil)
override lazy val inherentType = {
if (!(steps == Nil)) steps.last.inherentType
else {
rootElement.optPrimType match {
case Some(pt) => pt
case None => {
// this is the more common case. Only in unit tests will the
// root element be simple type.
NodeInfo.Complex
}
}
}
}
override def targetTypeForSubexpression(subexp: Expression) = {
Assert.usage(relPath.isDefined)
Assert.invariant(subexp == relPath.get)
super.targetTypeForSubexpression(this)
}
override lazy val children = relPath.toSeq
override lazy val compiledDPath = {
val rel = relPath.map { rp => rp.compiledDPath.ops.toList }.getOrElse(Nil)
val cdp = new CompiledDPath(ToRoot +: rel)
cdp
}
}
case class RelativePathExpression(steps1: List[StepExpression], isEvaluatedAbove: Boolean)
extends PathExpression() {
lazy val isAbsolutePath = {
parent != null && parent.isInstanceOf[RootPathExpression]
}
/**
* We must adjust the steps for the isEvaluatedAbove case.
* That's when something like dfdl:occursCount is written as { ../c }.
* In that case, it's written on an element decl as if it were accessing a
* peer, but in fact the expression is evaluated before any instances of
* the array are even allocated, so we must remove an ".." up move from
* every relative path contained inside the expression, except when
* part of an absolute path.
*/
private lazy val adjustedSteps = {
if (parent.isInstanceOf[RootPathExpression]) steps2
else {
// not an absolute path.
if (isEvaluatedAbove) {
// in this case, the relative path must begin with ".." to be
// meaningful.
Assert.invariant(steps2(0).isInstanceOf[Up])
steps2.tail // trim off the UP move at the start.
} else steps2
}
}
override lazy val compiledDPath: CompiledDPath = {
val cps = adjustedSteps.map {
_.compiledDPath
}
val ops = cps.map {
_.ops.toList
}
val res = new CompiledDPath(ops.flatten)
res
}
override lazy val steps = {
steps2
}
// remove any spurious "." in relative paths so "./.././.." becomes "../.."
// corner case "././././." should behave like "."
val steps2 = {
val noSelfSteps = steps1.filter { case Self(None) => false; case _ => true }
val res =
if (noSelfSteps.length == 0) List(Self(None)) // we need one "."
else noSelfSteps
res
}
override lazy val children: List[Expression] = steps
}
sealed abstract class StepExpression(val step: String, val pred: Option[PredicateExpression])
extends Expression {
def relPathErr() = {
val err = new RelativePathPastRootError("Relative path .. past root element.")
toss(err)
}
requiredEvaluations(priorStep)
requiredEvaluations(compileInfo)
requiredEvaluations(stepElement)
// override def toString = text
// Note: all instances are distinct regardless of contents.
override def equals(x: Any) = x match {
case ar: AnyRef => this _eq_ ar
case _ => false
}
override def hashCode() = super.hashCode()
override def hasReferenceTo(elem: DPathElementCompileInfo): Boolean = {
stepElement =:= elem
}
lazy val stepQName = {
val e = QName.resolveStep(step, namespaces)
e match {
case Failure(th) => SDE("Step %s prefix has no corresponding namespace.", step)
case Success(v) => v
}
}
override lazy val children = pred.toList
def stepElement: DPathElementCompileInfo
lazy val priorStep: Option[StepExpression] = {
val res =
if (isFirstStep)
None
else {
val pos = positionInStepsSequence - 1
val step = relPathParent.steps(pos)
Some(step)
}
res
}
lazy val relPathParent = {
parentOpt match {
case Some(rel: RelativePathExpression) => rel
case Some(x) => Assert.invariantFailed("StepExpression must have RelativePathExpression parent.")
case None => Assert.invariantFailed("StepExpression must have parent.")
}
}
lazy val isAbsolutePath = relPathParent.isAbsolutePath
lazy val positionInStepsSequence = {
val steps = relPathParent.steps
steps.indexOf(this)
}
lazy val isFirstStep = {
val res = (positionInStepsSequence == 0)
res
}
lazy val isLastStep = {
val res = positionInStepsSequence == (relPathParent.steps.length - 1)
res
}
lazy val isArray: Boolean = stepElement.isArray
/**
* Used when there is no match as part of error diagnostic message.
* It searches the local xml scope for a prefix binding for this
* namespace. Returns Nil if none, a list of strings for all the available
* prefixes for this namespace (because there can be more than one)
*/
def suggestedPossiblePrefixes(ns: NS): Seq[String] = {
val res = NS.allPrefixes(ns, compileInfo.namespaces)
res
}
override lazy val targetType: NodeInfo.Kind = {
if (isLastStep) parent.targetTypeForSubexpression(this)
else NodeInfo.Complex
}
override def targetTypeForSubexpression(subexp: Expression) = {
Assert.invariant(pred.isDefined)
Assert.invariant(pred.get == subexp)
NodeInfo.ArrayIndex
}
override lazy val inherentType: NodeInfo.Kind = {
if (!isLastStep) NodeInfo.Complex
else {
if (stepElement.optPrimType.isDefined) {
// simple type, so
val pt = stepElement.optPrimType.get
pt
} else {
NodeInfo.Complex
}
}
}
}
//TODO: Is ".[i]" ever a valid expression in DFDL?
case class Self(predArg: Option[PredicateExpression]) extends StepExpression(null, 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()
}
}
}
/**
* 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) {
requiredEvaluations(stepQName)
override lazy val compiledDPath = new CompiledDPath(SelfMove +: conversions)
override def text = "."
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()
}
}
if (!ci.namedQName.matches(stepQName))
ci.noMatchError(stepQName)
ci
}
}
case class Up(predArg: Option[PredicateExpression]) extends StepExpression(null, predArg) {
override lazy val compiledDPath = {
if (isLastStep && stepElement.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) {
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
}
if (!ci.namedQName.matches(stepQName))
ci.noMatchError(stepQName)
ci
}
}
case class NamedStep(s: String, predArg: Option[PredicateExpression])
extends StepExpression(s, predArg) {
requiredEvaluations(stepQName)
override lazy val compiledDPath = {
val d = downwardStep
val conv = conversions
val c = (if (isLastStep) conv else Nil)
val res = new CompiledDPath(d +: c)
res
}
lazy val dpathElementCompileInfo = stepElement
lazy val downwardStep = {
if (stepElement.isArray && pred.isDefined) {
Assert.invariant(pred.get.targetType == NodeInfo.ArrayIndex)
val indexRecipe = pred.get.compiledDPath
new DownArrayOccurrence(dpathElementCompileInfo, indexRecipe)
} else if (stepElement.isArray && targetType == NodeInfo.Exists) {
new DownArrayExists(dpathElementCompileInfo)
} else if (stepElement.isArray) {
schemaDefinitionUnless(targetType == NodeInfo.Array, "Query-style paths not supported. Must have '[...]' after array-element's name. Offending path step: '%s'.", step)
new DownArray(dpathElementCompileInfo)
} else {
//
// Note: DFDL spec allows a[exp] if a is not an array, but it's a processing
// error if exp doesn't evaluate to 1.
// TODO: Implement this.
if (pred.isDefined) subsetError("Indexing is only allowed on arrays. Offending path step: '%s%s'.", step, pred.get.text)
new DownElement(dpathElementCompileInfo)
}
}
override def text = step
private def die = {
Assert.invariantFailed("should have thrown")
}
/*
* The ERD of the element that corresponds to this path step
* (or SDE trying to find it.)
*/
override lazy val stepElement: DPathElementCompileInfo = {
val stepElem = if (isFirstStep) {
if (isAbsolutePath) {
// has to be the root element, but we have to make sure the name matches.
rootElement.elementCompileInfo.map { r =>
if (!r.namedQName.matches(stepQName))
r.noMatchError(stepQName)
}.getOrElse(die)
rootElement
} else {
// since we're first we start from the element, or nearest enclosing
val nc = compileInfo.elementCompileInfo.getOrElse {
// happens for example if you have defaultValue="false" since false looks like a path step, but is really illegal. should be fn:false().
compileInfo.SDE("The expression path step '%s' has no defined enclosing element.", s)
}.findNamedChild(stepQName)
nc
}
} else {
// not first step so we are extension of prior step
val e = priorStep.map { ps =>
val psse = ps.stepElement
psse
}.map { se =>
val nc = se.findNamedChild(stepQName)
nc
}.getOrElse(die)
e
}
stepElem
}
}
/**
* The thing in square brackets is indexing in DFDL, but XPath
* calls it a "predicate".
*/
case class PredicateExpression(ifOr: Expression)
extends Expression {
override def text = "[" + ifOr.text + "]"
override lazy val inherentType = NodeInfo.ArrayIndex
override def targetTypeForSubexpression(subexp: Expression) = {
Assert.invariant(ifOr == subexp)
NodeInfo.ArrayIndex
}
override lazy val compiledDPath = ifOr.compiledDPath
override lazy val children: List[Expression] = List(ifOr)
}
abstract class PrimaryExpression(expressions: List[Expression])
extends ExpressionLists(expressions) {
}
abstract class LiteralExpressionBase(value: Any)
extends PrimaryExpression(Nil) {
override def text = {
value match {
case s: String => s
case _ => value.toString
}
}
override def toString = text
def isValidInt(n: Number): Boolean = {
val bd = new JBigDecimal(n.toString())
val res = try {
bd.intValueExact()
true
} catch {
case e: java.lang.ArithmeticException => false
}
res
}
def isValidLong(n: Number): Boolean = {
val bd = new JBigDecimal(n.toString())
val res = try {
bd.longValueExact()
true
} catch {
case e: java.lang.ArithmeticException => false
}
res
}
def isDecimalDouble(bd: JBigDecimal): Boolean = {
val d = bd.doubleValue()
!d.isInfinity && equals(bd)
}
/**
* Convert to regular types from the pessimistic BigInt
* and BigDecimal that come in from the parser.
*/
lazy val litValue = value match {
case s: String => s
case i: Int => i
case i: BigInt => {
Assert.usageError("Expected java.math.BigInteger but received BigInt.")
}
case i: JBigInt => {
if (isValidInt(i)) i.intValue()
else if (isValidLong(i)) i.longValue()
else i
}
case bd: BigDecimal => {
Assert.usageError("Expected java.math.BigDecimal but received scala BigDecimal.")
}
case bd: JBigDecimal => {
if (isValidLong(bd)) bd.longValue()
else if (isDecimalDouble(bd)) bd.doubleValue()
else bd
}
case f: Float => f.toDouble
case d: Double => d
case b: Boolean => b // there are no literal booleans, but fn:true() and fn:false() turns into one.
case _ => Assert.invariantFailed("value not one of the expected types")
}
override lazy val compiledDPath: CompiledDPath = {
new CompiledDPath(Literal(litValue) +: conversions)
}
override lazy val inherentType = {
litValue match {
case s: String => NodeInfo.String
case i: BigInt => Assert.usageError("Expected java.math.BigInteger but got BigInt.")
case i: JBigInt => NodeInfo.Integer
case d: BigDecimal => Assert.usageError("Expected java.math.BigDecimal but got package.BigDecimal.")
case d: JBigDecimal => NodeInfo.Decimal
case df: Double => NodeInfo.Double
case l: Long => NodeInfo.Long
case i: Int => NodeInfo.Int
case b: Boolean => NodeInfo.Boolean
case _ => Assert.invariantFailed("value not one of the expected types " + litValue.getClass())
}
}
override def targetTypeForSubexpression(subexp: Expression) =
Assert.usageError("literal expressions have no subexpressions")
}
case class LiteralExpression(v: Any) extends LiteralExpressionBase(v)
/**
* This is a literal as used to replace the fn:true() and fn:false() functions.
*
* It has to behave like a function call with respect to target type.
*/
case class LiteralBooleanExpression(v: Any) extends LiteralExpressionBase(v) {
override lazy val targetType = {
val res = parent.targetType
res
}
}
case class VariableRef(val qnameString: String)
extends PrimaryExpression(Nil) {
override lazy val compiledDPath = {
val vrefOp = VRef(vrd, compileInfo)
new CompiledDPath(vrefOp +: conversions)
}
override def text = "$" + qnameString
lazy val theQName: GlobalQName = {
val refQ = resolveRef(qnameString)
val global = QName.createGlobal(refQ.local, refQ.namespace)
global
}
lazy val vrd = compileInfo.variableMap.getVariableRuntimeData(theQName).getOrElse(
SDE("Undefined variable: %s", text))
lazy val varType = {
val vt = vrd.primType
vt
}
override lazy val inherentType: NodeInfo.Kind = varType
override def targetTypeForSubexpression(subexp: Expression) =
Assert.usageError("variable reference expressions have no subexpressions")
}
/*
* Functions and operators that exist in tests as of 2014-08-05
*
* fn:dateTime
* xs:dateTime
* xs:time
* xs:date
* xs:hexBinary
*/
case class FunctionCallExpression(functionQNameString: String, expressions: List[Expression])
extends PrimaryExpression(expressions) {
override def text = functionObject.text
final override def leafContentLengthReferencedElements = functionObject.leafContentLengthReferencedElements
final override def leafValueLengthReferencedElements = functionObject.leafValueLengthReferencedElements
lazy val functionQName: RefQName = resolveRef(functionQNameString)
override lazy val compiledDPath = functionObject.compiledDPath
def inherentType: NodeInfo.Kind = functionObject.inherentType
def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = {
// This is called by the arguments to the function to get their target types.
// However, this is not the right parent. They are arguments to the function object
// once the FunctionCallExpression creates that function object, so
// we delegate this back to the function object
functionObject.targetTypeForSubexpression(childExpr)
}
lazy val functionObject: Expression = {
val DFDL = XMLUtils.DFDL_NAMESPACE
val FUNC = XMLUtils.XPATH_FUNCTION_NAMESPACE
val XSD = XMLUtils.XSD_NAMESPACE
val DAF = XMLUtils.EXT_NS
val funcObj = (functionQName, expressions) match {
case (RefQName(_, "trace", DAF), args) =>
DAFTraceExpr(functionQNameString, functionQName, args)
case (RefQName(_, "error", DAF), args) =>
DAFErrorExpr(functionQNameString, functionQName, args)
case (RefQName(_, "not", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean, NodeInfo.AnyAtomic, FNNot(_, _))
case (RefQName(_, "empty", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean, NodeInfo.Exists, FNEmpty(_, _))
case (RefQName(_, "contains", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean,
NodeInfo.String, NodeInfo.String, FNContains(_))
case (RefQName(_, "starts-with", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean,
NodeInfo.String, NodeInfo.String, FNStartsWith(_))
case (RefQName(_, "ends-with", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean,
NodeInfo.String, NodeInfo.String, FNEndsWith(_))
case (RefQName(_, "local-name", FUNC), args) if args.length == 0 =>
FNZeroArgExpr(functionQNameString, functionQName,
NodeInfo.String, NodeInfo.AnyAtomic, FNLocalName0(_, _))
case (RefQName(_, "local-name", FUNC), args) if args.length == 1 =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.AnyAtomic, FNLocalName1(_, _))
case (RefQName(_, "string", XSD), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.AnyAtomic, XSString(_, _))
case (RefQName(_, "dateTime", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.DateTime, NodeInfo.Date, NodeInfo.Time, FNDateTime(_))
case (RefQName(_, "dateTime", XSD), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.DateTime, NodeInfo.AnyAtomic, XSDateTime(_, _))
case (RefQName(_, "date", XSD), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Date, NodeInfo.AnyAtomic, XSDate(_, _))
case (RefQName(_, "time", XSD), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Time, NodeInfo.AnyAtomic, XSTime(_, _))
case (RefQName(_, "hexBinary", XSD), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.HexBinary, NodeInfo.AnyAtomic, XSHexBinary(_, _))
case (RefQName(_, "hexBinary", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.HexBinary, NodeInfo.AnyAtomic, DFDLHexBinary(_, _))
case (RefQName(_, "byte", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Byte, NodeInfo.AnyAtomic, DFDLByte(_, _))
case (RefQName(_, "unsignedByte", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.UnsignedByte, NodeInfo.AnyAtomic, DFDLUnsignedByte(_, _))
case (RefQName(_, "short", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Short, NodeInfo.AnyAtomic, DFDLShort(_, _))
case (RefQName(_, "unsignedShort", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.UnsignedShort, NodeInfo.AnyAtomic, DFDLUnsignedShort(_, _))
case (RefQName(_, "int", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Int, NodeInfo.AnyAtomic, DFDLInt(_, _))
case (RefQName(_, "unsignedInt", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.UnsignedInt, NodeInfo.AnyAtomic, DFDLUnsignedInt(_, _))
case (RefQName(_, "long", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Long, NodeInfo.AnyAtomic, DFDLLong(_, _))
case (RefQName(_, "unsignedLong", DFDL), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.UnsignedLong, NodeInfo.AnyAtomic, DFDLUnsignedLong(_, _))
case (RefQName(_, "nilled", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean, NodeInfo.Nillable, FNNilled(_, _))
case (RefQName(_, "exists", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean, NodeInfo.Exists, FNExists(_, _))
case (RefQName(_, "ceiling", FUNC), args) =>
FNOneArgMathExpr(functionQNameString, functionQName, args,
FNCeiling(_, _))
case (RefQName(_, "floor", FUNC), args) =>
FNOneArgMathExpr(functionQNameString, functionQName, args,
FNFloor(_, _))
case (RefQName(_, "round", FUNC), args) =>
FNOneArgMathExpr(functionQNameString, functionQName, args,
FNRound(_, _))
case (RefQName(_, "abs", FUNC), args) =>
FNOneArgMathExpr(functionQNameString, functionQName, args,
FNAbs(_, _))
case (RefQName(_, "concat", FUNC), args) =>
FNArgListExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, FNConcat(_))
// Note: there is no string-join function in DFDL
// keep this as we may put this in as a daffodil extension
// case (RefQName(_, "string-join", FUNC), args) =>
// FNTwoArgsExpr(functionQNameString, functionQName, args,
// NodeInfo.Array, NodeInfo.String, NodeInfo.String, FNStringJoin(_))
case (RefQName(_, "substring", FUNC), args) if args.length == 2 =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, NodeInfo.Double, FNSubstring2(_))
case (RefQName(_, "substring", FUNC), args) if args.length == 3 =>
FNThreeArgsExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, NodeInfo.Double, NodeInfo.Double, FNSubstring3(_))
case (RefQName(_, "substring-before", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, NodeInfo.String, FNSubstringBefore(_))
case (RefQName(_, "substring-after", FUNC), args) =>
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, NodeInfo.String, FNSubstringAfter(_))
case (RefQName(_, "true", FUNC), Nil) => LiteralBooleanExpression(true)
case (RefQName(_, "false", FUNC), Nil) => LiteralBooleanExpression(false)
case (RefQName(_, "count", FUNC), args) =>
FNCountExpr(functionQNameString, functionQName, args)
case (RefQName(_, "exactly-one", FUNC), args) =>
FNExactlyOneExpr(functionQNameString, functionQName, args)
case (RefQName(_, "year-from-dateTime", FUNC), args) => FNOneArgExprConversionDisallowed(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.DateTime, FNYearFromDateTime(_, _))
case (RefQName(_, "month-from-dateTime", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.DateTime, FNMonthFromDateTime(_, _))
case (RefQName(_, "day-from-dateTime", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.DateTime, FNDayFromDateTime(_, _))
case (RefQName(_, "hours-from-dateTime", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.DateTime, FNHoursFromDateTime(_, _))
case (RefQName(_, "minutes-from-dateTime", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.DateTime, FNMinutesFromDateTime(_, _))
case (RefQName(_, "seconds-from-dateTime", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Decimal, NodeInfo.DateTime, FNSecondsFromDateTime(_, _))
case (RefQName(_, "year-from-date", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.Date, FNYearFromDate(_, _))
case (RefQName(_, "month-from-date", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.Date, FNMonthFromDate(_, _))
case (RefQName(_, "day-from-date", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.Date, FNDayFromDate(_, _))
case (RefQName(_, "hours-from-time", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.Time, FNHoursFromTime(_, _))
case (RefQName(_, "minutes-from-time", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Long, NodeInfo.Time, FNMinutesFromTime(_, _))
case (RefQName(_, "seconds-from-time", FUNC), args) => FNOneArgExpr(functionQNameString, functionQName, args, NodeInfo.Decimal, NodeInfo.Time, FNSecondsFromTime(_, _))
case (RefQName(_, "occursIndex", DFDL), args) => {
DFDLOccursIndexExpr(functionQNameString, functionQName, args)
}
case (RefQName(_, "checkConstraints", DFDL), args) => {
DFDLCheckConstraintsExpr(functionQNameString, functionQName, args)
}
case (RefQName(_, "decodeDFDLEntities", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, DFDLDecodeDFDLEntities(_, _))
}
case (RefQName(_, "encodeDFDLEntities", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, DFDLEncodeDFDLEntities(_, _))
}
case (RefQName(_, "containsDFDLEntities", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Boolean, NodeInfo.String, DFDLContainsDFDLEntities(_, _))
}
case (RefQName(_, "testBit", DFDL), args) => {
DFDLTestBitExpr(functionQNameString, functionQName, args)
}
case (RefQName(_, "setBits", DFDL), args) => {
DFDLSetBitsExpr(functionQNameString, functionQName, args)
}
case (RefQName(_, "round-half-to-even", FUNC), args) if args.length == 1 => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.Numeric, NodeInfo.Numeric, FNRoundHalfToEven1(_, _))
}
case (RefQName(_, "round-half-to-even", FUNC), args) => {
FNTwoArgsExpr(functionQNameString, functionQName, args,
NodeInfo.Numeric, NodeInfo.Numeric, NodeInfo.Integer, FNRoundHalfToEven2(_))
}
case (RefQName(_, "string-length", FUNC), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.ArrayIndex, NodeInfo.String, FNStringLength(_, _))
}
case (RefQName(_, "contentLength", DFDL), args) =>
ContentLengthExpr(functionQNameString, functionQName, args,
NodeInfo.Long, NodeInfo.Exists, NodeInfo.String, DFDLContentLength(_))
// The above specifies Exists, because we want to know a node exists, but
// it has to be a node, not a simple value. E.g., dfdl:contentLength("foobar", "bits") makes no sense.
// The first argument has to be a path to a node, otherwise we don't have format properties and so
// can't determine a content length.
//
// One might argue that dfdl:contentLength("foobar", "characters") is meaningful and should be 6.
// But that's fairly pointless.
case (RefQName(_, "valueLength", DFDL), args) => {
ValueLengthExpr(functionQNameString, functionQName, args,
NodeInfo.Long, NodeInfo.Exists, NodeInfo.String, DFDLValueLength(_))
}
case (RefQName(_, "lower-case", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, FNLowerCase(_, _))
case (RefQName(_, "upper-case", FUNC), args) =>
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.String, FNUpperCase(_, _))
case (RefQName(_, "timeZoneFromDateTime", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.DateTime, DFDLTimeZoneFromDFDLCalendar(_, _))
}
case (RefQName(_, "timeZoneFromDate", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.Date, DFDLTimeZoneFromDFDLCalendar(_, _))
}
case (RefQName(_, "timeZoneFromTime", DFDL), args) => {
FNOneArgExpr(functionQNameString, functionQName, args,
NodeInfo.String, NodeInfo.Time, DFDLTimeZoneFromDFDLCalendar(_, _))
}
// conversion functions
case (RefQName(_, "integer", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Integer)
case (RefQName(_, "decimal", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Decimal)
case (RefQName(_, "boolean", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Boolean)
case (RefQName(_, "float", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Float)
case (RefQName(_, "double", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Double)
case (RefQName(_, "long", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Long)
case (RefQName(_, "unsignedLong", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.UnsignedLong)
case (RefQName(_, "int", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Int)
case (RefQName(_, "unsignedInt", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.UnsignedInt)
case (RefQName(_, "short", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Short)
case (RefQName(_, "unsignedShort", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.UnsignedShort)
case (RefQName(_, "nonNegativeInteger", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.NonNegativeInteger)
case (RefQName(_, "byte", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.Byte)
case (RefQName(_, "unsignedByte", XSD), args) =>
XSConverterExpr(functionQNameString, functionQName, args, NodeInfo.UnsignedByte)
case _ => SDE("Unsupported function: %s", functionQName)
}
funcObj.setOOLAGContext(this)
funcObj
}
}
abstract class FunctionCallBase(functionQNameString: String,
functionQName: RefQName,
expressions: List[Expression]) extends ExpressionLists(expressions) {
override def text = functionQNameString + "(" + expressions.map { _.text }.mkString(", ") + ")"
override lazy val targetType = {
val res = parent.targetType
res
}
final def checkArgCount(n: Int) {
if (expressions.length != n) argCountErr(n)
}
final def argCountErr(n: Int) = {
SDE("The %s function requires %s argument(s).", functionQName.toPrettyString, n)
}
final def argCountTooFewErr(n: Int) = {
//
// Digression: below illustrates what is a hard problem in internationalization
// of software, which is called pluralization.
//
// See that "(s)" fudge here where we mean - add plural 's' if the number is not one.
// Well there's no one consistent way to do that.
// Even consider zero. In English zero is plural. In French it is singular.
// In many languages there are different endings for 0, x1, x2, x3, etc.
// Much the way ordinal numbers work in English where we have 1st (st ending)
// 2nd (nd ending for 2nd 22nd, 32nd, 572nd - but not 12 - special case 12th has th ending).
// We seldom use ordinals in English, but in many languages CARDINAL numbers work
// this way, and must match the quantity and there's 3 or 4 different endings
// with exceptions for 11, 12, 13, etc.
//
SDE("The %s function requires at least %s argument(s).", functionQName.toPrettyString, n)
}
}
/**
* Tells the sub-expression that we want an array out of it.
*/
abstract class FunctionCallArrayBase(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallBase(nameAsParsed, fnQName, args) {
def funcName: String
lazy val arrPath = args(0) match {
case pe: PathExpression => pe
case _ => subsetError("The %s function must contain a path.", funcName)
}
override lazy val isTypeCorrect = {
checkArgCount(1)
arrPath.isPathToOneWholeArray
}
override def targetTypeForSubexpression(subExp: Expression): NodeInfo.Kind = NodeInfo.Array
}
/**
* Tells the sub-expression that we want an array out of it.
*/
case class FNCountExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallArrayBase(nameAsParsed, fnQName, args) {
val funcName: String = "fn:count"
override lazy val inherentType: NodeInfo.Kind = NodeInfo.Long
override lazy val compiledDPath = {
checkArgCount(1)
val arg0Recipe = args(0).compiledDPath
val arg0Type = args(0).inherentType
val c = conversions
val res = new CompiledDPath(FNCount(arg0Recipe, arg0Type) +: c)
res
}
}
case class FNExactlyOneExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallArrayBase(nameAsParsed, fnQName, args) {
val funcName: String = "fn:exactly-one"
override lazy val inherentType: NodeInfo.Kind = NodeInfo.AnyType
override lazy val compiledDPath = {
checkArgCount(1)
subsetError("fn:exactly-one is not supported.")
//new CompiledDPath((arrPath.compiledDPath.ops.toList :+ FNExactlyOne) ++ conversions)
}
}
case class FNRoundHalfToEvenExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression])
extends FunctionCallBase(nameAsParsed, fnQName, args) {
val (valueExpr, precisionExpr) = args match {
case List(ve) => (ve, LiteralExpression(0))
case List(ve, pe) => (ve, pe)
case _ => SDE("The %n function requires 1 or 2 arguments. But %s were found.",
fnQName.toPrettyString, args.length)
}
override lazy val children = List(valueExpr, precisionExpr)
override lazy val inherentType = valueExpr.inherentType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = {
if (childExpr == valueExpr) valueExpr.inherentType
else if (childExpr == precisionExpr) NodeInfo.Int
else Assert.invariantFailed("subexpression isn't one of the expected.")
}
override lazy val compiledDPath =
new CompiledDPath(
FNRoundHalfToEven(
valueExpr.compiledDPath,
precisionExpr.compiledDPath) +: conversions)
}
/**
* Preserves the inherent type of the argument to the function as the type of
* the result, that is if the argument type is a numeric type, and if
* the argument type is a subtype thereof.
*/
case class FNOneArgMathExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], constructor: (CompiledDPath, NodeInfo.Kind) => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = {
schemaDefinitionUnless(argInherentType.isSubtypeOf(NodeInfo.Numeric),
"Argument must be of numeric type but was %s.", argInherentType)
argInherentType
}
lazy val argInherentType = {
schemaDefinitionUnless(args.length == 1, "Function %s takes 1 argument.",
fnQName.toPrettyString)
args(0).inherentType
}
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = inherentType
override lazy val compiledDPath = {
val arg0Recipe = args(0).compiledDPath
val arg0Type = args(0).inherentType
val c = conversions
val res = new CompiledDPath(constructor(arg0Recipe, arg0Type) +: c)
res
}
}
case class FNZeroArgExpr(nameAsParsed: String, fnQName: RefQName,
resultType: NodeInfo.Kind, argType: NodeInfo.Kind, constructor: (CompiledDPath, NodeInfo.Kind) => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, Nil) {
override lazy val inherentType = resultType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = argType
override lazy val compiledDPath = {
checkArgCount(0)
val c = conversions
val res = new CompiledDPath(constructor(null, null) +: c)
res
}
}
case class FNOneArgExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, argType: NodeInfo.Kind, constructor: (CompiledDPath, NodeInfo.Kind) => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = argType
override lazy val compiledDPath = {
checkArgCount(1)
val arg0Recipe = args(0).compiledDPath
val arg0Type = args(0).inherentType
val c = conversions
val res = new CompiledDPath(constructor(arg0Recipe, arg0Type) +: c)
res
}
}
case class FNOneArgExprConversionDisallowed(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, argType: NodeInfo.Kind, constructor: (CompiledDPath, NodeInfo.Kind) => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = argType
override lazy val conversions = Nil
override lazy val compiledDPath = {
checkArgCount(1)
val arg0Recipe = args(0).compiledDPath
val arg0Type = args(0).inherentType
val c = Nil //conversions
val res = new CompiledDPath(constructor(arg0Recipe, arg0Type) +: c)
res
}
}
abstract class FNTwoArgsExprBase(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
lazy val List(arg1, arg2) = { checkArgCount(2); args }
override def targetTypeForSubexpression(subexp: Expression): NodeInfo.Kind = {
if (subexp == arg1)
arg1Type
else if (subexp == arg2)
arg2Type
else
Assert.invariantFailed("subexpression %s is not an argument.".format(subexp))
}
override lazy val compiledDPath = {
val arg1Recipe = arg1.compiledDPath
val arg2Recipe = arg2.compiledDPath
val res = new CompiledDPath(constructor(List(arg1Recipe, arg2Recipe)) +: conversions)
res
}
}
case class FNTwoArgsExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends FNTwoArgsExprBase(nameAsParsed, fnQName, args, resultType, arg1Type, arg2Type, constructor)
sealed abstract class LengthExprBase(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends FNTwoArgsExprBase(nameAsParsed, fnQName, args, resultType, arg1Type, arg2Type, constructor) {
protected final def leafReferencedElements = {
val arg = args(0).asInstanceOf[PathExpression]
val steps = arg.steps
val lst = steps.last
val elem = lst.stepElement
val res = Set(elem)
res
}
}
case class ContentLengthExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends LengthExprBase(nameAsParsed, fnQName, args, resultType, arg1Type, arg2Type, constructor) {
override lazy val leafContentLengthReferencedElements = leafReferencedElements
}
case class ValueLengthExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends LengthExprBase(nameAsParsed, fnQName, args, resultType, arg1Type, arg2Type, constructor) {
override lazy val leafValueLengthReferencedElements = leafReferencedElements
}
case class FNThreeArgsExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind,
arg1Type: NodeInfo.Kind, arg2Type: NodeInfo.Kind, arg3Type: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
lazy val List(arg1, arg2, arg3) = { checkArgCount(3); args }
override def targetTypeForSubexpression(subexp: Expression): NodeInfo.Kind = {
if (subexp == arg1) arg1Type
else if (subexp == arg2) arg2Type
else {
Assert.invariant(subexp == arg3)
arg3Type
}
}
override lazy val compiledDPath = {
val arg1Path = arg1.compiledDPath
val arg2Path = arg2.compiledDPath
val arg3Path = arg3.compiledDPath
val c = conversions
val res = new CompiledDPath(constructor(
List(arg1Path, arg2Path, arg3Path)) +: c)
res
}
}
case class XSConverterExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression],
resultType: NodeInfo.Kind)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
/*
* By using the result type as the target for the expression, conversions will
* do the work of converting into that type. We don't need to put down any
* additional recipe operator to convert anything.
*/
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = resultType // NodeInfo.AnyType
// TODO: this should work... why do we need to call an additional converter. The
// args(0).compiledDPath should already have taken into account converting into
// their target types which are the same as this conversion's output result type.
override lazy val compiledDPath = {
checkArgCount(1)
val arg0Recipe = args(0).compiledDPath
val c = conversions
val res = new CompiledDPath(arg0Recipe.ops.toList ++ c)
res
}
}
case class FNArgListExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression], resultType: NodeInfo.Kind, argType: NodeInfo.Kind,
constructor: List[CompiledDPath] => RecipeOp)
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = resultType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = argType
override lazy val compiledDPath = {
if (args.length < 1) argCountTooFewErr(1)
new CompiledDPath(constructor(args.map { _.compiledDPath }) +: conversions)
}
}
case class DFDLOccursIndexExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallBase(nameAsParsed, fnQName, args) {
// Note that even though there are no arguments, this cannot be a case object
// since it gets wired into expressions, and so the parent pointer must
// be set. So it's not a singleton.
override lazy val children = Nil
override def text = "dfdl:occursIndex"
override lazy val compiledDPath = {
schemaDefinitionUnless(args.length == 0, "Function %s takes no arguments.", fnQName.toPrettyString)
new CompiledDPath(DFDLOccursIndex +: conversions)
}
override lazy val inherentType = NodeInfo.ArrayIndex
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind =
Assert.usageError("No subexpressions")
}
case class DFDLTestBitExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression]) extends FunctionCallBase(nameAsParsed, fnQName, args) {
lazy val List(data, bitPos) = { checkArgCount(2); args }
override lazy val inherentType = NodeInfo.Boolean
override def targetTypeForSubexpression(subexpr: Expression): NodeInfo.Kind = {
subexpr match {
case `data` => NodeInfo.UnsignedByte
case `bitPos` => NodeInfo.UnsignedByte
case _ => Assert.invariantFailed("wasn't one of the subexpressions.")
}
}
override lazy val compiledDPath =
new CompiledDPath(DFDLTestBit(data.compiledDPath, bitPos.compiledDPath) +: conversions)
}
case class DFDLSetBitsExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression]) extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType = NodeInfo.UnsignedByte
override def targetTypeForSubexpression(subexpr: Expression): NodeInfo.Kind = NodeInfo.UnsignedByte
override lazy val compiledDPath = {
checkArgCount(8)
new CompiledDPath(DFDLSetBits(args.map { _.compiledDPath }) +: conversions)
}
}
case class DFDLCheckConstraintsExpr(nameAsParsed: String, fnQName: RefQName,
args: List[Expression]) extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val children = args
override lazy val compiledDPath = {
checkArgCount(1)
val argDPath = args(0).compiledDPath
val c = conversions
val res = new CompiledDPath(DFDLCheckConstraints(argDPath) +: c)
res
}
override def targetTypeForSubexpression(subexpr: Expression): NodeInfo.Kind = NodeInfo.AnyAtomic
override lazy val inherentType = NodeInfo.Boolean
}
/**
* Really this just delegates anything bottom-up to the contained expression
* and anything top-down to the parent
*/
case class ParenthesizedExpression(expression: Expression)
extends PrimaryExpression(List(expression)) {
override def inherentType: NodeInfo.Kind = expression.inherentType
override def targetTypeForSubexpression(childExpr: Expression): NodeInfo.Kind = targetType
override def text: String = "( " + expression.text + " )"
override lazy val compiledDPath = expression.compiledDPath // no conversions because we passed on targetType to subexp
}
case class DAFTraceExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallBase(nameAsParsed, fnQName, args) {
requiredEvaluations(realArg)
requiredEvaluations(msgText)
lazy val (realArg, msgText) = args match {
case List(arg0, LiteralExpression(txt: String)) => (arg0, txt)
case List(arg0, other) =>
SDE("The second argument to %n must be a string literal, but was %s.",
nameAsParsed, other.text)
case _ => {
argCountErr(2)
}
}
override lazy val inherentType: NodeInfo.Kind = realArg.inherentType
override def targetTypeForSubexpression(subExp: Expression): NodeInfo.Kind = targetType
override lazy val compiledDPath = {
new CompiledDPath(DAFTrace(realArg.compiledDPath, msgText) +: conversions)
}
}
case class DAFErrorExpr(nameAsParsed: String, fnQName: RefQName, args: List[Expression])
extends FunctionCallBase(nameAsParsed, fnQName, args) {
override lazy val inherentType: NodeInfo.Kind = NodeInfo.Nothing
override def targetTypeForSubexpression(subExp: Expression): NodeInfo.Kind =
Assert.invariantFailed("no subexpressions")
override lazy val compiledDPath = {
checkArgCount(0)
Assert.invariant(conversions == Nil)
new CompiledDPath(DAFError)
}
}