From 9be97ff38696ba8705538c08b0a39c075aea4ed3 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 16 Feb 2016 19:24:55 +0100 Subject: [PATCH] Added constructChild for better serialization. - Handle tag and anchor checks and ref types in constructChild - Added tests for using custom representObject and constructObject procs - Code cleanup --- private/serialization.nim | 322 +++++++++----------------------------- test/serializing.nim | 124 ++++++++------- 2 files changed, 140 insertions(+), 306 deletions(-) diff --git a/private/serialization.nim b/private/serialization.nim index 180d26e..1e8744e 100644 --- a/private/serialization.nim +++ b/private/serialization.nim @@ -44,7 +44,7 @@ static: var existingTuples = newSeq[NimNode]() -template presentTag(t: typedesc, ts: TagStyle): TagId = +template presentTag*(t: typedesc, ts: TagStyle): TagId = if ts == tsNone: yTagQuestionMark else: yamlTag(t) template setTagUriForType*(t: typedesc, uri: string): stmt = @@ -52,13 +52,15 @@ template setTagUriForType*(t: typedesc, uri: string): stmt = ## when loading and dumping values of this type. let id {.gensym.} = serializationTagLibrary.registerUri(uri) proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = id + ## autogenerated template setTagUriForType*(t: typedesc, uri: string, idName: expr): stmt = ## Like `setTagUriForType <#setTagUriForType,typedesc,string>`_, but lets ## you choose a symbol for the `TagId <#TagId>`_ of the uri. This is only ## necessary if you want to implement serialization / construction yourself. let idName* = serializationTagLibrary.registerUri(uri) - proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = idName + proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = + ## autogenerated setTagUriForType(char, "!nim:system:char", yTagNimChar) setTagUriForType(int8, "!nim:system:int8", yTagNimInt8) @@ -79,215 +81,46 @@ proc lazyLoadTag*(uri: string): TagId {.inline, raises: [].} = except KeyError: result = serializationTagLibrary.registerUri(uri) -macro serializable*(types: stmt): stmt = - ## Macro for customizing serialization of user-defined types. - ## Currently does not provide more features than just using the standard - ## serialization procs. This will change in the future. - assert types.kind == nnkTypeSection - result = newStmtList(types) - for typedef in types.children: - assert typedef.kind == nnkTypeDef - let - tName = $typedef[0].symbol - tIdent = newIdentNode(tName) - var - tUri: NimNode - recList: NimNode - assert typedef[1].kind == nnkEmpty - let objectTy = typedef[2] - case objectTy.kind - of nnkObjectTy: - assert objectTy[0].kind == nnkEmpty - assert objectTy[1].kind == nnkEmpty - tUri = newStrLitNode("!nim:custom:" & tName) - recList = objectTy[2] - of nnkTupleTy: - if objectTy in existingTuples: - continue - existingTuples.add(objectTy) - - recList = objectTy - tUri = newStmtList() - var - first = true - curStrLit = "!nim:tuple(" - curInfix = tUri - for field in objectFields(recList): - if first: - first = false - else: - curStrLit &= "," - curStrLit &= $field.name & "=" - var tmp = newNimNode(nnkInfix).add(newIdentNode("&"), - newStrLitNode(curStrLit)) - curInfix.add(tmp) - curInfix = tmp - tmp = newNimNode(nnkInfix).add(newIdentNode("&"), - newCall("safeTagUri", newCall("yamlTag", - newCall("type", field.t)))) - curInfix.add(tmp) - curInfix = tmp - curStrLit = "" - curInfix.add(newStrLitNode(curStrLit & ")")) - tUri = tUri[0] - else: - assert false - - # yamlTag() - - var yamlTagProc = newProc(newIdentNode("yamlTag"), [ - newIdentNode("TagId"), - newIdentDefs(newIdentNode("T"), newNimNode(nnkBracketExpr).add( - newIdentNode("typedesc"), tIdent))]) - var impl = newStmtList(newCall("lazyLoadTag", tUri)) - yamlTagProc[6] = impl - result.add(yamlTagProc) - - # constructObject() - - var constructProc = newProc(newIdentNode("constructObject"), [ - newEmptyNode(), - newIdentDefs(newIdentNode("s"), newIdentNode("YamlStream")), - newIdentDefs(newIdentNode("c"), - newIdentNode("ConstructionContext")), - newIdentDefs(newIdentNode("result"), - newNimNode(nnkVarTy).add(tIdent))]) - constructProc[4] = newNimNode(nnkPragma).add( - newNimNode(nnkExprColonExpr).add(newIdentNode("raises"), - newNimNode(nnkBracket).add( - newIdentNode("YamlConstructionError"), - newIdentNode("YamlStreamError")))) - impl = quote do: - var event = s() - if finished(s) or event.kind != yamlStartMap: - raise newException(YamlConstructionError, "Expected map start") - if event.mapTag != yTagQuestionMark and - event.mapTag != yamlTag(type(`tIdent`)): - raise newException(YamlConstructionError, - "Wrong tag for " & `tName`) - event = s() - assert(not finished(s)) - while event.kind != yamlEndMap: - assert event.kind == yamlScalar - assert event.scalarTag in [yTagQuestionMark, yTagString] - case event.scalarContent - else: - raise newException(YamlConstructionError, - "Unknown key for " & `tName` & ": " & - event.scalarContent) - event = s() - assert(not finished(s)) - var keyCase = impl[5][1][2] - assert keyCase.kind == nnkCaseStmt - for field in objectFields(recList): - keyCase.insert(1, newNimNode(nnkOfBranch).add( - newStrLitNode($field.name.ident)).add(newStmtList( - newCall("constructObject", [newIdentNode("s"), - newIdentNode("c"), - newDotExpr(newIdentNode("result"), field.name)]) - )) - ) - - constructProc[6] = impl - result.add(constructProc) - - # representObject() - - var representProc = newProc(newIdentNode("representObject"), [ - newIdentNode("RawYamlStream"), - newIdentDefs(newIdentNode("value"), tIdent), - newIdentDefs(newIdentNode("ts"), - newIdentNode("TagStyle")), - newIdentDefs(newIdentNode("c"), - newIdentNode("SerializationContext"))]) - representProc[4] = newNimNode(nnkPragma).add( - newNimNode(nnkExprColonExpr).add(newIdentNode("raises"), - newNimNode(nnkBracket))) - var iterBody = newStmtList( - newLetStmt(newIdentNode("childTagStyle"), newNimNode(nnkIfExpr).add( - newNimNode(nnkElifExpr).add( - newNimNode(nnkInfix).add(newIdentNode("=="), - newIdentNode("ts"), newIdentNode("tsRootOnly")), - newIdentNode("tsNone") - ), newNimNode(nnkElseExpr).add(newIdentNode("ts")))), - newNimNode(nnkYieldStmt).add( - newNimNode(nnkObjConstr).add(newIdentNode("YamlStreamEvent"), - newNimNode(nnkExprColonExpr).add(newIdentNode("kind"), - newIdentNode("yamlStartMap")), - newNimNode(nnkExprColonExpr).add(newIdentNode("mapTag"), - newNimNode(nnkIfExpr).add(newNimNode(nnkElifExpr).add( - newNimNode(nnkInfix).add(newIdentNode("=="), - newIdentNode("ts"), - newIdentNode("tsNone")), - newIdentNode("yTagQuestionMark") - ), newNimNode(nnkElseExpr).add( - newCall("yamlTag", newCall("type", tIdent)) - ))), - newNimNode(nnkExprColonExpr).add(newIdentNode("mapAnchor"), - newIdentNode("yAnchorNone")) - ) - ), newNimNode(nnkYieldStmt).add(newNimNode(nnkObjConstr).add( - newIdentNode("YamlStreamEvent"), newNimNode(nnkExprColonExpr).add( - newIdentNode("kind"), newIdentNode("yamlEndMap") - ) - ))) - - var i = 2 - for field in objectFields(recList): - let - fieldIterIdent = newIdentNode($field.name & "Events") - fieldNameString = newStrLitNode($field.name) - iterbody.insert(i, quote do: - yield YamlStreamEvent(kind: yamlScalar, - scalarTag: presentTag(string, - childTagStyle), - scalarAnchor: yAnchorNone, - scalarContent: `fieldNameString`) - ) - iterbody.insert(i + 1, newVarStmt(fieldIterIdent, - newCall("representObject", newDotExpr(newIdentNode("value"), - field.name), newIdentNode("childTagStyle"), - newIdentNode("c")))) - iterbody.insert(i + 2, quote do: - for event in `fieldIterIdent`(): - yield event - ) - i += 3 - impl = newStmtList(newAssignment(newIdentNode("result"), newProc( - newEmptyNode(), [newIdentNode("YamlStreamEvent")], iterBody, - nnkIteratorDef))) - representProc[6] = impl - result.add(representProc) - proc safeTagUri*(id: TagId): string {.raises: [].} = ## Internal function. Do not call explicitly. try: let uri = serializationTagLibrary.uri(id) if uri.len > 0 and uri[0] == '!': return uri[1..uri.len - 1] - else: - return uri + else: return uri except KeyError: # cannot happen (theoretically, you known) assert(false) -template constructScalarItem(bs: var YamlStream, item: YamlStreamEvent, - name: string, t: TagId, content: stmt) = +template constructScalarItem*(bs: var YamlStream, item: YamlStreamEvent, + name: string, content: stmt) = item = bs.next() if item.kind != yamlScalar: raise newException(YamlConstructionError, "Expected scalar") - if item.scalarTag notin [yTagQuestionMark, yTagExclamationMark, t]: - raise newException(YamlConstructionError, "Wrong tag for " & name) - try: - content - except YamlConstructionError: - raise + try: content + except YamlConstructionError: raise except Exception: var e = newException(YamlConstructionError, "Cannot construct to " & name & ": " & item.scalarContent) e.parent = getCurrentException() raise e +proc constructChild*[T](s: var YamlStream, c: ConstructionContext, + result: var T) + {.raises: [YamlConstructionError, YamlStreamError].} + ## Used for implementing ``constructObject`` on a non-scalar type. Call it + ## for constructing child values of the object that is constructed. It will + ## ensure correct tag and anchor on the input stream for the child object + ## and then call ``constructObject`` on the child object. + +proc constructChild*[O](s: var YamlStream, c: ConstructionContext, + result: var ref O) + {.raises: [YamlConstructionError, YamlStreamError].} + ## Used for implementing ``constructObject`` on a non-scalar type. Call it + ## for constructing child values of the object that is constructed. It will + ## handle anchors, aliases and nil values, and then call ``constructChild`` + ## on the base type. + proc yamlTag*(T: typedesc[string]): TagId {.inline, noSideEffect, raises: [].} = yTagString @@ -295,7 +128,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var string) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, "string", yTagString): + constructScalarItem(s, item, "string"): result = item.scalarContent proc representObject*(value: string, ts: TagStyle = tsNone, @@ -307,7 +140,7 @@ proc constructObject*[T: int8|int16|int32|int64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, name(T), yamlTag(T)): + constructScalarItem(s, item, name(T)): result = T(parseBiggestInt(item.scalarContent)) template constructObject*(s: var YamlStream, c: ConstructionContext, @@ -342,7 +175,7 @@ proc constructObject*[T: uint8|uint16|uint32|uint64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, name[T], yamlTag(T)): + constructScalarItem(s, item, name[T]): result = T(parseBiggestUInt(item.scalarContent)) template constructObject*(s: var YamlStream, c: ConstructionContext, @@ -367,7 +200,7 @@ proc constructObject*[T: float32|float64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, name(T), yamlTag(T)): + constructScalarItem(s, item, name(T)): let hint = guessType(item.scalarContent) case hint of yTypeFloat: @@ -394,14 +227,10 @@ proc representObject*[T: float32|float64](value: T, ts: TagStyle, var asString: string case value - of Inf: - asString = ".inf" - of NegInf: - asString = "-.inf" - of NaN: - asString = ".nan" - else: - asString = $value + of Inf: asString = ".inf" + of NegInf: asString = "-.inf" + of NaN: asString = ".nan" + else: asString = $value yield scalarEvent(asString, presentTag(T, ts), yAnchorNone) template representObject*(value: float, tagStyle: TagStyle, @@ -414,7 +243,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var bool) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, "bool", yTagBoolean): + constructScalarItem(s, item, "bool"): case guessType(item.scalarContent) of yTypeBoolTrue: result = true @@ -434,7 +263,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var char) {.raises: [YamlConstructionError, YamlStreamError].} = var item: YamlStreamEvent - constructScalarItem(s, item, "char", yTagNimChar): + constructScalarItem(s, item, "char"): if item.scalarContent.len != 1: raise newException(YamlConstructionError, "Cannot construct to char (length != 1): " & @@ -457,17 +286,10 @@ proc constructObject*[T](s: var YamlStream, c: ConstructionContext, let event = s.next() if event.kind != yamlStartSequence: raise newException(YamlConstructionError, "Expected sequence start") - if event.seqTag notin [yTagQuestionMark, yamlTag(seq[T])]: - raise newException(YamlConstructionError, "Wrong tag for seq[T]") result = newSeq[T]() while s.peek().kind != yamlEndSequence: var item: T - try: constructObject(s, c, item) - except AssertionError, YamlConstructionError, - YamlStreamError: raise - except: - # compiler bug: https://github.com/nim-lang/Nim/issues/3772 - assert(false) + constructChild(s, c, item) result.add(item) discard s.next() @@ -508,20 +330,13 @@ proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext, if event.kind != yamlStartMap: raise newException(YamlConstructionError, "Expected map start, got " & $event.kind) - if event.mapTag notin [yTagQuestionMark, yamlTag(Table[K, V])]: - raise newException(YamlConstructionError, "Wrong tag for Table[K, V]") result = initTable[K, V]() while s.peek.kind != yamlEndMap: var key: K value: V - try: - constructObject(s, c, key) - constructObject(s, c, value) - except AssertionError: raise - except Exception: - # compiler bug: https://github.com/nim-lang/Nim/issues/3772 - assert(false) + constructChild(s, c, key) + constructChild(s, c, value) result[key] = value discard s.next() @@ -566,10 +381,6 @@ template yamlTag*(T: typedesc[tuple]): expr = try: serializationTagLibrary.tags[uri] except KeyError: serializationTagLibrary.registerUri(uri) -proc constructObject*[O](s: var YamlStream, c: ConstructionContext, - result: var ref O) - {.raises: [YamlConstructionError, YamlStreamError].} - proc constructObject*[O: object|tuple](s: var YamlStream, c: ConstructionContext, result: var O) @@ -578,8 +389,6 @@ proc constructObject*[O: object|tuple](s: var YamlStream, if e.kind != yamlStartMap: raise newException(YamlConstructionError, "Expected map start, got " & $e.kind) - if e.mapAnchor != yAnchorNone: - raise newException(YamlConstructionError, "Anchor on a non-ref type") while s.peek.kind != yamlEndMap: let e = s.next() if e.kind != yamlScalar: @@ -588,7 +397,7 @@ proc constructObject*[O: object|tuple](s: var YamlStream, let name = e.scalarContent for fname, value in fieldPairs(result): if fname == name: - constructObject(s, c, value) + constructChild(s, c, value) break discard s.next() @@ -614,11 +423,6 @@ proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext, if e.kind != yamlScalar: raise newException(YamlConstructionError, "Expected scalar, got " & $e.kind) - if e.scalarAnchor != yAnchorNone: - raise newException(YamlConstructionError, "Anchor on a non-ref type") - if e.scalarTag notin [yTagQuestionMark, yamlTag(O)]: - raise newException(YamlConstructionError, - "Wrong tag for " & type(O).name) try: result = parseEnum[O](e.scalarContent) except ValueError: var ex = newException(YamlConstructionError, "Cannot parse '" & @@ -633,12 +437,38 @@ proc representObject*[O: enum](value: O, ts: TagStyle, proc yamlTag*[O](T: typedesc[ref O]): TagId {.inline, raises: [].} = yamlTag(O) -proc constructObject*[O](s: var YamlStream, c: ConstructionContext, +proc constructChild*[T](s: var YamlStream, c: ConstructionContext, + result: var T) = + let item = s.peek() + case item.kind + of yamlScalar: + if item.scalarTag notin [yTagQuestionMark, yTagExclamationMark, + yamlTag(T)]: + raise newException(YamlConstructionError, "Wrong tag for " & + typetraits.name(T)) + elif item.scalarAnchor != yAnchorNone: + raise newException(YamlConstructionError, "Anchor on non-ref type") + of yamlStartMap: + if item.mapTag notin [yTagQuestionMark, yamlTag(T)]: + raise newException(YamlConstructionError, "Wrong tag for " & + typetraits.name(T)) + elif item.mapAnchor != yAnchorNone: + raise newException(YamlConstructionError, "Anchor on non-ref type") + of yamlStartSequence: + if item.seqTag notin [yTagQuestionMark, yamlTag(T)]: + raise newException(YamlConstructionError, "Wrong tag for " & + typetraits.name(T)) + elif item.seqAnchor != yAnchorNone: + raise newException(YamlConstructionError, "Anchor on non-ref type") + else: assert false + constructObject(s, c, result) + +proc constructChild*[O](s: var YamlStream, c: ConstructionContext, result: var ref O) = var e = s.peek() if e.kind == yamlScalar: if e.scalarTag == yTagNull or ( - e.scalarTag in [yTagQuestionMark, yTagExclamationMark] and + e.scalarTag == yTagQuestionMark and guessType(e.scalarContent) == yTypeNull): result = nil discard s.next() @@ -664,7 +494,7 @@ proc constructObject*[O](s: var YamlStream, c: ConstructionContext, else: assert(false) s.peek = e try: - constructObject(s, c, result[]) + constructChild(s, c, result[]) except YamlConstructionError, YamlStreamError, AssertionError: raise except Exception: @@ -726,7 +556,7 @@ proc construct*[T](s: var YamlStream, target: var T) var e = s.next() assert(e.kind == yamlStartDocument) - constructObject(s, context, target) + constructChild(s, context, target) e = s.next() assert(e.kind == yamlEndDocument) except YamlConstructionError, YamlStreamError, AssertionError: @@ -745,8 +575,12 @@ proc load*[K](input: Stream, target: var K) events = parser.parse(input) try: construct(events, target) - except YamlConstructionError, AssertionError: - raise + except YamlConstructionError: + var e = (ref YamlConstructionError)(getCurrentException()) + e.line = parser.getLineNumber() + e.column = parser.getColNumber() + e.lineContent = parser.getLineContent() + raise e except YamlStreamError: let e = (ref YamlStreamError)(getCurrentException()) if e.parent of IOError: @@ -755,9 +589,6 @@ proc load*[K](input: Stream, target: var K) raise (ref YamlParserError)(e.parent) else: assert(false) - except Exception: - # compiler bug: https://github.com/nim-lang/Nim/issues/3772 - assert(false) proc setAnchor(a: var AnchorId, q: var seq[RefNodeData], n: var AnchorId) {.inline.} = @@ -770,8 +601,7 @@ proc setAnchor(a: var AnchorId, q: var seq[RefNodeData], n: var AnchorId) q[i].anchor = n a = n n = AnchorId(int(n) + 1) - else: - a = yAnchorNone + else: a = yAnchorNone break proc setAliasAnchor(a: var AnchorId, q: var seq[RefNodeData]) {.inline.} = diff --git a/test/serializing.nim b/test/serializing.nim index 072733a..0333011 100644 --- a/test/serializing.nim +++ b/test/serializing.nim @@ -1,5 +1,5 @@ import "../yaml" -import unittest +import unittest, strutils type MyTuple = tuple @@ -18,9 +18,30 @@ type Node = object value: string next: ref Node + + BetterInt = int setTagUriForType(TrafficLight, "!tl") setTagUriForType(Node, "!example.net:Node") +setTagUriForType(BetterInt, "!test:BetterInt") + +proc representObject*(value: BetterInt, ts: TagStyle = tsNone, + c: SerializationContext): RawYamlStream {.raises: [].} = + result = iterator(): YamlStreamEvent = + var + val = $value + i = val.len - 3 + while i > 0: + val.insert("_", i) + i -= 3 + yield scalarEvent(val, presentTag(BetterInt, ts), yAnchorNone) + +proc constructObject*(s: var YamlStream, c: ConstructionContext, + result: var BetterInt) + {.raises: [YamlConstructionError, YamlStreamError].} = + var item: YamlStreamEvent + constructScalarItem(s, item, "BetterInt"): + result = BetterInt(parseBiggestInt(item.scalarContent) + 1) template assertStringEqual(expected, actual: string) = for i in countup(0, min(expected.len, actual.len)): @@ -40,11 +61,8 @@ suite "Serialization": test "Serialization: Load string sequence": let input = newStringStream(" - a\n - b") - var - result: seq[string] - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: seq[string] + load(input, result) assert result.len == 2 assert result[0] == "a" assert result[1] == "b" @@ -57,11 +75,8 @@ suite "Serialization": test "Serialization: Load Table[int, string]": let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig") - var - result: Table[int32, string] - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: Table[int32, string] + load(input, result) assert result.len == 2 assert result[23] == "dreiundzwanzig" assert result[42] == "zweiundvierzig" @@ -78,11 +93,8 @@ suite "Serialization": test "Serialization: Load Sequences in Sequence": let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]") - var - result: seq[seq[int32]] - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: seq[seq[int32]] + load(input, result) assert result.len == 3 assert result[0] == @[1.int32, 2.int32, 3.int32] assert result[1] == @[4.int32, 5.int32] @@ -98,11 +110,8 @@ suite "Serialization": test "Serialization: Load Enum": let input = newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow") - var - result: seq[TrafficLight] - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: seq[TrafficLight] + load(input, result) assert result.len == 3 assert result[0] == tlRed assert result[1] == tlGreen @@ -117,11 +126,8 @@ suite "Serialization": test "Serialization: Load Tuple": let input = newStringStream("str: value\ni: 42\nb: true") - var - result: MyTuple - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: MyTuple + load(input, result) assert result.str == "value" assert result.i == 42 assert result.b == true @@ -135,11 +141,8 @@ suite "Serialization": test "Serialization: Load custom object": let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12") - var - result: Person - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: Person + load(input, result) assert result.firstnamechar == 'P' assert result.surname == "Pan" assert result.age == 12 @@ -155,11 +158,8 @@ suite "Serialization": test "Serialization: Load sequence with explicit tags": let input = newStringStream("--- !nim:system:seq(" & "tag:yaml.org,2002:str)\n- !!str one\n- !!str two") - var - result: seq[string] - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: seq[string] + load(input, result) assert result[0] == "one" assert result[1] == "two" @@ -174,11 +174,8 @@ suite "Serialization": test "Serialization: Load custom object with explicit root tag": let input = newStringStream( "--- !nim:custom:Person\nfirstnamechar: P\nsurname: Pan\nage: 12") - var - result: Person - parser = newYamlParser(tagLib) - events = parser.parse(input) - construct(events, result) + var result: Person + load(input, result) assert result.firstnamechar == 'P' assert result.surname == "Pan" assert result.age == 12 @@ -223,17 +220,12 @@ next: - *b - *c """) - var - result: seq[ref Node] - parser = newYamlParser(tagLib) - events = parser.parse(input) - try: - construct(events, result) + var result: seq[ref Node] + try: load(input, result) except YamlConstructionError: let ex = (ref YamlConstructionError)(getCurrentException()) - echo "line ", parser.getLineNumber, ", column ", - parser.getColNumber, ": ", ex.msg - echo parser.getLineContent + echo "line ", ex.line, ", column ", ex.column, ": ", ex.msg + echo ex.lineContent raise ex assert(result.len == 3) @@ -246,17 +238,12 @@ next: test "Serialization: Load nil values": let input = newStringStream("- ~\n- !!str ~") - var - result: seq[ref string] - parser = newYamlParser(tagLib) - events = parser.parse(input) - try: - construct(events, result) + var result: seq[ref string] + try: load(input, result) except YamlConstructionError: let ex = (ref YamlConstructionError)(getCurrentException()) - echo "line ", parser.getLineNumber, ", column ", - parser.getColNumber, ": ", ex.msg - echo parser.getLineContent + echo "line ", ex.line, ", column ", ex.column, ": ", ex.msg + echo ex.lineContent raise ex assert(result.len == 2) @@ -272,4 +259,21 @@ next: dump(input, output, psBlockOnly, tsRootOnly) assertStringEqual "%YAML 1.2\n--- !nim:system:seq(tag:yaml.org,2002:str) \n- !!null ~\n- !!str ~", output.data - \ No newline at end of file + + test "Serialization: Custom constructObject": + let input = newStringStream("- 1\n- !test:BetterInt 2") + var result: seq[BetterInt] + load(input, result) + assert(result.len == 2) + assert(result[0] == 2) + assert(result[1] == 3) + + test "Serialization: Custom representObject": + let input = @[1.BetterInt, 9998887.BetterInt, 98312.BetterInt] + var output = newStringStream() + dump(input, output, psBlockOnly, tsAll) + assertStringEqual """%YAML 1.2 +--- !nim:system:seq(test:BetterInt) +- !test:BetterInt 1 +- !test:BetterInt 9_998_887 +- !test:BetterInt 98_312""", output.data \ No newline at end of file