Skip to content

Instantly share code, notes, and snippets.

@jovial
Last active August 29, 2015 14:10
Show Gist options
  • Save jovial/1f3a6fbace89c4a2cf66 to your computer and use it in GitHub Desktop.
Save jovial/1f3a6fbace89c4a2cf66 to your computer and use it in GitHub Desktop.
import tables
import typetraits
import macros
proc newTemplate*(name = newEmptyNode(); params: openArray[PNimrodNode] = [newEmptyNode()];
body: PNimrodNode = newStmtList()): PNimrodNode {.compileTime.} =
## shortcut for creating a new template
##
## The ``params`` array must start with the return type of the template,
## followed by a list of IdentDefs which specify the params.
result = newNimNode(nnkTemplateDef).add(
name,
newEmptyNode(),
newEmptyNode(),
newNimNode(nnkFormalParams).add(params), ##params
newEmptyNode(), ## pragmas
newEmptyNode(),
body)
proc getFieldIndex(typ: typedesc): Table[int, string] {.compileTime.} =
# this didn't work in the template, so moved to helper proc
var dummy: typ
var index = 0
result = initTable[int, string]()
when compiles(dummy[]):
for name, value in dummy[].fieldPairs():
result[index] = name
index.inc
else:
for name, value in dummy.fieldPairs():
result[index] = name
index.inc
template indexFields*(typ: typedesc): stmt =
static:
var index = getFieldIndex(typ)
macro genFieldIndex(): stmt {.genSym.} =
# generate something like:
# template `[]`(obj: Foo, index: int): expr =
# when index == 0:
# obj.a
# elif index == 1:
# obj.b
# elif index == 2:
# obj.c
result = newStmtList()
let templateName = newNimNode(nnkAccQuoted)
.add ident("[]")
# idents for param's
let returnIdent = ident "expr"
let objIdent = ident "obj"
let objIdentDefs = newIdentDefs(objIdent, ident typ.name)
let indexIdent = ident "index"
let indexIdentDefs = newIdentDefs(indexIdent, ident "int")
var whenStmt = newNimNode(nnkWhenStmt)
for index, fieldName in index.pairs():
var elifBranch = newNimNode(nnkElifBranch)
let predicate = infix(indexIdent, "==", newLit index)
let body = newStmtList()
.add newDotExpr(objIdent, ident fieldName)
elifBranch.add predicate
elifBranch.add body
whenStmt.add elifBranch
let body = newStmtList().add whenStmt
result.add newTemplate(
templateName,
[returnIdent, objIdentDefs, indexIdentDefs],
body)
genFieldIndex()
type Foo = object
a: int
b: string
c: seq[int]
indexFields(Foo)
let test = Foo(a: 0, b: "hello", c: @[1,2,3])
echo repr test[0]
echo repr test[1]
echo repr test[2]
#TODO: bounds checking
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment