Support JS string output in presenter

This commit is contained in:
Felix Krause 2016-09-19 19:33:29 +02:00
parent 2bb7f45354
commit 2f3a809738
8 changed files with 219 additions and 187 deletions

View File

@ -13,6 +13,8 @@ type
ScalarStyle = enum ScalarStyle = enum
sLiteral, sFolded, sPlain, sDoubleQuoted sLiteral, sFolded, sPlain, sDoubleQuoted
PresenterTarget = Stream | ptr[string]
proc defineOptions*(style: PresentationStyle = psDefault, proc defineOptions*(style: PresentationStyle = psDefault,
indentationStep: int = 2, indentationStep: int = 2,
newlines: NewLineStyle = nlOSDefault, newlines: NewLineStyle = nlOSDefault,
@ -95,109 +97,117 @@ proc inspect(scalar: string, indentation: int,
elif canUsePlain: result = sPlain elif canUsePlain: result = sPlain
else: result = sDoubleQuoted else: result = sDoubleQuoted
proc writeDoubleQuoted(scalar: string, s: Stream, indentation: int, template append(target: Stream, val: string | char) =
target.write(val)
template append(target: ptr[string], val: string | char) =
target[].add(val)
proc writeDoubleQuoted(scalar: string, s: PresenterTarget, indentation: int,
newline: string) newline: string)
{.raises: [YamlPresenterOutputError].} = {.raises: [YamlPresenterOutputError].} =
var curPos = indentation var curPos = indentation
try: try:
s.write('"') s.append('"')
curPos.inc() curPos.inc()
for c in scalar: for c in scalar:
if curPos == 79: if curPos == 79:
s.write('\\') s.append('\\')
s.write(newline) s.append(newline)
s.write(repeat(' ', indentation)) s.append(repeat(' ', indentation))
curPos = indentation curPos = indentation
if c == ' ': if c == ' ':
s.write('\\') s.append('\\')
curPos.inc() curPos.inc()
case c case c
of '"': of '"':
s.write("\\\"") s.append("\\\"")
curPos.inc(2) curPos.inc(2)
of '\l': of '\l':
s.write("\\n") s.append("\\n")
curPos.inc(2) curPos.inc(2)
of '\t': of '\t':
s.write("\\t") s.append("\\t")
curPos.inc(2) curPos.inc(2)
of '\\': of '\\':
s.write("\\\\") s.append("\\\\")
curPos.inc(2) curPos.inc(2)
else: else:
if ord(c) < 32: if ord(c) < 32:
s.write("\\x" & toHex(ord(c), 2)) s.append("\\x" & toHex(ord(c), 2))
curPos.inc(4) curPos.inc(4)
else: else:
s.write(c) s.append(c)
curPos.inc() curPos.inc()
s.write('"') s.append('"')
except: except:
var e = newException(YamlPresenterOutputError, var e = newException(YamlPresenterOutputError,
"Error while writing to output stream") "Error while writing to output stream")
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
proc writeDoubleQuotedJson(scalar: string, s: Stream) proc writeDoubleQuotedJson(scalar: string, s: PresenterTarget)
{.raises: [YamlPresenterOutputError].} = {.raises: [YamlPresenterOutputError].} =
try: try:
s.write('"') s.append('"')
for c in scalar: for c in scalar:
case c case c
of '"': s.write("\\\"") of '"': s.append("\\\"")
of '\\': s.write("\\\\") of '\\': s.append("\\\\")
of '\l': s.write("\\n") of '\l': s.append("\\n")
of '\t': s.write("\\t") of '\t': s.append("\\t")
of '\f': s.write("\\f") of '\f': s.append("\\f")
of '\b': s.write("\\b") of '\b': s.append("\\b")
else: else:
if ord(c) < 32: s.write("\\u" & toHex(ord(c), 4)) else: s.write(c) if ord(c) < 32: s.append("\\u" & toHex(ord(c), 4)) else: s.append(c)
s.write('"') s.append('"')
except: except:
var e = newException(YamlPresenterOutputError, var e = newException(YamlPresenterOutputError,
"Error while writing to output stream") "Error while writing to output stream")
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
proc writeLiteral(scalar: string, indentation, indentStep: int, s: Stream, proc writeLiteral(scalar: string, indentation, indentStep: int,
lines: seq[tuple[start, finish: int]], newline: string) s: PresenterTarget, lines: seq[tuple[start, finish: int]],
newline: string)
{.raises: [YamlPresenterOutputError].} = {.raises: [YamlPresenterOutputError].} =
try: try:
s.write('|') s.append('|')
if scalar[^1] != '\l': s.write('-') if scalar[^1] != '\l': s.append('-')
if scalar[0] in [' ', '\t']: s.write($indentStep) if scalar[0] in [' ', '\t']: s.append($indentStep)
for line in lines: for line in lines:
s.write(newline) s.append(newline)
s.write(repeat(' ', indentation + indentStep)) s.append(repeat(' ', indentation + indentStep))
if line.finish >= line.start: if line.finish >= line.start:
s.write(scalar[line.start .. line.finish]) s.append(scalar[line.start .. line.finish])
except: except:
var e = newException(YamlPresenterOutputError, var e = newException(YamlPresenterOutputError,
"Error while writing to output stream") "Error while writing to output stream")
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
proc writeFolded(scalar: string, indentation, indentStep: int, s: Stream, proc writeFolded(scalar: string, indentation, indentStep: int,
words: seq[tuple[start, finish: int]], newline: string) s: PresenterTarget, words: seq[tuple[start, finish: int]],
newline: string)
{.raises: [YamlPresenterOutputError].} = {.raises: [YamlPresenterOutputError].} =
try: try:
s.write(">") s.append(">")
if scalar[^1] != '\l': s.write('-') if scalar[^1] != '\l': s.append('-')
if scalar[0] in [' ', '\t']: s.write($indentStep) if scalar[0] in [' ', '\t']: s.append($indentStep)
var curPos = 80 var curPos = 80
for word in words: for word in words:
if word.start > 0 and scalar[word.start - 1] == '\l': if word.start > 0 and scalar[word.start - 1] == '\l':
s.write(newline & newline) s.append(newline & newline)
s.write(repeat(' ', indentation + indentStep)) s.append(repeat(' ', indentation + indentStep))
curPos = indentation + indentStep curPos = indentation + indentStep
elif curPos + (word.finish - word.start) > 80: elif curPos + (word.finish - word.start) > 80:
s.write(newline) s.append(newline)
s.write(repeat(' ', indentation + indentStep)) s.append(repeat(' ', indentation + indentStep))
curPos = indentation + indentStep curPos = indentation + indentStep
else: else:
s.write(' ') s.append(' ')
curPos.inc() curPos.inc()
s.write(scalar[word.start .. word.finish]) s.append(scalar[word.start .. word.finish])
curPos += word.finish - word.start + 1 curPos += word.finish - word.start + 1
except: except:
var e = newException(YamlPresenterOutputError, var e = newException(YamlPresenterOutputError,
@ -205,82 +215,82 @@ proc writeFolded(scalar: string, indentation, indentStep: int, s: Stream,
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
template safeWrite(s: string or char) {.dirty.} = template safeWrite(target: PresenterTarget, s: string or char) =
try: target.write(s) try: target.append(s)
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
proc startItem(target: Stream, style: PresentationStyle, indentation: int, proc startItem(target: PresenterTarget, style: PresentationStyle,
state: var DumperState, isObject: bool, newline: string) indentation: int, state: var DumperState, isObject: bool,
{.raises: [YamlPresenterOutputError].} = newline: string) {.raises: [YamlPresenterOutputError].} =
try: try:
case state case state
of dBlockMapValue: of dBlockMapValue:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
if isObject or style == psCanonical: if isObject or style == psCanonical:
target.write("? ") target.append("? ")
state = dBlockExplicitMapKey state = dBlockExplicitMapKey
else: state = dBlockImplicitMapKey else: state = dBlockImplicitMapKey
of dBlockInlineMap: state = dBlockImplicitMapKey of dBlockInlineMap: state = dBlockImplicitMapKey
of dBlockExplicitMapKey: of dBlockExplicitMapKey:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
target.write(": ") target.append(": ")
state = dBlockMapValue state = dBlockMapValue
of dBlockImplicitMapKey: of dBlockImplicitMapKey:
target.write(": ") target.append(": ")
state = dBlockMapValue state = dBlockMapValue
of dFlowExplicitMapKey: of dFlowExplicitMapKey:
if style != psMinimal: if style != psMinimal:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
target.write(": ") target.append(": ")
state = dFlowMapValue state = dFlowMapValue
of dFlowMapValue: of dFlowMapValue:
if (isObject and style != psMinimal) or style in [psJson, psCanonical]: if (isObject and style != psMinimal) or style in [psJson, psCanonical]:
target.write(',' & newline & repeat(' ', indentation)) target.append(',' & newline & repeat(' ', indentation))
if style == psJson: state = dFlowImplicitMapKey if style == psJson: state = dFlowImplicitMapKey
else: else:
target.write("? ") target.append("? ")
state = dFlowExplicitMapKey state = dFlowExplicitMapKey
elif isObject and style == psMinimal: elif isObject and style == psMinimal:
target.write(", ? ") target.append(", ? ")
state = dFlowExplicitMapKey state = dFlowExplicitMapKey
else: else:
target.write(", ") target.append(", ")
state = dFlowImplicitMapKey state = dFlowImplicitMapKey
of dFlowMapStart: of dFlowMapStart:
if (isObject and style != psMinimal) or style in [psJson, psCanonical]: if (isObject and style != psMinimal) or style in [psJson, psCanonical]:
target.write(newline & repeat(' ', indentation)) target.append(newline & repeat(' ', indentation))
if style == psJson: state = dFlowImplicitMapKey if style == psJson: state = dFlowImplicitMapKey
else: else:
target.write("? ") target.append("? ")
state = dFlowExplicitMapKey state = dFlowExplicitMapKey
else: state = dFlowImplicitMapKey else: state = dFlowImplicitMapKey
of dFlowImplicitMapKey: of dFlowImplicitMapKey:
target.write(": ") target.append(": ")
state = dFlowMapValue state = dFlowMapValue
of dBlockSequenceItem: of dBlockSequenceItem:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
target.write("- ") target.append("- ")
of dFlowSequenceStart: of dFlowSequenceStart:
case style case style
of psMinimal, psDefault: discard of psMinimal, psDefault: discard
of psCanonical, psJson: of psCanonical, psJson:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
of psBlockOnly: discard # can never happen of psBlockOnly: discard # can never happen
state = dFlowSequenceItem state = dFlowSequenceItem
of dFlowSequenceItem: of dFlowSequenceItem:
case style case style
of psMinimal, psDefault: target.write(", ") of psMinimal, psDefault: target.append(", ")
of psCanonical, psJson: of psCanonical, psJson:
target.write(',' & newline) target.append(',' & newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
of psBlockOnly: discard # can never happen of psBlockOnly: discard # can never happen
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
@ -296,26 +306,27 @@ proc anchorName(a: AnchorId): string {.raises: [].} =
else: result.add(char(j + ord('0') - 26)) else: result.add(char(j + ord('0') - 26))
i -= 36 i -= 36
proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: TagLibrary, proc writeTagAndAnchor(target: PresenterTarget, tag: TagId,
tagLib: TagLibrary,
anchor: AnchorId) {.raises:[YamlPresenterOutputError].} = anchor: AnchorId) {.raises:[YamlPresenterOutputError].} =
try: try:
if tag notin [yTagQuestionMark, yTagExclamationMark]: if tag notin [yTagQuestionMark, yTagExclamationMark]:
let tagUri = tagLib.uri(tag) let tagUri = tagLib.uri(tag)
if tagUri.startsWith(tagLib.secondaryPrefix): if tagUri.startsWith(tagLib.secondaryPrefix):
target.write("!!") target.append("!!")
target.write(tagUri[18..tagUri.high]) target.append(tagUri[18..tagUri.high])
target.write(' ') target.append(' ')
elif tagUri.startsWith("!"): elif tagUri.startsWith("!"):
target.write(tagUri) target.append(tagUri)
target.write(' ') target.append(' ')
else: else:
target.write("!<") target.append("!<")
target.write(tagUri) target.append(tagUri)
target.write("> ") target.append("> ")
if anchor != yAnchorNone: if anchor != yAnchorNone:
target.write("&") target.append("&")
target.write(anchorName(anchor)) target.append(anchorName(anchor))
target.write(' ') target.append(' ')
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
@ -329,7 +340,8 @@ proc nextItem(c: var Queue, s: var YamlStream):
else: else:
result = s.next() result = s.next()
proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary, proc doPresent(s: var YamlStream, target: PresenterTarget,
tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions) = options: PresentationOptions = defaultPresentationOptions) =
var var
indentation = 0 indentation = 0
@ -345,19 +357,19 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
# TODO: tag directives # TODO: tag directives
try: try:
case options.outputVersion case options.outputVersion
of ov1_2: target.write("%YAML 1.2" & newline) of ov1_2: target.append("%YAML 1.2" & newline)
of ov1_1: target.write("%YAML 1.1" & newLine) of ov1_1: target.append("%YAML 1.1" & newLine)
of ovNone: discard of ovNone: discard
if tagLib.secondaryPrefix != yamlTagRepositoryPrefix: if tagLib.secondaryPrefix != yamlTagRepositoryPrefix:
target.write("%TAG !! " & tagLib.secondaryPrefix & newline) target.append("%TAG !! " & tagLib.secondaryPrefix & newline)
target.write("--- ") target.append("--- ")
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
raise e raise e
of yamlScalar: of yamlScalar:
if levels.len == 0: if levels.len == 0:
if options.style != psJson: safeWrite(newline) if options.style != psJson: target.safeWrite(newline)
else: else:
startItem(target, options.style, indentation, startItem(target, options.style, indentation,
levels[levels.high], false, newline) levels[levels.high], false, newline)
@ -368,22 +380,22 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
let hint = guessType(item.scalarContent) let hint = guessType(item.scalarContent)
if item.scalarTag in [yTagQuestionMark, yTagBoolean] and if item.scalarTag in [yTagQuestionMark, yTagBoolean] and
hint in {yTypeBoolTrue, yTypeBoolFalse}: hint in {yTypeBoolTrue, yTypeBoolFalse}:
safeWrite(if hint == yTypeBoolTrue: "true" else: "false") target.safeWrite(if hint == yTypeBoolTrue: "true" else: "false")
elif item.scalarTag in [yTagQuestionMark, yTagNull] and elif item.scalarTag in [yTagQuestionMark, yTagNull] and
hint == yTypeNull: hint == yTypeNull:
safeWrite("null") target.safeWrite("null")
elif item.scalarTag in [yTagQuestionMark, yTagInteger, elif item.scalarTag in [yTagQuestionMark, yTagInteger,
yTagNimInt8, yTagNimInt16, yTagNimInt32, yTagNimInt64, yTagNimInt8, yTagNimInt16, yTagNimInt32, yTagNimInt64,
yTagNimUInt8, yTagNimUInt16, yTagNimUInt32, yTagNimUInt64] and yTagNimUInt8, yTagNimUInt16, yTagNimUInt32, yTagNimUInt64] and
hint == yTypeInteger: hint == yTypeInteger:
safeWrite(item.scalarContent) target.safeWrite(item.scalarContent)
elif item.scalarTag in [yTagQuestionMark, yTagFloat, yTagNimFloat32, elif item.scalarTag in [yTagQuestionMark, yTagFloat, yTagNimFloat32,
yTagNimFloat64] and hint in {yTypeFloatInf, yTypeFloatNaN}: yTagNimFloat64] and hint in {yTypeFloatInf, yTypeFloatNaN}:
raise newException(YamlPresenterJsonError, raise newException(YamlPresenterJsonError,
"Infinity and not-a-number values cannot be presented as JSON!") "Infinity and not-a-number values cannot be presented as JSON!")
elif item.scalarTag in [yTagQuestionMark, yTagFloat] and elif item.scalarTag in [yTagQuestionMark, yTagFloat] and
hint == yTypeFloat: hint == yTypeFloat:
safeWrite(item.scalarContent) target.safeWrite(item.scalarContent)
else: writeDoubleQuotedJson(item.scalarContent, target) else: writeDoubleQuotedJson(item.scalarContent, target)
elif options.style == psCanonical: elif options.style == psCanonical:
writeDoubleQuoted(item.scalarContent, target, writeDoubleQuoted(item.scalarContent, target,
@ -396,7 +408,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
options.indentationStep, target, lines, newline) options.indentationStep, target, lines, newline)
of sFolded: writeFolded(item.scalarContent, indentation, of sFolded: writeFolded(item.scalarContent, indentation,
options.indentationStep, target, words, newline) options.indentationStep, target, words, newline)
of sPlain: safeWrite(item.scalarContent) of sPlain: target.safeWrite(item.scalarContent)
of sDoubleQuoted: writeDoubleQuoted(item.scalarContent, target, of sDoubleQuoted: writeDoubleQuoted(item.scalarContent, target,
indentation + options.indentationStep, newline) indentation + options.indentationStep, newline)
of yamlAlias: of yamlAlias:
@ -407,8 +419,8 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
startItem(target, options.style, indentation, levels[levels.high], startItem(target, options.style, indentation, levels[levels.high],
false, newline) false, newline)
try: try:
target.write('*') target.append('*')
target.write(cast[byte]('a') + cast[byte](item.aliasTarget)) target.append(char(byte('a') + byte(item.aliasTarget)))
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
@ -445,7 +457,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
if options.style != psJson: if options.style != psJson:
writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor) writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor)
of dFlowSequenceStart: of dFlowSequenceStart:
safeWrite(newline) target.safeWrite(newline)
if options.style != psJson: if options.style != psJson:
writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor) writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor)
indentation += options.indentationStep indentation += options.indentationStep
@ -457,7 +469,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor) writeTagAndAnchor(target, item.seqTag, tagLib, item.seqAnchor)
indentation += options.indentationStep indentation += options.indentationStep
if nextState == dFlowSequenceStart: safeWrite('[') if nextState == dFlowSequenceStart: target.safeWrite('[')
if levels.len > 0 and options.style in [psJson, psCanonical] and if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue, levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]: dBlockImplicitMapKey, dBlockSequenceItem]:
@ -496,11 +508,11 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor) writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor)
else: else:
if options.style != psJson: if options.style != psJson:
safeWrite(newline) target.safeWrite(newline)
writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor) writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor)
indentation += options.indentationStep indentation += options.indentationStep
of dFlowMapStart: of dFlowMapStart:
safeWrite(newline) target.safeWrite(newline)
if options.style != psJson: if options.style != psJson:
writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor) writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor)
indentation += options.indentationStep indentation += options.indentationStep
@ -519,7 +531,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor) writeTagAndAnchor(target, item.mapTag, tagLib, item.mapAnchor)
indentation += options.indentationStep indentation += options.indentationStep
if nextState == dFlowMapStart: safeWrite('{') if nextState == dFlowMapStart: target.safeWrite('{')
if levels.len > 0 and options.style in [psJson, psCanonical] and if levels.len > 0 and options.style in [psJson, psCanonical] and
levels[levels.high] in levels[levels.high] in
[dBlockExplicitMapKey, dBlockMapValue, [dBlockExplicitMapKey, dBlockMapValue,
@ -532,13 +544,13 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
case levels.pop() case levels.pop()
of dFlowSequenceItem: of dFlowSequenceItem:
case options.style case options.style
of psDefault, psMinimal, psBlockOnly: safeWrite(']') of psDefault, psMinimal, psBlockOnly: target.safeWrite(']')
of psJson, psCanonical: of psJson, psCanonical:
indentation -= options.indentationStep indentation -= options.indentationStep
try: try:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
target.write(']') target.append(']')
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
@ -552,7 +564,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue, levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]: dBlockImplicitMapKey, dBlockSequenceItem]:
indentation -= options.indentationStep indentation -= options.indentationStep
safeWrite(']') target.safeWrite(']')
of dBlockSequenceItem: discard of dBlockSequenceItem: discard
else: internalError("Invalid popped level") else: internalError("Invalid popped level")
indentation -= options.indentationStep indentation -= options.indentationStep
@ -562,13 +574,13 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
case level case level
of dFlowMapValue: of dFlowMapValue:
case options.style case options.style
of psDefault, psMinimal, psBlockOnly: safeWrite('}') of psDefault, psMinimal, psBlockOnly: target.safeWrite('}')
of psJson, psCanonical: of psJson, psCanonical:
indentation -= options.indentationStep indentation -= options.indentationStep
try: try:
target.write(newline) target.append(newline)
target.write(repeat(' ', indentation)) target.append(repeat(' ', indentation))
target.write('}') target.append('}')
except: except:
var e = newException(YamlPresenterOutputError, "") var e = newException(YamlPresenterOutputError, "")
e.parent = getCurrentException() e.parent = getCurrentException()
@ -582,7 +594,7 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue, levels[levels.high] in [dBlockExplicitMapKey, dBlockMapValue,
dBlockImplicitMapKey, dBlockSequenceItem]: dBlockImplicitMapKey, dBlockSequenceItem]:
indentation -= options.indentationStep indentation -= options.indentationStep
safeWrite('}') target.safeWrite('}')
of dBlockMapValue, dBlockInlineMap: discard of dBlockMapValue, dBlockInlineMap: discard
else: internalError("Invalid level: " & $level) else: internalError("Invalid level: " & $level)
indentation -= options.indentationStep indentation -= options.indentationStep
@ -591,9 +603,20 @@ proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
if options.style == psJson: if options.style == psJson:
raise newException(YamlPresenterJsonError, raise newException(YamlPresenterJsonError,
"Cannot output more than one document in JSON style") "Cannot output more than one document in JSON style")
safeWrite("..." & newline) target.safeWrite("..." & newline)
proc transform*(input: Stream, output: Stream, proc present*(s: var YamlStream, target: Stream,
tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions) =
doPresent(s, target, tagLib, options)
proc present*(s: var YamlStream, tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions):
string =
result = ""
doPresent(s, addr result, tagLib, options)
proc doTransform(input: Stream | string, output: PresenterTarget,
options: PresentationOptions = defaultPresentationOptions) = options: PresentationOptions = defaultPresentationOptions) =
var var
taglib = initExtendedTagLibrary() taglib = initExtendedTagLibrary()
@ -633,3 +656,13 @@ proc transform*(input: Stream, output: Stream,
if e.parent of IOError: raise (ref IOError)(e.parent) if e.parent of IOError: raise (ref IOError)(e.parent)
elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent) elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent)
else: internalError("Unexpected exception: " & e.parent.repr) else: internalError("Unexpected exception: " & e.parent.repr)
proc transform*(input: Stream | string, output: Stream,
options: PresentationOptions = defaultPresentationOptions) =
doTransform(input, output, options)
proc transform*(input: Stream | string,
options: PresentationOptions = defaultPresentationOptions):
string =
result = ""
doTransform(input, addr result, options)

