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#

  • YamlError if 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 than MAX_DEPTH (128) collections. offset gives the byte position of the problem.

  • OutOfMemoryError if the Allocator cannot 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#

silk
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#

silk
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#

silk
pub const MAX_DEPTH: usize

The deepest nesting of mappings and sequences that the parser accepts.

MAX_ALIAS_NODES#

silk
pub const MAX_ALIAS_NODES: usize

The most nodes that aliases may add to one stream, so that a small text cannot expand to a huge document through nested aliases.

YamlReason#

silk
pub enum YamlReason

The cause of a YamlError.

InvalidUtf8#

silk
InvalidUtf8

The input is not valid UTF-8.

UnexpectedCharacter#

silk
UnexpectedCharacter

A 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#

silk
UnexpectedEnd

The input ends inside a flow collection or a quoted scalar.

BadIndentation#

silk
BadIndentation

A line is indented in a way that does not match the collection it belongs to.

TabIndentation#

silk
TabIndentation

A tab character is used for indentation. YAML allows only spaces there.

InvalidEscape#

silk
InvalidEscape

A double-quoted scalar has an escape that YAML does not define, or an escape that is not a Unicode scalar value.

DuplicateKey#

silk
DuplicateKey

The same key appears two times in one mapping.

UnknownAlias#

silk
UnknownAlias

An alias names an anchor that is not defined before it.

AliasLimit#

silk
AliasLimit

Aliases would add more than MAX_ALIAS_NODES nodes.

DepthExceeded#

silk
DepthExceeded

More than MAX_DEPTH collections are nested, in the parsed text or in the values that a silk.yaml.output.YamlEmitter writes.

UnsupportedKey#

silk
UnsupportedKey

A mapping key is a collection or an alias of a collection. Keys must be scalars.

InvalidTag#

silk
InvalidTag

A scalar does not match its tag, such as !!int abc.

InvalidBlockScalar#

silk
InvalidBlockScalar

The block scalar header after | or > is not valid, or its lines are indented less than its first line.

MultipleDocuments#

silk
MultipleDocuments

YamlDocument.parse found more than one document.

TooLarge#

silk
TooLarge

The input is 4 GiB or longer.

Implementation Debug for YamlReason#

silk
impl Debug for YamlReason

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as DuplicateKey.

YamlError#

silk
pub struct YamlError

A YAML error: the cause and the byte position where it occurred.

Field reason#

silk
pub reason: YamlReason

The cause of the error.

Field offset#

silk
pub offset: usize

The byte position of the problem in the input.

Implementation Copy for YamlError#

silk
impl Copy for YamlError

Implementation Debug for YamlError#

silk
impl Debug for YamlError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the fields, as YamlError { reason: ..., offset: ... }.

YamlKind#

silk
pub enum YamlKind

The kind of a value in a YamlDocument.

Null#

silk
Null

null, ~, or an empty value.

Bool#

silk
Bool

true or false.

Integer#

silk
Integer

An integer, such as 42, -7, or 0xff.

Float#

silk
Float

A float, such as 1.5, 6e-3, .inf, or .nan.

String#

silk
String

A string.

Sequence#

silk
Sequence

A sequence of values.

Mapping#

silk
Mapping

A mapping from string keys to values.

Implementation YamlKind#

silk
impl YamlKind

Associated function ofPlain#

silk
pub fn ofPlain(text: string) -> YamlKind

Returns 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#

silk
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#

silk
pub struct YamlSpan

A range of bytes in the parsed text: from start up to, but not including, end.

Field start#

silk
pub start: usize

The position of the first byte.

Field end#

silk
pub end: usize

The position after the last byte.

Implementation Copy for YamlSpan#

silk
impl Copy for YamlSpan

YamlNode#

silk
pub struct YamlNode

One 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#

silk
impl Copy for YamlNode

YamlMember#

silk
pub struct YamlMember

One member of a mapping in a YamlDocument: the node of its key, which is a string, and the node of its value.

Field key#

silk
pub key: YamlNode

The key, a string node.

Field value#

silk
pub value: YamlNode

The value.

Implementation Copy for YamlMember#

silk
impl Copy for YamlMember

YamlItems#

silk
pub struct YamlItems

The elements of a sequence in a YamlDocument, in source order. YamlDocument.items makes it.

Implementation Copy for YamlItems#

silk
impl Copy for YamlItems

YamlMembers#

silk
pub struct YamlMembers

The members of a mapping in a YamlDocument, in source order. YamlDocument.members makes it.

Implementation Copy for YamlMembers#

silk
impl Copy for YamlMembers

Implementation YamlItems#

silk
impl YamlItems

Method next#

silk
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#

silk
impl YamlMembers

Method next#

silk
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#

silk
pub struct YamlDocument

A 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#

silk
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#

silk
impl YamlDocument

Associated function parse#

silk
pub effect fn parse(bytes: &[u8]) -> YamlDocument ! YamlError | OutOfMemoryError ? &mut Allocator

Parses 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#

  • YamlError if bytes is not valid YAML, or with YamlReason.MultipleDocuments if it holds more than one document. offset is the byte position of the problem.

  • OutOfMemoryError if the Allocator cannot supply the document.

Associated function parseAll#

silk
pub effect fn parseAll(bytes: &[u8]) -> YamlDocument ! YamlError | OutOfMemoryError ? &mut Allocator

Parses 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#

  • YamlError if bytes is not valid YAML. offset is the byte position of the problem.

  • OutOfMemoryError if the Allocator cannot supply the document.

Method documentCount#

silk
pub fn documentCount(self: &Self) -> usize

Returns the number of documents in the stream. A document from YamlDocument.parse has exactly one.

Method documentAt#

silk
pub fn documentAt(self: &Self, index: usize) -> YamlNode

Returns 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#

silk
pub fn root(self: &Self) -> YamlNode

Returns 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#

silk
pub fn kind(self: &Self, node: YamlNode) -> YamlKind

Returns the kind of node.

Method span#

silk
pub fn span(self: &Self, node: YamlNode) -> YamlSpan

Returns the span of the text of node. For a value that an alias copied, the span of the alias.

Method isNull#

silk
pub fn isNull(self: &Self, node: YamlNode) -> bool

Returns true if node is null.

Method asBool#

silk
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#

silk
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#

silk
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#

silk
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#

silk
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#

silk
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#

silk
pub fn length(self: &Self, node: YamlNode) -> usize

Returns the number of elements of a sequence or members of a mapping, or 0 for any other value.

Method items#

silk
pub fn items(self: &Self, node: YamlNode) -> YamlItems

Returns the elements of a sequence, or no elements if node is not a sequence.

Method members#

silk
pub fn members(self: &Self, node: YamlNode) -> YamlMembers

Returns the members of a mapping, or no members if node is not a mapping.

Method field#

silk
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#

silk
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#

silk
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#

silk
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#

silk
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.

View the source of this page