mirror of https://github.com/status-im/NimYAML.git
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:
parent
9ff93efb11
commit
a094089fb9
|
@ -13,8 +13,9 @@ type
|
||||||
dFlowSequenceStart
|
dFlowSequenceStart
|
||||||
|
|
||||||
proc needsEscaping(scalar: string): bool =
|
proc needsEscaping(scalar: string): bool =
|
||||||
scalar.find({'{', '}', '[', ']', ',', '#', '-', ':', '?', '%', '\x0A',
|
scalar.len == 0 or
|
||||||
'\c'}) != -1
|
scalar.find({'{', '}', '[', ']', ',', '#', '-', ':', '?', '%',
|
||||||
|
'\x0A', '\c'}) != -1
|
||||||
|
|
||||||
proc writeDoubleQuoted(scalar: string, s: Stream) =
|
proc writeDoubleQuoted(scalar: string, s: Stream) =
|
||||||
s.write('"')
|
s.write('"')
|
||||||
|
@ -116,8 +117,8 @@ proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: YamlTagLibrary,
|
||||||
target.write(cast[byte]('a') + cast[byte](anchor))
|
target.write(cast[byte]('a') + cast[byte](anchor))
|
||||||
target.write(' ')
|
target.write(' ')
|
||||||
|
|
||||||
proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
||||||
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2) =
|
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2) =
|
||||||
var
|
var
|
||||||
cached = initQueue[YamlStreamEvent]()
|
cached = initQueue[YamlStreamEvent]()
|
||||||
cacheIterator = iterator(): YamlStreamEvent =
|
cacheIterator = iterator(): YamlStreamEvent =
|
||||||
|
@ -147,8 +148,20 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
||||||
writeTagAndAnchor(target,
|
writeTagAndAnchor(target,
|
||||||
item.scalarTag, tagLib, item.scalarAnchor)
|
item.scalarTag, tagLib, item.scalarAnchor)
|
||||||
|
|
||||||
if item.scalarContent.needsEscaping or
|
if (style == yDumpJson and item.scalarTag in [yTagQuestionMark,
|
||||||
style in [yDumpCanonical, yDumpJson]:
|
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)
|
writeDoubleQuoted(item.scalarContent, target)
|
||||||
else:
|
else:
|
||||||
target.write(item.scalarContent)
|
target.write(item.scalarContent)
|
||||||
|
@ -193,21 +206,15 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
||||||
item.seqTag, tagLib, item.seqAnchor)
|
item.seqTag, tagLib, item.seqAnchor)
|
||||||
else:
|
else:
|
||||||
if style != yDumpJson:
|
if style != yDumpJson:
|
||||||
target.write('\x0A')
|
|
||||||
writeTagAndAnchor(target,
|
writeTagAndAnchor(target,
|
||||||
item.seqTag, tagLib, item.seqAnchor)
|
item.seqTag, tagLib, item.seqAnchor)
|
||||||
|
target.write('\x0A')
|
||||||
indentation += indentationStep
|
indentation += indentationStep
|
||||||
else:
|
else:
|
||||||
if nextState == dBlockSequenceItem:
|
startItem(target, style, indentation, levels[levels.high])
|
||||||
if style != yDumpJson:
|
if style != yDumpJson:
|
||||||
writeTagAndAnchor(target,
|
writeTagAndAnchor(target,
|
||||||
item.seqTag, tagLib, item.seqAnchor)
|
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)
|
|
||||||
indentation += indentationStep
|
indentation += indentationStep
|
||||||
|
|
||||||
if nextState == dFlowSequenceStart:
|
if nextState == dFlowSequenceStart:
|
||||||
|
@ -267,7 +274,8 @@ proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
||||||
indentation += indentationStep
|
indentation += indentationStep
|
||||||
else:
|
else:
|
||||||
if nextState in
|
if nextState in
|
||||||
[dBlockExplicitMapValue, dBlockImplicitMapValue]:
|
[dBlockExplicitMapValue, dBlockImplicitMapValue,
|
||||||
|
dBlockImplicitMapKey]:
|
||||||
if style != yDumpJson:
|
if style != yDumpJson:
|
||||||
writeTagAndAnchor(target,
|
writeTagAndAnchor(target,
|
||||||
item.mapTag, tagLib, item.mapAnchor)
|
item.mapTag, tagLib, item.mapAnchor)
|
||||||
|
@ -368,5 +376,5 @@ proc transform*(input: Stream, output: Stream, style: YamlDumpStyle,
|
||||||
var
|
var
|
||||||
tagLib = extendedTagLibrary()
|
tagLib = extendedTagLibrary()
|
||||||
parser = newParser(tagLib)
|
parser = newParser(tagLib)
|
||||||
dump(parser.parse(input), output, tagLib, style,
|
present(parser.parse(input), output, tagLib, style,
|
||||||
indentationStep)
|
indentationStep)
|
|
@ -29,6 +29,28 @@ type
|
||||||
BlockScalarStyle = enum
|
BlockScalarStyle = enum
|
||||||
bsLiteral, bsFolded
|
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 =
|
proc newParser*(tagLib: YamlTagLibrary): YamlSequentialParser =
|
||||||
new(result)
|
new(result)
|
||||||
result.tagLib = tagLib
|
result.tagLib = tagLib
|
||||||
|
@ -40,55 +62,6 @@ proc anchor*(parser: YamlSequentialParser, id: AnchorId): string =
|
||||||
return pair[0]
|
return pair[0]
|
||||||
return nil
|
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.} =
|
template yieldWarning(d: string) {.dirty.} =
|
||||||
yield YamlStreamEvent(kind: yamlWarning, description: d,
|
yield YamlStreamEvent(kind: yamlWarning, description: d,
|
||||||
line: lex.line, column: lex.column)
|
line: lex.line, column: lex.column)
|
||||||
|
|
|
@ -26,8 +26,7 @@ suite "Serialization":
|
||||||
test "Serialize string sequence":
|
test "Serialize string sequence":
|
||||||
var input = @["a", "b"]
|
var input = @["a", "b"]
|
||||||
var output = newStringStream()
|
var output = newStringStream()
|
||||||
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
|
dump(input, output, yDumpBlockOnly)
|
||||||
yDumpBlockOnly)
|
|
||||||
assert output.data == "%YAML 1.2\n--- \n- a\n- b"
|
assert output.data == "%YAML 1.2\n--- \n- a\n- b"
|
||||||
|
|
||||||
test "Load Table[int, string]":
|
test "Load Table[int, string]":
|
||||||
|
@ -47,8 +46,7 @@ suite "Serialization":
|
||||||
input[23] = "dreiundzwanzig"
|
input[23] = "dreiundzwanzig"
|
||||||
input[42] = "zweiundvierzig"
|
input[42] = "zweiundvierzig"
|
||||||
var output = newStringStream()
|
var output = newStringStream()
|
||||||
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
|
dump(input, output, yDumpBlockOnly)
|
||||||
yDumpBlockOnly)
|
|
||||||
assert output.data == "%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig"
|
assert output.data == "%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig"
|
||||||
|
|
||||||
test "Load Sequences in Sequence":
|
test "Load Sequences in Sequence":
|
||||||
|
@ -67,8 +65,7 @@ suite "Serialization":
|
||||||
test "Serialize Sequences in Sequence":
|
test "Serialize Sequences in Sequence":
|
||||||
let input = @[@[1, 2, 3], @[4, 5], @[6]]
|
let input = @[@[1, 2, 3], @[4, 5], @[6]]
|
||||||
var output = newStringStream()
|
var output = newStringStream()
|
||||||
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
|
dump(input, output, yDumpDefault)
|
||||||
yDumpDefault)
|
|
||||||
assert output.data == "%YAML 1.2\n--- \n- [1, 2, 3]\n- [4, 5]\n- [6]"
|
assert output.data == "%YAML 1.2\n--- \n- [1, 2, 3]\n- [4, 5]\n- [6]"
|
||||||
|
|
||||||
test "Load custom object":
|
test "Load custom object":
|
||||||
|
@ -86,6 +83,5 @@ suite "Serialization":
|
||||||
test "Serialize custom object":
|
test "Serialize custom object":
|
||||||
let input = Person(firstname: "Peter", surname: "Pan", age: 12)
|
let input = Person(firstname: "Peter", surname: "Pan", age: 12)
|
||||||
var output = newStringStream()
|
var output = newStringStream()
|
||||||
dump(wrapWithDocument(serialize(input)), output, coreTagLibrary(),
|
dump(input, output, yDumpBlockOnly)
|
||||||
yDumpBlockOnly)
|
|
||||||
assert output.data == "%YAML 1.2\n--- \nfirstname: Peter\nsurname: Pan\nage: 12"
|
assert output.data == "%YAML 1.2\n--- \nfirstname: Peter\nsurname: Pan\nage: 12"
|
23
yaml.nim
23
yaml.nim
|
@ -85,7 +85,7 @@ type
|
||||||
## The value ``mapMayHaveKeyObjects`` is a hint from a serializer and is
|
## The value ``mapMayHaveKeyObjects`` is a hint from a serializer and is
|
||||||
## used for choosing an appropriate presentation mode for a YAML map
|
## used for choosing an appropriate presentation mode for a YAML map
|
||||||
## (flow or block, explicit or implicit) by
|
## (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.
|
## If it is set to ``false``, the map may only have scalars as keys.
|
||||||
##
|
##
|
||||||
## The value ``scalarType`` is a hint from the lexer, see
|
## The value ``scalarType`` is a hint from the lexer, see
|
||||||
|
@ -239,8 +239,22 @@ proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool
|
||||||
proc `$`*(event: YamlStreamEvent): string
|
proc `$`*(event: YamlStreamEvent): string
|
||||||
## outputs a human-readable string describing the given event
|
## 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 `==`*(left, right: TagId): bool {.borrow.}
|
||||||
proc `$`*(id: TagId): string {.borrow.}
|
proc `$`*(id: TagId): string
|
||||||
proc hash*(id: TagId): Hash {.borrow.}
|
proc hash*(id: TagId): Hash {.borrow.}
|
||||||
|
|
||||||
proc `==`*(left, right: AnchorId): bool {.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
|
## check for these values and will output invalid JSON when rendering one
|
||||||
## of these values into a JSON character stream.
|
## of these values into a JSON character stream.
|
||||||
|
|
||||||
proc dump*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
||||||
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2)
|
style: YamlDumpStyle = yDumpDefault, indentationStep: int = 2)
|
||||||
## 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, style: YamlDumpStyle,
|
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.lexer
|
||||||
include private.tagLibrary
|
include private.tagLibrary
|
||||||
|
include private.events
|
||||||
include private.sequential
|
include private.sequential
|
||||||
include private.json
|
include private.json
|
||||||
include private.dumper
|
include private.dumper
|
|
@ -110,13 +110,6 @@ proc prepend*(event: YamlStreamEvent, s: YamlStream): YamlStream =
|
||||||
for e in s():
|
for e in s():
|
||||||
yield e
|
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) =
|
proc construct*(s: YamlStream, result: var string) =
|
||||||
let item = s()
|
let item = s()
|
||||||
if finished(s) or item.kind != yamlScalar:
|
if finished(s) or item.kind != yamlScalar:
|
||||||
|
@ -278,3 +271,21 @@ proc serialize*[K, V](value: Table[K, V],
|
||||||
for event in events():
|
for event in events():
|
||||||
yield event
|
yield event
|
||||||
yield YamlStreamEvent(kind: yamlEndMap)
|
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)
|
Loading…
Reference in New Issue