diff --git a/private/serialization.nim b/private/serialization.nim index 0e78a5a..1a95549 100644 --- a/private/serialization.nim +++ b/private/serialization.nim @@ -235,16 +235,14 @@ proc representObject*[T](value: seq[T], ts: TagStyle, ## represents a Nim seq as YAML sequence result = iterator(): YamlStreamEvent = let childTagStyle = if ts == tsRootOnly: tsNone else: ts - yield YamlStreamEvent(kind: yamlStartSeq, - seqTag: tag, - seqAnchor: yAnchorNone) + yield startSeqEvent(tag) for item in value: var events = representChild(item, childTagStyle, c) while true: let event = events() if finished(events): break yield event - yield YamlStreamEvent(kind: yamlEndSeq) + yield endSeqEvent() proc yamlTag*[K, V](T: typedesc[Table[K, V]]): TagId {.inline, raises: [].} = try: @@ -252,7 +250,7 @@ proc yamlTag*[K, V](T: typedesc[Table[K, V]]): TagId {.inline, raises: [].} = safeTagUri(yamlTag(V)) & ")" result = lazyLoadTag(uri) except KeyError: - # cannot happen (theoretically, you known) + # cannot happen (theoretically, you know) assert(false) proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext, @@ -270,6 +268,8 @@ proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext, value: V constructChild(s, c, key) constructChild(s, c, value) + if result.contains(key): + raise newException(YamlConstructionError, "Duplicate table key!") result[key] = value discard s.next() @@ -278,9 +278,7 @@ proc representObject*[K, V](value: Table[K, V], ts: TagStyle, ## represents a Nim Table as YAML mapping result = iterator(): YamlStreamEvent = let childTagStyle = if ts == tsRootOnly: tsNone else: ts - yield YamlStreamEvent(kind: yamlStartMap, - mapTag: tag, - mapAnchor: yAnchorNone) + yield startMapEvent(tag) for key, value in value.pairs: var events = representChild(key, childTagStyle, c) while true: @@ -292,7 +290,63 @@ proc representObject*[K, V](value: Table[K, V], ts: TagStyle, let event = events() if finished(events): break yield event - yield YamlStreamEvent(kind: yamlEndMap) + yield endMapEvent() + +proc yamlTag*[K, V](T: typedesc[OrderedTable[K, V]]): TagId + {.inline, raises: [].} = + try: + let uri = "!nim:tables:OrderedTable(" & safeTagUri(yamlTag(K)) & "," & + safeTagUri(yamlTag(V)) & ")" + result = lazyLoadTag(uri) + except KeyError: + # cannot happen (theoretically, you know) + assert(false) + +proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext, + result: var OrderedTable[K, V]) + {.raises: [YamlConstructionError, YamlStreamError].} = + ## constructs a Nim OrderedTable from a YAML mapping + let event = s.next() + if event.kind != yamlStartSeq: + raise newException(YamlConstructionError, "Expected seq start, got " & + $event.kind) + result = initOrderedTable[K, V]() + while s.peek.kind != yamlEndSeq: + var + key: K + value: V + if s.next().kind != yamlStartMap: + raise newException(YamlConstructionError, + "Expected map start, got " & $event.kind) + constructChild(s, c, key) + constructChild(s, c, value) + if s.next().kind != yamlEndMap: + raise newException(YamlConstructionError, + "Expected map end, got " & $event.kind) + if result.contains(key): + raise newException(YamlConstructionError, "Duplicate table key!") + result.add(key, value) + discard s.next() + +proc representObject*[K, V](value: OrderedTable[K, V], ts: TagStyle, + c: SerializationContext, tag: TagId): RawYamlStream {.raises: [].} = + result = iterator(): YamlStreamEvent = + let childTagStyle = if ts == tsRootOnly: tsNone else: ts + yield startSeqEvent(tag) + for key, value in value.pairs: + yield startMapEvent() + var events = representChild(key, childTagStyle, c) + while true: + let event = events() + if finished(events): break + yield event + events = representChild(value, childTagStyle, c) + while true: + let event = events() + if finished(events): break + yield event + yield endMapEvent() + yield endSeqEvent() template yamlTag*(T: typedesc[object|enum]): expr = var uri = when compiles(yamlTagId(T)): yamlTagId(T) else: @@ -546,13 +600,13 @@ proc represent*[T](value: T, ts: TagStyle = tsRootOnly, var context = newSerializationContext(a) objStream = iterator(): YamlStreamEvent = - yield YamlStreamEvent(kind: yamlStartDoc) + yield startDocEvent() var events = representChild(value, ts, context) while true: let e = events() if finished(events): break yield e - yield YamlStreamEvent(kind: yamlEndDoc) + yield endDocEvent() if a == asTidy: var objQueue = newSeq[YamlStreamEvent]() try: diff --git a/test/serializing.nim b/test/serializing.nim index f336d7d..67449ff 100644 --- a/test/serializing.nim +++ b/test/serializing.nim @@ -51,7 +51,8 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext, template assertStringEqual(expected, actual: string) = for i in countup(0, min(expected.len, actual.len)): if expected[i] != actual[i]: - echo "string mismatch at character #", i, ":" + echo "string mismatch at character #", i, "(expected:\'", + expected[i], "\', was \'", actual[i], "\'):" echo "expected:\n", expected, "\nactual:\n", actual assert(false) @@ -98,6 +99,41 @@ suite "Serialization": "%YAML 1.2\n--- \n23: dreiundzwanzig\n42: zweiundvierzig", output.data) + test "Serialization: Load OrderedTable[tuple[int32, int32], string]": + let input = newStringStream("- {a: 23, b: 42}: drzw\n- {a: 13, b: 47}: drsi") + var result: OrderedTable[tuple[a, b: int32], string] + load(input, result) + var i = 0 + for key, value in result.pairs: + case i + of 0: + assert key == (a: 23'i32, b: 42'i32) + assert value == "drzw" + of 1: + assert key == (a: 13'i32, b: 47'i32) + assert value == "drsi" + else: assert false + i.inc() + + test "Serialization: Represent OrderedTable[tuple[int32, int32], string]": + var input = initOrderedTable[tuple[a, b: int32], string]() + input.add((a: 23'i32, b: 42'i32), "dreiundzwanzigzweiundvierzig") + input.add((a: 13'i32, b: 47'i32), "dreizehnsiebenundvierzig") + var output = newStringStream() + dump(input, output, tsRootOnly, asTidy, blockOnly) + assertStringEqual("""%YAML 1.2 +--- !nim:tables:OrderedTable(nim:tuple(nim:system:int32,nim:system:int32),tag:yaml.org,2002:str) +- + ? + a: 23 + b: 42 + : dreiundzwanzigzweiundvierzig +- + ? + a: 13 + b: 47 + : dreizehnsiebenundvierzig""", output.data) + test "Serialization: Load Sequences in Sequence": let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]") var result: seq[seq[int32]]