-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathnesm.nim
160 lines (150 loc) · 5.46 KB
/
nesm.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
when defined(js):
error("Non C-like targets non supported yet.")
{.deadCodeElim: on.}
import macros
from streams import Stream
from nesm/typesinfo import TypeChunk, Context, initContext
from nesm/generator import genTypeChunk
from nesm/settings import applyOptions, splitSettingsExpr
when defined(nimdoc):
# Workaround to make nimdoc happy
proc generateProcs(ctx: Context, n: NimNode): NimNode = discard
include nesm/documentation
else:
from nesm/procgen import generateProcs
const NimCumulativeVersion = NimMajor * 10000 + NimMinor * 100 + NimPatch
when NimCumulativeVersion >= 1801:
from nesm/cache import storeContext, getContext
else:
proc getImpl(s: NimNode): NimNode =
s.symbol.getImpl()
static:
var ctx = initContext()
proc storeContext(context: Context) {.compileTime.} =
ctx = initContext()
ctx.declared = context.declared
proc getContext(): Context {.compileTime.} = ctx
proc prepare(context: var Context, statements: NimNode
): NimNode {.compileTime.} =
result = newStmtList()
case statements.kind
of nnkStmtList, nnkTypeSection, nnkStaticStmt:
let oldstatic = context.is_static
context.is_static = context.is_static or
(statements.kind == nnkStaticStmt)
for child in statements.children():
result.add(context.prepare(child))
context.is_static = oldstatic
of nnkTypeDef:
result.add(context.generateProcs(statements))
else:
error("Only type declarations can be serializable")
proc cleanupTypeDeclaration(declaration: NimNode): NimNode =
var children = newSeq[NimNode]()
let settingsKeyword = newIdentNode("set")
if declaration.len == 0:
return declaration
for c in declaration.children():
case c.kind
of nnkStaticStmt:
for cc in c.children():
children.add(cleanupTypeDeclaration(cc))
of nnkIdentDefs:
let last = if c[^1].kind == nnkEmpty: c.len - 2 else: c.len - 1
let (originalType, options) = c[last].splitSettingsExpr()
if options.len > 0:
# newID need to be a NimNode that contains IdentDefs node
# to utilze recursive call of cleanupTypeDeclaration
var newID = newTree(nnkStmtList, c)
#copyChildrenTo(c, newID[0])
# first element of CurlyExpr is an actual type
newID[0][last] = originalType
children.add(newID.cleanupTypeDeclaration()[0])
elif c[last].repr == "cstring":
var newID = copyNimNode(c)
copyChildrenTo(c, newID)
newID[last] = newIdentNode("string")
children.add(newID)
elif c.len == 3 and c[0] == settingsKeyword and
c[1].kind == nnkTableConstr:
continue
elif c[last].kind == nnkTupleTy:
var newID = copyNimNode(c)
copyChildrenTo(c, newID)
newID[last] = cleanupTypeDeclaration(c[last])
children.add(newID)
else:
children.add(c)
else:
children.add(cleanupTypeDeclaration(c))
copyNimNode(declaration).add(children)
macro nonIntrusiveBody(typename: typed, o: untyped, de: static[bool]): untyped =
let typebody = getTypeImpl(typename)
let ctx = getContext()
when defined(debug):
hint("Deserialize? " & $de)
hint(typebody.treeRepr)
hint(typebody.repr)
let chunk = ctx.genTypeChunk(typebody)
result = if de: chunk.deserialize(o) else: chunk.serialize(o)
when defined(debug):
hint(result.repr)
template nonIntrusiveTemplate[S](o: S, de: static[bool]) =
nonIntrusiveBody(S, o, de)
proc serialize*[T](obj: T, thestream: Stream) =
## The non-intrusive serialize proc which allows to perform object
## serialization without special declaration.
## The negative side-effect of such approach is inability to pass any options
## (like `endian` or `dynamic`) to the serializer and lack of nested objects
## support. This proc should be imported directly.
nonIntrusiveTemplate(obj, false)
proc deserialize*[T](thestream: Stream): T =
## The non-intrusive deserialize proc which allows to perform object
## deserialization without special declaration.
## The negative side-effect of such approach is inability to pass any options
## (like `endian` or `dynamic`) to the serializer and lack of nested objects
## support. This proc should be imported directly.
nonIntrusiveTemplate(result, true)
macro toSerializable*(typedecl: typed, settings: varargs[untyped]): untyped =
## Generate [de]serialize procedures for existing type with given settings.
##
## Settings should be supplied in the **key: value, key:value** format.
##
## For example:
##
## .. code-block:: nim
## toSerializable(TheType, endian: bigEndian, dynamic: false)
##
## Avalible options:
## * **endian** - set the endian of serialized object
## * **dynamic** - if set to 'false' then object treated as **static**
result = newStmtList()
let ctx = getContext()
when defined(debug):
hint(typedecl.getImpl().treeRepr())
var ast = typedecl.getImpl()
var newctx = ctx.applyOptions(settings)
when defined(debug):
hint(ast.treeRepr)
result.add(newctx.prepare(ast))
newctx.storeContext()
when defined(debug):
hint(result.repr)
macro serializable*(typedecl: untyped): untyped =
## The main macro that generates code.
##
## Usage:
##
## .. code-block:: nim
## serializable:
## # Type declaration
##
result = cleanupTypeDeclaration(typedecl)
var ctx = getContext()
when defined(debug):
hint(typedecl.treeRepr)
when not defined(nimdoc):
result.add(ctx.prepare(typedecl))
when defined(debug):
hint(result.repr)
ctx.storeContext()