2016-02-27 12:09:50 +00:00
|
|
|
# NimYAML - YAML implementation in Nim
|
2016-09-21 19:20:57 +00:00
|
|
|
# (c) Copyright 2016 Felix Krause
|
2016-02-27 12:09:50 +00:00
|
|
|
#
|
|
|
|
# See the file "copying.txt", included in this
|
|
|
|
# distribution, for details about the copyright.
|
|
|
|
|
2016-09-21 19:20:57 +00:00
|
|
|
## ===============
|
2021-05-17 22:31:47 +00:00
|
|
|
## Module yaml/dom
|
2016-09-21 19:20:57 +00:00
|
|
|
## ===============
|
|
|
|
##
|
|
|
|
## This is the DOM API, which enables you to load YAML into a tree-like
|
|
|
|
## structure. It can also dump the structure back to YAML. Formally, it
|
|
|
|
## represents the *Representation Graph* as defined in the YAML specification.
|
|
|
|
##
|
2016-11-28 19:17:04 +00:00
|
|
|
## The main interface of this API are ``loadDom`` and ``dumpDom``. The other
|
2016-09-21 19:20:57 +00:00
|
|
|
## exposed procs are low-level and useful if you want to load or generate parts
|
|
|
|
## of a ``YamlStream``.
|
2016-11-28 19:17:04 +00:00
|
|
|
##
|
|
|
|
## The ``YamlNode`` objects in the DOM can be used similarly to the ``JsonNode``
|
|
|
|
## objects of Nim's `json module <http://nim-lang.org/docs/json.html>`_.
|
2016-09-21 19:20:57 +00:00
|
|
|
|
2016-11-28 19:17:04 +00:00
|
|
|
import tables, streams, hashes, sets, strutils
|
2020-11-03 21:08:21 +00:00
|
|
|
import data, stream, taglib, serialization, private/internal, parser,
|
2016-09-20 19:53:38 +00:00
|
|
|
presenter
|
2020-11-03 21:08:21 +00:00
|
|
|
|
2021-05-17 22:42:58 +00:00
|
|
|
when defined(gcArc) and not defined(gcOrc):
|
|
|
|
{.error: "NimYAML's DOM API only supports ORC because ARC can't deal with cycles".}
|
|
|
|
|
2018-08-18 02:43:52 +00:00
|
|
|
when defined(nimNoNil):
|
|
|
|
{.experimental: "notnil".}
|
2016-09-20 19:53:38 +00:00
|
|
|
type
|
|
|
|
YamlNodeKind* = enum
|
|
|
|
yScalar, yMapping, ySequence
|
|
|
|
|
|
|
|
YamlNode* = ref YamlNodeObj not nil
|
|
|
|
## Represents a node in a ``YamlDocument``.
|
|
|
|
|
|
|
|
YamlNodeObj* = object
|
2021-03-23 17:51:05 +00:00
|
|
|
tag*: Tag
|
2016-09-20 19:53:38 +00:00
|
|
|
case kind*: YamlNodeKind
|
|
|
|
of yScalar: content*: string
|
2016-11-28 19:17:04 +00:00
|
|
|
of ySequence: elems*: seq[YamlNode]
|
|
|
|
of yMapping: fields*: TableRef[YamlNode, YamlNode]
|
|
|
|
# compiler does not like Table[YamlNode, YamlNode]
|
2016-09-20 19:53:38 +00:00
|
|
|
|
|
|
|
YamlDocument* = object
|
|
|
|
## Represents a YAML document.
|
|
|
|
root*: YamlNode
|
|
|
|
|
2016-11-28 19:17:04 +00:00
|
|
|
proc hash*(o: YamlNode): Hash =
|
|
|
|
result = o.tag.hash
|
|
|
|
case o.kind
|
|
|
|
of yScalar: result = result !& o.content.hash
|
|
|
|
of yMapping:
|
|
|
|
for key, value in o.fields.pairs:
|
|
|
|
result = result !& key.hash !& value.hash
|
|
|
|
of ySequence:
|
|
|
|
for item in o.elems:
|
|
|
|
result = result !& item.hash
|
|
|
|
result = !$result
|
|
|
|
|
|
|
|
proc eqImpl(x, y: YamlNode, alreadyVisited: var HashSet[pointer]): bool =
|
|
|
|
template compare(a, b: YamlNode) {.dirty.} =
|
|
|
|
if cast[pointer](a) != cast[pointer](b):
|
|
|
|
if cast[pointer](a) in alreadyVisited and
|
|
|
|
cast[pointer](b) in alreadyVisited:
|
|
|
|
# prevent infinite loop!
|
|
|
|
return false
|
|
|
|
elif a != b: return false
|
|
|
|
|
|
|
|
if x.kind != y.kind or x.tag != y.tag: return false
|
|
|
|
alreadyVisited.incl(cast[pointer](x))
|
|
|
|
alreadyVisited.incl(cast[pointer](y))
|
|
|
|
case x.kind
|
|
|
|
of yScalar: result = x.content == y.content
|
|
|
|
of ySequence:
|
|
|
|
if x.elems.len != y.elems.len: return false
|
|
|
|
for i in 0..<x.elems.len:
|
|
|
|
compare(x.elems[i], y.elems[i])
|
|
|
|
of yMapping:
|
|
|
|
if x.fields.len != y.fields.len: return false
|
|
|
|
for xKey, xValue in x.fields.pairs:
|
|
|
|
let xKeyVisited = cast[pointer](xKey) in alreadyVisited
|
|
|
|
var matchingValue: ref YamlNodeObj = nil
|
|
|
|
for yKey, yValue in y.fields.pairs:
|
|
|
|
if cast[pointer](yKey) != cast[pointer](xKey):
|
|
|
|
if cast[pointer](yKey) in alreadyVisited and xKeyVisited:
|
|
|
|
# prevent infinite loop!
|
|
|
|
continue
|
|
|
|
if xKey == yKey:
|
|
|
|
matchingValue = yValue
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
matchingValue = yValue
|
|
|
|
break
|
|
|
|
if isNil(matchingValue): return false
|
|
|
|
compare(xValue, matchingValue)
|
|
|
|
|
|
|
|
proc `==`*(x, y: YamlNode): bool =
|
2020-03-08 18:22:34 +00:00
|
|
|
var alreadyVisited = initHashSet[pointer]()
|
2016-11-28 19:17:04 +00:00
|
|
|
result = eqImpl(x, y, alreadyVisited)
|
|
|
|
|
|
|
|
proc `$`*(n: YamlNode): string =
|
2021-03-23 17:51:05 +00:00
|
|
|
result = "!<" & $n.tag & "> "
|
2016-11-28 19:17:04 +00:00
|
|
|
case n.kind
|
|
|
|
of yScalar: result.add(escape(n.content))
|
|
|
|
of ySequence:
|
|
|
|
result.add('[')
|
|
|
|
for item in n.elems:
|
|
|
|
result.add($item)
|
|
|
|
result.add(", ")
|
|
|
|
result.setLen(result.len - 1)
|
|
|
|
result[^1] = ']'
|
|
|
|
of yMapping:
|
|
|
|
result.add('{')
|
|
|
|
for key, value in n.fields.pairs:
|
|
|
|
result.add($key)
|
|
|
|
result.add(": ")
|
|
|
|
result.add($value)
|
|
|
|
result.add(", ")
|
|
|
|
result.setLen(result.len - 1)
|
|
|
|
result[^1] = '}'
|
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc newYamlNode*(content: string, tag: Tag = yTagQuestionMark): YamlNode =
|
2016-04-03 09:45:48 +00:00
|
|
|
YamlNode(kind: yScalar, content: content, tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc newYamlNode*(elems: openarray[YamlNode], tag: Tag = yTagQuestionMark):
|
2016-04-02 15:48:22 +00:00
|
|
|
YamlNode =
|
2016-11-28 19:17:04 +00:00
|
|
|
YamlNode(kind: ySequence, elems: @elems, tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2016-11-28 19:17:04 +00:00
|
|
|
proc newYamlNode*(fields: openarray[(YamlNode, YamlNode)],
|
2021-03-23 17:51:05 +00:00
|
|
|
tag: Tag = yTagQuestionMark): YamlNode =
|
2016-11-28 19:17:04 +00:00
|
|
|
YamlNode(kind: yMapping, fields: newTable(fields), tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2020-11-03 21:08:21 +00:00
|
|
|
proc initYamlDoc*(root: YamlNode): YamlDocument =
|
|
|
|
result = YamlDocument(root: root)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc composeNode(s: var YamlStream, c: ConstructionContext):
|
2016-04-02 15:48:22 +00:00
|
|
|
YamlNode {.raises: [YamlStreamError, YamlConstructionError].} =
|
2020-11-03 21:08:21 +00:00
|
|
|
template addAnchor(c: ConstructionContext, target: Anchor) =
|
2017-03-29 19:42:07 +00:00
|
|
|
if target != yAnchorNone:
|
2020-11-10 12:55:22 +00:00
|
|
|
yAssert(not c.refs.hasKey(target))
|
2020-11-10 13:48:19 +00:00
|
|
|
c.refs[target] = (tag: yamlTag(YamlNode), p: cast[pointer](result))
|
2017-03-29 19:42:07 +00:00
|
|
|
|
2020-11-03 21:08:21 +00:00
|
|
|
var start: Event
|
2016-09-14 16:31:09 +00:00
|
|
|
shallowCopy(start, s.next())
|
2016-04-02 15:48:22 +00:00
|
|
|
new(result)
|
|
|
|
try:
|
|
|
|
case start.kind
|
|
|
|
of yamlStartMap:
|
2021-03-23 17:51:05 +00:00
|
|
|
result = YamlNode(tag: start.mapProperties.tag,
|
2019-06-16 11:43:08 +00:00
|
|
|
kind: yMapping,
|
|
|
|
fields: newTable[YamlNode, YamlNode]())
|
2016-04-02 15:48:22 +00:00
|
|
|
while s.peek().kind != yamlEndMap:
|
|
|
|
let
|
2021-03-23 17:51:05 +00:00
|
|
|
key = composeNode(s, c)
|
|
|
|
value = composeNode(s, c)
|
2016-11-28 19:17:04 +00:00
|
|
|
if result.fields.hasKeyOrPut(key, value):
|
|
|
|
raise newException(YamlConstructionError,
|
|
|
|
"Duplicate key: " & $key)
|
2016-04-02 15:48:22 +00:00
|
|
|
discard s.next()
|
2020-11-03 21:08:21 +00:00
|
|
|
addAnchor(c, start.mapProperties.anchor)
|
2016-04-02 15:48:22 +00:00
|
|
|
of yamlStartSeq:
|
2021-03-23 17:51:05 +00:00
|
|
|
result = YamlNode(tag: start.seqProperties.tag,
|
2019-06-16 11:43:08 +00:00
|
|
|
kind: ySequence,
|
|
|
|
elems: newSeq[YamlNode]())
|
2016-04-02 15:48:22 +00:00
|
|
|
while s.peek().kind != yamlEndSeq:
|
2021-03-23 17:51:05 +00:00
|
|
|
result.elems.add(composeNode(s, c))
|
2020-11-03 21:08:21 +00:00
|
|
|
addAnchor(c, start.seqProperties.anchor)
|
2016-04-02 15:48:22 +00:00
|
|
|
discard s.next()
|
|
|
|
of yamlScalar:
|
2021-03-23 17:51:05 +00:00
|
|
|
result = YamlNode(tag: start.scalarProperties.tag,
|
2019-06-16 11:43:08 +00:00
|
|
|
kind: yScalar)
|
2016-04-02 15:48:22 +00:00
|
|
|
shallowCopy(result.content, start.scalarContent)
|
2020-11-03 21:08:21 +00:00
|
|
|
addAnchor(c, start.scalarProperties.anchor)
|
2016-04-02 15:48:22 +00:00
|
|
|
of yamlAlias:
|
2020-11-10 12:55:22 +00:00
|
|
|
result = cast[YamlNode](c.refs[start.aliasTarget].p)
|
2016-08-09 18:47:22 +00:00
|
|
|
else: internalError("Malformed YamlStream")
|
2016-04-02 15:48:22 +00:00
|
|
|
except KeyError:
|
|
|
|
raise newException(YamlConstructionError,
|
|
|
|
"Wrong tag library: TagId missing")
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc compose*(s: var YamlStream): YamlDocument
|
2016-04-02 15:48:22 +00:00
|
|
|
{.raises: [YamlStreamError, YamlConstructionError].} =
|
|
|
|
var context = newConstructionContext()
|
2020-11-03 21:08:21 +00:00
|
|
|
var n: Event
|
2016-09-14 16:31:09 +00:00
|
|
|
shallowCopy(n, s.next())
|
2016-08-10 18:42:44 +00:00
|
|
|
yAssert n.kind == yamlStartDoc
|
2021-03-23 17:51:05 +00:00
|
|
|
result = YamlDocument(root: composeNode(s, context))
|
2016-08-10 18:42:44 +00:00
|
|
|
n = s.next()
|
|
|
|
yAssert n.kind == yamlEndDoc
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2016-11-28 19:17:04 +00:00
|
|
|
proc loadDom*(s: Stream | string): YamlDocument
|
2020-11-06 15:21:58 +00:00
|
|
|
{.raises: [IOError, OSError, YamlParserError, YamlConstructionError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
2021-03-23 17:51:05 +00:00
|
|
|
parser = initYamlParser()
|
2020-11-10 18:07:46 +00:00
|
|
|
events = parser.parse(s)
|
|
|
|
e: Event
|
|
|
|
try:
|
|
|
|
e = events.next()
|
|
|
|
yAssert(e.kind == yamlStartStream)
|
2021-03-23 17:51:05 +00:00
|
|
|
result = compose(events)
|
2020-11-10 18:07:46 +00:00
|
|
|
e = events.next()
|
|
|
|
if e.kind != yamlEndStream:
|
|
|
|
raise newYamlConstructionError(events, e.startPos, "stream contains multiple documents")
|
2016-04-02 15:48:22 +00:00
|
|
|
except YamlStreamError:
|
2020-11-10 18:07:46 +00:00
|
|
|
let ex = getCurrentException()
|
|
|
|
if ex.parent of YamlParserError:
|
|
|
|
raise (ref YamlParserError)(ex.parent)
|
|
|
|
elif ex.parent of IOError:
|
|
|
|
raise (ref IOError)(ex.parent)
|
2020-11-10 20:28:56 +00:00
|
|
|
elif ex.parent of OSError:
|
|
|
|
raise (ref OSError)(ex.parent)
|
2020-11-10 18:07:46 +00:00
|
|
|
else: internalError("Unexpected exception: " & ex.parent.repr)
|
|
|
|
|
|
|
|
proc loadMultiDom*(s: Stream | string): seq[YamlDocument]
|
|
|
|
{.raises: [IOError, OSError, YamlParserError, YamlConstructionError].} =
|
|
|
|
var
|
|
|
|
parser = initYamlParser(tagLib)
|
|
|
|
events = parser.parse(s)
|
|
|
|
e: Event
|
|
|
|
try:
|
|
|
|
e = events.next()
|
|
|
|
yAssert(e.kind == yamlStartStream)
|
|
|
|
while events.peek().kind == yamlStartDoc:
|
|
|
|
result.add(compose(events, tagLib))
|
|
|
|
e = events.next()
|
|
|
|
yAssert(e.kind != yamlEndStream)
|
|
|
|
except YamlStreamError:
|
|
|
|
let ex = getCurrentException()
|
|
|
|
if ex.parent of YamlParserError:
|
|
|
|
raise (ref YamlParserError)(ex.parent)
|
|
|
|
elif ex.parent of IOError:
|
|
|
|
raise (ref IOError)(ex.parent)
|
2020-11-10 20:28:56 +00:00
|
|
|
elif ex.parent of OSError:
|
|
|
|
raise (ref OSError)(ex.parent)
|
2020-11-10 18:07:46 +00:00
|
|
|
else: internalError("Unexpected exception: " & ex.parent.repr)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle)
|
|
|
|
{.raises: [].}=
|
2020-11-10 18:07:46 +00:00
|
|
|
var anchor = yAnchorNone
|
2020-11-10 12:55:22 +00:00
|
|
|
let p = cast[pointer](n)
|
|
|
|
if a != asNone and c.refs.hasKey(p):
|
2020-11-10 18:07:46 +00:00
|
|
|
anchor = c.refs.getOrDefault(p).a
|
|
|
|
c.refs[p] = (anchor, true)
|
|
|
|
c.put(aliasEvent(anchor))
|
2016-04-02 15:48:22 +00:00
|
|
|
return
|
2020-11-10 12:55:22 +00:00
|
|
|
if a != asNone:
|
2020-11-10 18:07:46 +00:00
|
|
|
anchor = c.nextAnchorId.Anchor
|
2020-11-10 12:55:22 +00:00
|
|
|
c.refs[p] = (c.nextAnchorId.Anchor, false)
|
2020-11-03 21:08:21 +00:00
|
|
|
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
|
2016-09-19 17:33:29 +00:00
|
|
|
|
2016-09-01 18:56:34 +00:00
|
|
|
case n.kind
|
2021-03-23 17:51:05 +00:00
|
|
|
of yScalar: c.put(scalarEvent(n.content, n.tag, anchor))
|
2016-09-01 18:56:34 +00:00
|
|
|
of ySequence:
|
2021-03-23 17:51:05 +00:00
|
|
|
c.put(startSeqEvent(csBlock, (anchor, n.tag)))
|
2016-11-28 19:17:04 +00:00
|
|
|
for item in n.elems:
|
2021-03-23 17:51:05 +00:00
|
|
|
serializeNode(item, c, a)
|
2016-09-01 18:56:34 +00:00
|
|
|
c.put(endSeqEvent())
|
|
|
|
of yMapping:
|
2021-03-23 17:51:05 +00:00
|
|
|
c.put(startMapEvent(csBlock, (anchor, n.tag)))
|
2016-11-28 19:17:04 +00:00
|
|
|
for key, value in n.fields.pairs:
|
2021-03-23 17:51:05 +00:00
|
|
|
serializeNode(key, c, a)
|
|
|
|
serializeNode(value, c, a)
|
2016-09-01 18:56:34 +00:00
|
|
|
c.put(endMapEvent())
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2021-03-23 17:51:05 +00:00
|
|
|
proc serialize*(doc: YamlDocument, a: AnchorStyle = asTidy):
|
2016-09-20 19:53:38 +00:00
|
|
|
YamlStream {.raises: [].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
2016-09-01 18:56:34 +00:00
|
|
|
bys = newBufferYamlStream()
|
2020-11-03 21:08:21 +00:00
|
|
|
c = newSerializationContext(a, proc(e: Event) {.raises: [].} =
|
2016-09-20 19:53:38 +00:00
|
|
|
bys.put(e)
|
2016-09-01 18:56:34 +00:00
|
|
|
)
|
2020-11-10 18:07:46 +00:00
|
|
|
c.put(startStreamEvent())
|
2016-09-01 18:56:34 +00:00
|
|
|
c.put(startDocEvent())
|
2021-03-23 17:51:05 +00:00
|
|
|
serializeNode(doc.root, c, a)
|
2016-09-01 18:56:34 +00:00
|
|
|
c.put(endDocEvent())
|
2020-11-10 18:07:46 +00:00
|
|
|
c.put(endStreamEvent())
|
2016-04-02 15:48:22 +00:00
|
|
|
if a == asTidy:
|
2020-11-10 18:07:46 +00:00
|
|
|
var ctx = initAnchorContext()
|
2016-09-20 19:53:38 +00:00
|
|
|
for event in bys.mitems():
|
2016-09-01 18:56:34 +00:00
|
|
|
case event.kind
|
2020-11-10 18:07:46 +00:00
|
|
|
of yamlScalar: ctx.process(event.scalarProperties, c.refs)
|
|
|
|
of yamlStartMap: ctx.process(event.mapProperties, c.refs)
|
|
|
|
of yamlStartSeq: ctx.process(event.seqProperties, c.refs)
|
|
|
|
of yamlAlias:
|
|
|
|
event.aliasTarget = ctx.map(event.aliasTarget)
|
2016-09-01 18:56:34 +00:00
|
|
|
else: discard
|
|
|
|
result = bys
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2016-11-28 19:17:04 +00:00
|
|
|
proc dumpDom*(doc: YamlDocument, target: Stream,
|
2016-02-26 20:55:59 +00:00
|
|
|
anchorStyle: AnchorStyle = asTidy,
|
|
|
|
options: PresentationOptions = defaultPresentationOptions)
|
2016-08-09 18:47:22 +00:00
|
|
|
{.raises: [YamlPresenterJsonError, YamlPresenterOutputError,
|
|
|
|
YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
## Dump a YamlDocument as YAML character stream.
|
|
|
|
var
|
2021-03-23 17:51:05 +00:00
|
|
|
events = serialize(doc,
|
2016-04-02 15:48:22 +00:00
|
|
|
if options.style == psJson: asNone else: anchorStyle)
|
2021-03-23 17:51:05 +00:00
|
|
|
present(events, target, options)
|
2016-11-28 19:17:04 +00:00
|
|
|
|
|
|
|
proc `[]`*(node: YamlNode, i: int): YamlNode =
|
|
|
|
## Get the node at index *i* from a sequence. *node* must be a *ySequence*.
|
|
|
|
assert node.kind == ySequence
|
|
|
|
node.elems[i]
|
|
|
|
|
|
|
|
proc `[]=`*(node: var YamlNode, i: int, val: YamlNode) =
|
|
|
|
## Set the node at index *i* of a sequence. *node* must be a *ySequence*.
|
|
|
|
assert node.kind == ySequence
|
|
|
|
node.elems[i] = val
|
|
|
|
|
|
|
|
proc `[]`*(node: YamlNode, key: YamlNode): YamlNode =
|
|
|
|
## Get the value for a key in a mapping. *node* must be a *yMapping*.
|
|
|
|
assert node.kind == yMapping
|
|
|
|
node.fields[key]
|
|
|
|
|
|
|
|
proc `[]=`*(node: YamlNode, key: YamlNode, value: YamlNode) =
|
|
|
|
## Set the value for a key in a mapping. *node* must be a *yMapping*.
|
|
|
|
node.fields[key] = value
|
|
|
|
|
|
|
|
proc `[]`*(node: YamlNode, key: string): YamlNode =
|
|
|
|
## Get the value for a string key in a mapping. *node* must be a *yMapping*.
|
|
|
|
## This searches for a scalar key with content *key* and either no explicit
|
|
|
|
## tag or the explicit tag ``!!str``.
|
|
|
|
assert node.kind == yMapping
|
2021-03-23 17:51:05 +00:00
|
|
|
var keyNode = YamlNode(kind: yScalar, tag: yTagExclamationMark, content: key)
|
2016-11-28 19:17:04 +00:00
|
|
|
result = node.fields.getOrDefault(keyNode)
|
|
|
|
if isNil(result):
|
2021-03-23 17:51:05 +00:00
|
|
|
keyNode.tag = yTagQuestionMark
|
2016-11-28 19:17:04 +00:00
|
|
|
result = node.fields.getOrDefault(keyNode)
|
|
|
|
if isNil(result):
|
|
|
|
keyNode.tag = nimTag(yamlTagRepositoryPrefix & "str")
|
|
|
|
result = node.fields.getOrDefault(keyNode)
|
|
|
|
if isNil(result):
|
|
|
|
raise newException(KeyError, "No key " & escape(key) & " exists!")
|
|
|
|
|
|
|
|
proc len*(node: YamlNode): int =
|
|
|
|
## If *node* is a *yMapping*, return the number of key-value pairs. If *node*
|
|
|
|
## is a *ySequence*, return the number of elements. Else, return ``0``
|
|
|
|
case node.kind
|
|
|
|
of yMapping: result = node.fields.len
|
|
|
|
of ySequence: result = node.elems.len
|
|
|
|
of yScalar: result = 0
|
|
|
|
|
|
|
|
iterator items*(node: YamlNode): YamlNode =
|
|
|
|
## Iterates over all items of a sequence. *node* must be a *ySequence*.
|
|
|
|
assert node.kind == ySequence
|
|
|
|
for item in node.elems: yield item
|
|
|
|
|
|
|
|
iterator mitems*(node: var YamlNode): YamlNode =
|
|
|
|
## Iterates over all items of a sequence. *node* must be a *ySequence*.
|
|
|
|
## Values can be modified.
|
|
|
|
assert node.kind == ySequence
|
|
|
|
for item in node.elems.mitems: yield item
|
|
|
|
|
|
|
|
iterator pairs*(node: YamlNode): tuple[key, value: YamlNode] =
|
|
|
|
## Iterates over all key-value pairs of a mapping. *node* must be a
|
|
|
|
## *yMapping*.
|
|
|
|
assert node.kind == yMapping
|
|
|
|
for key, value in node.fields: yield (key, value)
|
|
|
|
|
|
|
|
iterator mpairs*(node: var YamlNode):
|
|
|
|
tuple[key: YamlNode, value: var YamlNode] =
|
|
|
|
## Iterates over all key-value pairs of a mapping. *node* must be a
|
|
|
|
## *yMapping*. Values can be modified.
|
|
|
|
doAssert node.kind == yMapping
|
|
|
|
for key, value in node.fields.mpairs: yield (key, value)
|