-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcascade.nim
83 lines (68 loc) · 2.34 KB
/
cascade.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 macros
# rewrite a tree of dot expressions to make the
# given `id` the topmost object in the chain
proc rewriteDotExpr (id, dotExpr: NimNode): NimNode =
var lhs = dotExpr[0]
var rhs = dotExpr[1]
if lhs.kind != nnkDotExpr:
return newDotExpr(newDotExpr(id, lhs), rhs)
result = newDotExpr(id, rhs)
var chain: seq[NimNode]
while true:
if lhs.kind == nnkDotExpr:
rhs = lhs[1]
lhs = lhs[0]
chain.add rhs
else:
chain.add lhs
break
for i in countdown(chain.len - 1, 0):
result[0] = newDotExpr(result[0], chain[i])
macro cascade* (obj: typed, body: untyped): untyped =
let statements =
if body.kind == nnkDo: body[6]
else: body
statements.expectKind(nnkStmtList)
proc transform (id, statementList: NimNode, init = newStmtList()): NimNode =
result = init
for statement in statementList:
case statement.kind
of nnkIfStmt, nnkWhenStmt:
# recurse into each branch of `if` or `when`
for branch in statement:
case branch.kind
of nnkElifBranch:
branch[1] = transform(id, branch[1])
of nnkElse:
branch[0] = transform(id, branch[0])
else:
discard
result.add statement
of nnkAsgn:
var lhs = statement[0]
if lhs.kind == nnkDotExpr:
lhs = rewriteDotExpr(id, lhs)
else:
lhs = newDotExpr(id, lhs)
result.add newAssignment(lhs, statement[1])
of nnkCall, nnkCommand:
var call: NimNode
if statement[0].kind == nnkDotExpr:
call = newCall(rewriteDotExpr(id, statement[0]))
else:
call = newCall(statement[0], id)
for i in 1 ..< statement.len:
call.add statement[i]
result.add call
else:
result.add statement
# create a `var` declaration with a unique generated identifier name
let id = genSym(kind = nskVar)
# assign the initial object to this new `var`
let assignment = newVarStmt(id, obj)
# kick off transformation on a new statement list
# each `statement` in `statements` will be added to this
# new statement list after being bound to the initial object
result = transform(id, statements, newStmtList(assignment))
# return the initial object
result.add id