The two monolithic XSD content/identity rules only satisfied the OpenAPIKit validation idiom at the wrong granularity: no per-constraint removability, no isolable failing/near-miss tests. Split them into 32 named rules, each finding tagged at its origin and cached per XSDContext so the walk still runs once per document (the #101 decision against duplicating XSD's recursive type resolution still holds). Wired the streaming path's ID/IDREF and identity checks through the same framework instead of calling the validators imperatively, and added the unwrapAndLookup combinator the rule already named as required. Also includes the mandated repo-wide swiftformat/swiftlint pass.
118 lines
4.1 KiB
Swift
118 lines
4.1 KiB
Swift
extension PureXML.XPath.Evaluator {
|
|
typealias BinaryOperator = PureXML.XPath.BinaryOperator
|
|
|
|
static func evalBinary(
|
|
_ oper: BinaryOperator,
|
|
_ left: Expression,
|
|
_ right: Expression,
|
|
_ context: EvaluationContext,
|
|
) throws -> Value {
|
|
switch oper {
|
|
case .logicalOr:
|
|
try .boolean(eval(left, context).boolean || eval(right, context).boolean)
|
|
case .logicalAnd:
|
|
try .boolean(eval(left, context).boolean && eval(right, context).boolean)
|
|
case .add, .subtract, .multiply, .divide, .modulo:
|
|
try .number(arithmetic(oper, eval(left, context).number, eval(right, context).number))
|
|
case .equal, .notEqual:
|
|
try .boolean(equality(eval(left, context), eval(right, context), negate: oper == .notEqual))
|
|
case .lessThan, .lessOrEqual, .greaterThan, .greaterOrEqual:
|
|
try .boolean(relational(eval(left, context), eval(right, context), oper))
|
|
}
|
|
}
|
|
|
|
private static func arithmetic(_ oper: BinaryOperator, _ lhs: Double, _ rhs: Double) -> Double {
|
|
switch oper {
|
|
case .add: lhs + rhs
|
|
case .subtract: lhs - rhs
|
|
case .multiply: lhs * rhs
|
|
case .divide: lhs / rhs
|
|
case .modulo: lhs.truncatingRemainder(dividingBy: rhs)
|
|
default: .nan
|
|
}
|
|
}
|
|
|
|
// MARK: Equality
|
|
|
|
private static func equality(_ lhs: Value, _ rhs: Value, negate: Bool) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case let (.nodeSet(left), .nodeSet(right)):
|
|
nodeSetEquality(left, right, negate: negate)
|
|
case let (.nodeSet(nodes), scalar):
|
|
nodeSetScalarEquality(nodes, scalar, negate: negate)
|
|
case let (scalar, .nodeSet(nodes)):
|
|
nodeSetScalarEquality(nodes, scalar, negate: negate)
|
|
default:
|
|
scalarEquality(lhs, rhs, negate: negate)
|
|
}
|
|
}
|
|
|
|
private static func nodeSetEquality(_ left: [Node], _ right: [Node], negate: Bool) -> Bool {
|
|
let rightStrings = right.map(\.stringValue)
|
|
for value in left.map(\.stringValue) {
|
|
for other in rightStrings where (value == other) != negate {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
private static func nodeSetScalarEquality(_ nodes: [Node], _ scalar: Value, negate: Bool) -> Bool {
|
|
switch scalar {
|
|
case let .boolean(flag):
|
|
(!nodes.isEmpty == flag) != negate
|
|
case let .number(number):
|
|
nodes.contains { (PureXML.XPath.Value.parseNumber($0.stringValue) == number) != negate }
|
|
case let .string(text):
|
|
nodes.contains { ($0.stringValue == text) != negate }
|
|
case .nodeSet:
|
|
false
|
|
}
|
|
}
|
|
|
|
private static func scalarEquality(_ lhs: Value, _ rhs: Value, negate: Bool) -> Bool {
|
|
let equal: Bool = if case .boolean = lhs {
|
|
lhs.boolean == rhs.boolean
|
|
} else if case .boolean = rhs {
|
|
lhs.boolean == rhs.boolean
|
|
} else if case .number = lhs {
|
|
lhs.number == rhs.number
|
|
} else if case .number = rhs {
|
|
lhs.number == rhs.number
|
|
} else {
|
|
lhs.string == rhs.string
|
|
}
|
|
return equal != negate
|
|
}
|
|
|
|
// MARK: Relational
|
|
|
|
private static func relational(_ lhs: Value, _ rhs: Value, _ oper: BinaryOperator) -> Bool {
|
|
let lefts = numbers(of: lhs)
|
|
let rights = numbers(of: rhs)
|
|
for left in lefts {
|
|
for right in rights where numericCompare(left, right, oper) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
private static func numbers(of value: Value) -> [Double] {
|
|
if case let .nodeSet(nodes) = value {
|
|
return nodes.map { PureXML.XPath.Value.parseNumber($0.stringValue) }
|
|
}
|
|
return [value.number]
|
|
}
|
|
|
|
private static func numericCompare(_ lhs: Double, _ rhs: Double, _ oper: BinaryOperator) -> Bool {
|
|
switch oper {
|
|
case .lessThan: lhs < rhs
|
|
case .lessOrEqual: lhs <= rhs
|
|
case .greaterThan: lhs > rhs
|
|
case .greaterOrEqual: lhs >= rhs
|
|
default: false
|
|
}
|
|
}
|
|
}
|