102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
#!/usr/bin/env python
|
|
"""Check mews-0.1.dtd against the XHTML Mobile 1.2 driver and the spec.
|
|
|
|
The Mews DTD is maintained by hand. It cannot be generated by stripping
|
|
xhtml-mobile12.dtd: that file is a driver which declares no elements (the
|
|
declarations live in the W3C and OMA module files it points to), DTD
|
|
attribute lists only ever add, a re-declared element is an error rather
|
|
than an override, and the rules that define Mews (the head model of
|
|
SPEC.md 3.3, the body variant classes of 5.3, the ban on nested tables
|
|
of 4.2, and lang and dir) do not exist in XHTML-MP to be kept.
|
|
|
|
This script verifies what the driver and the spec can check:
|
|
|
|
- the driver is the expected XHTML Mobile 1.2 driver, with event
|
|
attributes included and bidi ignored, so lang and dir are Mews
|
|
extensions (SPEC.md 3);
|
|
- the DTD declares exactly the elements SPEC.md 4.1 permits;
|
|
- the DTD keeps the driver's pre content model verbatim;
|
|
- the DTD keeps the li value and ol start attributes the driver adds.
|
|
|
|
Run with: uv run check_dtd.py
|
|
"""
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
DRIVER_PATH = ROOT / "xhtml-mobile12.dtd"
|
|
DTD_PATH = ROOT / "mews-0.1.dtd"
|
|
|
|
DRIVER_FPI = "-//OMA//DTD XHTML Mobile 1.2//EN"
|
|
|
|
# The Mews element set (SPEC.md 4.1).
|
|
KEPT_ELEMENTS = (
|
|
"html head title body "
|
|
"h1 h2 h3 h4 h5 h6 "
|
|
"p blockquote pre address hr "
|
|
"em strong b i code kbd samp var q cite abbr dfn br "
|
|
"a "
|
|
"ul ol li dl dt dd "
|
|
"table caption tr th td "
|
|
"img "
|
|
"form fieldset label input select option optgroup textarea "
|
|
"meta link"
|
|
).split()
|
|
|
|
|
|
def entity(text, name):
|
|
match = re.search(r"<!ENTITY\s+%\s*" + re.escape(name) + r'\s+"([^"]*)"', text)
|
|
return match.group(1).strip() if match else None
|
|
|
|
|
|
def pre_content(text):
|
|
match = re.search(r'<!ENTITY\s+%\s*pre\.content\s+"(.*?)"\s*>', text, re.DOTALL)
|
|
return re.sub(r"\s+", " ", match.group(1)) if match else None
|
|
|
|
|
|
def attlist(text, element):
|
|
match = re.search(r"<!ATTLIST\s+" + element + r"\s+(.*?)\n>", text, re.DOTALL)
|
|
return match.group(1) if match else None
|
|
|
|
|
|
def main():
|
|
driver = DRIVER_PATH.read_text(encoding="utf-8")
|
|
dtd = DTD_PATH.read_text(encoding="utf-8")
|
|
errors = []
|
|
|
|
def check(condition, message):
|
|
if not condition:
|
|
errors.append(message)
|
|
|
|
check(entity(driver, "XHTML.version") == DRIVER_FPI, "driver is not XHTML Mobile 1.2")
|
|
check(
|
|
entity(driver, "xhtml-events.module") == "INCLUDE",
|
|
"driver must include event attributes; Mews drops them",
|
|
)
|
|
check(
|
|
entity(driver, "XHTML.bidi") == "IGNORE",
|
|
"driver must ignore bidi, so dir is a Mews extension",
|
|
)
|
|
|
|
declared = set(re.findall(r"<!ELEMENT\s+([\w:-]+)", dtd))
|
|
expected = set(KEPT_ELEMENTS)
|
|
check(
|
|
declared == expected,
|
|
f"elements differ from SPEC.md 4.1: {sorted(declared ^ expected)}",
|
|
)
|
|
|
|
check(pre_content(dtd) == pre_content(driver), "pre content model differs from the driver's")
|
|
check("value" in (attlist(dtd, "li") or ""), "li is missing the driver's value attribute")
|
|
check("start" in (attlist(dtd, "ol") or ""), "ol is missing the driver's start attribute")
|
|
|
|
if errors:
|
|
for message in errors:
|
|
print(f"error: {message}", file=sys.stderr)
|
|
sys.exit(1)
|
|
print(f"{DTD_PATH.name} matches the driver and SPEC.md 4.1 ({len(expected)} elements)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|