Can construct basic types and custom objects

* Added construct() procs for some basic types
 * Added macro make_serializable
This commit is contained in:
Felix Krause 2015-12-29 15:09:37 +01:00
parent 6617033fd5
commit dc1a3c0fe2
6 changed files with 241 additions and 2 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ nimcache
test/tests
test/parsing
test/lexing
test/serializing
test/*.exe
test/*.pdb
test/*.ilk

View File

@ -18,6 +18,11 @@ task parserTests, "Run parser tests":
--verbosity:0
setCommand "c", "test/parsing"
task serializationTests, "Run serialization tests":
--r
--verbosity:0
setCommand "c", "test/serializing"
task doc, "Generate documentation":
setCommand "doc2", "yaml"

61
test/serializing.nim Normal file
View File

@ -0,0 +1,61 @@
import "../yaml/serialization"
import unittest
make_serializable:
type
Person = object
firstname, surname: string
age: int
suite "Serialization":
setup:
var parser = newParser(coreTagLibrary())
test "Load string sequence":
let input = newStringStream(" - a\n - b")
var
result: seq[string]
events = parser.parse(input)
assert events().kind == yamlStartDocument
construct(events, result)
assert events().kind == yamlEndDocument
assert result.len == 2
assert result[0] == "a"
assert result[1] == "b"
test "Load Table[int, string]":
let input = newStringStream("23: dreiundzwanzig\n42: zweiundvierzig")
var
result: Table[int, string]
events = parser.parse(input)
assert events().kind == yamlStartDocument
construct(events, result)
assert events().kind == yamlEndDocument
assert result.len == 2
assert result[23] == "dreiundzwanzig"
assert result[42] == "zweiundvierzig"
test "Load Sequences in Sequence":
let input = newStringStream(" - [1, 2, 3]\n - [4, 5]\n - [6]")
var
result: seq[seq[int]]
events = parser.parse(input)
assert events().kind == yamlStartDocument
construct(events, result)
assert events().kind == yamlEndDocument
assert result.len == 3
assert result[0] == @[1, 2, 3]
assert result[1] == @[4, 5]
assert result[2] == @[6]
test "Load custom object":
let input = newStringStream("firstname: Peter\nsurname: Pan\nage: 12")
var
result: Person
events = parser.parse(input)
assert events().kind == yamlStartDocument
construct(events, result)
assert events().kind == yamlEndDocument
assert result.firstname == "Peter"
assert result.surname == "Pan"
assert result.age == 12

View File

@ -1 +1 @@
import lexing, parsing
import lexing, parsing, serializing

BIN
yaml/serialization Executable file

Binary file not shown.

172
yaml/serialization.nim Normal file
View File

@ -0,0 +1,172 @@
import "../yaml"
import macros, strutils, streams, tables, json, hashes
export yaml, streams, tables, json
static:
iterator objectFields(n: NimNode): tuple[name: NimNode, t: NimNode] =
assert n.kind == nnkRecList
for identDefs in n.children:
let numFields = identDefs.len - 2
for i in 0..numFields - 1:
yield (name: identDefs[i], t: identDefs[^2])
macro make_serializable*(types: stmt): stmt =
assert types.kind == nnkTypeSection
result = newStmtList(types)
for typedef in types.children:
assert typedef.kind == nnkTypeDef
let
tName = $typedef[0].symbol
tIdent = newIdentNode(tName)
assert typedef[1].kind == nnkEmpty
let objectTy = typedef[2]
assert objectTy.kind == nnkObjectTy
assert objectTy[0].kind == nnkEmpty
assert objectTy[1].kind == nnkEmpty
let recList = objectTy[2]
assert recList.kind == nnkRecList
var constructProc = newProc(newIdentNode("construct"), [
newEmptyNode(),
newIdentDefs(newIdentNode("s"), newNimNode(nnkVarTy).add(
newIdentNode("YamlStream"))),
newIdentDefs(newIdentNode("result"),
newNimNode(nnkVarTy).add(tIdent))])
var impl = quote do:
var event = s()
if finished(s) or event.kind != yamlStartMap:
raise newException(ValueError, "Construction error!" & $event.scalarContent)
if event.mapTag != yTagQuestionMark:
raise newException(ValueError, "Wrong tag for " & `tName`)
event = s()
if finished(s):
raise newException(ValueError, "Construction error! b")
while event.kind != yamlEndMap:
assert event.kind == yamlScalar
assert event.scalarTag == yTagQuestionMark
case hash(event.scalarContent)
else:
raise newException(ValueError, "Unknown key for " &
`tName` & ": " & event.scalarContent)
event = s()
if finished(s):
raise newException(ValueError, "Construction error! c")
var keyCase = impl[5][1][2]
assert keyCase.kind == nnkCaseStmt
for field in objectFields(recList):
let nameHash = hash($field.name.ident)
keyCase.insert(1, newNimNode(nnkOfBranch).add(
newIntLitNode(nameHash)).add(newStmtList(
newCall("construct", [newIdentNode("s"), newDotExpr(
newIdentNode("result"), field.name)])
))
)
constructProc[6] = impl
result.add(constructProc)
proc prepend*(event: YamlStreamEvent, s: YamlStream): YamlStream =
result = iterator(): YamlStreamEvent =
yield event
for e in s():
yield e
proc construct*(s: var YamlStream, result: var string) =
let item = s()
if finished(s) or item.kind != yamlScalar:
raise newException(ValueError, "Construction error!")
if item.scalarTag notin [yTagQuestionMark, yTagExclamationMark, yTagString]:
raise newException(ValueError, "Wrong tag for string.")
result = item.scalarContent
proc construct*(s: var YamlStream, result: var int) =
let item = s()
if finished(s) or item.kind != yamlScalar:
raise newException(ValueError, "Construction error!")
if item.scalarTag notin [yTagQuestionMark, yTagInteger] or
item.scalarType != yTypeInteger:
raise newException(ValueError, "Wrong scalar type for int.")
result = parseInt(item.scalarContent)
proc contruct*(s: var YamlStream, result: var int64) =
let item = s()
if finished(s) or item.kind != yamlScalar:
raise newException(ValueError, "Construction error!")
if item.scalarTag notin [yTagQuestionMark, yTagInteger] or
item.scalarType != yTypeInteger:
raise newException(ValueError, "Wrong scalar type for int64.")
result = parseBiggestInt(item.scalarContent)
proc construct*(s: var YamlStream, result: var float) =
let item = s()
if finished(s) or item.kind != yamlScalar:
raise newException(ValueError, "Construction error!")
if item.scalarTag notin [yTagQuestionMark, yTagFloat]:
raise newException(ValueError, "Wrong scalar type for float.")
case item.scalarType
of yTypeFloat:
result = parseFloat(item.scalarContent)
of yTypeFloatInf:
if item.scalarContent[0] == '-':
result = NegInf
else:
result = Inf
of yTypeFloatNaN:
result = NaN
else:
raise newException(ValueError, "Wrong scalar type for float.")
proc construct*(s: var YamlStream, result: var bool) =
let item = s()
if finished(s) or item.kind != yamlScalar:
raise newException(ValueError, "Construction error!")
if item.scalarTag notin [yTagQuestionMark, yTagBoolean]:
raise newException(ValueError, "Wrong scalar type for bool.")
case item.scalarType
of yTypeBoolTrue:
result = true
of yTypeBoolFalse:
result = false
else:
raise newException(ValueError, "Wrong scalar type for bool.")
proc construct*[T](s: var YamlStream, result: var seq[T]) =
var event = s()
if finished(s) or event.kind != yamlStartSequence:
raise newException(ValueError, "Construction error!")
if event.seqTag != yTagQuestionMark:
raise newException(ValueError, "Wrong sequence type for seq[T]")
result = newSeq[T]()
event = s()
if finished(s):
raise newException(ValueError, "Construction error!")
while event.kind != yamlEndSequence:
var
item: T
events = prepend(event, s)
construct(events, item)
result.add(item)
event = s()
if finished(s):
raise newException(ValueError, "Construction error!")
proc construct*[K, V](s: var YamlStream, result: var Table[K, V]) =
var event = s()
if finished(s) or event.kind != yamlStartMap:
raise newException(ValueError, "Construction error!")
if event.mapTag != yTagQuestionMark:
raise newException(ValueError, "Wrong map type for Table[K, V]")
result = initTable[K, V]()
event = s()
if finished(s):
raise newException(ValueError, "Construction error!")
while event.kind != yamlEndMap:
var
key: K
value: V
events = prepend(event, s)
construct(events, key)
construct(s, value)
result[key] = value
event = s()
if finished(s):
raise newException(ValueError, "Construction error!")