Last active
March 16, 2020 15:06
-
-
Save oderwat/854dc491b396ed2d0f67 to your computer and use it in GitHub Desktop.
Access Objects in Nim by strings for fields (wrapper automatisation)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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