PureYAML/Sources/Tagged/Tagged.Node.swift
Mihaela Mihaljevic 2a84d6616f feat(tagged): preserve anchors, aliases, and per-node styles end to end
Tagged.Scalar/Sequence/Mapping gain anchor and style fields
(Tagged.ScalarStyle, Tagged.CollectionStyle), Tagged.Node gains an
alias(anchor:node:) case carrying both the reference and its resolution,
and parseTagged populates all of it from the parse events instead of
discarding anchors and expanding aliases into copies.

PureYAML.dump(_ node: Tagged.Node, options:) re-emits anchors, alias
references, tags, and per-node styles over the document-wide policy.
Plain scalars re-emit their source spelling (NO stays NO, an empty null
stays empty); literal blocks cover keep chomping, single-line strip
chomping, leading blank lines, and whitespace-led first lines via an
explicit indentation indicator; folded scalars re-emit as > when the
folding rules invert exactly and degrade to a literal block otherwise.

The round-trip law parseTagged(dump(parseTagged(d))) == parseTagged(d)
is corpus-tested over every real-world fixture, with one documented
degradation: single-quoted content containing line breaks re-emits
double-quoted, value unchanged.

Closes #9
2026-07-12 13:20:12 +02:00

69 lines
2.2 KiB
Swift

public extension PureYAML.Tagged {
/// YAML node tree that preserves explicit source tags, anchors, aliases,
/// and per-node presentation styles.
///
/// An `alias` case carries both the `*name` anchor it referenced and the
/// node it resolved to, so value-oriented consumers can read through the
/// alias without a separate resolution step while emitters can re-emit
/// the reference instead of expanding it. Resolution cannot cycle: the
/// composer registers an anchor only after its node is fully composed, so
/// a self-referential alias is an `undefinedAlias` parse error.
indirect enum Node: Equatable, Sendable {
case scalar(Scalar)
case sequence(Sequence)
case mapping(Mapping)
case alias(anchor: String, node: Node)
public var tag: Tag? {
switch self {
case let .mapping(mapping):
mapping.tag
case let .scalar(scalar):
scalar.tag
case let .sequence(sequence):
sequence.tag
case .alias:
nil
}
}
public var kind: NodeKind {
switch self {
case .mapping:
.mapping
case .scalar:
.scalar
case .sequence:
.sequence
case .alias:
.alias
}
}
/// The `&name` anchor attached to this node, `nil` when unanchored.
/// An alias reports the anchor it references.
public var anchor: String? {
switch self {
case let .mapping(mapping):
mapping.anchor
case let .scalar(scalar):
scalar.anchor
case let .sequence(sequence):
sequence.anchor
case let .alias(anchor, _):
anchor
}
}
/// The node an alias resolved to; any other node returns itself.
public var resolved: Node {
switch self {
case .mapping, .scalar, .sequence:
self
case let .alias(_, node):
node
}
}
}
}