mirror of https://github.com/status-im/NimYAML.git
Implemented and documented DOM
This commit is contained in:
parent
33a7f93480
commit
27670f63c9
21
doc/api.txt
21
doc/api.txt
|
@ -84,3 +84,24 @@ You can choose from multiple presentation styles. ``psJson`` is not able to
|
|||
process some features of ``YamlStream`` s, the other styles support all features
|
||||
and are guaranteed to round-trip to the same ``YamlStream`` if you parse the
|
||||
generated YAML character stream again.
|
||||
|
||||
The Document Object Model
|
||||
=========================
|
||||
|
||||
Much like XML, YAML also defines a *document object model*. If you cannot or do
|
||||
not want to load a YAML character stream to native Nim types, you can instead
|
||||
load it into a ``YamlDocument``. This ``YamlDocument`` can also be serialized
|
||||
into a YAML character stream. All tags will be preserved exactly as they are
|
||||
when transforming from and to a ``YamlDocument``. The only important thing to
|
||||
remember is that when a value has no tag, it will get the non-specific tag
|
||||
``"!"`` for quoted scalars and ``"?"`` for all other nodes.
|
||||
|
||||
While tags are preserved, anchors will be resolved during loading and re-added
|
||||
during serialization. It is allowed for a ``YamlNode`` to occur multiple times
|
||||
within a ``YamlDocument``, in which case it will be serialized once and referred
|
||||
to afterwards via aliases.
|
||||
|
||||
The document object model is provided for completeness, but you are encouraged
|
||||
to use native Nim types as start- or endpoint instead. That may be significantly
|
||||
faster, as every ``YamlNode`` is allocated on the heap and subject to garbage
|
||||
collection.
|
|
@ -1,6 +1,6 @@
|
|||
<svg version="1.1"
|
||||
baseProfile="full"
|
||||
width="600" height="340"
|
||||
width="600" height="420"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
|
@ -47,6 +47,16 @@
|
|||
<path d="M 0 60 q 0 10 10 10 h 115" fill="transparent" stroke="black"/>
|
||||
<path d="M 225 70 h 90 q 10 0 10 -10 v -60" fill="transparent" stroke="black"/>
|
||||
</g>
|
||||
<g id="dom-right">
|
||||
<rect x="330" y="45" width="100" height="30" stroke="black" fill="white" stroke-width="1"/>
|
||||
<line x1="170" y1="60" x2="330" y2="60" stroke="black" stroke-width="2"/>
|
||||
<line x1="430" y1="60" x2="583" y2="60" stroke="black" stroke-width="2" marker-end="url(#Triangle)"/>
|
||||
</g>
|
||||
<g id="dom-left">
|
||||
<rect x="330" y="5" width="100" height="30" stroke="black" fill="white" stroke-width="1"/>
|
||||
<line x1="330" y1="20" x2="182" y2="20" stroke="black" stroke-width="2" marker-end="url(#Triangle)"/>
|
||||
<line x1="430" y1="20" x2="595" y2="20" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
<g id="whole-right">
|
||||
<rect x="275" y="5" width="100" height="30" stroke="black" fill="white" stroke-width="1"/>
|
||||
<line x1="5" y1="20" x2="275" y2="20" stroke="black" stroke-width="2"/>
|
||||
|
@ -59,11 +69,11 @@
|
|||
</g>
|
||||
</defs>
|
||||
|
||||
<line x1="145" y1="0" x2="145" y2="340" stroke="gray"/>
|
||||
<line x1="145" y1="0" x2="145" y2="420" stroke="gray"/>
|
||||
<text x="55" y="25" fill="gray">Application</text>
|
||||
<text x="160" y="25" fill="gray">YAML</text>
|
||||
|
||||
<g id="boxes" text-anchor="middle" transform="translate(0 150)">
|
||||
<g id="boxes" text-anchor="middle" transform="translate(0 190)">
|
||||
<g id="native" transform="translate(0 0)">
|
||||
<use xlink:href="#box"/>
|
||||
<text x="60" y="25" font-style="italic">Native</text>
|
||||
|
@ -73,7 +83,9 @@
|
|||
<g id="representation" transform="translate(170 0)">
|
||||
<use xlink:href="#box"/>
|
||||
<text x="60" y="25" font-style="italic">Representation</text>
|
||||
<text x="60" y="45" font-weight="bold">not implemented</text>
|
||||
<a xlink:href="yaml.html#YamlDocument" target="_top">
|
||||
<text x="60" y="45" font-weight="bold">YamlDocument</text>
|
||||
</a>
|
||||
</g>
|
||||
|
||||
<g id="serialization" transform="translate(320 0)">
|
||||
|
@ -93,7 +105,7 @@
|
|||
</g>
|
||||
</g>
|
||||
|
||||
<g id="atomics" text-anchor="middle" transform="translate(0 90)">
|
||||
<g id="atomics" text-anchor="middle" transform="translate(0 130)">
|
||||
<g id="represent" transform="translate(65 10)">
|
||||
<use xlink:href="#atomic-right-long"/>
|
||||
<text x="80" y="25" font-style="italic">represent</text>
|
||||
|
@ -101,7 +113,9 @@
|
|||
|
||||
<g id="serialize" transform="translate(235 10)">
|
||||
<use xlink:href="#atomic-right"/>
|
||||
<text x="70" y="25" font-style="italic" font-weight="bold">serialize</text>
|
||||
<a xlink:href="yaml.html#serialize,YamlDocument,TagLibrary,AnchorStyle" target="_top">
|
||||
<text x="70" y="25" font-style="italic" font-weight="bold">serialize</text>
|
||||
</a>
|
||||
</g>
|
||||
|
||||
<g id="present" transform="translate(385 10)">
|
||||
|
@ -118,7 +132,9 @@
|
|||
|
||||
<g id="compose" transform="translate(235 120)">
|
||||
<use xlink:href="#atomic-left"/>
|
||||
<text x="70" y="35" font-style="italic" font-weight="bold">compose</text>
|
||||
<a xlink:href="yaml.html#compose,YamlStream,TagLibrary" target="_top">
|
||||
<text x="70" y="35" font-style="italic" font-weight="bold">compose</text>
|
||||
</a>
|
||||
</g>
|
||||
|
||||
<g id="parse" transform="translate(385 120)">
|
||||
|
@ -129,7 +145,7 @@
|
|||
</g>
|
||||
</g>
|
||||
|
||||
<g id="skipping-things" text-anchor="middle" transform="translate(0 50)">
|
||||
<g id="skipping-things" text-anchor="middle" transform="translate(0 90)">
|
||||
<g id="skipping-represent" transform="translate(55 10)">
|
||||
<use xlink:href="#skipping-right"/>
|
||||
<a xlink:href="yaml.html#represent,T,TagStyle,AnchorStyle" target="_top">
|
||||
|
@ -151,7 +167,19 @@
|
|||
<text x="325" y="25" font-style="italic" font-weight="bold">dump</text>
|
||||
</a>
|
||||
</g>
|
||||
<g id="load" transform="translate(0 280)">
|
||||
<g id="dumpDOM">
|
||||
<use xlink:href="#dom-right"/>
|
||||
<a xlink:href="yaml.html#dumpDOM,YamlDocument,Stream,PresentationStyle,AnchorStyle,int" target="_top">
|
||||
<text x="380" y="65" font-weight="bold">dumpDOM</text>
|
||||
</a>
|
||||
</g>
|
||||
<g id="loadDOM" transform="translate(0 320)">
|
||||
<use xlink:href="#dom-left"/>
|
||||
<a xlink:href="yaml.html#loadDOM,Stream" target="_top">
|
||||
<text x="380" y="25" font-weight="bold">loadDOM</text>
|
||||
</a>
|
||||
</g>
|
||||
<g id="load" transform="translate(0 360)">
|
||||
<use xlink:href="#whole-left"/>
|
||||
<a xlink:href="yaml.html#load,Stream,K" target="_top">
|
||||
<text x="325" y="25" font-style="italic" font-weight="bold">load</text>
|
||||
|
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.5 KiB |
|
@ -103,7 +103,7 @@ doc.file = """
|
|||
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://github.com/flyx/NimYAML"><img style="position: absolute; top: 0; right: 0; border: 0; z-index: 10;" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>
|
||||
<a href="https://github.com/flyx/NimYAML"><img style="position: fixed; top: 0; right: 0; border: 0; z-index: 10;" src="https://camo.githubusercontent.com/652c5b9acfaddf3a9c326fa6bde407b87f7be0f4/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png"></a>
|
||||
<header>
|
||||
<a class="pagetitle" href="/">NimYAML</a>
|
||||
<a href="index.html">Home</a>
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
proc newYamlNode*(content: string, tag: string = "?"): YamlNode =
|
||||
new(result)
|
||||
result.kind = yScalar
|
||||
result.content = content
|
||||
result.tag = tag
|
||||
|
||||
proc newYamlNode*(children: openarray[YamlNode], tag: string = "?"):
|
||||
YamlNode =
|
||||
new(result)
|
||||
result.kind = ySequence
|
||||
result.children = @children
|
||||
result.tag = tag
|
||||
|
||||
proc newYamlNode*(pairs: openarray[tuple[key, value: YamlNode]],
|
||||
tag: string = "?"): YamlNode =
|
||||
new(result)
|
||||
result.kind = yMapping
|
||||
result.pairs = @pairs
|
||||
result.tag = tag
|
||||
|
||||
proc initYamlDoc*(root: YamlNode): YamlDocument =
|
||||
result.root = root
|
||||
|
||||
proc composeNode(s: var YamlStream, tagLib: TagLibrary,
|
||||
c: ConstructionContext):
|
||||
YamlNode {.raises: [YamlStreamError, YamlConstructionError].} =
|
||||
let start = s.next()
|
||||
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:
|
||||
assert(not c.refs.hasKey(start.mapAnchor))
|
||||
c.refs[start.mapAnchor] = cast[pointer](result)
|
||||
of yamlStartSequence:
|
||||
result.tag = tagLib.uri(start.seqTag)
|
||||
result.kind = ySequence
|
||||
result.children = newSeq[YamlNode]()
|
||||
while s.peek().kind != yamlEndSequence:
|
||||
result.children.add(composeNode(s, tagLib, c))
|
||||
if start.seqAnchor != yAnchorNone:
|
||||
assert(not c.refs.hasKey(start.seqAnchor))
|
||||
c.refs[start.seqAnchor] = cast[pointer](result)
|
||||
discard s.next()
|
||||
of yamlScalar:
|
||||
result.tag = tagLib.uri(start.scalarTag)
|
||||
result.kind = yScalar
|
||||
result.content = start.scalarContent
|
||||
if start.scalarAnchor != yAnchorNone:
|
||||
assert(not c.refs.hasKey(start.scalarAnchor))
|
||||
c.refs[start.scalarAnchor] = cast[pointer](result)
|
||||
of yamlAlias:
|
||||
result = cast[YamlNode](c.refs[start.aliasTarget])
|
||||
else: assert false, "Malformed YamlStream"
|
||||
except KeyError:
|
||||
raise newException(YamlConstructionError,
|
||||
"Wrong tag library: TagId missing")
|
||||
|
||||
proc compose*(s: var YamlStream, tagLib: TagLibrary): YamlDocument
|
||||
{.raises: [YamlStreamError, YamlConstructionError].} =
|
||||
var context = newConstructionContext()
|
||||
assert s.next().kind == yamlStartDocument
|
||||
result.root = composeNode(s, tagLib, context)
|
||||
assert s.next().kind == yamlEndDocument
|
||||
|
||||
proc loadDOM*(s: Stream): YamlDocument
|
||||
{.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)
|
||||
else: assert false, "Never happens: " & e.parent.repr
|
||||
|
||||
proc serializeNode(n: YamlNode, c: SerializationContext, a: AnchorStyle,
|
||||
tagLib: TagLibrary): RawYamlStream {.raises: [].}=
|
||||
let p = cast[pointer](n)
|
||||
if a != asNone and c.refs.hasKey(p):
|
||||
try:
|
||||
if c.refs[p] == yAnchorNone:
|
||||
c.refs[p] = c.nextAnchorId
|
||||
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
|
||||
except KeyError: assert false, "Can never happen"
|
||||
result = iterator(): YamlStreamEvent {.raises: [].} =
|
||||
var event: YamlStreamEvent
|
||||
try: event = aliasEvent(c.refs[p])
|
||||
except KeyError: assert false, "Can never happen"
|
||||
yield event
|
||||
return
|
||||
var
|
||||
tagId: TagId
|
||||
anchor: AnchorId
|
||||
try:
|
||||
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[n.tag] else:
|
||||
tagLib.registerUri(n.tag)
|
||||
case a
|
||||
of asNone: anchor = yAnchorNone
|
||||
of asTidy: anchor = cast[AnchorId](n)
|
||||
of asAlways: anchor = c.refs[p]
|
||||
except KeyError: assert false, "Can never happen"
|
||||
result = iterator(): YamlStreamEvent =
|
||||
case n.kind
|
||||
of yScalar:
|
||||
yield scalarEvent(n.content, tagId, anchor)
|
||||
of ySequence:
|
||||
yield startSeqEvent(tagId, anchor)
|
||||
for item in n.children:
|
||||
var events = serializeNode(item, c, a, tagLib)
|
||||
while true:
|
||||
let event = events()
|
||||
if finished(events): break
|
||||
yield event
|
||||
yield endSeqEvent()
|
||||
of yMapping:
|
||||
yield startMapEvent(tagId, anchor)
|
||||
for i in n.pairs:
|
||||
var events = serializeNode(i.key, c, a, tagLib)
|
||||
while true:
|
||||
let event = events()
|
||||
if finished(events): break
|
||||
yield event
|
||||
events = serializeNode(i.value, c, a, tagLib)
|
||||
while true:
|
||||
let event = events()
|
||||
if finished(events): break
|
||||
yield event
|
||||
yield endMapEvent()
|
||||
|
||||
template processAnchoredEvent(target: expr, c: SerializationContext): stmt =
|
||||
try:
|
||||
let anchorId = c.refs[cast[pointer](target)]
|
||||
if anchorId != yAnchorNone:
|
||||
target = anchorId
|
||||
else: target = yAnchorNone
|
||||
except KeyError: assert false, "Can never happen"
|
||||
yield event
|
||||
|
||||
proc serialize*(doc: YamlDocument, tagLib: TagLibrary, a: AnchorStyle = asTidy):
|
||||
YamlStream {.raises: [].} =
|
||||
var
|
||||
context = newSerializationContext(a)
|
||||
events = serializeNode(doc.root, context, a, tagLib)
|
||||
if a == asTidy:
|
||||
var backend = iterator(): YamlStreamEvent {.raises: [].} =
|
||||
var output = newSeq[YamlStreamEvent]()
|
||||
while true:
|
||||
let event = events()
|
||||
if finished(events): break
|
||||
output.add(event)
|
||||
yield startDocEvent()
|
||||
for event in output.mitems():
|
||||
case event.kind
|
||||
of yamlScalar:
|
||||
processAnchoredEvent(event.scalarAnchor, context)
|
||||
of yamlStartMap: processAnchoredEvent(event.mapAnchor, context)
|
||||
of yamlStartSequence:
|
||||
processAnchoredEvent(event.seqAnchor, context)
|
||||
else: yield event
|
||||
yield endDocEvent()
|
||||
result = initYamlStream(backend)
|
||||
else:
|
||||
var backend = iterator(): YamlStreamEvent {.raises: [].} =
|
||||
yield startDocEvent()
|
||||
while true:
|
||||
let event = events()
|
||||
if finished(events): break
|
||||
yield event
|
||||
yield endDocEvent()
|
||||
result = initYamlStream(backend)
|
||||
|
||||
proc dumpDOM*(doc: YamlDocument, target: Stream,
|
||||
style: PresentationStyle = psDefault,
|
||||
anchorStyle: AnchorStyle = asTidy, indentationStep: int = 2)
|
||||
{.raises: [YamlPresenterJsonError, YamlPresenterOutputError].} =
|
||||
## Dump a YamlDocument as YAML character stream.
|
||||
var
|
||||
tagLib = initExtendedTagLibrary()
|
||||
events = serialize(doc, tagLib,
|
||||
if style == psJson: asNone else: anchorStyle)
|
||||
try:
|
||||
present(events, target, tagLib, style, indentationStep)
|
||||
except YamlStreamError:
|
||||
# serializing object does not raise any errors, so we can ignore this
|
||||
assert false, "Can never happen"
|
|
@ -9,8 +9,9 @@ proc newConstructionContext(): ConstructionContext =
|
|||
|
||||
proc newSerializationContext(s: AnchorStyle): SerializationContext =
|
||||
new(result)
|
||||
result.refsList = newSeq[RefNodeData]()
|
||||
result.refs = initTable[pointer, AnchorId]()
|
||||
result.style = s
|
||||
result.nextAnchorId = 0.AnchorId
|
||||
|
||||
proc initSerializationTagLibrary(): TagLibrary {.raises: [].} =
|
||||
result = initTagLibrary()
|
||||
|
@ -494,41 +495,49 @@ proc representObject*[O](value: ref O, ts: TagStyle, c: SerializationContext):
|
|||
elif c.style == asNone:
|
||||
result = representObject(value[], ts, c)
|
||||
else:
|
||||
let
|
||||
p = cast[pointer](value)
|
||||
for i in countup(0, c.refsList.high):
|
||||
if p == c.refsList[i].p:
|
||||
c.refsList[i].count.inc()
|
||||
result = iterator(): YamlStreamEvent =
|
||||
yield aliasEvent(if c.style == asAlways: AnchorId(i) else:
|
||||
cast[AnchorId](p))
|
||||
return
|
||||
c.refsList.add(initRefNodeData(p))
|
||||
let
|
||||
a = if c.style == asAlways: AnchorId(c.refsList.high) else:
|
||||
cast[AnchorId](p)
|
||||
childTagStyle = if ts == tsAll: tsAll else: tsRootOnly
|
||||
result = iterator(): YamlStreamEvent =
|
||||
var child = representObject(value[], childTagStyle, c)
|
||||
var first = child()
|
||||
assert(not finished(child))
|
||||
case first.kind
|
||||
of yamlStartMap:
|
||||
first.mapAnchor = a
|
||||
if ts == tsNone: first.mapTag = yTagQuestionMark
|
||||
of yamlStartSequence:
|
||||
first.seqAnchor = a
|
||||
if ts == tsNone: first.seqTag = yTagQuestionMark
|
||||
of yamlScalar:
|
||||
first.scalarAnchor = a
|
||||
if ts == tsNone and guessType(first.scalarContent) != yTypeNull:
|
||||
first.scalarTag = yTagQuestionMark
|
||||
else: discard
|
||||
yield first
|
||||
while true:
|
||||
let event = child()
|
||||
if finished(child): break
|
||||
let p = cast[pointer](value)
|
||||
if c.refs.hasKey(p):
|
||||
try:
|
||||
if c.refs[p] == yAnchorNone:
|
||||
c.refs[p] = c.nextAnchorId
|
||||
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
|
||||
except KeyError: assert false, "Can never happen"
|
||||
result = iterator(): YamlStreamEvent {.raises: [].} =
|
||||
var event: YamlStreamEvent
|
||||
try: event = aliasEvent(c.refs[p])
|
||||
except KeyError: assert false, "Can never happen"
|
||||
yield event
|
||||
return
|
||||
try:
|
||||
if c.style == asAlways:
|
||||
c.refs[p] = c.nextAnchorId
|
||||
c.nextAnchorId = AnchorId(int(c.nextAnchorId) + 1)
|
||||
else: c.refs[p] = yAnchorNone
|
||||
let
|
||||
a = if c.style == asAlways: c.refs[p] else: cast[AnchorId](p)
|
||||
childTagStyle = if ts == tsAll: tsAll else: tsRootOnly
|
||||
result = iterator(): YamlStreamEvent =
|
||||
var child = representObject(value[], childTagStyle, c)
|
||||
var first = child()
|
||||
assert(not finished(child))
|
||||
case first.kind
|
||||
of yamlStartMap:
|
||||
first.mapAnchor = a
|
||||
if ts == tsNone: first.mapTag = yTagQuestionMark
|
||||
of yamlStartSequence:
|
||||
first.seqAnchor = a
|
||||
if ts == tsNone: first.seqTag = yTagQuestionMark
|
||||
of yamlScalar:
|
||||
first.scalarAnchor = a
|
||||
if ts == tsNone and guessType(first.scalarContent) != yTypeNull:
|
||||
first.scalarTag = yTagQuestionMark
|
||||
else: discard
|
||||
yield first
|
||||
while true:
|
||||
let event = child()
|
||||
if finished(child): break
|
||||
yield event
|
||||
except KeyError: assert false, "Can never happen"
|
||||
|
||||
proc construct*[T](s: var YamlStream, target: var T)
|
||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||
|
@ -577,29 +586,11 @@ proc load*[K](input: Stream, target: var K)
|
|||
else:
|
||||
assert(false)
|
||||
|
||||
proc setAnchor(a: var AnchorId, q: var seq[RefNodeData], n: var AnchorId)
|
||||
proc setAnchor(a: var AnchorId, q: var Table[pointer, AnchorId])
|
||||
{.inline.} =
|
||||
if a != yAnchorNone:
|
||||
let p = cast[pointer](a)
|
||||
for i in countup(0, q.len - 1):
|
||||
if p == q[i].p:
|
||||
if q[i].count > 1:
|
||||
assert(q[i].anchor == yAnchorNone)
|
||||
q[i].anchor = n
|
||||
a = n
|
||||
n = AnchorId(int(n) + 1)
|
||||
else: a = yAnchorNone
|
||||
break
|
||||
|
||||
proc setAliasAnchor(a: var AnchorId, q: var seq[RefNodeData]) {.inline.} =
|
||||
let p = cast[pointer](a)
|
||||
for i in countup(0, q.len - 1):
|
||||
if p == q[i].p:
|
||||
assert q[i].count > 1
|
||||
assert q[i].anchor != yAnchorNone
|
||||
a = q[i].anchor
|
||||
return
|
||||
assert(false)
|
||||
try:a = q[cast[pointer](a)]
|
||||
except KeyError: assert false, "Can never happen"
|
||||
|
||||
proc represent*[T](value: T, ts: TagStyle = tsRootOnly,
|
||||
a: AnchorStyle = asTidy): YamlStream {.raises: [].} =
|
||||
|
@ -621,21 +612,17 @@ proc represent*[T](value: T, ts: TagStyle = tsRootOnly,
|
|||
objQueue.add(event)
|
||||
except Exception:
|
||||
assert(false)
|
||||
var next = 0.AnchorId
|
||||
var backend = iterator(): YamlStreamEvent =
|
||||
for i in countup(0, objQueue.len - 1):
|
||||
var event = objQueue[i]
|
||||
case event.kind
|
||||
of yamlStartMap:
|
||||
event.mapAnchor.setAnchor(context.refsList, next)
|
||||
event.mapAnchor.setAnchor(context.refs)
|
||||
of yamlStartSequence:
|
||||
event.seqAnchor.setAnchor(context.refsList, next)
|
||||
event.seqAnchor.setAnchor(context.refs)
|
||||
of yamlScalar:
|
||||
event.scalarAnchor.setAnchor(context.refsList, next)
|
||||
of yamlAlias:
|
||||
event.aliasTarget.setAliasAnchor(context.refsList)
|
||||
else:
|
||||
discard
|
||||
event.scalarAnchor.setAnchor(context.refs)
|
||||
else: discard
|
||||
yield event
|
||||
result = initYamlStream(backend)
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import "../yaml"
|
||||
|
||||
proc printDifference*(expected, actual: YamlStreamEvent) =
|
||||
if expected.kind != actual.kind:
|
||||
echo "expected " & $expected.kind & ", got " & $actual.kind
|
||||
else:
|
||||
case expected.kind
|
||||
of yamlScalar:
|
||||
if expected.scalarTag != actual.scalarTag:
|
||||
echo "[\"", actual.scalarContent, "\".tag] expected tag ",
|
||||
expected.scalarTag, ", got ", actual.scalarTag
|
||||
elif expected.scalarAnchor != actual.scalarAnchor:
|
||||
echo "[scalarEvent] expected anchor ", expected.scalarAnchor,
|
||||
", got ", actual.scalarAnchor
|
||||
elif expected.scalarContent != actual.scalarContent:
|
||||
let msg = "[scalarEvent] expected content \"" &
|
||||
expected.scalarContent & "\", got \"" &
|
||||
actual.scalarContent & "\" "
|
||||
if expected.scalarContent.len != actual.scalarContent.len:
|
||||
echo msg, "(length does not match)"
|
||||
else:
|
||||
for i in 0..expected.scalarContent.high:
|
||||
if expected.scalarContent[i] != actual.scalarContent[i]:
|
||||
echo msg, "(first different char at pos ", i,
|
||||
": expected ",
|
||||
cast[int](expected.scalarContent[i]),
|
||||
", got ",
|
||||
cast[int](actual.scalarContent[i]), ")"
|
||||
break
|
||||
else:
|
||||
echo "[scalarEvent] Unknown difference"
|
||||
of yamlStartMap:
|
||||
if expected.mapTag != actual.mapTag:
|
||||
echo "[map.tag] expected ", expected.mapTag, ", got ",
|
||||
actual.mapTag
|
||||
elif expected.mapAnchor != actual.mapAnchor:
|
||||
echo "[map.anchor] expected ", expected.mapAnchor, ", got ",
|
||||
actual.mapAnchor
|
||||
else:
|
||||
echo "[map.tag] Unknown difference"
|
||||
of yamlStartSequence:
|
||||
if expected.seqTag != actual.seqTag:
|
||||
echo "[seq.tag] expected ", expected.seqTag, ", got ",
|
||||
actual.seqTag
|
||||
elif expected.seqAnchor != actual.seqAnchor:
|
||||
echo "[seq.anchor] expected ", expected.seqAnchor, ", got ",
|
||||
actual.seqAnchor
|
||||
else:
|
||||
echo "[seq] Unknown difference"
|
||||
of yamlAlias:
|
||||
if expected.aliasTarget != actual.aliasTarget:
|
||||
echo "[alias] expected ", expected.aliasTarget, ", got ",
|
||||
actual.aliasTarget
|
||||
else:
|
||||
echo "[alias] Unknown difference"
|
||||
else:
|
||||
echo "Unknown difference in event kind " & $expected.kind
|
||||
|
||||
template ensure*(input: var YamlStream,
|
||||
expected: varargs[YamlStreamEvent]) {.dirty.} =
|
||||
var i = 0
|
||||
for token in input:
|
||||
if i >= expected.len:
|
||||
echo "received more tokens than expected (next token = ",
|
||||
token.kind, ")"
|
||||
fail()
|
||||
break
|
||||
if token != expected[i]:
|
||||
echo "at token #" & $i & ":"
|
||||
printDifference(expected[i], token)
|
||||
fail()
|
||||
break
|
||||
i.inc()
|
|
@ -0,0 +1,88 @@
|
|||
import "../yaml"
|
||||
import unittest, common
|
||||
|
||||
suite "DOM":
|
||||
test "DOM: Composing simple Scalar":
|
||||
let
|
||||
input = newStringStream("scalar")
|
||||
result = loadDOM(input)
|
||||
assert result.root.kind == yScalar
|
||||
assert result.root.content == "scalar"
|
||||
assert result.root.tag == "?"
|
||||
test "DOM: Serializing simple Scalar":
|
||||
let input = initYamlDoc(newYamlNode("scalar"))
|
||||
var result = serialize(input, initExtendedTagLibrary())
|
||||
ensure(result, startDocEvent(), scalarEvent("scalar"), endDocEvent())
|
||||
test "DOM: Composing sequence":
|
||||
let
|
||||
input = newStringStream("- !!str a\n- !!bool no")
|
||||
result = loadDOM(input)
|
||||
assert result.root.kind == ySequence
|
||||
assert result.root.tag == "?"
|
||||
assert result.root.children.len == 2
|
||||
assert result.root.children[0].kind == yScalar
|
||||
assert result.root.children[0].tag == "tag:yaml.org,2002:str"
|
||||
assert result.root.children[0].content == "a"
|
||||
assert result.root.children[1].kind == yScalar
|
||||
assert result.root.children[1].tag == "tag:yaml.org,2002:bool"
|
||||
assert result.root.children[1].content == "no"
|
||||
test "DOM: Serializing sequence":
|
||||
let input = initYamlDoc(newYamlNode([
|
||||
newYamlNode("a", "tag:yaml.org,2002:str"),
|
||||
newYamlNode("no", "tag:yaml.org,2002:bool")]))
|
||||
var result = serialize(input, initExtendedTagLibrary())
|
||||
ensure(result, startDocEvent(), startSeqEvent(),
|
||||
scalarEvent("a", yTagString), scalarEvent("no", yTagBoolean),
|
||||
endSeqEvent(), endDocEvent())
|
||||
test "DOM: Composing mapping":
|
||||
let
|
||||
input = newStringStream("--- !!map\n!foo bar: [a, b]")
|
||||
result = loadDOM(input)
|
||||
assert result.root.kind == yMapping
|
||||
assert result.root.tag == "tag:yaml.org,2002:map"
|
||||
assert result.root.pairs.len == 1
|
||||
assert result.root.pairs[0].key.kind == yScalar
|
||||
assert result.root.pairs[0].key.tag == "!foo"
|
||||
assert result.root.pairs[0].key.content == "bar"
|
||||
assert result.root.pairs[0].value.kind == ySequence
|
||||
assert result.root.pairs[0].value.children.len == 2
|
||||
test "DOM: Serializing mapping":
|
||||
let input = initYamlDoc(newYamlNode([
|
||||
(key: newYamlNode("bar"), value: newYamlNode([newYamlNode("a"),
|
||||
newYamlNode("b")]))]))
|
||||
var result = serialize(input, initExtendedTagLibrary())
|
||||
ensure(result, startDocEvent(), startMapEvent(), scalarEvent("bar"),
|
||||
startSeqEvent(), scalarEvent("a"), scalarEvent("b"),
|
||||
endSeqEvent(), endMapEvent(), endDocEvent())
|
||||
test "DOM: Composing with anchors":
|
||||
let
|
||||
input = newStringStream("- &a foo\n- &b bar\n- *a\n- *b")
|
||||
result = loadDOM(input)
|
||||
assert result.root.kind == ySequence
|
||||
assert result.root.children.len == 4
|
||||
assert result.root.children[0].kind == yScalar
|
||||
assert result.root.children[0].content == "foo"
|
||||
assert result.root.children[1].kind == yScalar
|
||||
assert result.root.children[1].content == "bar"
|
||||
assert result.root.children[0] == result.root.children[2]
|
||||
assert result.root.children[1] == result.root.children[3]
|
||||
test "DOM: Serializing with anchors":
|
||||
let
|
||||
a = newYamlNode("a")
|
||||
b = newYamlNode("b")
|
||||
input = initYamlDoc(newYamlNode([a, b, newYamlNode("c"), a, b]))
|
||||
var result = serialize(input, initExtendedTagLibrary())
|
||||
ensure(result, startDocEvent(), startSeqEvent(),
|
||||
scalarEvent("a", anchor=0.AnchorId),
|
||||
scalarEvent("b", anchor=1.AnchorId), scalarEvent("c"),
|
||||
aliasEvent(0.AnchorId), aliasEvent(1.AnchorId), endSeqEvent(),
|
||||
endDocEvent())
|
||||
test "DOM: Serializing with all anchors":
|
||||
let
|
||||
a = newYamlNode("a")
|
||||
input = initYamlDoc(newYamlNode([a, newYamlNode("b"), a]))
|
||||
var result = serialize(input, initExtendedTagLibrary(), asAlways)
|
||||
ensure(result, startDocEvent(), startSeqEvent(anchor=0.AnchorId),
|
||||
scalarEvent("a", anchor=1.AnchorId),
|
||||
scalarEvent("b", anchor=2.AnchorId), aliasEvent(1.AnchorId),
|
||||
endSeqEvent(), endDocEvent())
|
|
@ -1,73 +1,12 @@
|
|||
import "../yaml"
|
||||
|
||||
import unittest
|
||||
|
||||
proc printDifference(expected, actual: YamlStreamEvent) =
|
||||
if expected.kind != actual.kind:
|
||||
echo "expected " & $expected.kind & ", got " & $actual.kind
|
||||
else:
|
||||
case expected.kind
|
||||
of yamlScalar:
|
||||
if expected.scalarTag != actual.scalarTag:
|
||||
echo "[\"", actual.scalarContent, "\".tag] expected tag ",
|
||||
expected.scalarTag, ", got ", actual.scalarTag
|
||||
elif expected.scalarAnchor != actual.scalarAnchor:
|
||||
echo "[scalarEvent] expected anchor ", expected.scalarAnchor,
|
||||
", got ", actual.scalarAnchor
|
||||
elif expected.scalarContent != actual.scalarContent:
|
||||
let msg = "[scalarEvent] expected content \"" &
|
||||
expected.scalarContent & "\", got \"" &
|
||||
actual.scalarContent & "\" "
|
||||
if expected.scalarContent.len != actual.scalarContent.len:
|
||||
echo msg, "(length does not match)"
|
||||
else:
|
||||
for i in 0..expected.scalarContent.high:
|
||||
if expected.scalarContent[i] != actual.scalarContent[i]:
|
||||
echo msg, "(first different char at pos ", i,
|
||||
": expected ",
|
||||
cast[int](expected.scalarContent[i]),
|
||||
", got ",
|
||||
cast[int](actual.scalarContent[i]), ")"
|
||||
break
|
||||
else:
|
||||
echo "[scalarEvent] Unknown difference"
|
||||
of yamlStartMap:
|
||||
if expected.mapTag != actual.mapTag:
|
||||
echo "[map.tag] expected ", expected.mapTag, ", got ",
|
||||
actual.mapTag
|
||||
else:
|
||||
echo "[map.tag] Unknown difference"
|
||||
of yamlStartSequence:
|
||||
if expected.seqTag != actual.seqTag:
|
||||
echo "[seq.tag] expected ", expected.seqTag, ", got ",
|
||||
actual.seqTag
|
||||
of yamlAlias:
|
||||
if expected.aliasTarget != actual.aliasTarget:
|
||||
echo "[alias] expected ", expected.aliasTarget, ", got ",
|
||||
actual.aliasTarget
|
||||
else:
|
||||
echo "[alias] Unknown difference"
|
||||
else:
|
||||
echo "Unknown difference in event kind " & $expected.kind
|
||||
import unittest, common
|
||||
|
||||
template ensure(input: string, expected: varargs[YamlStreamEvent]) {.dirty.} =
|
||||
var
|
||||
i = 0
|
||||
parser = newYamlParser(tagLib)
|
||||
events = parser.parse(newStringStream(input))
|
||||
try:
|
||||
for token in events:
|
||||
if i >= expected.len:
|
||||
echo "received more tokens than expected (next token = ",
|
||||
token.kind, ")"
|
||||
fail()
|
||||
break
|
||||
if token != expected[i]:
|
||||
echo "at token #" & $i & ":"
|
||||
printDifference(expected[i], token)
|
||||
fail()
|
||||
break
|
||||
i.inc()
|
||||
try: ensure(events, expected)
|
||||
except YamlParserError:
|
||||
let e = cast[ref YamlParserError](getCurrentException())
|
||||
echo "Parser error:", getCurrentExceptionMsg()
|
||||
|
|
|
@ -1 +1 @@
|
|||
import parsing, constructingJson, serializing
|
||||
import parsing, constructingJson, serializing, dom
|
23
yaml.nim
23
yaml.nim
|
@ -218,12 +218,30 @@ type
|
|||
SerializationContext* = ref object
|
||||
## Context information for the process of serializing YAML from Nim
|
||||
## values.
|
||||
refsList: seq[RefNodeData]
|
||||
refs: Table[pointer, AnchorId]
|
||||
style: AnchorStyle
|
||||
nextAnchorId: AnchorId
|
||||
|
||||
RawYamlStream* = iterator(): YamlStreamEvent {.raises: [].} ## \
|
||||
## Stream of ``YamlStreamEvent``s returned by ``representObject`` procs.
|
||||
|
||||
YamlNodeKind* = enum
|
||||
yScalar, yMapping, ySequence
|
||||
|
||||
YamlNode* = ref YamlNodeObj not nil
|
||||
## Represents a node in a ``YamlDocument``.
|
||||
|
||||
YamlNodeObj* = object
|
||||
tag*: string
|
||||
case kind*: YamlNodeKind
|
||||
of yScalar: content*: string
|
||||
of ySequence: children*: seq[YamlNode]
|
||||
of yMapping: pairs*: seq[tuple[key, value: YamlNode]]
|
||||
|
||||
YamlDocument* = object
|
||||
## Represents a YAML document.
|
||||
root*: YamlNode
|
||||
|
||||
YamlLoadingError* = object of Exception
|
||||
## Base class for all exceptions that may be raised during the process
|
||||
## of loading a YAML character stream.
|
||||
|
@ -521,4 +539,5 @@ include private.presenter
|
|||
include private.hints
|
||||
include private.fastparse
|
||||
include private.streams
|
||||
include private.serialization
|
||||
include private.serialization
|
||||
include private.dom
|
Loading…
Reference in New Issue