PureXML/Sources/API.swift
Mihaela Mihaljevic 5c306482e5 feat(parser): add a streaming, iterative XML parser
Replace the notImplemented stub with a real parser built on a pull-based,
streaming event core that never requires the whole document in memory.

- Parsing.EventReader: pulls characters through a source closure with a bounded
  lookahead buffer and emits one Parsing.Event at a time, holding only an
  open-element stack. Drives chunked/streamed input.
- Parsing.Parser: builds a Model.Node tree iteratively over the event core
  (explicit stack, no recursion).
- Reader: streaming character cursor (closure-backed, bounded buffer) replacing
  the previous whole-string Array.
- EntityDecoder: the five predefined entities and decimal/hex char references.
- Handles elements, attributes, text, comments, CDATA, processing instructions,
  and an XML declaration in the prolog. Rejects <!DOCTYPE> by default (no
  DTD/XXE/entity-expansion exposure).
- Top-level API: parse(_:), parse(pulling:), events(_:), events(pulling:).
- 21 tests including event-stream and chunked-source coverage.
2026-06-08 00:10:50 +02:00

48 lines
1.9 KiB
Swift

public extension PureXML {
/// Parses an XML document into a ``Model/Node`` tree.
///
/// The parser is still being built; this entry point currently raises
/// ``Parsing/ParseError/notImplemented(_:)``. The serializer and model are
/// usable today for building and emitting trees.
static func parse(_ xml: String) throws -> Model.Node {
try Parsing.Parser().parse(xml)
}
/// Parses an XML document from an incremental character source into a
/// ``Model/Node`` tree. The closure returns the next character or nil at end
/// of input, so the document can arrive in chunks and is never held whole.
static func parse(pulling pull: @escaping () -> Character?) throws -> Model.Node {
try Parsing.Parser().parse(pulling: pull)
}
/// Returns a streaming ``Parsing/EventReader`` over an XML string. Pull events
/// one at a time with `next()` to process documents without building a tree.
static func events(_ xml: String) -> Parsing.EventReader {
Parsing.EventReader(xml)
}
/// Returns a streaming ``Parsing/EventReader`` over an incremental character
/// source, for processing arbitrarily large or chunked input.
static func events(pulling pull: @escaping () -> Character?) -> Parsing.EventReader {
Parsing.EventReader(pulling: pull)
}
/// Serializes a ``Model/Node`` tree into XML with the selected options.
static func serialize(
_ node: Model.Node,
options: Emitting.Options = .default,
) -> String {
Emitting.Serializer(options: options).serialize(node)
}
/// Validates a parsed XML node with the default validation rules.
@discardableResult
static func validate(
_ node: Model.Node,
using validator: Validation.Validator = .init(),
strict: Bool = true,
) throws -> [Validation.Issue] {
try validator.validate(node, strict: strict)
}
}