Skip to content

Instantly share code, notes, and snippets.

@oderwat
Last active March 16, 2020 15:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oderwat/854dc491b396ed2d0f67 to your computer and use it in GitHub Desktop.
Save oderwat/854dc491b396ed2d0f67 to your computer and use it in GitHub Desktop.
Access Objects in Nim by strings for fields (wrapper automatisation)
import macros
type Foo = object
a: int
b: int
c: float
#[
dumpTree:
proc `[]=`(obj: var Foo, field: string, value: int) =
case field
of "a": obj.a = value
of "b": obj.b = value
else: raise newException(FieldError, "no such field: " & field)
dumpTree:
proc `[]`(obj: Foo, field: string): int =
case field
of "a": return obj.a
of "b": return obj.b
else: raise newException(FieldError, "no such field: " & field)
]#
macro makeStringAccessObj(obj: stmt, fld: stmt): stmt =
var objType = obj.getType
result = newStmtList()
#[
proc newProc(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()];
body: NimNode = newStmtList(); procType = nnkProcDef): NimNode {.
compileTime, raises: [], tags: [].}
]#
let procName1 = newNimNode(nnkAccQuoted).add(newIdentNode("[]="))
var body1 = newStmtList()
var params1: seq[NimNode] = @[newEmptyNode()] # no return type
params1.add(newIdentDefs(ident("obj"), newNimNode(nnkVarTy).add(ident(obj.repr))))
params1.add(newIdentDefs(ident("field"), ident("string")))
params1.add(newIdentDefs(ident("value"), ident(fld.repr)))
var casof1 = newNimNode(nnkCaseStmt);
casof1.add(ident("field"))
var ty = objType
while ty[1].kind == nnkSym:
ty = getType(ty[1])
if ty.kind != nnkObjectTy or ty[1].kind != nnkRecList:
error "can't create named accessors"
for child in ty[1].children:
if fld.repr == repr(child.getType()):
var branch = newNimNode(nnkOfBranch)
branch.add(newStrLitNode($child))
let ass = newAssignment(newDotExpr(ident("obj"), ident($child)), ident("value"))
branch.add(newStmtList(ass))
casof1.add(branch)
let call1 = newCall(ident("newException"), ident("FieldError"),
infix(newStrLitNode("No such field: "), "&", ident("field")));
let els = newNimNode(nnkElse).add(newStmtList().add(newNimNode(nnkRaiseStmt).add(call1)))
casof1.add(els)
body1.add(casof1)
result.add(newProc(procName1, params1, body1))
let procName2 = newNimNode(nnkAccQuoted).add(newIdentNode("[]"))
var body2 = newStmtList()
var params2: seq[NimNode] = @[ident(fld.repr)] # returns int
params2.add(newIdentDefs(ident("obj"), newNimNode(nnkVarTy).add(ident(obj.repr))))
params2.add(newIdentDefs(ident("field"), ident("string")))
var casof2 = newNimNode(nnkCaseStmt);
casof2.add(ident("field"))
for child in ty[1].children:
if fld.repr == repr(child.getType()):
var branch = newNimNode(nnkOfBranch)
branch.add(newStrLitNode($child))
let ass = newNimNode(nnkReturnStmt).add(newDotExpr(ident("obj"), ident($child)))
branch.add(newStmtList(ass))
casof2.add(branch)
casof2.add(copyNimTree(els)) # same as in proc1
body2.add(casof2)
result.add(newProc(procName2, params2, body2))
#echo result.treeRepr()
makeStringAccessObj(Foo, int)
var o = Foo( a: 1, b: 2 )
echo o["b"], ' ', o.b
o["a"] = 3
o.b = 4
echo o.a, ' ', o["b"]
let x = "a"
let y = "b"
o[x]= o[x] + o[y] * 2
echo o.a, ' ', o.b
echo o.c, ' ', o["c"] # runtime error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment