Two semantics for XPath

              Philip Wadler, wadler@research.bell-labs.com

26 July 1999, revised 4 January 2000

This note presents two semantics for XPath, devised by Phil Wadler after conversations with James Clark at the 19–21 July XSL Face-to-Face meeting. The semantics are somewhat simplified, focussing on the role of axes in selection and on the special functions position() and last(). Some cases are omitted, and we ignore the coercions between nodesets, numbers, strings, and booleans. The first semantics is given in Figure 1, the second in Figure 2, and both semantics use some auxiliary functions on axes, given in Figure 3. (I consider the second semantics superior, but I gather that James disagrees, so I have presented both.) For a tutorial introduction to denotational semantics and its application to XSL, please see [1].

The first semantics has the advantage of being uniform, in that the three main semantic functions all have the same additional parameters: an axis a, a context node x, and a context node set S; furthermore, it is always the case that x ∈ S. We write S^{a}[[p]](x, S) for the set of nodes selected by pattern p, and Q^{a}[[q]](x, S) for boolean value of the qualifier q, and E^{a}[[e]](x, S) for the numerical value of the expression e. However, the uniformity may obscure more than it reveals: note that the S parameter is always ignored by S, as the line for p[q] always computes a fresh context node set S_{1} to pass in to the semantics of q. Also, the line for p_{1}/p_{2} is complicated by the need to introduce S_{1} in order to satisfy the invariant that the context node is always in the context node set. In any event, using a context node set is overkill, in that the only values that are relevant are the position of the context node in the context node set computed by the call to position(), and the size of the context node set computed by the call to last().

The second semantics is simplified in three ways. First, since the context node set is ignored, it is dropped as a parameter to S. Second, the context node set is replaced as a parameter to Q and E by the position of the context node (call this k) and size of the context node set (call this n). Third, since the position is computed at the first call to Q, it is no longer necessary for Q or E to take an axis parameter. Hence we write S^{a}[[p]]x for the set of nodes selected by pattern p, and Q[[q]](x, k, n) for boolean value of the qualifier q, and E[[e]](x, k, n) for the numerical value of the expression e.

There are three auxiliary semantic functions on axes. We write A[[a]]x for the set of nodes generated by axis a from context node x, and P[[a]] for the principal node type associated with axis a, and D[[a]] for the direction (forward or reverse node order) associated with axis a. The last two of these can be computed at compile time, as they are independent of the context node.

Comments on either this note or [1] would be much appreciated.

Acknowledgements Thanks to Michael Dyck and Matías Giovannini for spotting, and to Joe English for spotting a significant error.

References

[1] P. Wadler, A formal semantics of patterns in XSL, submitted to Markup Technologies 99.

   http://www.cs.bell-labs.com/~wadler/topics/xml#xsl-semantics

S  : Axis → Pattern → (Node, Set(Node)) → Set(Node)
S^{a}[[p_{1}|p_{2}]](x, S)  = S^{a}[[p_{1}]](x, S) ∪ S^{a}[[p_{2}]](x, S)
S^{a}[[/p]](x, S)  = S^{a}[[p]](root(x), { x })
S^{a}[[p_{1}/p_{2}]](x, S)  = let S_{1} = S^{a}[[p_{1}]](x, S) in { x_{2} | x_{1} ∈ S_{1}, x_{2} ∈ S^{a}[[p_{2}]](x_{1}, S_{1}) }
S^{a}[[a_{1}::p_{1}]](x, S)  = S^{a_{1}}[[p_{1}]](x, S)
S^{a}[[n]](x, S)  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = P[[a]], name(x_{1}) = n }
S^{a}[[*]](x, S)  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = P[[a]] }
S^{a}[[text()]](x, S)  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = Text }
S^{a}[[p[q]]](x, S)  = let S_{1} = S^{a}[[p]](x, S) in { x_{1} | x_{1} ∈ S_{1}, Q^{a}[[q]](x_{1}, S_{1}) }
Q  : Axis → Qualifier → (Node, Set(Node)) → Boolean
Q^{a}[[q_{1} and q_{2}]](x, S) = Q^{a}[[q_{1}]](x, S) ∧ Q^{a}[[q_{2}]](x, S)
Q^{a}[[q_{1} or q_{2}]](x, S)  = Q^{a}[[q_{1}]](x, S) ∨ Q^{a}[[q_{2}]](x, S)
Q^{a}[[not(q)]](x, S)  = ¬Q^{a}[[q]](x, S)
Q^{a}[[p]](x, S)  = S^{a}[[p]](x, S) ≠ ∅
Q^{a}[[e_{1}=e_{2}]](x, S)  = E^{a}[[e_{1}]](x, S) = E^{a}[[e_{2}]](x, S)
E  : Axis → Expr → (Node, Set(Node)) → Number
E^{a}[[e_{1} + e_{2}]](x, S)  = E^{a}[[e_{1}]](x, S) + E^{a}[[e_{2}]](x, S)
E^{a}[[e_{1} * e_{2}]](x, S)  = E^{a}[[e_{1}]](x, S) × E^{a}[[e_{2}]](x, S)
E^{a}[[position()]](x, S) = let j = size({ x_{1} | x_{1} ∈ S, x_{1} ≤_{doc} x }) in

