started making serializationTests green again

This commit is contained in:
Felix Krause 2020-11-06 21:39:50 +01:00
parent 48d601d959
commit 6238104622
8 changed files with 397 additions and 384 deletions

View File

@ -210,6 +210,9 @@ suite "Lexer":
assertEquals("foo:\n bar\n baz\n---\nderp", i(0), pl("foo"), mv(), i(2),
pl("bar baz"), dirE(), i(0), pl("derp"), e())
test "Sequence with compact maps":
assertEquals("""- a: drzw\n- b""", i(0), ss(), pl("a"), mv(), pl("drzw"), i(0), ss(), pl("b"), e())
test "Empty lines":
assertEquals("""block: foo

View File

@ -311,17 +311,17 @@ suite "Serialization":
input[(a: 13'i32, b: 47'i32)] = "dreizehnsiebenundvierzig"
var output = dump(input, tsRootOnly, asTidy, blockOnly)
assertStringEqual(yamlDirs &
"""!n!tables:OrderedTable(tag:nimyaml.org;2016:tuple(tag:nimyaml.org;2016:system:int32;tag:nimyaml.org;2016:system:int32);tag:yaml.org;2002:str)
-
?
a: 23
b: 42
: dreiundzwanzigzweiundvierzig
-
?
a: 13
b: 47
: dreizehnsiebenundvierzig""", output)
"!n!tables:OrderedTable(tag:nimyaml.org;2016:tuple(tag:nimyaml.org;2016:system:int32;tag:nimyaml.org;2016:system:int32);tag:yaml.org;2002:str) \n" &
"- \n" &
" ? \n" &
" a: 23\n" &
" b: 42\n" &
" : dreiundzwanzigzweiundvierzig\n" &
"- \n" &
" ? \n" &
" a: 13\n" &
" b: 47\n" &
" : dreizehnsiebenundvierzig", output)
test "Load Sequences in Sequence":
let input = " - [1, 2, 3]\n - [4, 5]\n - [6]"

View File

@ -68,6 +68,7 @@ type
of yamlStartDoc:
explicitDirectivesEnd*: bool
version*: string
handles*: seq[tuple[handle, uriPrefix: string]]
of yamlEndDoc:
explicitDocumentEnd*: bool
of yamlEndMap, yamlEndSeq: discard
@ -162,11 +163,13 @@ proc startStreamEvent*(): Event =
proc endStreamEvent*(): Event =
return Event(startPos: defaultMark, endPos: defaultMark, kind: yamlEndStream)
proc startDocEvent*(explicit: bool = false, version: string = "", startPos, endPos: Mark = defaultMark): Event
proc startDocEvent*(explicit: bool = false, version: string = "",
handles: seq[tuple[handle, uriPrefix: string]] = @[],
startPos, endPos: Mark = defaultMark): Event
{.inline, raises: [].} =
## creates a new event that marks the start of a YAML document
result = Event(startPos: startPos, endPos: endPos,
kind: yamlStartDoc, version: version,
kind: yamlStartDoc, version: version, handles: handles,
explicitDirectivesEnd: explicit)
proc endDocEvent*(explicit: bool = false, startPos, endPos: Mark = defaultMark): Event

View File

@ -35,6 +35,7 @@ type
Context = ref object of YamlStream
tagLib: TagLibrary
handles: seq[tuple[handle, uriPrefix: string]]
issueWarnings: bool
lex: Lexer
levels: seq[Level]
@ -121,7 +122,7 @@ proc afterFlowMapValue(c: Context, e: var Event): bool
proc afterFlowSeqSepProps(c: Context, e: var Event): bool
proc afterFlowSeqItem(c: Context, e: var Event): bool
proc afterPairValue(c: Context, e: var Event): bool
proc emitCollectionKey(c: Context, e: var Event): bool
proc emitCached(c: Context, e: var Event): bool
{.pop.}
template debug(message: string) {.dirty.} =
@ -153,6 +154,12 @@ template popLevel(c: Context) =
debug("parser: pop")
discard c.levels.pop()
proc resolveHandle(c: Context, handle: string): string {.raises: [].} =
for item in c.handles:
if item.handle == handle:
return item.uriPrefix
return ""
proc init[T](c: Context, p: YamlParser, source: T) {.inline.} =
c.pushLevel(atStreamStart, -2)
c.nextImpl = proc(s: YamlStream, e: var Event): bool =
@ -207,7 +214,7 @@ proc generateError(c: Context, message: string):
proc parseTag(c: Context): TagId =
let handle = c.lex.fullLexeme()
var uri = c.tagLib.resolve(handle)
var uri = c.resolveHandle(handle)
if uri == "":
raise c.generateError("unknown handle: " & escape(handle))
c.lex.next()
@ -251,7 +258,7 @@ proc atStreamStart(c: Context, e: var Event): bool =
c.pushLevel(beforeDoc, -1)
e = Event(startPos: c.lex.curStartPos, endPos: c.lex.curStartPos, kind: yamlStartStream)
c.lex.next()
c.tagLib.resetPrefixes()
resetHandles(c.handles)
return true
proc atStreamEnd(c: Context, e : var Event): bool =
@ -269,7 +276,7 @@ proc beforeDoc(c: Context, e: var Event): bool =
raise c.generateError("Missing `---` after directives")
c.lex.next()
of DirectivesEnd:
e = startDocEvent(true, version, c.lex.curStartPos, c.lex.curEndPos)
e = startDocEvent(true, version, c.handles, c.lex.curStartPos, c.lex.curEndPos)
c.lex.next()
c.transition(beforeDocEnd)
c.pushLevel(afterDirectivesEnd, -1)
@ -278,7 +285,7 @@ proc beforeDoc(c: Context, e: var Event): bool =
c.popLevel()
return false
of Indentation:
e = startDocEvent(false, version, c.lex.curStartPos, c.lex.curEndPos)
e = startDocEvent(false, version, c.handles, c.lex.curStartPos, c.lex.curEndPos)
c.transition(beforeDocEnd)
c.pushLevel(beforeImplicitRoot, -1)
return true
@ -302,7 +309,7 @@ proc beforeDoc(c: Context, e: var Event): bool =
c.lex.next()
if c.lex.cur != Token.Suffix:
raise c.generateError("Invalid token (expected tag URI): " & $c.lex.cur)
discard c.tagLib.registerHandle(c.lex.evaluated, tagHandle)
discard registerHandle(c.handles, tagHandle, c.lex.evaluated)
c.lex.next()
of UnknownDirective:
seenDirectives = true
@ -407,9 +414,10 @@ proc atBlockIndentation(c: Context, e: var Event): bool =
raise c.generateError("Implicit mapping key may not be multiline")
let props = e.scalarProperties
e.scalarProperties = autoScalarTag(defaultProperties, scalarToken)
c.peek = move(e)
c.keyCache.add(move(e))
e = startMapEvent(csBlock, props, c.headerStart, headerEnd)
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
else:
e.scalarProperties = autoScalarTag(e.scalarProperties, scalarToken)
c.popLevel()
@ -420,10 +428,11 @@ proc atBlockIndentation(c: Context, e: var Event): bool =
let headerEnd = c.lex.curStartPos
c.lex.next()
if c.lex.cur == Token.MapValueInd:
c.peek = move(e)
c.keyCache.add(move(e))
e = startMapEvent(csBlock, c.headerProps, c.headerStart, headerEnd)
c.headerProps = defaultProperties
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
elif not isEmpty(c.headerProps):
raise c.generateError("Alias may not have properties")
else:
@ -437,11 +446,12 @@ proc atBlockIndentationProps(c: Context, e: var Event): bool =
c.updateIndentation(c.lex.recentIndentation())
case c.lex.cur
of MapValueInd:
c.peek = scalarEvent("", c.inlineProps, ssPlain, c.inlineStart, c.lex.curEndPos)
c.keyCache.add(scalarEvent("", c.inlineProps, ssPlain, c.inlineStart, c.lex.curEndPos))
c.inlineProps = defaultProperties
e = startMapEvent(csBlock, c.headerProps, c.lex.curStartPos, c.lex.curEndPos)
c.headerProps = defaultProperties
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
return true
of Plain, SingleQuoted, DoubleQuoted:
e = scalarEvent(c.lex.evaluated, autoScalarTag(c.inlineProps, c.lex.cur),
@ -452,10 +462,11 @@ proc atBlockIndentationProps(c: Context, e: var Event): bool =
if c.lex.cur == Token.MapValueInd:
if c.lex.lastScalarWasMultiline():
raise c.generateError("Implicit mapping key may not be multiline")
c.peek = move(e)
c.keyCache.add(move(e))
e = startMapEvent(csBlock, c.headerProps, c.headerStart, headerEnd)
c.headerProps = defaultProperties
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
else:
c.mergeProps(c.headerProps, e.scalarProperties)
c.popLevel()
@ -463,23 +474,23 @@ proc atBlockIndentationProps(c: Context, e: var Event): bool =
of MapStart, SeqStart:
let
startPos = c.lex.curStartPos
indent = c.levels[^1].indentation
indent = c.lex.currentIndentation()
levelDepth = c.levels.len
c.transition(beforeFlowItemProps)
c.caching = true
while c.lex.flowDepth > 0:
c.keyCache.add(c.next())
while c.levels.len >= levelDepth:
c.keyCache.add(c.next())
c.caching = false
if c.lex.cur == Token.MapValueInd:
c.pushLevel(afterImplicitKey, indent)
c.pushLevel(emitCollectionKey)
c.pushLevel(emitCached)
if c.lex.curStartPos.line != startPos.line:
raise c.generateError("Implicit mapping key may not be multiline")
e = startMapEvent(csBlock, c.headerProps, c.headerStart, startPos)
c.headerProps = defaultProperties
return true
else:
c.pushLevel(emitCollectionKey)
c.pushLevel(emitCached)
return false
of Literal, Folded:
c.mergeProps(c.inlineProps, c.headerProps)
@ -491,6 +502,7 @@ proc atBlockIndentationProps(c: Context, e: var Event): bool =
return true
of Indentation:
c.lex.next()
c.transition(atBlockIndentation)
return false
else:
raise c.generateError("Unexpected token (expected block content): " & $c.lex.cur)
@ -567,19 +579,21 @@ proc afterCompactParentProps(c: Context, e: var Event): bool =
c.popLevel()
return true
of MapValueInd:
c.peek = scalarEvent("", c.inlineProps, ssPlain, c.inlineStart, c.lex.curStartPos)
c.keyCache.add(scalarEvent("", c.inlineProps, ssPlain, c.inlineStart, c.lex.curStartPos))
c.inlineProps = defaultProperties
e = startMapEvent(csBlock, defaultProperties, c.lex.curStartPos, c.lex.curStartPos)
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
return true
of Alias:
e = aliasEvent(c.lex.shortLexeme().Anchor, c.inlineStart, c.lex.curEndPos)
let headerEnd = c.lex.curStartPos
c.lex.next()
if c.lex.cur == Token.MapValueInd:
c.peek = move(e)
c.keyCache.add(move(e))
e = startMapEvent(csBlock, defaultProperties, headerEnd, headerEnd)
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
else:
c.popLevel()
return true
@ -593,24 +607,16 @@ proc afterCompactParentProps(c: Context, e: var Event): bool =
if c.lex.cur == Token.MapValueInd:
if c.lex.lastScalarWasMultiline():
raise c.generateError("Implicit mapping key may not be multiline")
c.peek = move(e)
c.keyCache.add(move(e))
e = startMapEvent(csBlock, defaultProperties, headerEnd, headerEnd)
c.transition(afterImplicitKey)
c.pushLevel(emitCached)
else:
c.popLevel()
return true
of MapStart:
e = startMapEvent(csFlow, c.inlineProps, c.inlineStart, c.lex.curEndPos)
c.inlineProps = defaultProperties
c.transition(afterFlowMapSep)
c.lex.next()
return true
of SeqStart:
e = startSeqEvent(csFlow, c.inlineProps, c.inlineStart, c.lex.curEndPos)
c.inlineProps = defaultProperties
c.transition(afterFlowSeqSep)
c.lex.next()
return true
of MapStart, SeqStart:
c.transition(atBlockIndentationProps)
return false
else:
raise c.generateError("Unexpected token (expected newline or flow item start: " & $c.lex.cur)
@ -660,14 +666,14 @@ proc beforeDocEnd(c: Context, e: var Event): bool =
e = endDocEvent(true, c.lex.curStartPos, c.lex.curEndPos)
c.transition(beforeDoc)
c.lex.next()
c.tagLib.resetPrefixes()
resetHandles(c.handles)
of StreamEnd:
e = endDocEvent(false, c.lex.curStartPos, c.lex.curEndPos)
c.popLevel()
of DirectivesEnd:
e = endDocEvent(false, c.lex.curStartPos, c.lex.curStartPos)
c.transition(beforeDoc)
c.tagLib.resetPrefixes()
resetHandles(c.handles)
else:
raise c.generateError("Unexpected token (expected document end): " & $c.lex.cur)
return true
@ -693,6 +699,7 @@ proc inBlockSeq(c: Context, e: var Event): bool =
proc beforeBlockMapKey(c: Context, e: var Event): bool =
if c.blockIndentation > c.levels[^1].indentation:
raise c.generateError("Invalid indentation: got " & $c.blockIndentation & ", expected " & $c.levels[^1].indentation)
c.inlineStart = c.lex.curStartPos
case c.lex.cur
of MapKeyInd:
c.transition(beforeBlockMapValue)
@ -959,21 +966,24 @@ proc afterFlowSeqSepProps(c: Context, e: var Event): bool =
c.inlineProps = defaultProperties
c.lex.next()
if c.lex.cur == Token.MapValueInd:
c.peek = move(e)
e = startMapEvent(csFlow, defaultProperties, c.lex.curStartPos, c.lex.curStartPos)
c.pushLevel(afterImplicitPairStart)
if c.caching:
c.keyCache.add(startMapEvent(csFlow, defaultProperties, c.lex.curStartPos, c.lex.curStartPos))
else:
c.keyCache.add(move(e))
e = startMapEvent(csFlow, defaultProperties, c.lex.curStartPos, c.lex.curStartPos)
c.pushLevel(emitCached)
return true
of MapStart, SeqStart:
let
startPos = c.lex.curStartPos
indent = c.levels[^1].indentation
cacheStart = c.keyCache.len
targetFlowDepth = c.lex.flowDepth - 1
levelDepth = c.levels.len
alreadyCaching = c.caching
c.pushLevel(beforeFlowItemProps)
c.caching = true
while c.lex.flowDepth > targetFlowDepth:
c.keyCache.add(c.next())
while c.levels.len > levelDepth:
c.keyCache.add(c.next())
c.caching = alreadyCaching
if c.lex.cur == Token.MapValueInd:
@ -981,7 +991,7 @@ proc afterFlowSeqSepProps(c: Context, e: var Event): bool =
if c.lex.curStartPos.line != startPos.line:
raise c.generateError("Implicit mapping key may not be multiline")
if not alreadyCaching:
c.pushLevel(emitCollectionKey)
c.pushLevel(emitCached)
e = startMapEvent(csPair, defaultProperties, startPos, startPos)
return true
else:
@ -991,7 +1001,7 @@ proc afterFlowSeqSepProps(c: Context, e: var Event): bool =
return false
else:
if not alreadyCaching:
c.pushLevel(emitCollectionKey)
c.pushLevel(emitCached)
return false
else:
c.pushLevel(beforeFlowItem)
@ -1025,7 +1035,7 @@ proc afterPairValue(c: Context, e: var Event): bool =
c.popLevel()
return true
proc emitCollectionKey(c: Context, e: var Event): bool =
proc emitCached(c: Context, e: var Event): bool =
debug("emitCollection key: pos = " & $c.keyCachePos & ", len = " & $c.keyCache.len)
yAssert(c.keyCachePos < c.keyCache.len)
e = move(c.keyCache[c.keyCachePos])

View File

@ -107,7 +107,12 @@ type
ScalarStyle = enum
sLiteral, sFolded, sPlain, sDoubleQuoted
PresenterTarget = Stream | ptr[string]
Context = object
target: Stream
tagLib: TagLibrary
options: PresentationOptions
handles: seq[tuple[handle, uriPrefix: string]]
levels: seq[DumperState]
const
defaultPresentationOptions* =
@ -124,6 +129,24 @@ proc defineOptions*(style: PresentationStyle = psDefault,
PresentationOptions(style: style, indentationStep: indentationStep,
newlines: newlines, outputVersion: outputVersion)
proc state(c: Context): DumperState = c.levels[^1]
proc `state=`(c: var Context, v: DumperState) =
c.levels[^1] = v
proc searchHandle(c: Context, tag: string):
tuple[handle: string, len: int] {.raises: [].} =
## search in the registered tag handles for one whose prefix matches the start
## of the given tag. If multiple registered handles match, the one with the
## longest prefix is returned. If no registered handle matches, ("", 0) is
## returned.
result.len = 0
for item in c.handles:
if item.uriPrefix.len > result.len:
if tag.startsWith(item.uriPrefix):
result.len = item.uriPrefix.len
result.handle = item.handle
proc inspect(scalar: string, indentation: int,
words, lines: var seq[tuple[start, finish: int]]):
ScalarStyle {.raises: [].} =
@ -204,111 +227,114 @@ template append(target: Stream, val: string | char) =
template append(target: ptr[string], val: string | char) =
target[].add(val)
proc writeDoubleQuoted(scalar: string, s: PresenterTarget, indentation: int,
proc writeDoubleQuoted(c: Context, scalar: string, indentation: int,
newline: string)
{.raises: [YamlPresenterOutputError].} =
var curPos = indentation
let t = c.target
try:
s.append('"')
t.append('"')
curPos.inc()
for c in scalar:
if curPos == 79:
s.append('\\')
s.append(newline)
s.append(repeat(' ', indentation))
t.append('\\')
t.append(newline)
t.append(repeat(' ', indentation))
curPos = indentation
if c == ' ':
s.append('\\')
t.append('\\')
curPos.inc()
case c
of '"':
s.append("\\\"")
t.append("\\\"")
curPos.inc(2)
of '\l':
s.append("\\n")
t.append("\\n")
curPos.inc(2)
of '\t':
s.append("\\t")
t.append("\\t")
curPos.inc(2)
of '\\':
s.append("\\\\")
t.append("\\\\")
curPos.inc(2)
else:
if ord(c) < 32:
s.append("\\x" & toHex(ord(c), 2))
t.append("\\x" & toHex(ord(c), 2))
curPos.inc(4)
else:
s.append(c)
t.append(c)
curPos.inc()
s.append('"')
t.append('"')
except:
var e = newException(YamlPresenterOutputError,
"Error while writing to output stream")
e.parent = getCurrentException()
raise e
proc writeDoubleQuotedJson(scalar: string, s: PresenterTarget)
proc writeDoubleQuotedJson(c: Context, scalar: string)
{.raises: [YamlPresenterOutputError].} =
let t = c.target
try:
s.append('"')
t.append('"')
for c in scalar:
case c
of '"': s.append("\\\"")
of '\\': s.append("\\\\")
of '\l': s.append("\\n")
of '\t': s.append("\\t")
of '\f': s.append("\\f")
of '\b': s.append("\\b")
of '"': t.append("\\\"")
of '\\': t.append("\\\\")
of '\l': t.append("\\n")
of '\t': t.append("\\t")
of '\f': t.append("\\f")
of '\b': t.append("\\b")
else:
if ord(c) < 32: s.append("\\u" & toHex(ord(c), 4)) else: s.append(c)
s.append('"')
if ord(c) < 32: t.append("\\u" & toHex(ord(c), 4)) else: t.append(c)
t.append('"')
except:
var e = newException(YamlPresenterOutputError,
"Error while writing to output stream")
e.parent = getCurrentException()
raise e
proc writeLiteral(scalar: string, indentation, indentStep: int,
s: PresenterTarget, lines: seq[tuple[start, finish: int]],
newline: string)
proc writeLiteral(c: Context, scalar: string, indentation, indentStep: int,
lines: seq[tuple[start, finish: int]], newline: string)
{.raises: [YamlPresenterOutputError].} =
let t = c.target
try:
s.append('|')
if scalar[^1] != '\l': s.append('-')
if scalar[0] in [' ', '\t']: s.append($indentStep)
t.append('|')
if scalar[^1] != '\l': t.append('-')
if scalar[0] in [' ', '\t']: t.append($indentStep)
for line in lines:
s.append(newline)
s.append(repeat(' ', indentation + indentStep))
t.append(newline)
t.append(repeat(' ', indentation + indentStep))
if line.finish >= line.start:
s.append(scalar[line.start .. line.finish])
t.append(scalar[line.start .. line.finish])
except:
var e = newException(YamlPresenterOutputError,
"Error while writing to output stream")
e.parent = getCurrentException()
raise e
proc writeFolded(scalar: string, indentation, indentStep: int,
s: PresenterTarget, words: seq[tuple[start, finish: int]],
proc writeFolded(c: Context, scalar: string, indentation, indentStep: int,
words: seq[tuple[start, finish: int]],
newline: string)
{.raises: [YamlPresenterOutputError].} =
let t = c.target
try:
s.append(">")
if scalar[^1] != '\l': s.append('-')
if scalar[0] in [' ', '\t']: s.append($indentStep)
t.append(">")
if scalar[^1] != '\l': t.append('-')
if scalar[0] in [' ', '\t']: t.append($indentStep)
var curPos = 80
for word in words:
if word.start > 0 and scalar[word.start - 1] == '\l':
s.append(newline & newline)
s.append(repeat(' ', indentation + indentStep))
t.append(newline & newline)
t.append(repeat(' ', indentation + indentStep))
curPos = indentation + indentStep
elif curPos + (word.finish - word.start) > 80:
s.append(newline)
s.append(repeat(' ', indentation + indentStep))
t.append(newline)
t.append(repeat(' ', indentation + indentStep))
curPos = indentation + indentStep
else:
s.append(' ')
t.append(' ')
curPos.inc()
s.append(scalar[word.start .. word.finish])
t.append(scalar[word.start .. word.finish])
curPos += word.finish - word.start + 1
except:
var e = newException(YamlPresenterOutputError,
@ -316,106 +342,106 @@ proc writeFolded(scalar: string, indentation, indentStep: int,
e.parent = getCurrentException()
raise e
template safeWrite(target: PresenterTarget, s: string or char) =
try: target.append(s)
template safeWrite(c: Context, s: string or char) =
try: c.target.append(s)
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
proc startItem(target: PresenterTarget, style: PresentationStyle,
indentation: int, state: var DumperState, isObject: bool,
proc startItem(c: var Context, indentation: int, isObject: bool,
newline: string) {.raises: [YamlPresenterOutputError].} =
let t = c.target
try:
case state
case c.state
of dBlockMapValue:
target.append(newline)
target.append(repeat(' ', indentation))
if isObject or style == psCanonical:
target.append("? ")
state = dBlockExplicitMapKey
else: state = dBlockImplicitMapKey
of dBlockInlineMap: state = dBlockImplicitMapKey
t.append(newline)
t.append(repeat(' ', indentation))
if isObject or c.options.style == psCanonical:
t.append("? ")
c.state = dBlockExplicitMapKey
else: c.state = dBlockImplicitMapKey
of dBlockInlineMap: c.state = dBlockImplicitMapKey
of dBlockExplicitMapKey:
target.append(newline)
target.append(repeat(' ', indentation))
target.append(": ")
state = dBlockMapValue
t.append(newline)
t.append(repeat(' ', indentation))
t.append(": ")
c.state = dBlockMapValue
of dBlockImplicitMapKey:
target.append(": ")
state = dBlockMapValue
t.append(": ")
c.state = dBlockMapValue
of dFlowExplicitMapKey:
if style != psMinimal:
target.append(newline)
target.append(repeat(' ', indentation))
target.append(": ")
state = dFlowMapValue
if c.options.style != psMinimal:
t.append(newline)
t.append(repeat(' ', indentation))
t.append(": ")
c.state = dFlowMapValue
of dFlowMapValue:
if (isObject and style != psMinimal) or style in [psJson, psCanonical]:
target.append(',' & newline & repeat(' ', indentation))
if style == psJson: state = dFlowImplicitMapKey
if (isObject and c.options.style != psMinimal) or c.options.style in [psJson, psCanonical]:
t.append(',' & newline & repeat(' ', indentation))
if c.options.style == psJson: c.state = dFlowImplicitMapKey
else:
target.append("? ")
state = dFlowExplicitMapKey
elif isObject and style == psMinimal:
target.append(", ? ")
state = dFlowExplicitMapKey
t.append("? ")
c.state = dFlowExplicitMapKey
elif isObject and c.options.style == psMinimal:
t.append(", ? ")
c.state = dFlowExplicitMapKey
else:
target.append(", ")
state = dFlowImplicitMapKey
t.append(", ")
c.state = dFlowImplicitMapKey
of dFlowMapStart:
if (isObject and style != psMinimal) or style in [psJson, psCanonical]:
target.append(newline & repeat(' ', indentation))
if style == psJson: state = dFlowImplicitMapKey
if (isObject and c.options.style != psMinimal) or c.options.style in [psJson, psCanonical]:
t.append(newline & repeat(' ', indentation))
if c.options.style == psJson: c.state = dFlowImplicitMapKey
else:
target.append("? ")
state = dFlowExplicitMapKey
else: state = dFlowImplicitMapKey
t.append("? ")
c.state = dFlowExplicitMapKey
else: c.state = dFlowImplicitMapKey
of dFlowImplicitMapKey:
target.append(": ")
state = dFlowMapValue
t.append(": ")
c.state = dFlowMapValue
of dBlockSequenceItem:
target.append(newline)
target.append(repeat(' ', indentation))
target.append("- ")
t.append(newline)
t.append(repeat(' ', indentation))
t.append("- ")
of dFlowSequenceStart:
case style
case c.options.style
of psMinimal, psDefault: discard
of psCanonical, psJson:
target.append(newline)
target.append(repeat(' ', indentation))
t.append(newline)
t.append(repeat(' ', indentation))
of psBlockOnly: discard # can never happen
state = dFlowSequenceItem
c.state = dFlowSequenceItem
of dFlowSequenceItem:
case style
of psMinimal, psDefault: target.append(", ")
case c.options.style
of psMinimal, psDefault: t.append(", ")
of psCanonical, psJson:
target.append(',' & newline)
target.append(repeat(' ', indentation))
t.append(',' & newline)
t.append(repeat(' ', indentation))
of psBlockOnly: discard # can never happen
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
proc writeTagAndAnchor(target: PresenterTarget, props: Properties,
tagLib: TagLibrary) {.raises: [YamlPresenterOutputError].} =
proc writeTagAndAnchor(c: Context, props: Properties) {.raises: [YamlPresenterOutputError].} =
let t = c.target
try:
if props.tag notin [yTagQuestionMark, yTagExclamationMark]:
let tagUri = tagLib.uri(props.tag)
let (handle, length) = tagLib.searchHandle(tagUri)
let tagUri = c.tagLib.uri(props.tag)
let (handle, length) = c.searchHandle(tagUri)
if length > 0:
target.append(handle)
target.append(tagUri[length..tagUri.high])
target.append(' ')
t.append(handle)
t.append(tagUri[length..tagUri.high])
t.append(' ')
else:
target.append("!<")
target.append(tagUri)
target.append("> ")
t.append("!<")
t.append(tagUri)
t.append("> ")
if props.anchor != yAnchorNone:
target.append("&")
target.append($props.anchor)
target.append(' ')
t.append("&")
t.append($props.anchor)
t.append(' ')
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
@ -429,15 +455,12 @@ proc nextItem(c: var Deque, s: var YamlStream):
else:
result = s.next()
proc doPresent(s: var YamlStream, target: PresenterTarget,
tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions) =
proc doPresent(c: var Context, s: var YamlStream) =
var
indentation = 0
levels = newSeq[DumperState]()
cached = initDeQue[Event]()
let newline = if options.newlines == nlLF: "\l"
elif options.newlines == nlCRLF: "\c\l" else: "\n"
let newline = if c.options.newlines == nlLF: "\l"
elif c.options.newlines == nlCRLF: "\c\l" else: "\n"
var firstDoc = true
while true:
let item = nextItem(cached, s)
@ -445,94 +468,95 @@ proc doPresent(s: var YamlStream, target: PresenterTarget,
of yamlStartStream: discard
of yamlEndStream: break
of yamlStartDoc:
resetHandles(c.handles)
for v in item.handles:
discard registerHandle(c.handles, v.handle, v.uriPrefix)
if not firstDoc:
if options.style == psJson:
if c.options.style == psJson:
raise newException(YamlPresenterJsonError,
"Cannot output more than one document in JSON style")
target.safeWrite("..." & newline)
c.safeWrite("..." & newline)
if options.style != psJson:
if c.options.style != psJson:
try:
case options.outputVersion
of ov1_2: target.append("%YAML 1.2" & newline)
of ov1_1: target.append("%YAML 1.1" & newLine)
case c.options.outputVersion
of ov1_2: c.target.append("%YAML 1.2" & newline)
of ov1_1: c.target.append("%YAML 1.1" & newLine)
of ovNone: discard
for prefix, uri in tagLib.handles():
if prefix == "!":
if uri != "!":
target.append("%TAG ! " & uri & newline)
elif prefix == "!!":
if uri != yamlTagRepositoryPrefix:
target.append("%TAG !! " & uri & newline)
for v in c.handles:
if v.handle == "!":
if v.uriPrefix != "!":
c.target.append("%TAG ! " & v.uriPrefix & newline)
elif v.handle == "!!":
if v.uriPrefix != yamlTagRepositoryPrefix:
c.target.append("%TAG !! " & v.uriPrefix & newline)
else:
target.append("%TAG " & prefix & ' ' & uri & newline)
target.append("--- ")
c.target.append("%TAG " & v.handle & ' ' & v.uriPrefix & newline)
c.target.append("--- ")
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
of yamlScalar:
if levels.len == 0:
if options.style != psJson: target.safeWrite(newline)
if c.levels.len == 0:
if c.options.style != psJson: c.safeWrite(newline)
else:
startItem(target, options.style, indentation,
levels[levels.high], false, newline)
if options.style != psJson:
writeTagAndAnchor(target, item.scalarProperties, taglib)
c.startItem(indentation, false, newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.scalarProperties)
if options.style == psJson:
if c.options.style == psJson:
let hint = guessType(item.scalarContent)
let tag = item.scalarProperties.tag
if tag in [yTagQuestionMark, yTagBoolean] and
hint in {yTypeBoolTrue, yTypeBoolFalse}:
target.safeWrite(if hint == yTypeBoolTrue: "true" else: "false")
c.safeWrite(if hint == yTypeBoolTrue: "true" else: "false")
elif tag in [yTagQuestionMark, yTagNull] and
hint == yTypeNull:
target.safeWrite("null")
c.safeWrite("null")
elif tag in [yTagQuestionMark, yTagInteger,
yTagNimInt8, yTagNimInt16, yTagNimInt32, yTagNimInt64,
yTagNimUInt8, yTagNimUInt16, yTagNimUInt32, yTagNimUInt64] and
hint == yTypeInteger:
target.safeWrite(item.scalarContent)
c.safeWrite(item.scalarContent)
elif tag in [yTagQuestionMark, yTagFloat, yTagNimFloat32,
yTagNimFloat64] and hint in {yTypeFloatInf, yTypeFloatNaN}:
raise newException(YamlPresenterJsonError,
"Infinity and not-a-number values cannot be presented as JSON!")
elif tag in [yTagQuestionMark, yTagFloat] and
hint == yTypeFloat:
target.safeWrite(item.scalarContent)
else: writeDoubleQuotedJson(item.scalarContent, target)
elif options.style == psCanonical:
writeDoubleQuoted(item.scalarContent, target,
indentation + options.indentationStep, newline)
c.safeWrite(item.scalarContent)
else: c.writeDoubleQuotedJson(item.scalarContent)
elif c.options.style == psCanonical:
c.writeDoubleQuoted(item.scalarContent,
indentation + c.options.indentationStep, newline)
else:
var words, lines = newSeq[tuple[start, finish: int]]()
case item.scalarContent.inspect(
indentation + options.indentationStep, words, lines)
of sLiteral: writeLiteral(item.scalarContent, indentation,
options.indentationStep, target, lines, newline)
of sFolded: writeFolded(item.scalarContent, indentation,
options.indentationStep, target, words, newline)
of sPlain: target.safeWrite(item.scalarContent)
of sDoubleQuoted: writeDoubleQuoted(item.scalarContent, target,
indentation + options.indentationStep, newline)
indentation + c.options.indentationStep, words, lines)
of sLiteral: c.writeLiteral(item.scalarContent, indentation,
c.options.indentationStep, lines, newline)
of sFolded: c.writeFolded(item.scalarContent, indentation,
c.options.indentationStep, words, newline)
of sPlain: c.safeWrite(item.scalarContent)
of sDoubleQuoted: c.writeDoubleQuoted(item.scalarContent,
indentation + c.options.indentationStep, newline)
of yamlAlias:
if options.style == psJson:
if c.options.style == psJson:
raise newException(YamlPresenterJsonError,
"Alias not allowed in JSON output")
yAssert levels.len > 0
startItem(target, options.style, indentation, levels[levels.high],
false, newline)
yAssert c.levels.len > 0
c.startItem(indentation, false, newline)
try:
target.append('*')
target.append($item.aliasTarget)
c.target.append('*')
c.target.append($item.aliasTarget)
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
of yamlStartSeq:
var nextState: DumperState
case options.style
case c.options.style
of psDefault:
var length = 0
while true:
@ -547,8 +571,7 @@ proc doPresent(s: var YamlStream, target: PresenterTarget,
break
nextState = if length <= 60: dFlowSequenceStart else: dBlockSequenceItem
of psJson:
if levels.len > 0 and levels[levels.high] in
[dFlowMapStart, dFlowMapValue]:
if c.levels.len > 0 and c.state in [dFlowMapStart, dFlowMapValue]:
raise newException(YamlPresenterJsonError, "Cannot have sequence as map key in JSON output!")
nextState = dFlowSequenceStart
of psMinimal, psCanonical: nextState = dFlowSequenceStart
@ -557,33 +580,32 @@ proc doPresent(s: var YamlStream, target: PresenterTarget,
if next.kind == yamlEndSeq: nextState = dFlowSequenceStart
else: nextState = dBlockSequenceItem
if levels.len == 0:
if c.levels.len == 0:
case nextState
of dBlockSequenceItem:
if options.style != psJson:
writeTagAndAnchor(target, item.seqProperties, tagLib)
if c.options.style != psJson:
c.writeTagAndAnchor(item.seqProperties)
of dFlowSequenceStart:
target.safeWrite(newline)
if options.style != psJson:
writeTagAndAnchor(target, item.seqProperties, tagLib)
indentation += options.indentationStep
c.safeWrite(newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.seqProperties)
indentation += c.options.indentationStep
else: internalError("Invalid nextState: " & $nextState)
else:
startItem(target, options.style, indentation,
levels[levels.high], true, newline)
if options.style != psJson:
writeTagAndAnchor(target, item.seqProperties, tagLib)
indentation += options.indentationStep
c.startItem(indentation, true, newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.seqProperties)
indentation += c.options.indentationStep
if nextState == dFlowSequenceStart: target.safeWrite('[')
if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
if nextState == dFlowSequenceStart: c.safeWrite('[')
if c.levels.len > 0 and c.options.style in [psJson, psCanonical] and
c.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
indentation += options.indentationStep
levels.add(nextState)
indentation += c.options.indentationStep
c.levels.add(nextState)
of yamlStartMap:
var nextState: DumperState
case options.style
case c.options.style
of psDefault:
type MapParseState = enum
mpInitial, mpKey, mpValue, mpNeedBlock
@ -601,8 +623,7 @@ proc doPresent(s: var YamlStream, target: PresenterTarget,
of psMinimal: nextState = dFlowMapStart
of psCanonical: nextState = dFlowMapStart
of psJson:
if levels.len > 0 and levels[levels.high] in
[dFlowMapStart, dFlowMapValue]:
if c.levels.len > 0 and c.state in [dFlowMapStart, dFlowMapValue]:
raise newException(YamlPresenterJsonError,
"Cannot have map as map key in JSON output!")
nextState = dFlowMapStart
@ -610,103 +631,101 @@ proc doPresent(s: var YamlStream, target: PresenterTarget,
let next = s.peek()
if next.kind == yamlEndMap: nextState = dFlowMapStart
else: nextState = dBlockMapValue
if levels.len == 0:
if c.levels.len == 0:
case nextState
of dBlockMapValue:
if options.style != psJson:
writeTagAndAnchor(target, item.mapProperties, tagLib)
if c.options.style != psJson:
c.writeTagAndAnchor(item.mapProperties)
else:
if options.style != psJson:
target.safeWrite(newline)
writeTagAndAnchor(target, item.mapProperties, tagLib)
indentation += options.indentationStep
if c.options.style != psJson:
c.safeWrite(newline)
c.writeTagAndAnchor(item.mapProperties)
indentation += c.options.indentationStep
of dFlowMapStart:
target.safeWrite(newline)
if options.style != psJson:
writeTagAndAnchor(target, item.mapProperties, tagLib)
indentation += options.indentationStep
c.safeWrite(newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.mapProperties)
indentation += c.options.indentationStep
of dBlockInlineMap: discard
else: internalError("Invalid nextState: " & $nextState)
else:
if nextState in [dBlockMapValue, dBlockImplicitMapKey]:
startItem(target, options.style, indentation,
levels[levels.high], true, newline)
if options.style != psJson:
writeTagAndAnchor(target, item.mapProperties, tagLib)
c.startItem(indentation, true, newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.mapProperties)
else:
startItem(target, options.style, indentation,
levels[levels.high], true, newline)
if options.style != psJson:
writeTagAndAnchor(target, item.mapProperties, tagLib)
indentation += options.indentationStep
c.startItem(indentation, true, newline)
if c.options.style != psJson:
c.writeTagAndAnchor(item.mapProperties)
indentation += c.options.indentationStep
if nextState == dFlowMapStart: target.safeWrite('{')
if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in
[dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
indentation += options.indentationStep
levels.add(nextState)
if nextState == dFlowMapStart: c.safeWrite('{')
if c.levels.len > 0 and c.options.style in [psJson, psCanonical] and
c.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockImplicitMapKey,
dBlockSequenceItem]:
indentation += c.options.indentationStep
c.levels.add(nextState)
of yamlEndSeq:
yAssert levels.len > 0
case levels.pop()
yAssert c.levels.len > 0
case c.levels.pop()
of dFlowSequenceItem:
case options.style
of psDefault, psMinimal, psBlockOnly: target.safeWrite(']')
case c.options.style
of psDefault, psMinimal, psBlockOnly: c.safeWrite(']')
of psJson, psCanonical:
indentation -= options.indentationStep
indentation -= c.options.indentationStep
try:
target.append(newline)
target.append(repeat(' ', indentation))
target.append(']')
c.target.append(newline)
c.target.append(repeat(' ', indentation))
c.target.append(']')
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
if levels.len == 0 or levels[levels.high] notin
if c.levels.len == 0 or c.state notin
[dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
continue
of dFlowSequenceStart:
if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
if c.levels.len > 0 and c.options.style in [psJson, psCanonical] and
c.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
indentation -= options.indentationStep
target.safeWrite(']')
indentation -= c.options.indentationStep
c.safeWrite(']')
of dBlockSequenceItem: discard
else: internalError("Invalid popped level")
indentation -= options.indentationStep
indentation -= c.options.indentationStep
of yamlEndMap:
yAssert levels.len > 0
let level = levels.pop()
yAssert c.levels.len > 0
let level = c.levels.pop()
case level
of dFlowMapValue:
case options.style
of psDefault, psMinimal, psBlockOnly: target.safeWrite('}')
case c.options.style
of psDefault, psMinimal, psBlockOnly: c.safeWrite('}')
of psJson, psCanonical:
indentation -= options.indentationStep
indentation -= c.options.indentationStep
try:
target.append(newline)
target.append(repeat(' ', indentation))
target.append('}')
c.target.append(newline)
c.target.append(repeat(' ', indentation))
c.target.append('}')
except:
var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException()
raise e
if levels.len == 0 or levels[levels.high] notin
if c.levels.len == 0 or c.state notin
[dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
continue
of dFlowMapStart:
if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
if c.levels.len > 0 and c.options.style in [psJson, psCanonical] and
c.state in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]:
indentation -= options.indentationStep
target.safeWrite('}')
indentation -= c.options.indentationStep
c.safeWrite('}')
of dBlockMapValue, dBlockInlineMap: discard
else: internalError("Invalid level: " & $level)
indentation -= options.indentationStep
indentation -= c.options.indentationStep
of yamlEndDoc:
firstDoc = false
@ -716,57 +735,60 @@ proc present*(s: var YamlStream, target: Stream,
{.raises: [YamlPresenterJsonError, YamlPresenterOutputError,
YamlStreamError].} =
## Convert ``s`` to a YAML character stream and write it to ``target``.
doPresent(s, target, tagLib, options)
var c = Context(target: target, tagLib: tagLib, options: options)
doPresent(c, s)
proc present*(s: var YamlStream, tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions):
string {.raises: [YamlPresenterJsonError, YamlPresenterOutputError,
YamlStreamError].} =
## Convert ``s`` to a YAML character stream and return it as string.
result = ""
doPresent(s, addr result, tagLib, options)
proc doTransform(input: Stream | string, output: PresenterTarget,
options: PresentationOptions, resolveToCoreYamlTags: bool) =
var
ss = newStringStream()
c = Context(target: ss, tagLib: tagLib, options: options)
doPresent(c, s)
return ss.data
proc doTransform(c: var Context, input: Stream,
resolveToCoreYamlTags: bool) =
var
taglib = initExtendedTagLibrary()
parser: YamlParser
parser.init(tagLib)
var events = parser.parse(input)
try:
if options.style == psCanonical:
if c.options.style == psCanonical:
var bys: YamlStream = newBufferYamlStream()
for e in events:
if resolveToCoreYamlTags:
var event = e
case event.kind
of yamlStartDoc, yamlEndDoc, yamlEndMap, yamlAlias, yamlEndSeq:
of yamlStartStream, yamlEndStream, yamlStartDoc, yamlEndDoc, yamlEndMap, yamlAlias, yamlEndSeq:
discard
of yamlStartMap:
if event.mapTag in [yTagQuestionMark, yTagExclamationMark]:
event.mapTag = yTagMapping
if event.mapProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
event.mapProperties.tag = yTagMapping
of yamlStartSeq:
if event.seqTag in [yTagQuestionMark, yTagExclamationMark]:
event.seqTag = yTagSequence
if event.seqProperties.tag in [yTagQuestionMark, yTagExclamationMark]:
event.seqProperties.tag = yTagSequence
of yamlScalar:
if event.scalarTag == yTagQuestionMark:
if event.scalarProperties.tag == yTagQuestionMark:
case guessType(event.scalarContent)
of yTypeInteger: event.scalarTag = yTagInteger
of yTypeInteger: event.scalarProperties.tag = yTagInteger
of yTypeFloat, yTypeFloatInf, yTypeFloatNaN:
event.scalarTag = yTagFloat
of yTypeBoolTrue, yTypeBoolFalse: event.scalarTag = yTagBoolean
of yTypeNull: event.scalarTag = yTagNull
of yTypeTimestamp: event.scalarTag = yTagTimestamp
of yTypeUnknown: event.scalarTag = yTagString
elif event.scalarTag == yTagExclamationMark:
event.scalarTag = yTagString
event.scalarProperties.tag = yTagFloat
of yTypeBoolTrue, yTypeBoolFalse: event.scalarProperties.tag = yTagBoolean
of yTypeNull: event.scalarProperties.tag = yTagNull
of yTypeTimestamp: event.scalarProperties.tag = yTagTimestamp
of yTypeUnknown: event.scalarProperties.tag = yTagString
elif event.scalarProperties.tag == yTagExclamationMark:
event.scalarProperties.tag = yTagString
BufferYamlStream(bys).put(event)
else: BufferYamlStream(bys).put(e)
when output is ptr[string]: output[] = present(bys, tagLib, options)
else: present(bys, output, tagLib, options)
doPresent(c, bys)
else:
when output is ptr[string]: output[] = present(events, tagLib, options)
else: present(events, output, tagLib, options)
doPresent(c, events)
except YamlStreamError:
var e = getCurrentException()
while e.parent of YamlStreamError: e = e.parent
@ -774,6 +796,9 @@ proc doTransform(input: Stream | string, output: PresenterTarget,
elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent)
else: internalError("Unexpected exception: " & e.parent.repr)
proc genInput(input: Stream): Stream = input
proc genInput(input: string): Stream = newStringStream(input)
proc transform*(input: Stream | string, output: Stream,
options: PresentationOptions = defaultPresentationOptions,
resolveToCoreYamlTags: bool = false)
@ -783,7 +808,7 @@ proc transform*(input: Stream | string, output: Stream,
## while resolving non-specific tags to the ones in the YAML core tag
## library. If ``resolveToCoreYamlTags`` is ``true``, non-specific tags will
## be replaced by specific tags according to the YAML core schema.
doTransform(input, output, options, resolveToCoreYamlTags)
doTransform(genInput(input), output, options, resolveToCoreYamlTags)
proc transform*(input: Stream | string,
options: PresentationOptions = defaultPresentationOptions,
@ -796,4 +821,4 @@ proc transform*(input: Stream | string,
## ``true``, non-specific tags will be replaced by specific tags according to
## the YAML core schema.
result = ""
doTransform(input, addr result, options, resolveToCoreYamlTags)
doTransform(genInput(input), addr result, options, resolveToCoreYamlTags)

View File

@ -62,3 +62,16 @@ proc nextAnchor*(s: var string, i: int) =
nextAnchor(s, i - 1)
else:
inc(s[i])
template resetHandles*(handles: var seq[tuple[handle, uriPrefix: string]]) {.dirty.} =
handles.setLen(0)
handles.add(("!", "!"))
handles.add(("!!", yamlTagRepositoryPrefix))
proc registerHandle*(handles: var seq[tuple[handle, uriPrefix: string]], handle, uriPrefix: string): bool =
for i in countup(0, len(handles)-1):
if handles[i].handle == handle:
handles[i].uriPrefix = uriPrefix
return false
handles.add((handle, uriPrefix))
return false

View File

@ -1361,15 +1361,11 @@ proc construct*[T](s: var YamlStream, target: var T)
var context = newConstructionContext()
try:
var e = s.next()
yAssert(e.kind == yamlStartStream)
e = s.next()
yAssert(e.kind == yamlStartDoc)
constructChild(s, context, target)
e = s.next()
yAssert(e.kind == yamlEndDoc)
e = s.next()
yAssert(e.kind == yamlEndStream)
except YamlConstructionError:
raise (ref YamlConstructionError)(getCurrentException())
except YamlStreamError:
@ -1386,10 +1382,19 @@ proc construct*[T](s: var YamlStream, target: var T)
proc load*[K](input: Stream | string, target: var K)
{.raises: [YamlConstructionError, IOError, YamlParserError].} =
## Loads a Nim value from a YAML character stream.
var parser: YamlParser
parser.init(serializationTagLibrary)
var events = parser.parse(input)
try: construct(events, target)
var
parser = initYamlParser(serializationTagLibrary)
events = parser.parse(input)
try:
var e = events.next()
yAssert(e.kind == yamlStartStream)
construct(events, target)
e = events.next()
if e.kind != yamlEndStream:
var ex = (ref YamlConstructionError)(
mark: e.startPos, msg: "stream contains multiple document")
discard events.getLastTokenContext(ex.lineContent)
raise ex
except YamlStreamError:
let e = (ref YamlStreamError)(getCurrentException())
if e.parent of IOError: raise (ref IOError)(e.parent)
@ -1397,16 +1402,18 @@ proc load*[K](input: Stream | string, target: var K)
else: internalError("Unexpected exception: " & $e.parent.name)
proc loadMultiDoc*[K](input: Stream | string, target: var seq[K]) =
var parser: YamlParser
parser.init(serializationTagLibrary)
var events = parser.parse(input)
discard events.next() # stream start
var
parser = initYamlParser(serializationTagLibrary)
events = parser.parse(input)
e = events.next()
yAssert(e.kind == yamlStartStream)
try:
while events.peek().kind == yamlStartDoc:
var item: K
construct(events, item)
target.add(item)
discard events.next() # stream end
e = events.next()
yAssert(e.kind == yamlEndStream)
except YamlConstructionError:
var e = (ref YamlConstructionError)(getCurrentException())
discard events.getLastTokenContext(e.lineContent)
@ -1433,7 +1440,7 @@ proc represent*[T](value: T, ts: TagStyle = tsRootOnly,
bys.put(e)
)
bys.put(startStreamEvent())
bys.put(startDocEvent())
bys.put(startDocEvent(handles = @[("!n!", nimyamlTagRepositoryPrefix)]))
representChild(value, ts, context)
bys.put(endDocEvent())
bys.put(endStreamEvent())

View File

@ -12,7 +12,7 @@
## and create own tags. It also enables you to define tags for types used with
## the serialization API.
import tables, macros, hashes, strutils
import tables, macros, hashes
import data
type
@ -30,20 +30,14 @@ type
## `initExtendedTagLibrary <#initExtendedTagLibrary>`_.
tags*: Table[string, TagId]
nextCustomTagId*: TagId
prefixes: seq[tuple[prefix, uri: string]]
proc initTagLibrary*(): TagLibrary {.raises: [].} =
## initializes the ``tags`` table and sets ``nextCustomTagId`` to
## ``yFirstCustomTagId``.
new(result)
result.tags = initTable[string, TagId]()
result.prefixes = @[("!", "!"), ("!!", yamlTagRepositoryPrefix)]
result.nextCustomTagId = yFirstCustomTagId
proc resetPrefixes*(tagLib: TagLibrary) {.raises: [].} =
## resets the registered prefixes in the given tag library, so that it
## only contains the prefixes `!` and `!!`.
tagLib.prefixes.setLen(2)
result.nextCustomTagId = yFirstCustomTagId
proc registerUri*(tagLib: TagLibrary, uri: string): TagId {.raises: [].} =
## registers a custom tag URI with a ``TagLibrary``. The URI will get
@ -108,7 +102,6 @@ proc initExtendedTagLibrary*(): TagLibrary {.raises: [].} =
proc initSerializationTagLibrary*(): TagLibrary =
result = initTagLibrary()
result.prefixes.add(("!n!", nimyamlTagRepositoryPrefix))
result.tags["!"] = yTagExclamationMark
result.tags["?"] = yTagQuestionMark
result.tags[y"str"] = yTagString
@ -195,47 +188,6 @@ setTagUri(uint64, n"system:uint64", yTagNimUInt64)
setTagUri(float32, n"system:float32", yTagNimFloat32)
setTagUri(float64, n"system:float64", yTagNimFloat64)
proc registerHandle*(tagLib: TagLibrary, uri, prefix: string): bool =
## Registers a handle for a prefix. When presenting any tag that starts with
## this prefix, the handle is used instead. Also causes the presenter to
## output a TAG directive for the handle.
##
## Returns true iff a new item has been created, false if an existing item
## has been updated.
for i in countup(0, len(tagLib.prefixes)-1):
if tagLib.prefixes[i].prefix == prefix:
tagLib.prefixes[i].uri = uri
return false
taglib.prefixes.add((prefix, uri))
return false
proc searchHandle*(tagLib: TagLibrary, tag: string):
tuple[handle: string, len: int] {.raises: [].} =
## search in the registered tag handles for one whose prefix matches the start
## of the given tag. If multiple registered handles match, the one with the
## longest prefix is returned. If no registered handle matches, (nil, 0) is
## returned.
result.len = 0
for item in tagLib.prefixes:
if item.uri.len > result.len:
if tag.startsWith(item.uri):
result.len = item.uri.len
result.handle = item.prefix
proc resolve*(tagLib: TagLibrary, handle: string): string {.raises: [].} =
## try to resolve the given tag handle.
## return the registered URI if the tag handle is found.
## if the handle is unknown, return the empty string.
for item in tagLib.prefixes:
if item.prefix == handle:
return item.uri
return ""
iterator handles*(tagLib: TagLibrary): tuple[prefix, uri: string] =
## iterate over registered tag handles that may be used as shortcuts
## (e.g. ``!n!`` for ``tag:nimyaml.org,2016:``)
for item in tagLib.prefixes.items(): yield item
proc nimTag*(suffix: string): string =
## prepends NimYAML's tag repository prefix to the given suffix. For example,
## ``nimTag("system:char")`` yields ``"tag:nimyaml.org,2016:system:char"``.