2016-02-27 12:09:50 +00:00
|
|
|
# NimYAML - YAML implementation in Nim
|
|
|
|
# (c) Copyright 2015 Felix Krause
|
|
|
|
#
|
|
|
|
# See the file "copying.txt", included in this
|
|
|
|
# distribution, for details about the copyright.
|
|
|
|
|
2016-02-22 20:56:30 +00:00
|
|
|
proc newYamlNode*(content: string, tag: string = "?"): YamlNode =
|
2016-04-03 09:45:48 +00:00
|
|
|
YamlNode(kind: yScalar, content: content, tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
|
|
|
proc newYamlNode*(children: openarray[YamlNode], tag: string = "?"):
|
2016-04-02 15:48:22 +00:00
|
|
|
YamlNode =
|
2016-04-03 09:45:48 +00:00
|
|
|
YamlNode(kind: ySequence, children: @children, tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
|
|
|
proc newYamlNode*(pairs: openarray[tuple[key, value: YamlNode]],
|
|
|
|
tag: string = "?"): YamlNode =
|
2016-04-03 09:45:48 +00:00
|
|
|
YamlNode(kind: yMapping, pairs: @pairs, tag: tag)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2016-04-02 15:48:22 +00:00
|
|
|
proc initYamlDoc*(root: YamlNode): YamlDocument = result.root = root
|
2016-02-22 20:56:30 +00:00
|
|
|
|
|
|
|
proc composeNode(s: var YamlStream, tagLib: TagLibrary,
|
|
|
|
c: ConstructionContext):
|
2016-04-02 15:48:22 +00:00
|
|
|
YamlNode {.raises: [YamlStreamError, YamlConstructionError].} =
|
2016-09-14 16:31:09 +00:00
|
|
|
var start: YamlStreamEvent
|
|
|
|
shallowCopy(start, s.next())
|
2016-04-02 15:48:22 +00:00
|
|
|
new(result)
|
|
|
|
try:
|
|
|
|
case start.kind
|
|
|
|
of yamlStartMap:
|
|
|
|
result.tag = tagLib.uri(start.mapTag)
|
|
|
|
result.kind = yMapping
|
|
|
|
result.pairs = newSeq[tuple[key, value: YamlNode]]()
|
|
|
|
while s.peek().kind != yamlEndMap:
|
|
|
|
let
|
|
|
|
key = composeNode(s, tagLib, c)
|
|
|
|
value = composeNode(s, tagLib, c)
|
|
|
|
result.pairs.add((key: key, value: value))
|
|
|
|
discard s.next()
|
|
|
|
if start.mapAnchor != yAnchorNone:
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert(not c.refs.hasKey(start.mapAnchor))
|
2016-04-02 15:48:22 +00:00
|
|
|
c.refs[start.mapAnchor] = cast[pointer](result)
|
|
|
|
of yamlStartSeq:
|
|
|
|
result.tag = tagLib.uri(start.seqTag)
|
|
|
|
result.kind = ySequence
|
|
|
|
result.children = newSeq[YamlNode]()
|
|
|
|
while s.peek().kind != yamlEndSeq:
|
|
|
|
result.children.add(composeNode(s, tagLib, c))
|
|
|
|
if start.seqAnchor != yAnchorNone:
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert(not c.refs.hasKey(start.seqAnchor))
|
2016-04-02 15:48:22 +00:00
|
|
|
c.refs[start.seqAnchor] = cast[pointer](result)
|
|
|
|
discard s.next()
|
|
|
|
of yamlScalar:
|
|
|
|
result.tag = tagLib.uri(start.scalarTag)
|
|
|
|
result.kind = yScalar
|
|
|
|
shallowCopy(result.content, start.scalarContent)
|
|
|
|
if start.scalarAnchor != yAnchorNone:
|
2016-08-09 18:47:22 +00:00
|
|
|
yAssert(not c.refs.hasKey(start.scalarAnchor))
|
2016-04-02 15:48:22 +00:00
|
|
|
c.refs[start.scalarAnchor] = cast[pointer](result)
|
|
|
|
of yamlAlias:
|
|
|
|
result = cast[YamlNode](c.refs[start.aliasTarget])
|
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
|
|
|
|
|
|
|
proc compose*(s: var YamlStream, tagLib: TagLibrary): YamlDocument
|
2016-04-02 15:48:22 +00:00
|
|
|
{.raises: [YamlStreamError, YamlConstructionError].} =
|
|
|
|
var context = newConstructionContext()
|
2016-09-14 16:31:09 +00:00
|
|
|
var n: YamlStreamEvent
|
|
|
|
shallowCopy(n, s.next())
|
2016-08-10 18:42:44 +00:00
|
|
|
yAssert n.kind == yamlStartDoc
|
2016-04-02 15:48:22 +00:00
|
|
|
result.root = composeNode(s, tagLib, 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-09-14 12:35:41 +00:00
|
|
|
proc loadDOM*(s: Stream | string): YamlDocument
|
2016-04-02 15:48:22 +00:00
|
|
|
{.raises: [IOError, YamlParserError, YamlConstructionError].} =
|
|
|
|
var
|
|
|
|
tagLib = initExtendedTagLibrary()
|
|
|
|
parser = newYamlParser(tagLib)
|
|
|
|
events = parser.parse(s)
|
|
|
|
try: result = compose(events, tagLib)
|
|
|
|
except YamlStreamError:
|
|
|
|
let e = getCurrentException()
|
|
|
|
if e.parent of YamlParserError:
|
|
|
|
raise (ref YamlParserError)(e.parent)
|
|
|
|
elif e.parent of IOError:
|
|
|
|
raise (ref IOError)(e.parent)
|
2016-08-09 18:47:22 +00:00
|
|
|
else: internalError("Unexpected exception: " & e.parent.repr)
|
2016-02-22 20:56:30 +00:00
|
|
|
|
|
|
|
proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle,
|
2016-09-01 18:56:34 +00:00
|
|
|
tagLib: TagLibrary) {.raises: [].}=
|
2016-04-02 15:48:22 +00:00
|
|
|
let p = cast[pointer](n)
|
|
|
|
if a != asNone and c.refs.hasKey(p):
|
2016-08-09 18:47:22 +00:00
|
|
|
if c.refs.getOrDefault(p) == yAnchorNone:
|
|
|
|
c.refs[p] = c.nextAnchorId
|
|
|
|
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
|
2016-09-01 18:56:34 +00:00
|
|
|
c.put(aliasEvent(c.refs.getOrDefault(p)))
|
2016-04-02 15:48:22 +00:00
|
|
|
return
|
|
|
|
var
|
|
|
|
tagId: TagId
|
|
|
|
anchor: AnchorId
|
2016-08-09 18:47:22 +00:00
|
|
|
if a == asAlways:
|
|
|
|
c.refs[p] = c.nextAnchorId
|
|
|
|
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
|
|
|
|
else: c.refs[p] = yAnchorNone
|
|
|
|
tagId = if tagLib.tags.hasKey(n.tag): tagLib.tags.getOrDefault(n.tag) else:
|
|
|
|
tagLib.registerUri(n.tag)
|
|
|
|
case a
|
|
|
|
of asNone: anchor = yAnchorNone
|
|
|
|
of asTidy: anchor = cast[AnchorId](n)
|
|
|
|
of asAlways: anchor = c.refs.getOrDefault(p)
|
2016-09-01 18:56:34 +00:00
|
|
|
|
|
|
|
case n.kind
|
|
|
|
of yScalar: c.put(scalarEvent(n.content, tagId, anchor))
|
|
|
|
of ySequence:
|
|
|
|
c.put(startSeqEvent(tagId, anchor))
|
|
|
|
for item in n.children:
|
|
|
|
serializeNode(item, c, a, tagLib)
|
|
|
|
c.put(endSeqEvent())
|
|
|
|
of yMapping:
|
|
|
|
c.put(startMapEvent(tagId, anchor))
|
|
|
|
for i in n.pairs:
|
|
|
|
serializeNode(i.key, c, a, tagLib)
|
|
|
|
serializeNode(i.value, c, a, tagLib)
|
|
|
|
c.put(endMapEvent())
|
2016-02-22 20:56:30 +00:00
|
|
|
|
2016-08-17 20:50:37 +00:00
|
|
|
template processAnchoredEvent(target: untyped, c: SerializationContext): typed =
|
2016-08-09 18:47:22 +00:00
|
|
|
let anchorId = c.refs.getOrDefault(cast[pointer](target))
|
|
|
|
if anchorId != yAnchorNone: target = anchorId
|
|
|
|
else: target = yAnchorNone
|
2016-02-22 20:56:30 +00:00
|
|
|
|
|
|
|
proc serialize*(doc: YamlDocument, tagLib: TagLibrary, a: AnchorStyle = asTidy):
|
2016-08-09 18:47:22 +00:00
|
|
|
YamlStream {.raises: [YamlStreamError].} =
|
2016-04-02 15:48:22 +00:00
|
|
|
var
|
2016-09-01 18:56:34 +00:00
|
|
|
bys = newBufferYamlStream()
|
|
|
|
c = newSerializationContext(a, proc(e: YamlStreamEvent) {.raises: [].} =
|
|
|
|
bys.buf.add(e)
|
|
|
|
)
|
|
|
|
c.put(startDocEvent())
|
|
|
|
serializeNode(doc.root, c, a, tagLib)
|
|
|
|
c.put(endDocEvent())
|
2016-04-02 15:48:22 +00:00
|
|
|
if a == asTidy:
|
2016-09-01 18:56:34 +00:00
|
|
|
for event in bys.buf.mitems():
|
|
|
|
case event.kind
|
|
|
|
of yamlScalar: processAnchoredEvent(event.scalarAnchor, c)
|
|
|
|
of yamlStartMap: processAnchoredEvent(event.mapAnchor, c)
|
|
|
|
of yamlStartSeq: processAnchoredEvent(event.seqAnchor, c)
|
|
|
|
else: discard
|
|
|
|
result = bys
|
2016-02-22 20:56:30 +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
|
|
|
|
tagLib = initExtendedTagLibrary()
|
|
|
|
events = serialize(doc, tagLib,
|
|
|
|
if options.style == psJson: asNone else: anchorStyle)
|
2016-08-09 18:47:22 +00:00
|
|
|
present(events, target, tagLib, options)
|