///|
/// A scalar of the native YAML profile. `text` is the value after the
/// profile removes one pair of matching outer quotes; there is no escape
/// processing. `raw` is the source spelling, or `None` for a constructed
/// value. Logical comparison and canonical writing use only `text`.
pub struct Scalar {
text : String
raw : String?
} derive(Eq, Debug)
///|
pub extend Scalar with Eq::{equal, not_equal}
///|
pub extend Scalar with @debug.Debug::{to_repr}
///|
pub fn Scalar::new(text : String) -> Scalar {
{ text, raw: None, }
}
///|
/// The part of a `key:` line after the colon.
pub(all) enum Entry {
/// `key:` with no value. The native profile reads the next lines to
/// decide between an empty string, joined text and a nested collection.
Empty
/// `key: value`.
Value(Scalar)
/// `key: [a, b]`. The items are decoded as the native profile does.
Flow(ReadOnlyArray[String])
/// `key: |` with its content lines. Each content line is trimmed.
Block(ReadOnlyArray[String])
/// `key: >`. The pinned profile refuses folded text.
Folded
} derive(Eq, Debug)
///|
pub extend Entry with Eq::{equal, not_equal}
///|
pub extend Entry with @debug.Debug::{to_repr}
///|
/// The content of one native YAML line.
pub(all) enum Content {
/// A lone `-`: a list item whose content is on the next lines.
Dash
/// `- value`: a list item with a scalar.
Item(Scalar)
/// A line without a key. It continues an empty `key:` value; anywhere
/// else the pinned profile refuses it.
Text(Scalar)
/// `key: ...`, or `- key: ...` when the flag is true.
Pair(Bool, Scalar, Entry)
} derive(Eq, Debug)
///|
pub extend Content with Eq::{equal, not_equal}
///|
pub extend Content with @debug.Debug::{to_repr}
///|
/// One native YAML line. `indent` is the number of leading spaces, with a
/// tab counted as four spaces, before any `- `.
pub struct Line {
indent : Int32
content : Content
span : @diagrams.Span?
} derive(Eq, Debug)
///|
pub extend Line with Eq::{equal, not_equal}
///|
pub extend Line with @debug.Debug::{to_repr}
///|
pub fn Line::new(indent : Int32, content : Content) -> Line {
{ indent, content, span: None, }
}
///|
/// The indentation that the native profile compares: the column after `- `
/// for a list item, one more than the dash for a lone `-`.
pub fn Line::native_indent(self : Line) -> Int32 {
match self.content {
Dash => self.indent + 1
Item(_) | Pair(true, _, _) => self.indent + 2
_ => self.indent
}
}
///|
/// One parsed YAML diagram block: the native line forms in source order.
/// Comment-only and blank lines are trivia; block content lines belong to
/// their `key: |` line.
pub struct Document {
name : String?
statements : ReadOnlyArray[@envelope.Statement]
highlights : ReadOnlyArray[@data.Selector]
lines : ReadOnlyArray[Line]
} derive(Eq, Debug)
///|
pub extend Document with Eq::{equal, not_equal}
///|
pub extend Document with @debug.Debug::{to_repr}
///|
/// A document. The constructor copies its arrays.
pub fn Document::new(
lines : ArrayView[Line],
name? : String,
statements~ : ArrayView[@envelope.Statement] = [],
highlights~ : ArrayView[@data.Selector] = [],
) -> Document {
{
name,
statements: ReadOnlyArray::from_array(statements),
highlights: ReadOnlyArray::from_array(highlights),
lines: ReadOnlyArray::from_array(lines),
}
}
///|
fn content_logical_equal(a : Content, b : Content) -> Bool {
match (a, b) {
(Dash, Dash) => true
(Item(x), Item(y)) | (Text(x), Text(y)) => x.text == y.text
(Pair(i, k, e), Pair(j, l, f)) =>
i == j &&
k.text == l.text &&
(match (e, f) {
(Value(x), Value(y)) => x.text == y.text
_ => e == f
})
_ => false
}
}
///|
/// Compare two lines without spans and source spellings.
pub fn Line::logical_equal(self : Line, other : Line) -> Bool {
self.indent == other.indent &&
content_logical_equal(self.content, other.content)
}
///|
/// Compare two documents without spans and source spellings.
pub fn Document::logical_equal(self : Document, other : Document) -> Bool {
if self.name != other.name ||
self.statements != other.statements ||
self.highlights.length() != other.highlights.length() ||
self.lines.length() != other.lines.length() {
return false
}
for i in 0..