This works just fine:
import macros
type
Edge = object
neighbor {.cursor.}: Node
Node = ref NodeObj
NodeObj = object
neighbors: seq[Edge]
label: string
visited: bool
Graph = object
nodes: seq[Node]
# without lent it creates a colonTmp, sinks it to the cursor var, and then
destroys it.
proc addNode(self: var Graph; label: string): lent Node =
let node = Node(label: label)
self.nodes.add(node)
result = self.nodes[^1]
proc addEdge(self: Graph; source, neighbor: Node) =
let edge = Edge(neighbor: neighbor)
source.neighbors.add(edge)
macro operateOn*(x: typed; calls: untyped) =
result = copyNimNode(calls)
expectKind calls, {nnkStmtList, nnkStmtListExpr}
# non-recursive processing because that's exactly what we need here:
for y in calls:
expectKind y, nnkCallKinds
var call = newTree(y.kind)
call.add y[0]
call.add x
for j in 1 ..< y.len: call.add y[j]
result.add call
proc main =
var graph: Graph
let nodeA = graph.addNode("a")
let nodeB = graph.addNode("b")
let nodeC = graph.addNode("c")
let nodeD = graph.addNode("d")
let nodeE = graph.addNode("e")
let nodeF = graph.addNode("f")
let nodeG = graph.addNode("g")
let nodeH = graph.addNode("h")
echo "before addEdge"
operateOn(graph):
addEdge(nodeA, neighbor = nodeB)
addEdge(nodeA, neighbor = nodeC)
addEdge(nodeB, neighbor = nodeD)
addEdge(nodeB, neighbor = nodeE)
addEdge(nodeC, neighbor = nodeF)
addEdge(nodeC, neighbor = nodeG)
addEdge(nodeE, neighbor = nodeH)
addEdge(nodeE, neighbor = nodeF)
addEdge(nodeF, neighbor = nodeG)
# addEdge(nodeF, neighbor = nodeF) # cycle
# addEdge(nodeG, neighbor = nodeF) # more cycles
echo "after addEdge"
main()
echo getOccupiedMem()
Run