Compile and evaluate full XPath 1.0 expressions, not just location paths. Add the four-type Value (node-set, boolean, number, string) with the spec's coercion rules and canonical number formatting, a recursive-descent parser for the operator grammar (or/and, equality, relational, additive, multiplicative, unary minus, union) whose grammatical position disambiguates * and the operator keywords, function calls, variable references, and filter expressions. Predicates are now arbitrary expressions (a numeric predicate is a position test). Comparison follows the node-set quantification rules. Query gains value/number/string/boolean accessors and a $variable binding map; the core functions last, position, count, not, true, false, boolean, number, and string ship with it. Closes #42
57 lines
2.6 KiB
Swift
57 lines
2.6 KiB
Swift
public extension PureXML.XPath {
|
|
/// A compiled XPath 1.0 expression: all thirteen axes with their
|
|
/// abbreviations, the node tests, the operator grammar (`or`, `and`, `=`,
|
|
/// `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `*`, `div`, `mod`, unary `-`, `|`),
|
|
/// number and string literals, variable references, function calls, and
|
|
/// filter expressions, all over the four-type model (node-set, boolean,
|
|
/// number, string). Reimplemented from the XPath 1.0 specification.
|
|
struct Query: Sendable {
|
|
let expression: Expression
|
|
|
|
/// Compiles an XPath expression, throwing ``QueryError`` on a syntax error.
|
|
public init(_ path: String) throws {
|
|
expression = try Compiler.compile(path)
|
|
}
|
|
|
|
/// Evaluates the query over a node, returning the selected node-set in
|
|
/// document order. A non-node-set expression yields an empty list; use
|
|
/// ``value(over:variables:)`` for typed results.
|
|
public func evaluate(over node: PureXML.Model.Node) -> [Selection] {
|
|
Evaluator.evaluate(expression, 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)
|
|
}
|
|
|
|
/// Evaluates the query to a typed ``Value`` with optional variable bindings.
|
|
public func value(
|
|
over node: PureXML.Model.Node,
|
|
variables: [String: Value] = [:],
|
|
) throws -> Value {
|
|
try Evaluator.value(expression, over: node, variables: variables)
|
|
}
|
|
|
|
/// Evaluates the query and coerces the result to a number.
|
|
public func number(over node: PureXML.Model.Node, variables: [String: Value] = [:]) throws -> Double {
|
|
try value(over: node, variables: variables).number
|
|
}
|
|
|
|
/// Evaluates the query and coerces the result to a string.
|
|
public func string(over node: PureXML.Model.Node, variables: [String: Value] = [:]) throws -> String {
|
|
try value(over: node, variables: variables).string
|
|
}
|
|
|
|
/// Evaluates the query and coerces the result to a boolean.
|
|
public func boolean(over node: PureXML.Model.Node, variables: [String: Value] = [:]) throws -> Bool {
|
|
try value(over: node, variables: variables).boolean
|
|
}
|
|
}
|
|
}
|