PureXML/Sources/XPath/Query.swift
Mihaela Mihaljevic 6b7853f2d5 feat(xpath): all thirteen axes via parent-aware navigation
Rebuild the XPath engine on the parent-aware tree. Add an XPath node
abstraction (tree, attribute, namespace) with document order, and navigate
all thirteen axes: child, descendant, parent, ancestor, following-sibling,
preceding-sibling, following, preceding, attribute, namespace, self,
descendant-or-self, ancestor-or-self. Support the ./../@/// abbreviations
and processing-instruction() node tests. Reverse axes number nearest-first
for positional predicates; results return de-duplicated in document order.
The attribute axis excludes xmlns declarations; the namespace axis surfaces
in-scope namespace nodes.

Closes #40
2026-06-08 05:15:14 +02:00

39 lines
1.7 KiB
Swift

public extension PureXML.XPath {
/// A compiled XPath location-path query: all thirteen axes (`child`,
/// `descendant`, `parent`, `ancestor`, `following-sibling`,
/// `preceding-sibling`, `following`, `preceding`, `attribute`, `namespace`,
/// `self`, `descendant-or-self`, `ancestor-or-self`) with their abbreviations
/// (`.`, `..`, `@`, `//`), the node tests (name, `*`, `text()`, `node()`,
/// `comment()`, `processing-instruction()`), and the predicate subset `[n]`,
/// `[@a]`, `[@a='v']`, `[child]`, `[child='v']`.
///
/// The expression language (operators, the full function library, variables)
/// arrives in later steps. Reimplemented from the XPath 1.0 specification.
struct Query: Sendable {
let absolute: Bool
let steps: [Step]
/// Compiles an XPath location path, throwing ``QueryError`` on a path
/// outside the supported grammar.
public init(_ path: String) throws {
(absolute, steps) = try Compiler.compile(path)
}
/// Evaluates the query over a node, returning the selected node-set in
/// document order. The node is the starting context.
public func evaluate(over node: PureXML.Model.Node) -> [Selection] {
Evaluator.evaluate(steps: steps, over: node)
}
/// Evaluates the query and returns the selected elements only.
public func elements(over node: PureXML.Model.Node) -> [PureXML.Model.Element] {
evaluate(over: node).compactMap(\.element)
}
/// Evaluates the query and returns the string-value of each selection.
public func strings(over node: PureXML.Model.Node) -> [String] {
evaluate(over: node).map(\.stringValue)
}
}
}