Skip to content

Instantly share code, notes, and snippets.

@Araq
Created November 28, 2017 01:54
Show Gist options
  • Save Araq/67c7701f78fc527330d1875b5fa3a1cf to your computer and use it in GitHub Desktop.
Save Araq/67c7701f78fc527330d1875b5fa3a1cf to your computer and use it in GitHub Desktop.
import macros, sets, strutils
type
Elem = object
x, y: int
Elems = seq[Elem]
ElemId = distinct uint16 # as ptr
Obj = object
p: ptr Elem
# UnitId = distinct int32
# p.fieldname
# a[i].fieldname
template `@`(a, i): untyped = addr(a[int(i)])
proc mutate(e: var seq[Elem]) =
for i in 0..<100:
e.add Elem(x: 12, y: 88)
proc h(n, firstParam: NimNode; interesting: HashSet[string]): NimNode =
if n.kind == nnkDotExpr and repr(n[0]) in interesting:
result = newTree(nnkDotExpr, newTree(nnkBracketExpr, firstParam, newCall(bindSym"int", n[0])), n[1])
else:
result = copyNimNode(n)
for x in n: result.add h(x, firstParam, interesting)
proc ptrsOnSeq(n: NimNode): NimNode =
# - Collect the parameter names we want to "transform" into pointers.
# - Process the body of the proc, replace 'param.field' with 'firstParam[param].field'
let params = params(n)
var interesting = initSet[string]()
for i in 1..<params.len:
let it = params[i]
expectKind(it, nnkIdentDefs) # one, two: TypeHere = default
if repr(it[it.len-2]).endsWith"Id":
for j in 0..it.len-3:
interesting.incl repr(it[j])
result = copyNimTree(n)
result.body = h(n.body, params[1][0], interesting)
template derivedPtrName(x): untyped = x & "DerivedPtr"
proc h2(n: NimNode; interesting: HashSet[string]): NimNode =
if n.kind == nnkDotExpr and repr(n[0]) in interesting:
result = newTree(nnkDotExpr, ident(derivedPtrName repr(n[0])), n[1])
else:
result = copyNimNode(n)
for x in n: result.add h2(x, interesting)
proc ptrsOnOpenArray(n: NimNode): NimNode =
# - Collect the parameter names we want to "transform" into pointers.
# - Add statements like let paramIdDerivedPtr = addr(firstParam[paramId.int])
# - Process the body of the proc, replace
# with 'derivedPtr(firstParam[param]).field'
let params = params(n)
var interesting = initSet[string]()
let initSection = newStmtList()
let theArray = params[1][0]
for i in 1..<params.len:
let it = params[i]
expectKind(it, nnkIdentDefs) # one, two: TypeHere = default
if repr(it[it.len-2]).endsWith"Id":
for j in 0..it.len-3:
interesting.incl repr(it[j])
template addrOf(a, i): untyped = addr(a[int(i)])
initSection.add newLetStmt(ident(derivedPtrName repr(it[j])),
getAst(addrOf(theArray, it[j])))
result = copyNimTree(n)
initSection.add h2(n.body, interesting)
result.body = initSection
proc isOpenArray(n: NimNode): bool =
var n = n
if n.kind == nnkVarTy: n = n[^1]
if n.kind == nnkBracketExpr:
result = eqIdent(n[0], "openArray")
macro borrowPtrs(n: untyped): untyped =
let params = params(n)
expectKind(params[1], nnkIdentDefs)
let theArray = params[1][^2]
if isOpenArray(theArray):
result = ptrsOnOpenArray(n)
else:
result = ptrsOnSeq(n)
when defined(debugPtrsDsl):
echo repr result
proc output(e: var openArray[Elem]; idA, idB: ElemId) {.borrowPtrs.} =
echo idA.x, " ", idB.y
var testdata = @[Elem(x: 5, y: 15)]
output(testdata, ElemId 0, ElemId 0)
@planetis-m
Copy link

planetis-m commented May 26, 2020

The macro can make templates that shadow the proc's arguments, this way there is no need to process the body and would be simpler. Any reason it doesn't do that instead?

Edit: Error: template instantiation too nested nvm

@Araq
Copy link
Author

Araq commented May 27, 2020

I wrote it 3 years ago, feel free to take it and create a Nimble package for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment