Various improvements

* Better JSON serialization (map bool literals to true/false if
   possible)
 * Fixed a problem where the tag of block sequence would be
   wrongly placed
 * Always output "" when dumping empty strings
 * Added helper functions to create YamlStreamEvents.
 * Moved procs for YamlStreamEvents to private/events.nim
 * renamed dump() to present()
 * Added dump() and load() to yaml.serialization
This commit is contained in:
Felix Krause 2015-12-29 18:22:55 +01:00
parent 9ff93efb11
commit a094089fb9
5 changed files with 92 additions and 89 deletions

View File

@ -13,8 +13,9 @@ type
dFlowSequenceStart
proc needsEscaping(scalar: string): bool =
scalar.find({'{', '}', '[', ']', ',', '#', '-', ':', '?', '%', '\x0A',
'\c'}) != -1
scalar.len == 0 or
scalar.find({'{', '}', '[', ']', ',', '#', '-', ':', '?', '%',
'\x0A', '\c'}) != -1
proc writeDoubleQuoted(scalar: string, s: Stream) =
s.write('"')
@ -116,8 +117,8 @@ proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: YamlTagLibrary,
target.write(cast[byte]('a') + cast[byte](anchor))
target.write(' ')
proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2) =
proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2) =
var
cached = initQueue[YamlStreamEvent]()
cacheIterator = iterator(): YamlStreamEvent =
@ -147,8 +148,20 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
writeTagAndAnchor(target,
item.scalarTag, tagLib, item.scalarAnchor)
if item.scalarContent.needsEscaping or
style in [yDumpCanonical, yDumpJson]:
if (style == yDumpJson and item.scalarTag in [yTagQuestionMark,
yTagBoolean] and
item.scalarType in [yTypeBoolTrue, yTypeBoolFalse]):
if item.scalarType == yTypeBoolTrue:
target.write("true")
else:
target.write("false")
elif style == yDumpCanonical or item.scalarContent.needsEscaping or
(style == yDumpJson and
(item.scalarTag notin [yTagQuestionMark, yTagInteger, yTagFloat,
yTagBoolean, yTagNull] or
(item.scalarTag == yTagQuestionMark and item.scalarType notin
[yTypeBoolFalse, yTypeBoolTrue, yTypeInteger, yTypeFloat,
yTypeNull]))):
writeDoubleQuoted(item.scalarContent, target)
else:
target.write(item.scalarContent)
@ -193,21 +206,15 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
item.seqTag, tagLib, item.seqAnchor)
else:
if style != yDumpJson:
target.write('\x0A')
writeTagAndAnchor(target,
item.seqTag, tagLib, item.seqAnchor)
target.write('\x0A')
indentation += indentationStep
else:
if nextState == dBlockSequenceItem:
if style != yDumpJson:
writeTagAndAnchor(target,
item.seqTag, tagLib, item.seqAnchor)
startItem(target, style, indentation, levels[levels.high])
else:
startItem(target, style, indentation, levels[levels.high])
if style != yDumpJson:
writeTagAndAnchor(target,
item.seqTag, tagLib, item.seqAnchor)
startItem(target, style, indentation, levels[levels.high])
if style != yDumpJson:
writeTagAndAnchor(target,
item.seqTag, tagLib, item.seqAnchor)
indentation += indentationStep
if nextState == dFlowSequenceStart:
@ -267,7 +274,8 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
indentation += indentationStep
else:
if nextState in
[dBlockExplicitMapValue, dBlockImplicitMapValue]:
[dBlockExplicitMapValue, dBlockImplicitMapValue,
dBlockImplicitMapKey]:
if style != yDumpJson:
writeTagAndAnchor(target,
item.mapTag, tagLib, item.mapAnchor)
@ -368,5 +376,5 @@ proc transform*(input: Stream, output: Stream, style: YamlDumpStyle,
var
tagLib = extendedTagLibrary()
parser = newParser(tagLib)
dump(parser.parse(input), output, tagLib, style,
indentationStep)
present(parser.parse(input), output, tagLib, style,
indentationStep)

View File

@ -29,6 +29,28 @@ type
BlockScalarStyle = enum
bsLiteral, bsFolded
proc `$`*(id: TagId): string =
case id
of yTagQuestionMark: "?"
of yTagExclamationMark: "!"
of yTagString: "!!str"
of yTagSequence: "!!seq"
of yTagMap: "!!map"
of yTagNull: "!!null"
of yTagBoolean: "!!bool"
of yTagInteger: "!!int"
of yTagFloat: "!!float"
of yTagOrderedMap: "!!omap"
of yTagPairs: "!!pairs"
of yTagSet: "!!set"
of yTagBinary: "!!binary"
of yTagMerge: "!!merge"
of yTagTimestamp: "!!timestamp"
of yTagValue: "!!value"
of yTagYaml: "!!yaml"
else:
"<" & $cast[int](id) & ">"
proc newParser*(tagLib: YamlTagLibrary): YamlSequentialParser =
new(result)
result.tagLib = tagLib
@ -40,55 +62,6 @@ proc anchor*(parser: YamlSequentialParser, id: AnchorId): string =
return pair[0]
return nil
proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool =
if left.kind != right.kind:
return false
case left.kind
of yamlStartDocument, yamlEndDocument, yamlEndMap, yamlEndSequence:
result = true
of yamlStartMap:
result = left.mapAnchor == right.mapAnchor and
left.mapTag == right.mapTag
of yamlStartSequence:
result = left.seqAnchor == right.seqAnchor and
left.seqTag == right.seqTag
of yamlScalar:
result = left.scalarAnchor == right.scalarAnchor and
left.scalarTag == right.scalarTag and
left.scalarContent == right.scalarContent and
left.scalarType == right.scalarType
of yamlAlias:
result = left.aliasTarget == right.aliasTarget
of yamlError, yamlWarning:
result = left.description == right.description and
left.line == right.line and left.column == right.column
proc `$`*(event: YamlStreamEvent): string =
result = $event.kind & '('
case event.kind
of yamlEndMap, yamlEndSequence, yamlStartDocument, yamlEndDocument:
discard
of yamlStartMap:
result &= "tag=" & $event.mapTag
if event.mapAnchor != yAnchorNone:
result &= ", anchor=" & $event.mapAnchor
of yamlStartSequence:
result &= "tag=" & $event.seqTag
if event.seqAnchor != yAnchorNone:
result &= ", anchor=" & $event.seqAnchor
of yamlScalar:
result &= "tag=" & $event.scalarTag
if event.scalarAnchor != yAnchorNone:
result &= ", anchor=" & $event.scalarAnchor
result &= ", typeHint=" & $event.scalarType
result &= ", content=\"" & event.scalarContent & '\"'
of yamlAlias:
result &= "aliasTarget=" & $event.aliasTarget
of yamlWarning, yamlError:
result &= "line=" & $event.line & ", column=" & $event.column
result &= ", description=\"" & event.description & '\"'
result &= ")"
template yieldWarning(d: string) {.dirty.} =
yield YamlStreamEvent(kind: yamlWarning, description: d,
line: lex.line, column: lex.column)

View File

@ -26,8 +26,7 @@ suite "Serialization":
test "Serialize string sequence":
var input = @["a", "b"]
var output = newStringStream()
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
yDumpBlockOnly)
dump(input, output, yDumpBlockOnly)
assert output.data == "%YAML 1.2\n--- \n- a\n- b"
test "Load Table[int, string]":
@ -47,8 +46,7 @@ suite "Serialization":
input[23] = "dreiundzwanzig"
input[42] = "zweiundvierzig"
var output = newStringStream()
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
yDumpBlockOnly)
dump(input, output, yDumpBlockOnly)
assert output.data == "%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig"
test "Load Sequences in Sequence":
@ -67,8 +65,7 @@ suite "Serialization":
test "Serialize Sequences in Sequence":
let input = @[@[1, 2, 3], @[4, 5], @[6]]
var output = newStringStream()
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
yDumpDefault)
dump(input, output, yDumpDefault)
assert output.data == "%YAML 1.2\n--- \n- [1, 2, 3]\n- [4, 5]\n- [6]"
test "Load custom object":
@ -86,6 +83,5 @@ suite "Serialization":
test "Serialize custom object":
let input = Person(firstname: "Peter", surname: "Pan", age: 12)
var output = newStringStream()
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
yDumpBlockOnly)
dump(input, output, yDumpBlockOnly)
assert output.data == "%YAML 1.2\n--- \nfirstname: Peter\nsurname: Pan\nage: 12"

View File

@ -85,7 +85,7 @@ type
## The value ``mapMayHaveKeyObjects`` is a hint from a serializer and is
## used for choosing an appropriate presentation mode for a YAML map
## (flow or block, explicit or implicit) by
## `dump <#dump,YamlStream,Stream,YamlTagLibrary,YamlDumpStyle,int>`_.
## `present <#present,YamlStream,Stream,YamlTagLibrary,YamlDumpStyle,int>`_.
## If it is set to ``false``, the map may only have scalars as keys.
##
## The value ``scalarType`` is a hint from the lexer, see
@ -239,8 +239,22 @@ proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool
proc `$`*(event: YamlStreamEvent): string
## outputs a human-readable string describing the given event
proc startDocEvent*(): YamlStreamEvent {.inline.}
proc endDocEvent*(): YamlStreamEvent {.inline.}
proc startMapEvent*(tag: TagId = yTagQuestionMark,
anchor: AnchorId = yAnchorNone,
mayHaveKeyObjects: bool = true): YamlStreamEvent {.inline.}
proc endMapEvent*(): YamlStreamEvent {.inline.}
proc startSeqEvent*(tag: TagId = yTagQuestionMark,
anchor: AnchorId = yAnchorNone): YamlStreamEvent {.inline.}
proc endSeqEvent*(): YamlStreamEvent {.inline.}
proc scalarEvent*(content: string = "", tag: TagId = yTagQuestionMark,
anchor: AnchorId = yAnchorNone,
typeHint: YamlTypeHint = yTypeUnknown):
YamlStreamEvent {.inline.}
proc `==`*(left, right: TagId): bool {.borrow.}
proc `$`*(id: TagId): string {.borrow.}
proc `$`*(id: TagId): string
proc hash*(id: TagId): Hash {.borrow.}
proc `==`*(left, right: AnchorId): bool {.borrow.}
@ -310,8 +324,8 @@ proc constructJson*(s: YamlStream): seq[JsonNode]
## check for these values and will output invalid JSON when rendering one
## of these values into a JSON character stream.
proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2)
proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2)
## Convert ``s`` to a YAML character stream and write it to ``target``.
proc transform*(input: Stream, output: Stream, style: YamlDumpStyle,
@ -323,6 +337,7 @@ proc transform*(input: Stream, output: Stream, style: YamlDumpStyle,
include private.lexer
include private.tagLibrary
include private.events
include private.sequential
include private.json
include private.dumper

View File

@ -110,13 +110,6 @@ proc prepend*(event: YamlStreamEvent, s: YamlStream): YamlStream =
for e in s():
yield e
proc wrapWithDocument*(s: YamlStream): YamlStream =
result = iterator(): YamlStreamEvent =
yield YamlStreamEvent(kind: yamlStartDocument)
for event in s():
yield event
yield YamlStreamEvent(kind: yamlEndDocument)
proc construct*(s: YamlStream, result: var string) =
let item = s()
if finished(s) or item.kind != yamlScalar:
@ -278,3 +271,21 @@ proc serialize*[K, V](value: Table[K, V],
for event in events():
yield event
yield YamlStreamEvent(kind: yamlEndMap)
proc load*[K](input: Stream, target: var K) =
var
parser = newParser(coreTagLibrary())
events = parser.parse(input)
assert events().kind == yamlStartDocument
construct(events, target)
assert events().kind == yamlEndDocument
proc dump*[K](value: K, target: Stream, style: YamlDumpStyle = yDumpDefault,
indentationStep: int = 2) =
var serialized = serialize(value, style == yDumpCanonical)
var events = iterator(): YamlStreamEvent =
yield YamlStreamEvent(kind: yamlStartDocument)
for event in serialized():
yield event
yield YamlStreamEvent(kind: yamlEndDocument)
present(events, target, coreTagLibrary(), style, indentationStep)