Element expressions can be used to access the ancestry of an element.
The operators that make up an element expression work in a relative fashion, starting from the innermost element
of the markup state, and working their way out to the root. In this way, they function to move an invisible cursor
up the element stack. Since the meaning of these operators is best understood by example, each of them will be
discussed in turn, using the following simple well-formed XML instance as an input:
<manual id="M01"> <chapter id="C01"> <title id="C01.T">First Chapter</title> <p>Introduction paragraph</p> <section id="S01.01"> <title id="S01.01.T">First Section</title> <section id="S01.01.01"> <title id="S01.01.01.T">Nested Section</title> <p>We are here.</p> </section> </section> </chapter> </manual>
element
is in a sense an identity operator when it appears in an element expression: in fact it leaves
the cursor unmoved. So while processing the innermost p
element, the expression name of element
would evaluate to p
, and the expression number of attributes of
element
would evaluate to 0
.
Because it serves largely as an identity operator, element
can typically be left out of most element
expressions. However, there are times when it is necessary: a simple example is that of name of
, given
previously. A more subtle example occurs when processing an external data entity:
inside an external-data-entity
rule, attribute
and attributes
refer to the attributes of
the entity, not those of the current element. So in the following external-data-entity
rule,
external-data-entity #implied repeat over attributes as a output key of attribute a || "=%"" || attribute a || "%"%n" again repeat over attributes of element as a output key of attribute a || "=%"" || attribute a || "%"%n" again
the first loop iterates over the attributes of the external data entity, whereas the second loop qualifies
attributes
with of element
to specify that it is the attributes of the current element which are
to be iterated over, not those of the entity.
parent
moves the cursor up one level in the element stack, toward the root.
In the example above, while processing the innermost p
element, the expression name of
parent
would evaluate to section
, the expression number of attributes of parent
would
evaluate to 1
, and the expression attribute "id" of parent
would evaluate to S01.01.01
.
If there is no parent element, a run-time error is thrown. This would be the case, for instance, if the current element is the root of the document.
Unlike the element
and parent
operators, the open element
operator must be followed by an
element name or a parenthesized list of element names. It moves the cursor to the innermost currently-opened
element whose name matches one of the provided names; this search includes the current element and its parent.
The expression attribute "id" of open element
"section"
would evaluate to S01.01.01
. Since open element
includes the current
element, it could also be used to leave the cursor where it is, if the provided list of element names includes
p
, the name of the current element.
The search for a matching element always stops as soon as a match is found: so using the same example, the
expression number of attributes of open element ("manual" | "chapter" | "section")
would evaluate to
1
, even though elements manual
and chapter
have no attributes and appear
first in the list of provided element names.
If open element
fails to find any element matching the provided names, a run-time
error is thrown.
The ancestor
operator is much like the open element
operator, except it excludes the current
element.
In the example above, if the cursor is currently sitting on the innermost section
element, the
expression attribute "id" of ancestor "section"
would evaluate to S01.01
.
If ancestor
fails to find any element matching the provided names, a run-time
error is thrown.
The preparent
operator is much like the open element
and ancestor
operators, except it
excludes both the current element and its parent.
Using the example instance above, if the cursor is currently sitting on the innermost p
element, the expression attribute "id" of preparent "section"
would evaluate to S01.01
.
If preparent
fails to find any element matching the provided names, a run-time
error is thrown.
previous
is different from any of the other operators mentioned thus far: instead of going up the element
stack to ancestor elements, it goes across to a previously-encountered peer element. previous
is also
more restricted than the other operators mentioned above: it can only be used once in an element expression, and
when used it can only appear at the beginning of the expression.
In the example instance above, if the cursor is currently sitting on the innermost p
element,
the previous element would be the preceding title
element, so the expression attribute
"id" of previous
would evaluate to S01.01.01.T
.
If there is no previous element, a run-time error is thrown. This would happen, for instance, if the current element is the first child of its parent.
doctype
is different from any of the other operators mentioned thus far: it does not represent a
relative motion of the cursor through the element stack, but rather an absolute repositioning of the cursor to
the root the element stack. Because of this, doctype
cannot be combined with any other operator: any
operator
preceding it would be attempting to reach beyond the root of the element stack, and any operator following it
would be redundant.
Using the same instance as above, regardless of the current element, the expression attribute "id"
of doctype
would evaluate to M01
.
If there is no root element, a run-time error is thrown. This would happen, for instance, if there is no active markup parse in scope.
The operators described above can be combined to form more complex queries, subject to the restrictions given
(e.g., previous
must appear first, doctype
cannot be combined with other operators). When reading
these more complex element expressions, it is usually best to start from the rightmost operator and work to the
left. For example, while processing the innermost p
element, the expression attribute "id"
of parent of parent
will evaluate to S01.01
: the rightmost parent
operator would move the
cursor to the section
element whose id
attribute is S01.01.01
,
while the next parent
operator to the left moves the cursor one more level, thereby yielding the section
element with an id
attribute of S01.01
. For a more complex
example, given the same context the expression attribute "id" of parent of preparent "section"
would
evaluate to C01
: the rightmost preparent "section"
moves to the cursor to the section
element with an id
attribute of S01.01
, so the parent
operator
applied to this yields its parent, which would be the chapter
element.
An expression of type markup-element-event
can be used in element expressions, subject to certain
restrictions:
markup-element-event
expression must appear as the last (rightmost) component of the
element expression, and
markup-element-event
expression must evaluate to a valid markup event.
If the second restriction is violated, a run-time error is thrown. A valid expression of
type markup-element-event
could be obtained by capturing the value of #current-markup-event
in an
element
rule, by creating the event directly using create-element-event
, or by picking out
values from the elements of
shelf.
The effect of applying other operators to an expression of type markup-element-event
yields the same
results as if the operators were applied when the event was created. So the following brief program
global markup-element-event event process do xml-parse scan "<a a.a=%"Hello, World!%"><b/></a>" output "%c" done output attribute "a.a" of parent of event || "%n" element "b" set event to #current-markup-event output "%c" element #implied output "%c"
will output Hello, World!
at the end of the process
rule: the event was created when
element b
was active, and so event
represents the markup state at this point in
time. The parent of event
at that time was element a
, and its attribute a.a
had value Hello, World!
. Therefore, the expression attribute "a.a" of
parent of event
evaluates to Hello, World!
.
Just as an expression of type markup-element-event
can be used in an element expression, so can a
valid but otherwise arbitrary element expression be used as an expression of type markup-element-event
.
In the previous example, if we know in the element
rule for element b
that we will
eventually only care about the event that fired the parent element
rule, then the program can be
rewritten
global markup-element-event event process do xml-parse scan "<a a.a=%"Hello, World!%"><b/></a>" output "%c" done output attribute "a.a" of event || "%n" element "b" set event to parent output "%c" element #implied output "%c"
with the same net output.