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
39 lines
1.7 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|