-
Notifications
You must be signed in to change notification settings - Fork 1
/
uniony.nim
83 lines (71 loc) · 2.4 KB
/
uniony.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
import
std/[macros, genasts, sugar, algorithm],
pkg/[jsony, union],
pkg/union/uniontraits
proc parseHook*[T: Union](s: string, i: var int, v: var T) =
macro parseHookAux[T: Union](s: string, i: var int, v: var T): untyped =
let
union = v.getUnionType
# The symbol for the copy of `i` in case we have to rollback.
origIdx = nskLet.genSym("origIdx")
result = newStmtList()
# Add the declaration for origIdx
result.add newLetStmt(origIdx, i)
# Construct a block to handle parsing.
#
# The idea is to construct a block like this:
#
# block parser:
# doParse(<a type in union (ie. int)>)
# if successful:
# break parser
#
# doParse(<an another type (ie. string)>)
# if successful:
# break parser
#
# raise JsonError otherwise
let
parser = nskLabel.genSym("parser")
# The statements inside block
blkStmt = newStmtList()
blk = nnkBlockStmt.newTree(copy(parser), blkStmt)
# Collect the types within union
var types = collect(newSeq):
for _, _, typ in union.variants:
typ
# Sort them based on their kinds, with more specific types on top
#
# This exploits the natural ordering of NimTypeKind.
types.sort do (x, y: NimNode) -> int:
cmp(x.typeKind, y.typeKind)
# Construct parsing blocks for each type in union.
for typ in types.items:
blkStmt.add:
genAst(
typ, # A type within union
parser = copy(parser), # The parser label to break out of on success
i, # The index variable used by parseHook
origIdx, # The original position to rollback if parsing failed
v # The union we are creating
):
try:
var x: typ
parseHook(s, i, x)
# If parseHook succeed, assign the result to `v` and we are done.
v <- x
break parser
except JsonError:
# Otherwise rollback the index and let the next block try again
i = origIdx
# Add a raise at the end of the block. This raise is only reachable if all
# other parsers failed.
blkStmt.add:
genAst(i, v):
raise newException(JsonError, $typeof(v) & " expected at: " & $i)
# Add the block to the result.
result.add blk
parseHookAux(s, i, v)
proc dumpHook*[T: Union](s: var string, v: T) =
unpack(v):
dumpHook(s, it)