Commit Graph

5 Commits

Author SHA1 Message Date
7f1aada240 feat(dtd): conditional sections, notations, NDATA, external parameter entities
Deepen DTD processing in the doctype scanner:

- Conditional sections are processed, not skipped: INCLUDE sections have
  their declarations scanned, IGNORE sections are discarded, the keyword
  may be a parameter-entity reference, and nested sections are handled.
- <!NOTATION> declarations are parsed and stored on DocumentType.
- Unparsed entities (<!ENTITY ... NDATA n>) are parsed and stored with
  their notation, distinct from general entities.
- External parameter entities are loaded through the injected resolver so
  their declarations expand; the default refusing resolver loads nothing,
  keeping XXE closed.
- NOTATION attributes are a distinct type, validated against the declared
  notations (value must be listed and the notation declared).

Tracking unparsed entities also lets ENTITY/ENTITIES attribute values be
validated against the declared NDATA entities, closing the gap left when
#73 could only check name syntax.

Closes #72
2026-06-08 14:39:42 +02:00
89f8ccc95a refactor(validation): migrate DTD validation onto the framework
Slice 3 of #92. The DTD validator's imperative walk becomes declared Validation
values over a DTDSchema document:

- DTDSchema is now just the schema (content models + attribute declarations) and
  serves as the rules' Document context; its validate/walk methods are gone.
- contentModel and attributeDeclarations are Validation<Element, DTDSchema>, so
  every content-model and attribute violation is now located by coding path
  ("... at path: r/a"). undeclaredElement is added only in strict mode.
- identifierIntegrity is a Validation<Node, DTDSchema> gated to the root, doing
  the document-wide ID-uniqueness and IDREF-resolution checks in one pass.
- DTD.validator(strict:) assembles the rule set; validateAgainstInternalDTD runs
  it and returns [ValidationError] instead of [Issue].

A new test pins the located result. Schema (XSD) and Schematron validators still
use Issue and migrate in the following slices.
2026-06-08 12:36:56 +02:00
537b455ce8 feat(validation): add DTD ID/IDREF/IDREFS validation
Complete the structural DTD validation layer. AttributeType now distinguishes
the ID family; the validator does a second pass after the structural walk
(since an IDREF may point forward to an ID later in the document):

- ID-typed attribute values must be unique across the document.
- IDREF values must each resolve to a declared ID.
- IDREFS values are split on whitespace and each token must resolve.

Reimplemented from the DTD ID/IDREF spec; no upstream code copied. 4 tests; 79
total.
2026-06-08 02:13:11 +02:00
5cbe938826 feat(validation): add DTD attribute (ATTLIST) validation
Surface <!ATTLIST> declarations from the parser and validate attributes against
them. Reimplemented from the DTD attribute-list spec; no upstream code copied.

- DocumentType gains attributeLists (raw ATTLIST bodies per element); the
  DoctypeScanner captures them and the EventReader plumbs them through.
- AttributeListParser parses a body into AttributeDeclaration values
  (AttributeType: cdata or enumeration; AttributeDefault: required/implied/
  fixed/value).
- DTDSchema now checks #REQUIRED presence, #FIXED value match (when present),
  and enumerated-value membership, in addition to content models.
- Tokenized types (ID/IDREF/NMTOKEN) parse but are not yet value-enforced.
- 5 attribute tests; 75 total.
2026-06-08 02:07:01 +02:00
f02561b01d feat(validation): add DTD content-model validation
Surface the parsed <!DOCTYPE> internal subset's <!ELEMENT> declarations from the
parser and validate a tree against them. Reimplemented from the DTD content-model
spec; no upstream code copied.

- DoctypeScanner captures <!ELEMENT name model> declarations; the parser surfaces
  them as Parsing.DocumentType (via parseWithDocumentType).
- Validation.ContentModel + ContentModelParser parse a model string (EMPTY, ANY,
  (#PCDATA), mixed, and element content with sequence/choice and ?/*/+).
- ContentModelMatcher matches the child element-name sequence against a model as
  a regular language (NFA-position matching).
- Validation.DTDSchema.validate reports issues for EMPTY/PCDATA/mixed violations,
  element content with stray text, and child sequences that do not match.
- PureXML.validateAgainstInternalDTD(_:) parses and validates in one call.
- ATTLIST validation is out of scope. 9 tests; 70 total.
2026-06-08 01:59:00 +02:00