if D[[a]] = forward then j else size(S) + 1 − j

E^{a}[[last()]](x, S)  = size(S)
E^{a}[[i]](x, S)  = i

Figure 1: First semantics of XPath

S  : Axis → Pattern → Node → Set(Node)
S^{a}[[p_{1}|p_{2}]]x  = S^{a}[[p_{1}]]x ∪ S^{a}[[p_{2}]]x
S^{a}[[/p]]x  = S^{a}[[p]](root(x))
S^{a}[[p_{1}/p_{2}]]x  = { x_{2} | x_{1} ∈ S^{a}[[p_{1}]]x, x_{2} ∈ S^{a}[[p_{2}]]x_{1} }
S^{a}[[a_{1}::p_{1}]]x  = S^{a_{1}}[[p_{1}]]x
S^{a}[[n]]x  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = P[[a]], name(x_{1}) = n }
S^{a}[[*]]x  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = P[[a]] }
S^{a}[[text()]]x  = { x_{1} | x_{1} ∈ A[[a]]x, nodetype(x_{1}) = Text }
S^{a}[[p[q]]]x  = let S_{1} = S^{a}[[p]]x in

let n = size(S_{1}) in {x_{1} |

x_{1} ∈ S_{1}

let j = size({ x_{2} | x_{2} ∈ S_{1}, x_{2} ≤_{doc} x_{1} }) in

let k = (if D[[a]] = forward then j else n + 1 − j) in Q[[q]](x_{1}, k, n)}

Q  : Qualifier → (Node, Number, Number) → Boolean
Q[[q_{1} and q_{2}]](x, k, n) = Q[[q_{1}]](x, k, n) ∧ Q[[q_{2}]](x, k, n)
Q[[q_{1} or q_{2}]](x, k, n)  = Q[[q_{1}]](x, k, n) ∨ Q[[q_{2}]](x, k, n)
Q[[not(q)]](x, k, n)  = ¬Q[[q]](x, k, n)
Q[[p]](x, k, n)  = S^{child}[[p]]x ≠ ∅
Q[[e_{1}=e_{2}]](x, k, n)  = E[[e_{1}]](x, k, n) = E[[e_{2}]](x, k, n)
E  : Expr → (Node, Number, Number) → Number
E[[e_{1} + e_{2}]](x, k, n)  = E[[e_{1}]](x, k, n) + E[[e_{2}]](x, k, n)
E[[e_{1} * e_{2}]](x, k, n)  = E[[e_{1}]](x, k, n) × E[[e_{2}]](x, k, n)
E[[position()]](x, k, n) = k
E[[last()]](x, k, n)  = n
E[[i]](x, k, n)  = i

Figure 2: Second semantics of XPath

A  : Axis → Node → Set(Node)
A[[child]]x  = children(x)
A[[parent]]x  = parent(x)
A[[descendant]]x = children^{+}(x)
A[[ancestor]]x  = parent^{+}(x)
A[[self]]x  = { x }
A[[attribute]]x = attributes(x)
A[[namespace]]x = namespaces(x)
P  : Axis → Nodetype

P[[child]]  = Element
P[[parent]]  = Element
P[[descendant]] = Element
P[[ancestor]]  = Element
P[[self]]  = Element
P[[attribute]]  = Attribute
P[[namespace]]  = Namespace

D  : Axis → Direction

D[[child]]  = forward
D[[parent]]  = reverse
D[[descendant]] = forward
D[[ancestor]]  = reverse
D[[self]]  = forward
D[[attribute]]  = forward
D[[namespace]]  = forward

Figure 3: Auxiliary functions