silk.yaml.document.
Parse YAML into a compact document, and read values from it.
When to use#
Use YamlDocument.parse to read a configuration file, a lockfile, or any other YAML text whose
shape is not fixed. Read values with YamlDocument.field, YamlDocument.item, and the as...
functions. Use YamlDocument.parseAll for a stream of several documents. Use
silk.schema.yaml.SchemaYaml to read YAML straight into a typed value, and silk.yaml.output to
write YAML.
Details#
The parser follows YAML 1.2 with the core schema. It reads block mappings and sequences, flow
collections ([a, b] and {a: 1}), plain, single-quoted, and double-quoted scalars, literal
(|) and folded (>) block scalars, comments, anchors and aliases, tags, directives, and several
documents in one stream. Mappings keep source order.
A plain scalar is resolved with the core schema: null, ~, and an empty value are null; true
and false (also capitalized or upper case) are booleans; 12, -3, 0o17, and 0xff are
integers; 1.5, 6e-3, .inf, and .nan are floats; everything else is a string. A quoted
scalar is always a string. The tags !!str, !!int, !!float, !!bool, !!null, and the
non-specific ! change the resolution. Other tags are accepted and ignored.
An alias is replaced with a copy of the node its anchor names. Merge keys (<<) have no special
meaning: << is an ordinary key.
Errors#
YamlErrorif the text is not valid YAML, repeats a key in one mapping, uses a key that is not a scalar, names an alias that is not defined before it, or nests more thanMAX_DEPTH(128) collections.offsetgives the byte position of the problem.OutOfMemoryErrorif theAllocatorcannot supply the document.
Gotchas#
A key is always text: the key 1 in 1: one reads as the string "1", and an empty key as
"". A key that is a collection, such as [a, b]: c or ? [a, b], is refused with
YamlReason.UnsupportedKey.
Examples#
Read fields from a document#
import silk.core.effect { Effect }
import silk.core.option { Option }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.yaml.document { YamlDocument, YamlError, YamlNode }
effect fn read() -> i32 ! YamlError | OutOfMemoryError ? &mut Allocator {
let document = run YamlDocument.parse(b"name: Ada\nports:\n - 80\n - 443\n")
let root = YamlDocument.root(&document)
let name = YamlDocument.field(&document, root, "name") |> YamlDocument.stringAt(&document)
|> Option<string>.unwrapOr("")
let port = match YamlDocument.field(&document, root, "ports") {
Option<YamlNode>.Some { value } => YamlDocument.item(&document, value, 1) |> YamlDocument.i64At(&document)
|> Option<i64>.unwrapOr(0)
Option<YamlNode>.None => 0
}
if name != "Ada" || port != 443 {
return 1
}
return 0
}
pub effect fn main() -> i32 ! YamlError | OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
return run read() |> Effect.provideMut<Allocator>(&mut allocator)
}Report where a key repeats#
import silk.core.effect { Effect }
import silk.core.result { Result }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.yaml.document { YamlDocument, YamlError, YamlReason }
pub effect fn main() -> i32 ! OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let parsed = run Effect.result(YamlDocument.parse(b"name: Ada\nname: Grace\n")) |> Effect.provideMut<Allocator>(&mut allocator)
return match move parsed {
Result<YamlDocument, YamlError | OutOfMemoryError>.Success {..} => 1
Result<YamlDocument, YamlError | OutOfMemoryError>.Failure {error} => match move error {
// The second `name` starts at byte 10.
YamlError {reason, offset} => if reason == YamlReason.DuplicateKey && offset == 10 { 0 } else { 2 }
OutOfMemoryError {} => { fail OutOfMemoryError {} }
}
}
}Import as YamlDocument with import silk.yaml.document { YamlDocument }.
Public declarations: 11.
MAX_DEPTH#
pub const MAX_DEPTH: usizeThe deepest nesting of mappings and sequences that the parser accepts.
MAX_ALIAS_NODES#
pub const MAX_ALIAS_NODES: usizeThe most nodes that aliases may add to one stream, so that a small text cannot expand to a huge document through nested aliases.
YamlReason#
pub enum YamlReasonThe cause of a YamlError.
InvalidUtf8#
InvalidUtf8The input is not valid UTF-8.
UnexpectedCharacter#
UnexpectedCharacterA character that YAML does not allow at this position, such as a : after a value on the same
line, or text after the end of a node.
UnexpectedEnd#
UnexpectedEndThe input ends inside a flow collection or a quoted scalar.
BadIndentation#
BadIndentationA line is indented in a way that does not match the collection it belongs to.
TabIndentation#
TabIndentationA tab character is used for indentation. YAML allows only spaces there.
InvalidEscape#
InvalidEscapeA double-quoted scalar has an escape that YAML does not define, or an escape that is not a Unicode scalar value.
DuplicateKey#
DuplicateKeyThe same key appears two times in one mapping.
UnknownAlias#
UnknownAliasAn alias names an anchor that is not defined before it.
AliasLimit#
AliasLimitAliases would add more than MAX_ALIAS_NODES nodes.
DepthExceeded#
DepthExceededMore than MAX_DEPTH collections are nested, in the parsed text or in the values that a
silk.yaml.output.YamlEmitter writes.
UnsupportedKey#
UnsupportedKeyA mapping key is a collection or an alias of a collection. Keys must be scalars.
InvalidTag#
InvalidTagA scalar does not match its tag, such as !!int abc.
InvalidBlockScalar#
InvalidBlockScalarThe block scalar header after | or > is not valid, or its lines are indented less than its
first line.
MultipleDocuments#
MultipleDocumentsYamlDocument.parse found more than one document.
TooLarge#
TooLargeThe input is 4 GiB or longer.
Implementation Debug for YamlReason#
impl Debug for YamlReasonOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the name of the variant, such as DuplicateKey.
YamlError#
pub struct YamlErrorA YAML error: the cause and the byte position where it occurred.
Field reason#
pub reason: YamlReasonThe cause of the error.
Field offset#
pub offset: usizeThe byte position of the problem in the input.
Implementation Copy for YamlError#
impl Copy for YamlErrorImplementation Debug for YamlError#
impl Debug for YamlErrorOperation debug#
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut WriterWrites the fields, as YamlError { reason: ..., offset: ... }.
YamlKind#
pub enum YamlKindThe kind of a value in a YamlDocument.
Null#
Nullnull, ~, or an empty value.
Bool#
Booltrue or false.
Integer#
IntegerAn integer, such as 42, -7, or 0xff.
Float#
FloatA float, such as 1.5, 6e-3, .inf, or .nan.
String#
StringA string.
Sequence#
SequenceA sequence of values.
Mapping#
MappingA mapping from string keys to values.
Implementation YamlKind#
impl YamlKindAssociated function ofPlain#
pub fn ofPlain(text: string) -> YamlKindReturns the kind that the plain (unquoted) scalar text resolves to with the YAML 1.2 core
schema.
When to use#
Check if a string can be written without quotes: if the result is not YamlKind.String, a
reader would read the unquoted text as another kind of value.
Details#
The empty text, null, Null, NULL, and ~ give YamlKind.Null. true and false in
the same three spellings give YamlKind.Bool. Decimal, 0o octal, and 0x hexadecimal
integers give YamlKind.Integer. Decimals with a fraction or an exponent, .inf, -.inf, and
.nan give YamlKind.Float. All other text gives YamlKind.String. The text is not checked
for characters that a plain scalar cannot hold.
Examples#
import silk.yaml.document { YamlKind }
pub fn main() -> i32 {
if YamlKind.ofPlain("0x1f") != YamlKind.Integer || YamlKind.ofPlain("yes") != YamlKind.String {
return 1
}
return 0
}YamlSpan#
pub struct YamlSpanA range of bytes in the parsed text: from start up to, but not including, end.
Field start#
pub start: usizeThe position of the first byte.
Field end#
pub end: usizeThe position after the last byte.
Implementation Copy for YamlSpan#
impl Copy for YamlSpanYamlNode#
pub struct YamlNodeOne value in a YamlDocument: its position in the document.
Gotchas#
A YamlNode is only meaningful with the document that returned it. With another document it
names some other value or traps.
Implementation Copy for YamlNode#
impl Copy for YamlNodeYamlMember#
pub struct YamlMemberOne member of a mapping in a YamlDocument: the node of its key, which is a string, and the
node of its value.
Field key#
pub key: YamlNodeThe key, a string node.
Field value#
pub value: YamlNodeThe value.
Implementation Copy for YamlMember#
impl Copy for YamlMemberYamlItems#
pub struct YamlItemsThe elements of a sequence in a YamlDocument, in source order. YamlDocument.items makes
it.
Implementation Copy for YamlItems#
impl Copy for YamlItemsYamlMembers#
pub struct YamlMembersThe members of a mapping in a YamlDocument, in source order. YamlDocument.members makes
it.
Implementation Copy for YamlMembers#
impl Copy for YamlMembersImplementation YamlItems#
impl YamlItemsMethod next#
pub fn next(self: &mut Self, document: &YamlDocument) -> Option<YamlNode>Returns the next element, or an absent option after the last one. document must be the one
that made these items.
Implementation YamlMembers#
impl YamlMembersMethod next#
pub fn next(self: &mut Self, document: &YamlDocument) -> Option<YamlMember>Returns the next member, or an absent option after the last one. document must be the one
that made these members.
YamlDocument#
pub struct YamlDocumentA parsed YAML stream: one or more documents, held as a flat list of values.
When to use#
Use YamlDocument.parse to read one document and YamlDocument.parseAll to read a stream of
several. Start reading at YamlDocument.root, or at YamlDocument.documentAt for a stream.
Details#
Strings are decoded when the text is parsed, and numbers are converted. Each value also keeps the
span of its text, which YamlDocument.span returns. The document owns all of its text, so it
does not borrow the input.
Gotchas#
A YamlNode is only meaningful with the document that returned it.
Examples#
Walk the members of a mapping#
import silk.core.effect { Effect }
import silk.core.option { Option }
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.yaml.document { YamlDocument, YamlError, YamlMember }
effect fn total() -> i64 ! YamlError | OutOfMemoryError ? &mut Allocator {
let document = run YamlDocument.parse(b"a: 1\nb: 2\nc: 3\n")
let mut members = YamlDocument.members(&document, YamlDocument.root(&document))
let mut sum: i64 = 0
while let Option<YamlMember>.Some { value } = members.next(&document) {
sum = sum + Option<i64>.unwrapOr(YamlDocument.asI64(&document, value.value), 0)
}
return sum
}
pub effect fn main() -> i32 ! YamlError | OutOfMemoryError {
let mut allocator = Allocator.systemAllocatorProvider()
let sum = run total() |> Effect.provideMut<Allocator>(&mut allocator)
return if sum == 6 { 0 } else { 1 }
}Implementation YamlDocument#
impl YamlDocumentAssociated function parse#
pub effect fn parse(bytes: &[u8]) -> YamlDocument ! YamlError | OutOfMemoryError ? &mut AllocatorParses bytes as one YAML document.
Details#
The text may start with a --- marker and end with a ... marker. Text with no document,
such as an empty file or one with only comments, gives a document whose root is null.
Errors#
YamlErrorifbytesis not valid YAML, or withYamlReason.MultipleDocumentsif it holds more than one document.offsetis the byte position of the problem.OutOfMemoryErrorif theAllocatorcannot supply the document.
Associated function parseAll#
pub effect fn parseAll(bytes: &[u8]) -> YamlDocument ! YamlError | OutOfMemoryError ? &mut AllocatorParses bytes as a stream of YAML documents, separated by --- or ... markers.
Details#
Read the number of documents with YamlDocument.documentCount and each one with
YamlDocument.documentAt. Text with no document gives a stream with no documents.
Errors#
YamlErrorifbytesis not valid YAML.offsetis the byte position of the problem.OutOfMemoryErrorif theAllocatorcannot supply the document.
Method documentCount#
pub fn documentCount(self: &Self) -> usizeReturns the number of documents in the stream. A document from YamlDocument.parse has
exactly one.
Method documentAt#
pub fn documentAt(self: &Self, index: usize) -> YamlNodeReturns the root node of the document at index in the stream. The first document has index
0.
Gotchas#
index must be less than YamlDocument.documentCount, or the call traps.
Method root#
pub fn root(self: &Self) -> YamlNodeReturns the root node of the first document.
Gotchas#
A stream from YamlDocument.parseAll with no documents has no root, and the call traps.
Method kind#
pub fn kind(self: &Self, node: YamlNode) -> YamlKindReturns the kind of node.
Method span#
pub fn span(self: &Self, node: YamlNode) -> YamlSpanReturns the span of the text of node. For a value that an alias copied, the span of the
alias.
Method isNull#
pub fn isNull(self: &Self, node: YamlNode) -> boolReturns true if node is null.
Method asBool#
pub fn asBool(self: &Self, node: YamlNode) -> Option<bool>Returns the value of a boolean, or None if node is not a boolean.
Method asI64#
pub fn asI64(self: &Self, node: YamlNode) -> Option<i64>Returns the value of an integer that fits in i64, or None if node is not such an
integer.
Method asU64#
pub fn asU64(self: &Self, node: YamlNode) -> Option<u64>Returns the value of an integer that fits in u64, or None if node is not such an
integer.
Method asF64#
pub fn asF64(self: &Self, node: YamlNode) -> Option<f64>Returns a number as an f64: a float, or an integer converted to the nearest f64. Returns
None if node is not a number.
Method asString#
pub fn asString<'a>(self: &'a Self, node: YamlNode) -> Option<string<'a>>Returns the decoded text of a string, or None if node is not a string. The text borrows
the document.
Method scalarText#
pub fn scalarText<'a>(self: &'a Self, node: YamlNode) -> Option<string<'a>>Returns the text of a scalar: the decoded text of a string, or the source spelling of a null,
a boolean, or a number, such as 0x1F or ~. Returns None for a sequence or a mapping. The
text borrows the document.
When to use#
Use it to read a value as text whatever it resolved to, such as a version 1.10 that the core
schema reads as a float.
Method length#
pub fn length(self: &Self, node: YamlNode) -> usizeReturns the number of elements of a sequence or members of a mapping, or 0 for any other value.
Method items#
pub fn items(self: &Self, node: YamlNode) -> YamlItemsReturns the elements of a sequence, or no elements if node is not a sequence.
Method members#
pub fn members(self: &Self, node: YamlNode) -> YamlMembersReturns the members of a mapping, or no members if node is not a mapping.
Method field#
pub fn field(self: &Self, node: YamlNode, key: string) -> Option<YamlNode>Returns the value of member key of a mapping. Returns None if node is not a mapping or
has no such key.
Method item#
pub fn item(self: &Self, node: YamlNode, index: usize) -> Option<YamlNode>Returns the element at index of a sequence. The first element has index 0. Returns None if
node is not a sequence or index is out of range.
Associated function fieldAt#
pub fn fieldAt(node: Option<YamlNode>, document: &YamlDocument, key: string) -> Option<YamlNode>Returns the member key of an optional mapping node. Use it to chain lookups with |>.
Returns None if node is None, is not a mapping, or has no such key.
Associated function stringAt#
pub fn stringAt<'a>(node: Option<YamlNode>, document: &'a YamlDocument) -> Option<string<'a>>Returns the text of an optional string node. Use it at the end of a |> chain.
Returns None if node is None or is not a string. The text borrows document.
Associated function i64At#
pub fn i64At(node: Option<YamlNode>, document: &YamlDocument) -> Option<i64>Returns the value of an optional integer node. Use it at the end of a |> chain.
Returns None if node is None or is not an integer that fits in i64.
Category: Data formats.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, x86_64-pc-windows-msvc, wasm64-unknown-unknown.