mirror of https://github.com/status-im/NimYAML.git
Fixes and improvements to DOM API.
* added loadFlattened. Fixes #117 * fixed an error where recursive nodes were not loaded properly into YamlNodes. * made dump with asNone raise an error when a cycle is encountered, instead of running into an endless loop.
This commit is contained in:
parent
5f7677d914
commit
7bd562e37e
31
yaml/dom.nim
31
yaml/dom.nim
|
@ -19,7 +19,7 @@
|
|||
## 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>`_.
|
||||
|
||||
import tables, streams, hashes, sets, strutils
|
||||
import std / [tables, streams, hashes, sets, strutils]
|
||||
import data, stream, taglib, serialization, private/internal, parser,
|
||||
presenter
|
||||
|
||||
|
@ -174,6 +174,7 @@ proc constructChild*(s: var YamlStream, c: ConstructionContext,
|
|||
fields: newTable[YamlNode, YamlNode](),
|
||||
mapStyle: start.mapStyle,
|
||||
startPos: start.startPos, endPos: start.endPos)
|
||||
addAnchor(c, start.mapProperties.anchor)
|
||||
while s.peek().kind != yamlEndMap:
|
||||
var
|
||||
key: YamlNode = nil
|
||||
|
@ -184,28 +185,27 @@ proc constructChild*(s: var YamlStream, c: ConstructionContext,
|
|||
raise newException(YamlConstructionError,
|
||||
"Duplicate key: " & $key)
|
||||
discard s.next()
|
||||
addAnchor(c, start.mapProperties.anchor)
|
||||
of yamlStartSeq:
|
||||
result = YamlNode(tag: start.seqProperties.tag,
|
||||
kind: ySequence,
|
||||
elems: newSeq[YamlNode](),
|
||||
seqStyle: start.seqStyle,
|
||||
startPos: start.startPos, endPos: start.endPos)
|
||||
addAnchor(c, start.seqProperties.anchor)
|
||||
while s.peek().kind != yamlEndSeq:
|
||||
var item: YamlNode = nil
|
||||
constructChild(s, c, item)
|
||||
result.elems.add(item)
|
||||
addAnchor(c, start.seqProperties.anchor)
|
||||
discard s.next()
|
||||
of yamlScalar:
|
||||
result = YamlNode(tag: start.scalarProperties.tag,
|
||||
kind: yScalar, scalarStyle: start.scalarStyle,
|
||||
startPos: start.startPos, endPos: start.endPos)
|
||||
addAnchor(c, start.scalarProperties.anchor)
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
result.content = move start.scalarContent
|
||||
else:
|
||||
shallowCopy(result.content, start.scalarContent)
|
||||
addAnchor(c, start.scalarProperties.anchor)
|
||||
of yamlAlias:
|
||||
result = cast[YamlNode](c.refs.getOrDefault(start.aliasTarget).p)
|
||||
else: internalError("Malformed YamlStream")
|
||||
|
@ -342,3 +342,26 @@ iterator mpairs*(node: var YamlNode):
|
|||
## *yMapping*. Values can be modified.
|
||||
doAssert node.kind == yMapping
|
||||
for key, value in node.fields.mpairs: yield (key, value)
|
||||
|
||||
proc loadFlattened*[K](input: Stream | string, target: var K)
|
||||
{.raises: [YamlConstructionError, YamlSerializationError, IOError, OSError,
|
||||
YamlParserError].} =
|
||||
## Replaces all aliases with the referenced nodes in the input, then loads
|
||||
## the resulting YAML into K. Can be used when anchors & aliases are used like
|
||||
## variables in the input, to avoid having to define `ref` types for the
|
||||
## anchored data.
|
||||
var node: YamlNode
|
||||
load(input, node)
|
||||
var stream = represent(node, tsNone, asNone)
|
||||
try:
|
||||
var e = stream.next()
|
||||
yAssert(e.kind == yamlStartStream)
|
||||
construct(stream, target)
|
||||
e = stream.next()
|
||||
yAssert(e.kind == yamlEndStream)
|
||||
except YamlStreamError:
|
||||
let e = (ref YamlStreamError)(getCurrentException())
|
||||
if e.parent of IOError: raise (ref IOError)(e.parent)
|
||||
if e.parent of OSError: raise (ref OSError)(e.parent)
|
||||
elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent)
|
||||
else: internalError("Unexpected exception: " & $e.parent.name)
|
|
@ -46,10 +46,10 @@ type
|
|||
AnchorStyle* = enum
|
||||
## How ref object should be serialized.
|
||||
##
|
||||
## - ``asNone``: No anchors will be outputted. Values present at
|
||||
## - ``asNone``: No anchors will be written. Values present at
|
||||
## multiple places in the content that should be serialized will be
|
||||
## fully serialized at every occurence. If the content is cyclic, this
|
||||
## will lead to an endless loop!
|
||||
## will raise a YamlSerializationError.
|
||||
## - ``asTidy``: Anchors will only be generated for objects that
|
||||
## actually occur more than once in the content to be serialized.
|
||||
## This is a bit slower and needs more memory than ``asAlways``.
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
## type. Please consult the serialization guide on the NimYAML website for more
|
||||
## information.
|
||||
|
||||
import tables, typetraits, strutils, macros, streams, times, parseutils, options
|
||||
import std / [tables, typetraits, strutils, macros, streams, times, parseutils, options]
|
||||
import data, parser, taglib, presenter, stream, private/internal, hints, annotations
|
||||
export data, stream, macros, annotations, options
|
||||
# *something* in here needs externally visible `==`(x,y: AnchorId),
|
||||
|
@ -1251,21 +1251,26 @@ proc representChild*[T](value: seq[T], ts: TagStyle, c: SerializationContext) =
|
|||
|
||||
proc representChild*[O](value: ref O, ts: TagStyle, c: SerializationContext) =
|
||||
if isNil(value): c.put(scalarEvent("~", yTagNull))
|
||||
elif c.style == asNone: representChild(value[], ts, c)
|
||||
else:
|
||||
var val: tuple[a: Anchor, referenced: bool]
|
||||
let p = cast[pointer](value)
|
||||
if c.refs.hasKey(p):
|
||||
val = c.refs.getOrDefault(p)
|
||||
yAssert(val.a != yAnchorNone)
|
||||
if not val.referenced:
|
||||
c.refs[p] = (val.a, true)
|
||||
c.put(aliasEvent(val.a))
|
||||
return
|
||||
if c.style != asNone:
|
||||
val = (c.nextAnchorId.Anchor, false)
|
||||
c.refs[p] = val
|
||||
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
|
||||
# when c.style == asNone, `referenced` is used as indicator that we are
|
||||
# currently in the process of serializing this node. This enables us to
|
||||
# detect cycles and raise an error.
|
||||
var val = c.refs.getOrDefault(p, (c.nextAnchorId.Anchor, c.style == asNone))
|
||||
if val.a != c.nextAnchorId.Anchor:
|
||||
if c.style == asNone:
|
||||
if val.referenced:
|
||||
raise newException(YamlSerializationError,
|
||||
"tried to serialize cyclic graph with asNone")
|
||||
else:
|
||||
val = c.refs.getOrDefault(p)
|
||||
yAssert(val.a != yAnchorNone)
|
||||
if not val.referenced:
|
||||
c.refs[p] = (val.a, true)
|
||||
c.put(aliasEvent(val.a))
|
||||
return
|
||||
c.refs[p] = val
|
||||
nextAnchor(c.nextAnchorId, len(c.nextAnchorId) - 1)
|
||||
let
|
||||
childTagStyle = if ts == tsAll: tsAll else: tsRootOnly
|
||||
origPut = c.put
|
||||
|
@ -1273,19 +1278,20 @@ proc representChild*[O](value: ref O, ts: TagStyle, c: SerializationContext) =
|
|||
var ex = e
|
||||
case ex.kind
|
||||
of yamlStartMap:
|
||||
ex.mapProperties.anchor = val.a
|
||||
if c.style != asNone: ex.mapProperties.anchor = val.a
|
||||
if ts == tsNone: ex.mapProperties.tag = yTagQuestionMark
|
||||
of yamlStartSeq:
|
||||
ex.seqProperties.anchor = val.a
|
||||
if c.style != asNone: ex.seqProperties.anchor = val.a
|
||||
if ts == tsNone: ex.seqProperties.tag = yTagQuestionMark
|
||||
of yamlScalar:
|
||||
ex.scalarProperties.anchor = val.a
|
||||
if c.style != asNone: ex.scalarProperties.anchor = val.a
|
||||
if ts == tsNone and guessType(ex.scalarContent) != yTypeNull:
|
||||
ex.scalarProperties.tag = yTagQuestionMark
|
||||
else: discard
|
||||
c.put = origPut
|
||||
c.put(ex)
|
||||
representChild(value[], childTagStyle, c)
|
||||
if c.style == asNone: c.refs[p] = (val.a, false)
|
||||
|
||||
proc representChild*[T](value: Option[T], ts: TagStyle,
|
||||
c: SerializationContext) =
|
||||
|
|
Loading…
Reference in New Issue