mirror of https://github.com/status-im/NimYAML.git
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
This commit is contained in:
parent
0a7f87a539
commit
9be97ff386
|
@ -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.} =
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
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
|
Loading…
Reference in New Issue