View File

@ -809,6 +809,9 @@ proc dump*[K](value: K, tagStyle: TagStyle = tsRootOnly,
anchorStyle: AnchorStyle = asTidy, anchorStyle: AnchorStyle = asTidy,
options: PresentationOptions = defaultPresentationOptions): options: PresentationOptions = defaultPresentationOptions):
string = string =
var s = newStringStream() var events = represent(value,
dump(value, s, tagStyle, anchorStyle, options) if options.style == psCanonical: tsAll else: tagStyle,
shallowCopy(result, s.data) if options.style == psJson: asNone else: anchorStyle)
try: result = present(events, serializationTagLibrary, options)
except YamlStreamError:
internalError("Unexpected exception: " & getCurrentException().repr)

View File

@ -91,14 +91,13 @@ suite "Serialization":
test "Dump integer without fixed length": test "Dump integer without fixed length":
var input = -4247 var input = -4247
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n\"-4247\"", output
assertStringEqual "%YAML 1.2\n--- \n\"-4247\"", output.data
when sizeof(int) == sizeof(int64): when sizeof(int) == sizeof(int64):
input = int(int32.high) + 1 input = int(int32.high) + 1
var gotException = false var gotException = false
try: dump(input, output, tsNone, asTidy, blockOnly) try: output = dump(input, tsNone, asTidy, blockOnly)
except: gotException = true except: gotException = true
assert gotException, "Expected exception, got none." assert gotException, "Expected exception, got none."
@ -110,9 +109,8 @@ suite "Serialization":
test "Dump nil string": test "Dump nil string":
let input: string = nil let input: string = nil
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n!nim:nil:string \"\"", output
assertStringEqual "%YAML 1.2\n--- \n!nim:nil:string \"\"", output.data
test "Load string sequence": test "Load string sequence":
let input = newStringStream(" - a\n - b") let input = newStringStream(" - a\n - b")
@ -124,9 +122,8 @@ suite "Serialization":
test "Dump string sequence": test "Dump string sequence":
var input = @["a", "b"] var input = @["a", "b"]
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output
assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output.data
test "Load nil seq": test "Load nil seq":
let input = newStringStream("!nim:nil:seq \"\"") let input = newStringStream("!nim:nil:seq \"\"")
@ -136,9 +133,8 @@ suite "Serialization":
test "Dump nil seq": test "Dump nil seq":
let input: seq[int] = nil let input: seq[int] = nil
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n!nim:nil:seq \"\"", output
assertStringEqual "%YAML 1.2\n--- \n!nim:nil:seq \"\"", output.data
test "Load char set": test "Load char set":
let input = newStringStream("- a\n- b") let input = newStringStream("- a\n- b")
@ -150,9 +146,8 @@ suite "Serialization":
test "Dump char set": test "Dump char set":
var input = {'a', 'b'} var input = {'a', 'b'}
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output
assertStringEqual "%YAML 1.2\n--- \n- a\n- b", output.data
test "Load array": test "Load array":
let input = newStringStream("- 23\n- 42\n- 47") let input = newStringStream("- 23\n- 42\n- 47")
@ -164,9 +159,8 @@ suite "Serialization":
test "Dump array": test "Dump array":
let input = [23'i32, 42'i32, 47'i32] let input = [23'i32, 42'i32, 47'i32]
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n- 23\n- 42\n- 47", output
assertStringEqual "%YAML 1.2\n--- \n- 23\n- 42\n- 47", output.data
test "Load Table[int, string]": test "Load Table[int, string]":
let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig") let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig")
@ -180,10 +174,9 @@ suite "Serialization":
var input = initTable[int32, string]() var input = initTable[int32, string]()
input[23] = "dreiundzwanzig" input[23] = "dreiundzwanzig"
input[42] = "zweiundvierzig" input[42] = "zweiundvierzig"
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual("%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig", assertStringEqual("%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig",
output.data) output)
test "Load OrderedTable[tuple[int32, int32], string]": test "Load OrderedTable[tuple[int32, int32], string]":
let input = newStringStream("- {a: 23, b: 42}: drzw\n- {a: 13, b: 47}: drsi") let input = newStringStream("- {a: 23, b: 42}: drzw\n- {a: 13, b: 47}: drsi")
@ -205,8 +198,7 @@ suite "Serialization":
var input = initOrderedTable[tuple[a, b: int32], string]() var input = initOrderedTable[tuple[a, b: int32], string]()
input.add((a: 23'i32, b: 42'i32), "dreiundzwanzigzweiundvierzig") input.add((a: 23'i32, b: 42'i32), "dreiundzwanzigzweiundvierzig")
input.add((a: 13'i32, b: 47'i32), "dreizehnsiebenundvierzig") input.add((a: 13'i32, b: 47'i32), "dreizehnsiebenundvierzig")
var output = newStringStream() var output = dump(input, tsRootOnly, asTidy, blockOnly)
dump(input, output, tsRootOnly, asTidy, blockOnly)
assertStringEqual("""%YAML 1.2 assertStringEqual("""%YAML 1.2
--- !nim:tables:OrderedTable(nim:tuple(nim:system:int32,nim:system:int32),tag:yaml.org,2002:str) --- !nim:tables:OrderedTable(nim:tuple(nim:system:int32,nim:system:int32),tag:yaml.org,2002:str)
- -
@ -218,7 +210,7 @@ suite "Serialization":
? ?
a: 13 a: 13
b: 47 b: 47
: dreizehnsiebenundvierzig""", output.data) : dreizehnsiebenundvierzig""", output)
test "Load Sequences in Sequence": test "Load Sequences in Sequence":
let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]") let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]")
@ -231,13 +223,12 @@ suite "Serialization":
test "Dump Sequences in Sequence": test "Dump Sequences in Sequence":
let input = @[@[1.int32, 2.int32, 3.int32], @[4.int32, 5.int32], @[6.int32]] let input = @[@[1.int32, 2.int32, 3.int32], @[4.int32, 5.int32], @[6.int32]]
var output = newStringStream() var output = dump(input, tsNone)
dump(input, output, tsNone) assertStringEqual "%YAML 1.2\n--- \n- [1, 2, 3]\n- [4, 5]\n- [6]", output
assertStringEqual "%YAML 1.2\n--- \n- [1, 2, 3]\n- [4, 5]\n- [6]",
output.data
test "Load Enum": test "Load Enum":
let input = newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow") let input =
newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow")
var result: seq[TrafficLight] var result: seq[TrafficLight]
load(input, result) load(input, result)
assert result.len == 3 assert result.len == 3
@ -247,10 +238,8 @@ suite "Serialization":
test "Dump Enum": test "Dump Enum":
let input = @[tlRed, tlGreen, tlYellow] let input = @[tlRed, tlGreen, tlYellow]
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly) assertStringEqual "%YAML 1.2\n--- \n- tlRed\n- tlGreen\n- tlYellow", output
assertStringEqual "%YAML 1.2\n--- \n- tlRed\n- tlGreen\n- tlYellow",
output.data
test "Load Tuple": test "Load Tuple":
let input = newStringStream("str: value\ni: 42\nb: true") let input = newStringStream("str: value\ni: 42\nb: true")
@ -262,9 +251,8 @@ suite "Serialization":
test "Dump Tuple": test "Dump Tuple":
let input = (str: "value", i: 42.int32, b: true) let input = (str: "value", i: 42.int32, b: true)
var output = newStringStream() var output = dump(input, tsNone)
dump(input, output, tsNone) assertStringEqual "%YAML 1.2\n--- \nstr: value\ni: 42\nb: y", output
assertStringEqual "%YAML 1.2\n--- \nstr: value\ni: 42\nb: y", output.data
test "Load custom object": test "Load custom object":
let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12") let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12")
@ -276,10 +264,9 @@ suite "Serialization":
test "Dump custom object": test "Dump custom object":
let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) let input = Person(firstnamechar: 'P', surname: "Pan", age: 12)
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual( assertStringEqual(
"%YAML 1.2\n--- \nfirstnamechar: P\nsurname: Pan\nage: 12", output.data) "%YAML 1.2\n--- \nfirstnamechar: P\nsurname: Pan\nage: 12", output)
test "Serialization: Load sequence with explicit tags": test "Serialization: Load sequence with explicit tags":
let input = newStringStream("--- !nim:system:seq(" & let input = newStringStream("--- !nim:system:seq(" &
@ -291,10 +278,9 @@ suite "Serialization":
test "Dump sequence with explicit tags": test "Dump sequence with explicit tags":
let input = @["one", "two"] let input = @["one", "two"]
var output = newStringStream() var output = dump(input, tsAll, asTidy, blockOnly)
dump(input, output, tsAll, asTidy, blockOnly)
assertStringEqual("%YAML 1.2\n--- !nim:system:seq(" & assertStringEqual("%YAML 1.2\n--- !nim:system:seq(" &
"tag:yaml.org,2002:str) \n- !!str one\n- !!str two", output.data) "tag:yaml.org,2002:str) \n- !!str one\n- !!str two", output)
test "Load custom object with explicit root tag": test "Load custom object with explicit root tag":
let input = newStringStream( let input = newStringStream(
@ -307,11 +293,10 @@ suite "Serialization":
test "Dump custom object with explicit root tag": test "Dump custom object with explicit root tag":
let input = Person(firstnamechar: 'P', surname: "Pan", age: 12) let input = Person(firstnamechar: 'P', surname: "Pan", age: 12)
var output = newStringStream() var output = dump(input, tsRootOnly, asTidy, blockOnly)
dump(input, output, tsRootOnly, asTidy, blockOnly)
assertStringEqual("%YAML 1.2\n" & assertStringEqual("%YAML 1.2\n" &
"--- !nim:custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12", "--- !nim:custom:Person \nfirstnamechar: P\nsurname: Pan\nage: 12",
output.data) output)
test "Load custom variant object": test "Load custom variant object":
let input = newStringStream( let input = newStringStream(
@ -330,8 +315,7 @@ suite "Serialization":
test "Dump custom variant object": test "Dump custom variant object":
let input = @[Animal(name: "Bastet", kind: akCat, purringIntensity: 7), let input = @[Animal(name: "Bastet", kind: akCat, purringIntensity: 7),
Animal(name: "Anubis", kind: akDog, barkometer: 13)] Animal(name: "Anubis", kind: akDog, barkometer: 13)]
var output = newStringStream() var output = dump(input, tsNone, asTidy, blockOnly)
dump(input, output, tsNone, asTidy, blockOnly)
assertStringEqual """%YAML 1.2 assertStringEqual """%YAML 1.2
--- ---
- -
@ -347,7 +331,7 @@ suite "Serialization":
- -
kind: akDog kind: akDog
- -
barkometer: 13""", output.data barkometer: 13""", output
test "Dump cyclic data structure": test "Dump cyclic data structure":
var var
@ -357,8 +341,7 @@ suite "Serialization":
a.next = b a.next = b
b.next = c b.next = c
c.next = a c.next = a
var output = newStringStream() var output = dump(a, tsRootOnly, asTidy, blockOnly)
dump(a, output, tsRootOnly, asTidy, blockOnly)
assertStringEqual """%YAML 1.2 assertStringEqual """%YAML 1.2
--- !example.net:Node &a --- !example.net:Node &a
value: a value: a
@ -366,7 +349,7 @@ next:
value: b value: b
next: next:
value: c value: c
next: *a""", output.data next: *a""", output
test "Load cyclic data structure": test "Load cyclic data structure":
let input = newStringStream("""%YAML 1.2 let input = newStringStream("""%YAML 1.2
@ -416,11 +399,10 @@ next:
input.add(nil) input.add(nil)
input.add(new string) input.add(new string)
input[1][] = "~" input[1][] = "~"
var output = newStringStream() var output = dump(input, tsRootOnly, asTidy, blockOnly)
dump(input, output, tsRootOnly, asTidy, blockOnly)
assertStringEqual( assertStringEqual(
"%YAML 1.2\n--- !nim:system:seq(tag:yaml.org,2002:str) \n- !!null ~\n- !!str ~", "%YAML 1.2\n--- !nim:system:seq(tag:yaml.org,2002:str) \n- !!null ~\n- !!str ~",
output.data) output)
test "Custom constructObject": test "Custom constructObject":
let input = newStringStream("- 1\n- !test:BetterInt 2") let input = newStringStream("- 1\n- !test:BetterInt 2")
@ -432,10 +414,9 @@ next:
test "Custom representObject": test "Custom representObject":
let input = @[1.BetterInt, 9998887.BetterInt, 98312.BetterInt] let input = @[1.BetterInt, 9998887.BetterInt, 98312.BetterInt]
var output = newStringStream() var output = dump(input, tsAll, asTidy, blockOnly)
dump(input, output, tsAll, asTidy, blockOnly)
assertStringEqual """%YAML 1.2 assertStringEqual """%YAML 1.2
--- !nim:system:seq(test:BetterInt) --- !nim:system:seq(test:BetterInt)
- !test:BetterInt 1 - !test:BetterInt 1
- !test:BetterInt 9_998_887 - !test:BetterInt 9_998_887
- !test:BetterInt 98_312""", output.data - !test:BetterInt 98_312""", output

View File

@ -554,13 +554,20 @@ proc loadToJson*(s: Stream): seq[JsonNode] {.raises: [YamlParserError].}
## `constructJson <#constructJson>`_ to construct an in-memory JSON tree ## `constructJson <#constructJson>`_ to construct an in-memory JSON tree
## from a YAML character stream. ## from a YAML character stream.
proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary, proc present*(s: var YamlStream, target: Stream,
tagLib: TagLibrary,
options: PresentationOptions = defaultPresentationOptions) options: PresentationOptions = defaultPresentationOptions)
{.raises: [YamlPresenterJsonError, YamlPresenterOutputError, {.raises: [YamlPresenterJsonError, YamlPresenterOutputError,
YamlStreamError].} YamlStreamError].}
## Convert ``s`` to a YAML character stream and write it to ``target``. ## Convert ``s`` to a YAML character stream and write it to ``target``.
proc transform*(input: Stream, output: Stream, 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.
proc transform*(input: Stream | string, output: Stream,
options: PresentationOptions = defaultPresentationOptions) options: PresentationOptions = defaultPresentationOptions)
{.raises: [IOError, YamlParserError, YamlPresenterJsonError, {.raises: [IOError, YamlParserError, YamlPresenterJsonError,
YamlPresenterOutputError].} YamlPresenterOutputError].}
@ -568,6 +575,14 @@ proc transform*(input: Stream, output: Stream,
## while resolving non-specific tags to the ones in the YAML core tag ## while resolving non-specific tags to the ones in the YAML core tag
## library. ## library.
proc transform*(input: Stream | string,
options: PresentationOptions = defaultPresentationOptions):
string {.raises: [IOError, YamlParserError, YamlPresenterJsonError,
YamlPresenterOutputError].}
## Parser ``input`` as YAML character stream, resolves non-specific tags to
## the ones in the YAML core tag library, and then returns a serialized
## YAML string that represents the stream.
proc constructChild*[T](s: var YamlStream, c: ConstructionContext, proc constructChild*[T](s: var YamlStream, c: ConstructionContext,
result: var T) result: var T)
{.raises: [YamlConstructionError, YamlStreamError].} {.raises: [YamlConstructionError, YamlStreamError].}