From 3887b33e4e6931c67f7f998919c2375b94425bd1 Mon Sep 17 00:00:00 2001 From: Mihaela Mihaljevic Date: Mon, 8 Jun 2026 16:58:24 +0200 Subject: [PATCH] feat(relaxng): compact name-class subtraction (* - name) The compact-syntax name class * - nameClass now parses as anyName except the subtracted class (the model already had anyNameExcept). ns:* - name would need an nsName-except model case and is left as a refinement. Part of #79 --- Sources/Schema/RelaxNGCompactParser.swift | 5 +++++ Tests/RelaxNGNameClassTests.swift | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 Tests/RelaxNGNameClassTests.swift diff --git a/Sources/Schema/RelaxNGCompactParser.swift b/Sources/Schema/RelaxNGCompactParser.swift index cbaaf9a..d5d90ea 100644 --- a/Sources/Schema/RelaxNGCompactParser.swift +++ b/Sources/Schema/RelaxNGCompactParser.swift @@ -308,6 +308,11 @@ private extension RNCParser { switch peek() { case .symbol("*"): advance() + // `* - nameClass` is any name except the subtracted class. + if peek() == .symbol("-") { + advance() + return .anyNameExcept(parseSimpleNameClass(attribute: attribute)) + } return .anyName case .symbol("("): advance() diff --git a/Tests/RelaxNGNameClassTests.swift b/Tests/RelaxNGNameClassTests.swift new file mode 100644 index 0000000..7ac91f4 --- /dev/null +++ b/Tests/RelaxNGNameClassTests.swift @@ -0,0 +1,22 @@ +@testable import PureXML +import Testing + +@Suite("RELAX NG compact name-class subtraction") +struct RelaxNGNameClassTests { + private func valid(_ rnc: String, _ xml: String) throws -> Bool { + try PureXML.Schema.RelaxNG(compact: rnc).validate(xml) + } + + @Test("* - name admits any element except the subtracted one") + func test_subtraction() throws { + let rnc = "element root { element * - skip { empty }* }" + #expect(try valid(rnc, "")) + #expect(try !valid(rnc, "")) + } + + @Test("A plain wildcard still admits everything") + func test_plainWildcard() throws { + let rnc = "element root { element * { empty }* }" + #expect(try valid(rnc, "")) + } +}