From a094089fb9cb315801397adf6693c4075b1f448f Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 29 Dec 2015 18:22:55 +0100 Subject: [PATCH] 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 --- private/dumper.nim | 48 ++++++++++++++++------------ private/sequential.nim | 71 +++++++++++++----------------------------- test/serializing.nim | 12 +++---- yaml.nim | 23 +++++++++++--- yaml/serialization.nim | 27 +++++++++++----- 5 files changed, 92 insertions(+), 89 deletions(-) diff --git a/private/dumper.nim b/private/dumper.nim index 8064be8..42bf111 100644 --- a/private/dumper.nim +++ b/private/dumper.nim @@ -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) \ No newline at end of file + present(parser.parse(input), output, tagLib, style, + indentationStep) \ No newline at end of file diff --git a/private/sequential.nim b/private/sequential.nim index 432249b..7e3b4f2 100644 --- a/private/sequential.nim +++ b/private/sequential.nim @@ -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 @@ -39,55 +61,6 @@ proc anchor*(parser: YamlSequentialParser, id: AnchorId): string = if pair[1] == id: 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, diff --git a/test/serializing.nim b/test/serializing.nim index dce301c..7e456db 100644 --- a/test/serializing.nim +++ b/test/serializing.nim @@ -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" \ No newline at end of file diff --git a/yaml.nim b/yaml.nim index f587265..2f46621 100644 --- a/yaml.nim +++ b/yaml.nim @@ -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 \ No newline at end of file diff --git a/yaml/serialization.nim b/yaml/serialization.nim index 5622140..e8cf32c 100644 --- a/yaml/serialization.nim +++ b/yaml/serialization.nim @@ -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: @@ -277,4 +270,22 @@ proc serialize*[K, V](value: Table[K, V], events = serialize(value, verboseTags) for event in events(): yield event - yield YamlStreamEvent(kind: yamlEndMap) \ No newline at end of file + 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) \ No newline at end of file