2015-12-28 21:24:05 +00:00
|
|
|
# NimYAML - YAML implementation in Nim
|
|
|
|
# (c) Copyright 2015 Felix Krause
|
|
|
|
#
|
|
|
|
# See the file "copying.txt", included in this
|
|
|
|
# distribution, for details about the copyright.
|
|
|
|
|
2016-01-24 20:38:29 +00:00
|
|
|
import typetraits
|
|
|
|
|
2015-12-27 15:36:32 +00:00
|
|
|
type
|
|
|
|
DumperState = enum
|
2016-01-11 18:12:55 +00:00
|
|
|
dBlockExplicitMapKey, dBlockImplicitMapKey, dBlockMapValue,
|
2016-01-11 18:53:19 +00:00
|
|
|
dBlockInlineMap, dBlockSequenceItem, dFlowImplicitMapKey, dFlowMapValue,
|
|
|
|
dFlowExplicitMapKey, dFlowSequenceItem, dFlowMapStart,
|
|
|
|
dFlowSequenceStart
|
2016-02-25 20:04:44 +00:00
|
|
|
|
|
|
|
ScalarStyle = enum
|
|
|
|
sLiteral, sFolded, sPlain, sDoubleQuoted
|
2015-12-27 15:36:32 +00:00
|
|
|
|
2016-02-26 20:55:59 +00:00
|
|
|
proc defineOptions*(style: PresentationStyle = psDefault,
|
|
|
|
indentationStep: int = 2, newlines:
|
|
|
|
NewLineStyle = nlOSDefault): PresentationOptions =
|
|
|
|
result = PresentationOptions(style: style, indentationStep: indentationStep,
|
|
|
|
newlines: newlines)
|
|
|
|
|
2016-02-25 20:04:44 +00:00
|
|
|
proc inspect(scalar: string, indentation: int,
|
|
|
|
words, lines: var seq[tuple[start, finish: int]]):
|
|
|
|
ScalarStyle {.raises: [].} =
|
|
|
|
var
|
|
|
|
inLine = false
|
|
|
|
inWord = false
|
|
|
|
multipleSpaces = true
|
|
|
|
curWord, curLine: tuple[start, finish: int]
|
|
|
|
canUseFolded = true
|
|
|
|
canUseLiteral = true
|
|
|
|
canUsePlain = scalar.len > 0 and scalar[0] notin {'@', '`'}
|
|
|
|
for i, c in scalar:
|
|
|
|
case c
|
|
|
|
of ' ':
|
|
|
|
if inWord:
|
|
|
|
if not multipleSpaces:
|
|
|
|
curWord.finish = i - 1
|
|
|
|
inWord = false
|
|
|
|
else:
|
|
|
|
multipleSpaces = true
|
|
|
|
inWord = true
|
|
|
|
if not inLine:
|
|
|
|
inLine = true
|
|
|
|
curLine.start = i
|
|
|
|
# space at beginning of line will preserve previous and next
|
|
|
|
# linebreak. that is currently too complex to handle.
|
|
|
|
canUseFolded = false
|
2016-02-26 18:28:28 +00:00
|
|
|
of '\l':
|
2016-02-25 20:04:44 +00:00
|
|
|
canUsePlain = false # we don't use multiline plain scalars
|
|
|
|
curWord.finish = i - 1
|
|
|
|
if curWord.finish - curWord.start + 1 > 80 - indentation:
|
|
|
|
return if canUsePlain: sPlain else: sDoubleQuoted
|
|
|
|
words.add(curWord)
|
|
|
|
inWord = false
|
|
|
|
curWord.start = i + 1
|
|
|
|
multipleSpaces = true
|
|
|
|
if not inLine: curLine.start = i
|
|
|
|
inLine = false
|
|
|
|
curLine.finish = i - 1
|
|
|
|
if curLine.start - curLine.finish + 1 > 80 - indentation:
|
|
|
|
canUseLiteral = false
|
|
|
|
lines.add(curLine)
|
|
|
|
else:
|
|
|
|
if c in {'{', '}', '[', ']', ',', '#', '-', ':', '?', '%', '"',
|
|
|
|
'\''} or c.ord < 32: canUsePlain = false
|
|
|
|
if not inLine:
|
|
|
|
curLine.start = i
|
|
|
|
inLine = true
|
|
|
|
if not inWord:
|
|
|
|
if not multipleSpaces:
|
|
|
|
if curWord.finish - curWord.start + 1 > 80 - indentation:
|
|
|
|
return if canUsePlain: sPlain else: sDoubleQuoted
|
|
|
|
words.add(curWord)
|
|
|
|
curWord.start = i
|
|
|
|
inWord = true
|
|
|
|
multipleSpaces = false
|
|
|
|
if inWord:
|
|
|
|
curWord.finish = scalar.len - 1
|
|
|
|
if curWord.finish - curWord.start + 1 > 80 - indentation:
|
|
|
|
return if canUsePlain: sPlain else: sDoubleQuoted
|
|
|
|
words.add(curWord)
|
|
|
|
if inLine:
|
|
|
|
curLine.finish = scalar.len - 1
|
|
|
|
if curLine.finish - curLine.start + 1 > 80 - indentation:
|
|
|
|
canUseLiteral = false
|
|
|
|
lines.add(curLine)
|
|
|
|
if scalar.len <= 80 - indentation:
|
|
|
|
result = if canUsePlain: sPlain else: sDoubleQuoted
|
|
|
|
elif canUseLiteral: result = sLiteral
|
|
|
|
elif canUseFolded: result = sFolded
|
|
|
|
elif canUsePlain: result = sPlain
|
|
|
|
else: result = sDoubleQuoted
|
|
|
|
|
2016-02-26 20:55:59 +00:00
|
|
|
proc writeDoubleQuoted(scalar: string, s: Stream, indentation: int,
|
|
|
|
newline: string)
|
2016-01-05 18:58:46 +00:00
|
|
|
{.raises: [YamlPresenterOutputError].} =
|
2016-02-25 21:32:50 +00:00
|
|
|
var curPos = indentation
|
|
|
|
try:
|
|
|
|
s.write('"')
|
|
|
|
curPos.inc()
|
|
|
|
for c in scalar:
|
|
|
|
if curPos == 79:
|
2016-02-26 20:55:59 +00:00
|
|
|
s.write('\\')
|
|
|
|
s.write(newline)
|
2016-02-25 21:32:50 +00:00
|
|
|
s.write(repeat(' ', indentation))
|
|
|
|
curPos = indentation
|
|
|
|
if c == ' ':
|
|
|
|
s.write('\\')
|
|
|
|
curPos.inc()
|
|
|
|
case c
|
|
|
|
of '"':
|
|
|
|
s.write("\\\"")
|
|
|
|
curPos.inc(2)
|
2016-02-26 18:28:28 +00:00
|
|
|
of '\l':
|
2016-02-25 21:32:50 +00:00
|
|
|
s.write("\\n")
|
|
|
|
curPos.inc(2)
|
|
|
|
of '\t':
|
|
|
|
s.write("\\t")
|
|
|
|
curPos.inc(2)
|
|
|
|
of '\\':
|
|
|
|
s.write("\\\\")
|
|
|
|
curPos.inc(2)
|
|
|
|
else:
|
|
|
|
if ord(c) < 32:
|
|
|
|
s.write("\\x" & toHex(ord(c), 2))
|
|
|
|
curPos.inc(4)
|
|
|
|
else:
|
|
|
|
s.write(c)
|
|
|
|
curPos.inc()
|
|
|
|
s.write('"')
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError,
|
|
|
|
"Error while writing to output stream")
|
|
|
|
e.parent = getCurrentException()
|
|
|
|
raise e
|
|
|
|
|
|
|
|
proc writeDoubleQuotedJson(scalar: string, s: Stream)
|
|
|
|
{.raises: [YamlPresenterOutputError].} =
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
|
|
|
s.write('"')
|
|
|
|
for c in scalar:
|
2016-02-25 20:04:44 +00:00
|
|
|
case c
|
|
|
|
of '"': s.write("\\\"")
|
2016-02-25 21:32:50 +00:00
|
|
|
of '\\': s.write("\\\\")
|
2016-02-26 18:28:28 +00:00
|
|
|
of '\l': s.write("\\n")
|
2016-02-25 21:32:50 +00:00
|
|
|
of '\t': s.write("\\t")
|
|
|
|
of '\f': s.write("\\f")
|
|
|
|
of '\b': s.write("\\b")
|
|
|
|
else:
|
|
|
|
if ord(c) < 32:
|
|
|
|
s.write("\\u" & toHex(ord(c), 4))
|
|
|
|
else:
|
|
|
|
s.write(c)
|
2016-01-14 18:00:03 +00:00
|
|
|
s.write('"')
|
2016-01-05 18:58:46 +00:00
|
|
|
except:
|
2016-02-25 20:04:44 +00:00
|
|
|
var e = newException(YamlPresenterOutputError,
|
|
|
|
"Error while writing to output stream")
|
|
|
|
e.parent = getCurrentException()
|
|
|
|
raise e
|
|
|
|
|
|
|
|
proc writeLiteral(scalar: string, indentation, indentStep: int, s: Stream,
|
2016-02-26 20:55:59 +00:00
|
|
|
lines: seq[tuple[start, finish: int]], newline: string)
|
2016-02-25 20:04:44 +00:00
|
|
|
{.raises: [YamlPresenterOutputError].} =
|
|
|
|
try:
|
|
|
|
s.write('|')
|
2016-02-26 18:28:28 +00:00
|
|
|
if scalar[^1] != '\l': s.write('-')
|
2016-02-25 20:04:44 +00:00
|
|
|
if scalar[0] in [' ', '\t']: s.write($indentStep)
|
|
|
|
for line in lines:
|
2016-02-26 20:55:59 +00:00
|
|
|
s.write(newline)
|
2016-02-25 20:04:44 +00:00
|
|
|
s.write(repeat(' ', indentation + indentStep))
|
|
|
|
if line.finish >= line.start:
|
|
|
|
s.write(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: Stream,
|
2016-02-26 20:55:59 +00:00
|
|
|
words: seq[tuple[start, finish: int]], newline: string)
|
2016-02-25 20:04:44 +00:00
|
|
|
{.raises: [YamlPresenterOutputError].} =
|
|
|
|
try:
|
|
|
|
s.write("|-")
|
|
|
|
var curPos = 80
|
|
|
|
for word in words:
|
2016-02-26 18:28:28 +00:00
|
|
|
if word.start > 0 and scalar[word.start - 1] == '\l':
|
2016-02-26 20:55:59 +00:00
|
|
|
s.write(newline & newline)
|
2016-02-25 20:04:44 +00:00
|
|
|
s.write(repeat(' ', indentation + indentStep))
|
|
|
|
curPos = indentation + indentStep
|
|
|
|
elif curPos + (word.finish - word.start) > 80:
|
2016-02-26 20:55:59 +00:00
|
|
|
s.write(newline)
|
2016-02-25 20:04:44 +00:00
|
|
|
s.write(repeat(' ', indentation + indentStep))
|
|
|
|
curPos = indentation + indentStep
|
|
|
|
else:
|
|
|
|
s.write(' ')
|
|
|
|
curPos.inc()
|
|
|
|
s.write(scalar[word.start .. word.finish])
|
|
|
|
curPos += word.finish - word.start + 1
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError,
|
|
|
|
"Error while writing to output stream")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
|
|
|
|
|
|
|
template safeWrite(s: string or char) {.dirty.} =
|
|
|
|
try:
|
|
|
|
target.write(s)
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
|
2016-01-14 21:51:30 +00:00
|
|
|
proc startItem(target: Stream, style: PresentationStyle, indentation: int,
|
2016-02-26 20:55:59 +00:00
|
|
|
state: var DumperState, isObject: bool, newline: string)
|
2016-01-11 18:12:55 +00:00
|
|
|
{.raises: [YamlPresenterOutputError].} =
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
|
|
|
case state
|
2016-01-11 18:12:55 +00:00
|
|
|
of dBlockMapValue:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
2016-01-14 21:51:30 +00:00
|
|
|
if isObject or style == psCanonical:
|
2016-01-11 18:12:55 +00:00
|
|
|
target.write("? ")
|
|
|
|
state = dBlockExplicitMapKey
|
|
|
|
else:
|
|
|
|
state = dBlockImplicitMapKey
|
2016-01-11 18:53:19 +00:00
|
|
|
of dBlockInlineMap:
|
|
|
|
state = dBlockImplicitMapKey
|
2016-01-05 18:58:46 +00:00
|
|
|
of dBlockExplicitMapKey:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(": ")
|
2016-01-11 18:12:55 +00:00
|
|
|
state = dBlockMapValue
|
2016-01-05 18:58:46 +00:00
|
|
|
of dBlockImplicitMapKey:
|
|
|
|
target.write(": ")
|
2016-01-11 18:12:55 +00:00
|
|
|
state = dBlockMapValue
|
2016-01-05 18:58:46 +00:00
|
|
|
of dFlowExplicitMapKey:
|
2016-01-14 21:51:30 +00:00
|
|
|
if style != psMinimal:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2016-01-11 19:58:24 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(": ")
|
2016-01-11 18:12:55 +00:00
|
|
|
state = dFlowMapValue
|
|
|
|
of dFlowMapValue:
|
2016-01-14 21:51:30 +00:00
|
|
|
if (isObject and style != psMinimal) or
|
|
|
|
style in [psJson, psCanonical]:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(',' & newline & repeat(' ', indentation))
|
2016-01-14 21:51:30 +00:00
|
|
|
if style == psJson:
|
2016-01-14 18:00:03 +00:00
|
|
|
state = dFlowImplicitMapKey
|
|
|
|
else:
|
2016-01-11 18:12:55 +00:00
|
|
|
target.write("? ")
|
2016-01-14 18:00:03 +00:00
|
|
|
state = dFlowExplicitMapKey
|
2016-01-14 21:51:30 +00:00
|
|
|
elif isObject and style == psMinimal:
|
2016-01-11 20:01:36 +00:00
|
|
|
target.write(", ? ")
|
2016-01-11 20:12:38 +00:00
|
|
|
state = dFlowExplicitMapKey
|
2016-01-05 18:58:46 +00:00
|
|
|
else:
|
|
|
|
target.write(", ")
|
2016-01-11 18:12:55 +00:00
|
|
|
state = dFlowImplicitMapKey
|
|
|
|
of dFlowMapStart:
|
2016-01-14 21:51:30 +00:00
|
|
|
if (isObject and style != psMinimal) or
|
|
|
|
style in [psJson, psCanonical]:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline & repeat(' ', indentation))
|
2016-01-14 21:51:30 +00:00
|
|
|
if style == psJson:
|
2016-01-14 18:00:03 +00:00
|
|
|
state = dFlowImplicitMapKey
|
|
|
|
else:
|
2016-01-11 18:12:55 +00:00
|
|
|
target.write("? ")
|
2016-01-14 18:00:03 +00:00
|
|
|
state = dFlowExplicitMapKey
|
2016-01-11 18:12:55 +00:00
|
|
|
else:
|
|
|
|
state = dFlowImplicitMapKey
|
|
|
|
of dFlowImplicitMapKey:
|
|
|
|
target.write(": ")
|
|
|
|
state = dFlowMapValue
|
2016-01-05 18:58:46 +00:00
|
|
|
of dBlockSequenceItem:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
|
|
|
target.write("- ")
|
|
|
|
of dFlowSequenceStart:
|
|
|
|
case style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psMinimal, psDefault:
|
2016-01-05 18:58:46 +00:00
|
|
|
discard
|
2016-01-14 21:51:30 +00:00
|
|
|
of psCanonical, psJson:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
2016-01-14 21:51:30 +00:00
|
|
|
of psBlockOnly:
|
2016-01-05 18:58:46 +00:00
|
|
|
discard # can never happen
|
|
|
|
state = dFlowSequenceItem
|
|
|
|
of dFlowSequenceItem:
|
|
|
|
case style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psMinimal, psDefault:
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(", ")
|
2016-01-14 21:51:30 +00:00
|
|
|
of psCanonical, psJson:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(',' & newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
2016-01-14 21:51:30 +00:00
|
|
|
of psBlockOnly:
|
2016-01-05 18:58:46 +00:00
|
|
|
discard # can never happen
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2016-01-28 20:59:26 +00:00
|
|
|
|
|
|
|
proc anchorName(a: AnchorId): string {.raises: [].} =
|
|
|
|
result = ""
|
|
|
|
var i = int(a)
|
|
|
|
while i >= 0:
|
|
|
|
let j = i mod 36
|
|
|
|
if j < 26: result.add(char(j + ord('a')))
|
|
|
|
else: result.add(char(j + ord('0') - 26))
|
|
|
|
i -= 36
|
|
|
|
|
2016-01-14 21:51:30 +00:00
|
|
|
proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: TagLibrary,
|
2016-01-05 18:58:46 +00:00
|
|
|
anchor: AnchorId) {.raises:[YamlPresenterOutputError].} =
|
|
|
|
try:
|
|
|
|
if tag notin [yTagQuestionMark, yTagExclamationMark]:
|
|
|
|
let tagUri = tagLib.uri(tag)
|
|
|
|
if tagUri.startsWith(tagLib.secondaryPrefix):
|
|
|
|
target.write("!!")
|
2016-01-24 10:44:10 +00:00
|
|
|
target.write(tagUri[18..tagUri.high])
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(' ')
|
|
|
|
elif tagUri.startsWith("!"):
|
|
|
|
target.write(tagUri)
|
|
|
|
target.write(' ')
|
|
|
|
else:
|
|
|
|
target.write("!<")
|
|
|
|
target.write(tagUri)
|
|
|
|
target.write("> ")
|
|
|
|
if anchor != yAnchorNone:
|
|
|
|
target.write("&")
|
2016-01-28 20:59:26 +00:00
|
|
|
target.write(anchorName(anchor))
|
2016-01-04 20:46:33 +00:00
|
|
|
target.write(' ')
|
2016-01-05 18:58:46 +00:00
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
|
2016-02-12 18:53:25 +00:00
|
|
|
proc present*(s: var YamlStream, target: Stream, tagLib: TagLibrary,
|
2016-02-26 20:55:59 +00:00
|
|
|
options: PresentationOptions = defaultPresentationOptions) =
|
2015-12-27 15:36:32 +00:00
|
|
|
var
|
|
|
|
indentation = 0
|
|
|
|
levels = newSeq[DumperState]()
|
2016-02-12 18:53:25 +00:00
|
|
|
cached = initQueue[YamlStreamEvent]()
|
2016-02-26 20:55:59 +00:00
|
|
|
let newline = if options.newlines == nlLF: "\l"
|
|
|
|
elif options.newlines == nlCRLF: "\c\l" else: "\n"
|
2016-02-12 18:53:25 +00:00
|
|
|
while cached.len > 0 or not s.finished():
|
|
|
|
let item = if cached.len > 0: cached.dequeue else: s.next()
|
2015-12-27 15:36:32 +00:00
|
|
|
case item.kind
|
|
|
|
of yamlStartDocument:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
# TODO: tag directives
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write("%YAML 1.2" & newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
if tagLib.secondaryPrefix != yamlTagRepositoryPrefix:
|
|
|
|
target.write("%TAG !! " &
|
2016-02-26 20:55:59 +00:00
|
|
|
tagLib.secondaryPrefix & newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write("--- ")
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
of yamlScalar:
|
|
|
|
if levels.len == 0:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
|
|
|
safeWrite(newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
startItem(target, options.style, indentation,
|
|
|
|
levels[levels.high], false, newline)
|
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.scalarTag, tagLib, item.scalarAnchor)
|
|
|
|
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style == psJson:
|
2016-01-14 18:58:38 +00:00
|
|
|
let hint = guessType(item.scalarContent)
|
2016-01-05 18:06:55 +00:00
|
|
|
if item.scalarTag in [yTagQuestionMark, yTagBoolean] and
|
2016-01-14 18:58:38 +00:00
|
|
|
hint in [yTypeBoolTrue, yTypeBoolFalse]:
|
|
|
|
if hint == yTypeBoolTrue:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite("true")
|
2016-01-05 18:06:55 +00:00
|
|
|
else:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite("false")
|
2016-01-05 18:06:55 +00:00
|
|
|
elif item.scalarTag in [yTagQuestionMark, yTagNull] and
|
2016-01-14 18:58:38 +00:00
|
|
|
hint == yTypeNull:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite("null")
|
2016-01-24 19:53:51 +00:00
|
|
|
elif item.scalarTag in [yTagQuestionMark, yTagInteger] and
|
|
|
|
hint == yTypeInteger:
|
|
|
|
safeWrite(item.scalarContent)
|
2016-01-05 18:06:55 +00:00
|
|
|
elif item.scalarTag in [yTagQuestionMark, yTagFloat] and
|
2016-01-14 18:58:38 +00:00
|
|
|
hint in [yTypeFloatInf, yTypeFloatNaN]:
|
2016-01-05 18:58:46 +00:00
|
|
|
raise newException(YamlPresenterJsonError,
|
2016-01-05 18:06:55 +00:00
|
|
|
"Infinity and not-a-number values cannot be presented as JSON!")
|
2016-01-24 19:53:51 +00:00
|
|
|
elif item.scalarTag in [yTagQuestionMark, yTagFloat] and
|
|
|
|
hint == yTypeFloat:
|
|
|
|
safeWrite(item.scalarContent)
|
2015-12-29 17:22:55 +00:00
|
|
|
else:
|
2016-02-25 21:32:50 +00:00
|
|
|
writeDoubleQuotedJson(item.scalarContent, target)
|
2016-02-26 20:55:59 +00:00
|
|
|
elif options.style == psCanonical:
|
2016-02-25 21:32:50 +00:00
|
|
|
writeDoubleQuoted(item.scalarContent, target,
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation + options.indentationStep,
|
|
|
|
newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
else:
|
2016-02-25 20:04:44 +00:00
|
|
|
var words, lines = newSeq[tuple[start, finish: int]]()
|
2016-02-26 20:55:59 +00:00
|
|
|
case item.scalarContent.inspect(
|
|
|
|
indentation + options.indentationStep, words, lines)
|
2016-02-25 20:04:44 +00:00
|
|
|
of sLiteral: writeLiteral(item.scalarContent, indentation,
|
2016-02-26 20:55:59 +00:00
|
|
|
options.indentationStep, target, lines, newline)
|
2016-02-25 20:04:44 +00:00
|
|
|
of sFolded: writeFolded(item.scalarContent, indentation,
|
2016-02-26 20:55:59 +00:00
|
|
|
options.indentationStep, target, words, newline)
|
2016-02-25 20:04:44 +00:00
|
|
|
of sPlain: safeWrite(item.scalarContent)
|
2016-02-25 21:32:50 +00:00
|
|
|
of sDoubleQuoted: writeDoubleQuoted(item.scalarContent, target,
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation + options.indentationStep, newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
of yamlAlias:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style == psJson:
|
2016-01-14 18:00:03 +00:00
|
|
|
raise newException(YamlPresenterJsonError,
|
|
|
|
"Alias not allowed in JSON output")
|
2016-01-05 18:06:55 +00:00
|
|
|
assert levels.len > 0
|
2016-02-26 20:55:59 +00:00
|
|
|
startItem(target, options.style, indentation, levels[levels.high],
|
|
|
|
false, newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
|
|
|
target.write('*')
|
|
|
|
target.write(cast[byte]('a') + cast[byte](item.aliasTarget))
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
of yamlStartSequence:
|
|
|
|
var nextState: DumperState
|
2016-02-26 20:55:59 +00:00
|
|
|
case options.style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psDefault:
|
2015-12-27 15:36:32 +00:00
|
|
|
var length = 0
|
|
|
|
while true:
|
2016-02-12 18:53:25 +00:00
|
|
|
assert(not(s.finished()))
|
|
|
|
let next = s.next()
|
|
|
|
cached.enqueue(next)
|
|
|
|
case next.kind
|
|
|
|
of yamlScalar:
|
|
|
|
length += 2 + next.scalarContent.len
|
|
|
|
of yamlAlias:
|
|
|
|
length += 6
|
|
|
|
of yamlEndSequence:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
length = high(int)
|
|
|
|
break
|
2015-12-27 15:36:32 +00:00
|
|
|
nextState = if length <= 60: dFlowSequenceStart else:
|
|
|
|
dBlockSequenceItem
|
2016-01-14 21:51:30 +00:00
|
|
|
of psJson:
|
2016-01-07 10:58:33 +00:00
|
|
|
if levels.len > 0 and levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dFlowMapStart, dFlowMapValue]:
|
2016-01-05 18:58:46 +00:00
|
|
|
raise newException(YamlPresenterJsonError,
|
2016-01-05 18:06:55 +00:00
|
|
|
"Cannot have sequence as map key in JSON output!")
|
|
|
|
nextState = dFlowSequenceStart
|
2016-01-14 21:51:30 +00:00
|
|
|
of psMinimal, psCanonical:
|
2015-12-27 15:36:32 +00:00
|
|
|
nextState = dFlowSequenceStart
|
2016-01-14 21:51:30 +00:00
|
|
|
of psBlockOnly:
|
2015-12-27 15:36:32 +00:00
|
|
|
nextState = dBlockSequenceItem
|
|
|
|
|
|
|
|
if levels.len == 0:
|
|
|
|
if nextState == dBlockSequenceItem:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.seqTag, tagLib, item.seqAnchor)
|
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.seqTag, tagLib, item.seqAnchor)
|
2016-02-26 20:55:59 +00:00
|
|
|
safeWrite(newline)
|
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
startItem(target, options.style, indentation,
|
|
|
|
levels[levels.high], true, newline)
|
|
|
|
if options.style != psJson:
|
2015-12-29 17:22:55 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.seqTag, tagLib, item.seqAnchor)
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
|
|
|
|
if nextState == dFlowSequenceStart:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite('[')
|
2016-02-26 20:55:59 +00:00
|
|
|
if levels.len > 0 and options.style in [psJson, psCanonical] and
|
2015-12-27 15:36:32 +00:00
|
|
|
levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
levels.add(nextState)
|
|
|
|
of yamlStartMap:
|
|
|
|
var nextState: DumperState
|
2016-02-26 20:55:59 +00:00
|
|
|
case options.style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psDefault:
|
2016-02-19 17:25:01 +00:00
|
|
|
type MapParseState = enum
|
2016-01-11 18:53:19 +00:00
|
|
|
mpInitial, mpKey, mpValue, mpNeedBlock
|
2016-02-19 17:25:01 +00:00
|
|
|
var mps: MapParseState = mpInitial
|
2016-01-11 18:53:19 +00:00
|
|
|
while mps != mpNeedBlock:
|
2016-02-12 18:53:25 +00:00
|
|
|
case s.peek().kind
|
|
|
|
of yamlScalar, yamlAlias:
|
|
|
|
case mps
|
|
|
|
of mpInitial: mps = mpKey
|
|
|
|
of mpKey: mps = mpValue
|
|
|
|
else: mps = mpNeedBlock
|
|
|
|
of yamlEndMap:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
mps = mpNeedBlock
|
2016-01-11 18:53:19 +00:00
|
|
|
nextState = if mps == mpNeedBlock: dBlockMapValue else:
|
|
|
|
dBlockInlineMap
|
2016-01-14 21:51:30 +00:00
|
|
|
of psMinimal:
|
2016-01-11 18:12:55 +00:00
|
|
|
nextState = dFlowMapStart
|
2016-01-14 21:51:30 +00:00
|
|
|
of psCanonical:
|
2016-01-11 18:12:55 +00:00
|
|
|
nextState = dFlowMapStart
|
2016-01-14 21:51:30 +00:00
|
|
|
of psJson:
|
2016-01-05 20:34:07 +00:00
|
|
|
if levels.len > 0 and levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dFlowMapStart, dFlowMapValue]:
|
2016-01-05 18:58:46 +00:00
|
|
|
raise newException(YamlPresenterJsonError,
|
2016-01-05 18:06:55 +00:00
|
|
|
"Cannot have map as map key in JSON output!")
|
2016-01-11 18:12:55 +00:00
|
|
|
nextState = dFlowMapStart
|
2016-01-14 21:51:30 +00:00
|
|
|
of psBlockOnly:
|
2016-01-11 18:12:55 +00:00
|
|
|
nextState = dBlockMapValue
|
2015-12-27 15:36:32 +00:00
|
|
|
if levels.len == 0:
|
2016-01-11 18:12:55 +00:00
|
|
|
if nextState == dBlockMapValue:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.mapTag, tagLib, item.mapAnchor)
|
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style != psJson:
|
|
|
|
safeWrite(newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.mapTag, tagLib, item.mapAnchor)
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
else:
|
2016-01-11 18:12:55 +00:00
|
|
|
if nextState in [dBlockMapValue, dBlockImplicitMapKey]:
|
2016-02-26 20:55:59 +00:00
|
|
|
startItem(target, options.style, indentation,
|
|
|
|
levels[levels.high], true, newline)
|
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.mapTag, tagLib, item.mapAnchor)
|
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
startItem(target, options.style, indentation,
|
|
|
|
levels[levels.high], true, newline)
|
|
|
|
if options.style != psJson:
|
2015-12-27 15:36:32 +00:00
|
|
|
writeTagAndAnchor(target,
|
|
|
|
item.mapTag, tagLib, item.mapAnchor)
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
|
2016-01-11 18:12:55 +00:00
|
|
|
if nextState == dFlowMapStart:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite('{')
|
2016-02-26 20:55:59 +00:00
|
|
|
if levels.len > 0 and options.style in [psJson, psCanonical] and
|
2015-12-27 15:36:32 +00:00
|
|
|
levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation += options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
levels.add(nextState)
|
|
|
|
|
|
|
|
of yamlEndSequence:
|
2016-01-05 18:06:55 +00:00
|
|
|
assert levels.len > 0
|
2015-12-27 15:36:32 +00:00
|
|
|
case levels.pop()
|
|
|
|
of dFlowSequenceItem:
|
2016-02-26 20:55:59 +00:00
|
|
|
case options.style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psDefault, psMinimal, psBlockOnly:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite(']')
|
2016-01-14 21:51:30 +00:00
|
|
|
of psJson, psCanonical:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
|
|
|
target.write(']')
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
if levels.len == 0 or levels[levels.high] notin
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2015-12-27 15:36:32 +00:00
|
|
|
continue
|
|
|
|
of dFlowSequenceStart:
|
2016-02-26 20:55:59 +00:00
|
|
|
if levels.len > 0 and options.style in [psJson, psCanonical] and
|
2015-12-27 15:36:32 +00:00
|
|
|
levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite(']')
|
2015-12-27 15:36:32 +00:00
|
|
|
of dBlockSequenceItem:
|
|
|
|
discard
|
|
|
|
else:
|
2016-01-05 18:06:55 +00:00
|
|
|
assert false
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
of yamlEndMap:
|
2016-01-05 18:06:55 +00:00
|
|
|
assert levels.len > 0
|
2016-01-11 20:10:30 +00:00
|
|
|
let level = levels.pop()
|
|
|
|
case level
|
2016-01-11 18:12:55 +00:00
|
|
|
of dFlowMapValue:
|
2016-02-26 20:55:59 +00:00
|
|
|
case options.style
|
2016-01-14 21:51:30 +00:00
|
|
|
of psDefault, psMinimal, psBlockOnly:
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite('}')
|
2016-01-14 21:51:30 +00:00
|
|
|
of psJson, psCanonical:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2016-01-05 18:58:46 +00:00
|
|
|
try:
|
2016-02-26 20:55:59 +00:00
|
|
|
target.write(newline)
|
2016-01-05 18:58:46 +00:00
|
|
|
target.write(repeat(' ', indentation))
|
|
|
|
target.write('}')
|
|
|
|
except:
|
|
|
|
var e = newException(YamlPresenterOutputError, "")
|
2016-01-24 17:24:09 +00:00
|
|
|
e.parent = getCurrentException()
|
2016-01-05 18:58:46 +00:00
|
|
|
raise e
|
2015-12-27 15:36:32 +00:00
|
|
|
if levels.len == 0 or levels[levels.high] notin
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2015-12-27 15:36:32 +00:00
|
|
|
continue
|
2016-01-11 18:12:55 +00:00
|
|
|
of dFlowMapStart:
|
2016-02-26 20:55:59 +00:00
|
|
|
if levels.len > 0 and options.style in [psJson, psCanonical] and
|
2015-12-27 15:36:32 +00:00
|
|
|
levels[levels.high] in
|
2016-01-11 18:12:55 +00:00
|
|
|
[dBlockExplicitMapKey, dBlockMapValue,
|
|
|
|
dBlockImplicitMapKey, dBlockSequenceItem]:
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2016-01-05 18:58:46 +00:00
|
|
|
safeWrite('}')
|
2016-01-11 20:12:38 +00:00
|
|
|
of dBlockMapValue, dBlockInlineMap:
|
2015-12-27 15:36:32 +00:00
|
|
|
discard
|
|
|
|
else:
|
2016-01-05 18:06:55 +00:00
|
|
|
assert false
|
2016-02-26 20:55:59 +00:00
|
|
|
indentation -= options.indentationStep
|
2015-12-27 15:36:32 +00:00
|
|
|
of yamlEndDocument:
|
2016-02-12 18:53:25 +00:00
|
|
|
if finished(s): break
|
2016-02-26 20:55:59 +00:00
|
|
|
safeWrite("..." & newline)
|
2015-12-27 15:36:32 +00:00
|
|
|
|
2016-02-26 20:55:59 +00:00
|
|
|
proc transform*(input: Stream, output: Stream,
|
|
|
|
options: PresentationOptions = defaultPresentationOptions) =
|
2015-12-27 15:36:32 +00:00
|
|
|
var
|
2016-01-24 10:44:10 +00:00
|
|
|
taglib = initExtendedTagLibrary()
|
|
|
|
parser = newYamlParser(tagLib)
|
2016-01-11 19:54:06 +00:00
|
|
|
events = parser.parse(input)
|
2016-01-24 17:24:09 +00:00
|
|
|
try:
|
2016-02-26 20:55:59 +00:00
|
|
|
if options.style == psCanonical:
|
2016-01-24 17:24:09 +00:00
|
|
|
var specificTagEvents = iterator(): YamlStreamEvent =
|
2016-02-12 18:53:25 +00:00
|
|
|
for e in events:
|
2016-01-24 17:24:09 +00:00
|
|
|
var event = e
|
|
|
|
case event.kind
|
|
|
|
of yamlStartDocument, yamlEndDocument, yamlEndMap,
|
|
|
|
yamlAlias, yamlEndSequence:
|
|
|
|
discard
|
|
|
|
of yamlStartMap:
|
|
|
|
if event.mapTag in [yTagQuestionMark,
|
|
|
|
yTagExclamationMark]:
|
|
|
|
event.mapTag = yTagMap
|
|
|
|
of yamlStartSequence:
|
|
|
|
if event.seqTag in [yTagQuestionMark,
|
|
|
|
yTagExclamationMark]:
|
|
|
|
event.seqTag = yTagSequence
|
|
|
|
of yamlScalar:
|
|
|
|
if event.scalarTag == yTagQuestionMark:
|
|
|
|
case guessType(event.scalarContent)
|
|
|
|
of yTypeInteger:
|
|
|
|
event.scalarTag = yTagInteger
|
|
|
|
of yTypeFloat, yTypeFloatInf, yTypeFloatNaN:
|
|
|
|
event.scalarTag = yTagFloat
|
|
|
|
of yTypeBoolTrue, yTypeBoolFalse:
|
|
|
|
event.scalarTag = yTagBoolean
|
|
|
|
of yTypeNull:
|
|
|
|
event.scalarTag = yTagNull
|
|
|
|
of yTypeUnknown:
|
|
|
|
event.scalarTag = yTagString
|
|
|
|
elif event.scalarTag == yTagExclamationMark:
|
2016-01-11 19:54:06 +00:00
|
|
|
event.scalarTag = yTagString
|
2016-01-24 17:24:09 +00:00
|
|
|
yield event
|
2016-02-12 18:53:25 +00:00
|
|
|
var s = initYamlStream(specificTagEvents)
|
2016-02-26 20:55:59 +00:00
|
|
|
present(s, output, tagLib, options)
|
2016-01-24 17:24:09 +00:00
|
|
|
else:
|
2016-02-26 20:55:59 +00:00
|
|
|
present(events, output, tagLib, options)
|
2016-02-12 18:53:25 +00:00
|
|
|
except YamlStreamError:
|
|
|
|
var e = getCurrentException()
|
|
|
|
while e.parent of YamlStreamError: e = e.parent
|
2016-01-24 20:38:29 +00:00
|
|
|
if e.parent of IOError:
|
2016-02-19 17:25:01 +00:00
|
|
|
raise (ref IOError)(e.parent)
|
2016-01-24 20:38:29 +00:00
|
|
|
elif e.parent of YamlParserError:
|
2016-02-19 17:25:01 +00:00
|
|
|
raise (ref YamlParserError)(e.parent)
|
2016-01-24 17:24:09 +00:00
|
|
|
else:
|
|
|
|
# never happens
|
|
|
|
assert(false)
|
2016-02-19 17:25:01 +00:00
|
|
|
except YamlPresenterJsonError:
|
|
|
|
raise (ref YamlPresenterJsonError)(getCurrentException())
|
|
|
|
except YamlPresenterOutputError:
|
|
|
|
raise (ref YamlPresenterOutputError)(getCurrentException())
|