mirror of
https://github.com/status-im/NimYAML.git
synced 2025-01-11 20:14:19 +00:00
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]()
|
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)
|
if ts == tsNone: yTagQuestionMark else: yamlTag(t)
|
||||||
|
|
||||||
template setTagUriForType*(t: typedesc, uri: string): stmt =
|
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.
|
## when loading and dumping values of this type.
|
||||||
let id {.gensym.} = serializationTagLibrary.registerUri(uri)
|
let id {.gensym.} = serializationTagLibrary.registerUri(uri)
|
||||||
proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = id
|
proc yamlTag*(T: typedesc[t]): TagId {.inline, raises: [].} = id
|
||||||
|
## autogenerated
|
||||||
|
|
||||||
template setTagUriForType*(t: typedesc, uri: string, idName: expr): stmt =
|
template setTagUriForType*(t: typedesc, uri: string, idName: expr): stmt =
|
||||||
## Like `setTagUriForType <#setTagUriForType,typedesc,string>`_, but lets
|
## Like `setTagUriForType <#setTagUriForType,typedesc,string>`_, but lets
|
||||||
## you choose a symbol for the `TagId <#TagId>`_ of the uri. This is only
|
## you choose a symbol for the `TagId <#TagId>`_ of the uri. This is only
|
||||||
## necessary if you want to implement serialization / construction yourself.
|
## necessary if you want to implement serialization / construction yourself.
|
||||||
let idName* = serializationTagLibrary.registerUri(uri)
|
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(char, "!nim:system:char", yTagNimChar)
|
||||||
setTagUriForType(int8, "!nim:system:int8", yTagNimInt8)
|
setTagUriForType(int8, "!nim:system:int8", yTagNimInt8)
|
||||||
@ -79,215 +81,46 @@ proc lazyLoadTag*(uri: string): TagId {.inline, raises: [].} =
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
result = serializationTagLibrary.registerUri(uri)
|
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: [].} =
|
proc safeTagUri*(id: TagId): string {.raises: [].} =
|
||||||
## Internal function. Do not call explicitly.
|
## Internal function. Do not call explicitly.
|
||||||
try:
|
try:
|
||||||
let uri = serializationTagLibrary.uri(id)
|
let uri = serializationTagLibrary.uri(id)
|
||||||
if uri.len > 0 and uri[0] == '!':
|
if uri.len > 0 and uri[0] == '!':
|
||||||
return uri[1..uri.len - 1]
|
return uri[1..uri.len - 1]
|
||||||
else:
|
else: return uri
|
||||||
return uri
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# cannot happen (theoretically, you known)
|
# cannot happen (theoretically, you known)
|
||||||
assert(false)
|
assert(false)
|
||||||
|
|
||||||
template constructScalarItem(bs: var YamlStream, item: YamlStreamEvent,
|
template constructScalarItem*(bs: var YamlStream, item: YamlStreamEvent,
|
||||||
name: string, t: TagId, content: stmt) =
|
name: string, content: stmt) =
|
||||||
item = bs.next()
|
item = bs.next()
|
||||||
if item.kind != yamlScalar:
|
if item.kind != yamlScalar:
|
||||||
raise newException(YamlConstructionError, "Expected scalar")
|
raise newException(YamlConstructionError, "Expected scalar")
|
||||||
if item.scalarTag notin [yTagQuestionMark, yTagExclamationMark, t]:
|
try: content
|
||||||
raise newException(YamlConstructionError, "Wrong tag for " & name)
|
except YamlConstructionError: raise
|
||||||
try:
|
|
||||||
content
|
|
||||||
except YamlConstructionError:
|
|
||||||
raise
|
|
||||||
except Exception:
|
except Exception:
|
||||||
var e = newException(YamlConstructionError,
|
var e = newException(YamlConstructionError,
|
||||||
"Cannot construct to " & name & ": " & item.scalarContent)
|
"Cannot construct to " & name & ": " & item.scalarContent)
|
||||||
e.parent = getCurrentException()
|
e.parent = getCurrentException()
|
||||||
raise e
|
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: [].} =
|
proc yamlTag*(T: typedesc[string]): TagId {.inline, noSideEffect, raises: [].} =
|
||||||
yTagString
|
yTagString
|
||||||
|
|
||||||
@ -295,7 +128,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
|
|||||||
result: var string)
|
result: var string)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, "string", yTagString):
|
constructScalarItem(s, item, "string"):
|
||||||
result = item.scalarContent
|
result = item.scalarContent
|
||||||
|
|
||||||
proc representObject*(value: string, ts: TagStyle = tsNone,
|
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)
|
s: var YamlStream, c: ConstructionContext, result: var T)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, name(T), yamlTag(T)):
|
constructScalarItem(s, item, name(T)):
|
||||||
result = T(parseBiggestInt(item.scalarContent))
|
result = T(parseBiggestInt(item.scalarContent))
|
||||||
|
|
||||||
template constructObject*(s: var YamlStream, c: ConstructionContext,
|
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)
|
s: var YamlStream, c: ConstructionContext, result: var T)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, name[T], yamlTag(T)):
|
constructScalarItem(s, item, name[T]):
|
||||||
result = T(parseBiggestUInt(item.scalarContent))
|
result = T(parseBiggestUInt(item.scalarContent))
|
||||||
|
|
||||||
template constructObject*(s: var YamlStream, c: ConstructionContext,
|
template constructObject*(s: var YamlStream, c: ConstructionContext,
|
||||||
@ -367,7 +200,7 @@ proc constructObject*[T: float32|float64](
|
|||||||
s: var YamlStream, c: ConstructionContext, result: var T)
|
s: var YamlStream, c: ConstructionContext, result: var T)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, name(T), yamlTag(T)):
|
constructScalarItem(s, item, name(T)):
|
||||||
let hint = guessType(item.scalarContent)
|
let hint = guessType(item.scalarContent)
|
||||||
case hint
|
case hint
|
||||||
of yTypeFloat:
|
of yTypeFloat:
|
||||||
@ -394,14 +227,10 @@ proc representObject*[T: float32|float64](value: T, ts: TagStyle,
|
|||||||
var
|
var
|
||||||
asString: string
|
asString: string
|
||||||
case value
|
case value
|
||||||
of Inf:
|
of Inf: asString = ".inf"
|
||||||
asString = ".inf"
|
of NegInf: asString = "-.inf"
|
||||||
of NegInf:
|
of NaN: asString = ".nan"
|
||||||
asString = "-.inf"
|
else: asString = $value
|
||||||
of NaN:
|
|
||||||
asString = ".nan"
|
|
||||||
else:
|
|
||||||
asString = $value
|
|
||||||
yield scalarEvent(asString, presentTag(T, ts), yAnchorNone)
|
yield scalarEvent(asString, presentTag(T, ts), yAnchorNone)
|
||||||
|
|
||||||
template representObject*(value: float, tagStyle: TagStyle,
|
template representObject*(value: float, tagStyle: TagStyle,
|
||||||
@ -414,7 +243,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
|
|||||||
result: var bool)
|
result: var bool)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, "bool", yTagBoolean):
|
constructScalarItem(s, item, "bool"):
|
||||||
case guessType(item.scalarContent)
|
case guessType(item.scalarContent)
|
||||||
of yTypeBoolTrue:
|
of yTypeBoolTrue:
|
||||||
result = true
|
result = true
|
||||||
@ -434,7 +263,7 @@ proc constructObject*(s: var YamlStream, c: ConstructionContext,
|
|||||||
result: var char)
|
result: var char)
|
||||||
{.raises: [YamlConstructionError, YamlStreamError].} =
|
{.raises: [YamlConstructionError, YamlStreamError].} =
|
||||||
var item: YamlStreamEvent
|
var item: YamlStreamEvent
|
||||||
constructScalarItem(s, item, "char", yTagNimChar):
|
constructScalarItem(s, item, "char"):
|
||||||
if item.scalarContent.len != 1:
|
if item.scalarContent.len != 1:
|
||||||
raise newException(YamlConstructionError,
|
raise newException(YamlConstructionError,
|
||||||
"Cannot construct to char (length != 1): " &
|
"Cannot construct to char (length != 1): " &
|
||||||
@ -457,17 +286,10 @@ proc constructObject*[T](s: var YamlStream, c: ConstructionContext,
|
|||||||
let event = s.next()
|
let event = s.next()
|
||||||
if event.kind != yamlStartSequence:
|
if event.kind != yamlStartSequence:
|
||||||
raise newException(YamlConstructionError, "Expected sequence start")
|
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]()
|
result = newSeq[T]()
|
||||||
while s.peek().kind != yamlEndSequence:
|
while s.peek().kind != yamlEndSequence:
|
||||||
var item: T
|
var item: T
|
||||||
try: constructObject(s, c, item)
|
constructChild(s, c, item)
|
||||||
except AssertionError, YamlConstructionError,
|
|
||||||
YamlStreamError: raise
|
|
||||||
except:
|
|
||||||
# compiler bug: https://github.com/nim-lang/Nim/issues/3772
|
|
||||||
assert(false)
|
|
||||||
result.add(item)
|
result.add(item)
|
||||||
discard s.next()
|
discard s.next()
|
||||||
|
|
||||||
@ -508,20 +330,13 @@ proc constructObject*[K, V](s: var YamlStream, c: ConstructionContext,
|
|||||||
if event.kind != yamlStartMap:
|
if event.kind != yamlStartMap:
|
||||||
raise newException(YamlConstructionError, "Expected map start, got " &
|
raise newException(YamlConstructionError, "Expected map start, got " &
|
||||||
$event.kind)
|
$event.kind)
|
||||||
if event.mapTag notin [yTagQuestionMark, yamlTag(Table[K, V])]:
|
|
||||||
raise newException(YamlConstructionError, "Wrong tag for Table[K, V]")
|
|
||||||
result = initTable[K, V]()
|
result = initTable[K, V]()
|
||||||
while s.peek.kind != yamlEndMap:
|
while s.peek.kind != yamlEndMap:
|
||||||
var
|
var
|
||||||
key: K
|
key: K
|
||||||
value: V
|
value: V
|
||||||
try:
|
constructChild(s, c, key)
|
||||||
constructObject(s, c, key)
|
constructChild(s, c, value)
|
||||||
constructObject(s, c, value)
|
|
||||||
except AssertionError: raise
|
|
||||||
except Exception:
|
|
||||||
# compiler bug: https://github.com/nim-lang/Nim/issues/3772
|
|
||||||
assert(false)
|
|
||||||
result[key] = value
|
result[key] = value
|
||||||
discard s.next()
|
discard s.next()
|
||||||
|
|
||||||
@ -566,10 +381,6 @@ template yamlTag*(T: typedesc[tuple]): expr =
|
|||||||
try: serializationTagLibrary.tags[uri]
|
try: serializationTagLibrary.tags[uri]
|
||||||
except KeyError: serializationTagLibrary.registerUri(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,
|
proc constructObject*[O: object|tuple](s: var YamlStream,
|
||||||
c: ConstructionContext,
|
c: ConstructionContext,
|
||||||
result: var O)
|
result: var O)
|
||||||
@ -578,8 +389,6 @@ proc constructObject*[O: object|tuple](s: var YamlStream,
|
|||||||
if e.kind != yamlStartMap:
|
if e.kind != yamlStartMap:
|
||||||
raise newException(YamlConstructionError, "Expected map start, got " &
|
raise newException(YamlConstructionError, "Expected map start, got " &
|
||||||
$e.kind)
|
$e.kind)
|
||||||
if e.mapAnchor != yAnchorNone:
|
|
||||||
raise newException(YamlConstructionError, "Anchor on a non-ref type")
|
|
||||||
while s.peek.kind != yamlEndMap:
|
while s.peek.kind != yamlEndMap:
|
||||||
let e = s.next()
|
let e = s.next()
|
||||||
if e.kind != yamlScalar:
|
if e.kind != yamlScalar:
|
||||||
@ -588,7 +397,7 @@ proc constructObject*[O: object|tuple](s: var YamlStream,
|
|||||||
let name = e.scalarContent
|
let name = e.scalarContent
|
||||||
for fname, value in fieldPairs(result):
|
for fname, value in fieldPairs(result):
|
||||||
if fname == name:
|
if fname == name:
|
||||||
constructObject(s, c, value)
|
constructChild(s, c, value)
|
||||||
break
|
break
|
||||||
discard s.next()
|
discard s.next()
|
||||||
|
|
||||||
@ -614,11 +423,6 @@ proc constructObject*[O: enum](s: var YamlStream, c: ConstructionContext,
|
|||||||
if e.kind != yamlScalar:
|
if e.kind != yamlScalar:
|
||||||
raise newException(YamlConstructionError, "Expected scalar, got " &
|
raise newException(YamlConstructionError, "Expected scalar, got " &
|
||||||
$e.kind)
|
$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)
|
try: result = parseEnum[O](e.scalarContent)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
var ex = newException(YamlConstructionError, "Cannot parse '" &
|
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 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) =
|
result: var ref O) =
|
||||||
var e = s.peek()
|
var e = s.peek()
|
||||||
if e.kind == yamlScalar:
|
if e.kind == yamlScalar:
|
||||||
if e.scalarTag == yTagNull or (
|
if e.scalarTag == yTagNull or (
|
||||||
e.scalarTag in [yTagQuestionMark, yTagExclamationMark] and
|
e.scalarTag == yTagQuestionMark and
|
||||||
guessType(e.scalarContent) == yTypeNull):
|
guessType(e.scalarContent) == yTypeNull):
|
||||||
result = nil
|
result = nil
|
||||||
discard s.next()
|
discard s.next()
|
||||||
@ -664,7 +494,7 @@ proc constructObject*[O](s: var YamlStream, c: ConstructionContext,
|
|||||||
else: assert(false)
|
else: assert(false)
|
||||||
s.peek = e
|
s.peek = e
|
||||||
try:
|
try:
|
||||||
constructObject(s, c, result[])
|
constructChild(s, c, result[])
|
||||||
except YamlConstructionError, YamlStreamError, AssertionError:
|
except YamlConstructionError, YamlStreamError, AssertionError:
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -726,7 +556,7 @@ proc construct*[T](s: var YamlStream, target: var T)
|
|||||||
var e = s.next()
|
var e = s.next()
|
||||||
assert(e.kind == yamlStartDocument)
|
assert(e.kind == yamlStartDocument)
|
||||||
|
|
||||||
constructObject(s, context, target)
|
constructChild(s, context, target)
|
||||||
e = s.next()
|
e = s.next()
|
||||||
assert(e.kind == yamlEndDocument)
|
assert(e.kind == yamlEndDocument)
|
||||||
except YamlConstructionError, YamlStreamError, AssertionError:
|
except YamlConstructionError, YamlStreamError, AssertionError:
|
||||||
@ -745,8 +575,12 @@ proc load*[K](input: Stream, target: var K)
|
|||||||
events = parser.parse(input)
|
events = parser.parse(input)
|
||||||
try:
|
try:
|
||||||
construct(events, target)
|
construct(events, target)
|
||||||
except YamlConstructionError, AssertionError:
|
except YamlConstructionError:
|
||||||
raise
|
var e = (ref YamlConstructionError)(getCurrentException())
|
||||||
|
e.line = parser.getLineNumber()
|
||||||
|
e.column = parser.getColNumber()
|
||||||
|
e.lineContent = parser.getLineContent()
|
||||||
|
raise e
|
||||||
except YamlStreamError:
|
except YamlStreamError:
|
||||||
let e = (ref YamlStreamError)(getCurrentException())
|
let e = (ref YamlStreamError)(getCurrentException())
|
||||||
if e.parent of IOError:
|
if e.parent of IOError:
|
||||||
@ -755,9 +589,6 @@ proc load*[K](input: Stream, target: var K)
|
|||||||
raise (ref YamlParserError)(e.parent)
|
raise (ref YamlParserError)(e.parent)
|
||||||
else:
|
else:
|
||||||
assert(false)
|
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)
|
proc setAnchor(a: var AnchorId, q: var seq[RefNodeData], n: var AnchorId)
|
||||||
{.inline.} =
|
{.inline.} =
|
||||||
@ -770,8 +601,7 @@ proc setAnchor(a: var AnchorId, q: var seq[RefNodeData], n: var AnchorId)
|
|||||||
q[i].anchor = n
|
q[i].anchor = n
|
||||||
a = n
|
a = n
|
||||||
n = AnchorId(int(n) + 1)
|
n = AnchorId(int(n) + 1)
|
||||||
else:
|
else: a = yAnchorNone
|
||||||
a = yAnchorNone
|
|
||||||
break
|
break
|
||||||
|
|
||||||
proc setAliasAnchor(a: var AnchorId, q: var seq[RefNodeData]) {.inline.} =
|
proc setAliasAnchor(a: var AnchorId, q: var seq[RefNodeData]) {.inline.} =
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "../yaml"
|
import "../yaml"
|
||||||
import unittest
|
import unittest, strutils
|
||||||
|
|
||||||
type
|
type
|
||||||
MyTuple = tuple
|
MyTuple = tuple
|
||||||
@ -19,8 +19,29 @@ type
|
|||||||
value: string
|
value: string
|
||||||
next: ref Node
|
next: ref Node
|
||||||
|
|
||||||
|
BetterInt = int
|
||||||
|
|
||||||
setTagUriForType(TrafficLight, "!tl")
|
setTagUriForType(TrafficLight, "!tl")
|
||||||
setTagUriForType(Node, "!example.net:Node")
|
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) =
|
template assertStringEqual(expected, actual: string) =
|
||||||
for i in countup(0, min(expected.len, actual.len)):
|
for i in countup(0, min(expected.len, actual.len)):
|
||||||
@ -40,11 +61,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load string sequence":
|
test "Serialization: Load string sequence":
|
||||||
let input = newStringStream(" - a\n - b")
|
let input = newStringStream(" - a\n - b")
|
||||||
var
|
var result: seq[string]
|
||||||
result: seq[string]
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.len == 2
|
assert result.len == 2
|
||||||
assert result[0] == "a"
|
assert result[0] == "a"
|
||||||
assert result[1] == "b"
|
assert result[1] == "b"
|
||||||
@ -57,11 +75,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load Table[int, string]":
|
test "Serialization: Load Table[int, string]":
|
||||||
let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig")
|
let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig")
|
||||||
var
|
var result: Table[int32, string]
|
||||||
result: Table[int32, string]
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.len == 2
|
assert result.len == 2
|
||||||
assert result[23] == "dreiundzwanzig"
|
assert result[23] == "dreiundzwanzig"
|
||||||
assert result[42] == "zweiundvierzig"
|
assert result[42] == "zweiundvierzig"
|
||||||
@ -78,11 +93,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load Sequences in Sequence":
|
test "Serialization: Load Sequences in Sequence":
|
||||||
let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]")
|
let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]")
|
||||||
var
|
var result: seq[seq[int32]]
|
||||||
result: seq[seq[int32]]
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.len == 3
|
assert result.len == 3
|
||||||
assert result[0] == @[1.int32, 2.int32, 3.int32]
|
assert result[0] == @[1.int32, 2.int32, 3.int32]
|
||||||
assert result[1] == @[4.int32, 5.int32]
|
assert result[1] == @[4.int32, 5.int32]
|
||||||
@ -98,11 +110,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load Enum":
|
test "Serialization: Load Enum":
|
||||||
let input = newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow")
|
let input = newStringStream("!nim:system:seq(tl)\n- !tl tlRed\n- tlGreen\n- tlYellow")
|
||||||
var
|
var result: seq[TrafficLight]
|
||||||
result: seq[TrafficLight]
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.len == 3
|
assert result.len == 3
|
||||||
assert result[0] == tlRed
|
assert result[0] == tlRed
|
||||||
assert result[1] == tlGreen
|
assert result[1] == tlGreen
|
||||||
@ -117,11 +126,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load Tuple":
|
test "Serialization: Load Tuple":
|
||||||
let input = newStringStream("str: value\ni: 42\nb: true")
|
let input = newStringStream("str: value\ni: 42\nb: true")
|
||||||
var
|
var result: MyTuple
|
||||||
result: MyTuple
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.str == "value"
|
assert result.str == "value"
|
||||||
assert result.i == 42
|
assert result.i == 42
|
||||||
assert result.b == true
|
assert result.b == true
|
||||||
@ -135,11 +141,8 @@ suite "Serialization":
|
|||||||
|
|
||||||
test "Serialization: Load custom object":
|
test "Serialization: Load custom object":
|
||||||
let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12")
|
let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12")
|
||||||
var
|
var result: Person
|
||||||
result: Person
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.firstnamechar == 'P'
|
assert result.firstnamechar == 'P'
|
||||||
assert result.surname == "Pan"
|
assert result.surname == "Pan"
|
||||||
assert result.age == 12
|
assert result.age == 12
|
||||||
@ -155,11 +158,8 @@ suite "Serialization":
|
|||||||
test "Serialization: Load sequence with explicit tags":
|
test "Serialization: Load sequence with explicit tags":
|
||||||
let input = newStringStream("--- !nim:system:seq(" &
|
let input = newStringStream("--- !nim:system:seq(" &
|
||||||
"tag:yaml.org,2002:str)\n- !!str one\n- !!str two")
|
"tag:yaml.org,2002:str)\n- !!str one\n- !!str two")
|
||||||
var
|
var result: seq[string]
|
||||||
result: seq[string]
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result[0] == "one"
|
assert result[0] == "one"
|
||||||
assert result[1] == "two"
|
assert result[1] == "two"
|
||||||
|
|
||||||
@ -174,11 +174,8 @@ suite "Serialization":
|
|||||||
test "Serialization: Load custom object with explicit root tag":
|
test "Serialization: Load custom object with explicit root tag":
|
||||||
let input = newStringStream(
|
let input = newStringStream(
|
||||||
"--- !nim:custom:Person\nfirstnamechar: P\nsurname: Pan\nage: 12")
|
"--- !nim:custom:Person\nfirstnamechar: P\nsurname: Pan\nage: 12")
|
||||||
var
|
var result: Person
|
||||||
result: Person
|
load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
construct(events, result)
|
|
||||||
assert result.firstnamechar == 'P'
|
assert result.firstnamechar == 'P'
|
||||||
assert result.surname == "Pan"
|
assert result.surname == "Pan"
|
||||||
assert result.age == 12
|
assert result.age == 12
|
||||||
@ -223,17 +220,12 @@ next:
|
|||||||
- *b
|
- *b
|
||||||
- *c
|
- *c
|
||||||
""")
|
""")
|
||||||
var
|
var result: seq[ref Node]
|
||||||
result: seq[ref Node]
|
try: load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
try:
|
|
||||||
construct(events, result)
|
|
||||||
except YamlConstructionError:
|
except YamlConstructionError:
|
||||||
let ex = (ref YamlConstructionError)(getCurrentException())
|
let ex = (ref YamlConstructionError)(getCurrentException())
|
||||||
echo "line ", parser.getLineNumber, ", column ",
|
echo "line ", ex.line, ", column ", ex.column, ": ", ex.msg
|
||||||
parser.getColNumber, ": ", ex.msg
|
echo ex.lineContent
|
||||||
echo parser.getLineContent
|
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
assert(result.len == 3)
|
assert(result.len == 3)
|
||||||
@ -246,17 +238,12 @@ next:
|
|||||||
|
|
||||||
test "Serialization: Load nil values":
|
test "Serialization: Load nil values":
|
||||||
let input = newStringStream("- ~\n- !!str ~")
|
let input = newStringStream("- ~\n- !!str ~")
|
||||||
var
|
var result: seq[ref string]
|
||||||
result: seq[ref string]
|
try: load(input, result)
|
||||||
parser = newYamlParser(tagLib)
|
|
||||||
events = parser.parse(input)
|
|
||||||
try:
|
|
||||||
construct(events, result)
|
|
||||||
except YamlConstructionError:
|
except YamlConstructionError:
|
||||||
let ex = (ref YamlConstructionError)(getCurrentException())
|
let ex = (ref YamlConstructionError)(getCurrentException())
|
||||||
echo "line ", parser.getLineNumber, ", column ",
|
echo "line ", ex.line, ", column ", ex.column, ": ", ex.msg
|
||||||
parser.getColNumber, ": ", ex.msg
|
echo ex.lineContent
|
||||||
echo parser.getLineContent
|
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
assert(result.len == 2)
|
assert(result.len == 2)
|
||||||
@ -273,3 +260,20 @@ next:
|
|||||||
assertStringEqual "%YAML 1.2\n--- !nim:system:seq(tag:yaml.org,2002:str) \n- !!null ~\n- !!str ~",
|
assertStringEqual "%YAML 1.2\n--- !nim:system:seq(tag:yaml.org,2002:str) \n- !!null ~\n- !!str ~",
|
||||||
output.data
|
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…
x
Reference in New Issue
Block a user