template opt() {.pragma.}
type A = object
name {.opt.}: string
age: int
var a = A(name: "foo", age: 10)
# expect like below:
#
# case k
# of "name":
# a.name = "π€"
# else:
# discard
#
# but result is:
#
# case k
# else:
# discard
macro parse(a: typed; k: untyped): untyped =
result = nnkCaseStmt.newTree(k)
for fieldDef in a.getTypeImpl()[2]:
# unexpected: why fieldDef[0].kind is nnkSym ?
# how to get pragma of the field
if fieldDef[0].kind == nnkPragmaExpr and fieldDef[0][1][0].eqIdent("opt"):
let fieldNameIdent = fieldDef[0][0]
result.add nnkOfBranch.newTree(
newLit(fieldNameIdent.strVal),
nnkStmtList.newTree(
nnkAsgn.newTree(nnkDotExpr.newTree(a, fieldNameIdent), newLit("π€"))
),
)
result.add nnkElse.newTree(
nnkStmtList.newTree(nnkDiscardStmt.newTree(newEmptyNode()))
)
let k = "name"
expandMacros:
parse a, k
Thanks for your advice ;-)
i modified code and got error as below:
macro parse(a: typed; k: untyped): untyped =
result = nnkCaseStmt.newTree(k)
for fieldDef in a.getTypeImpl()[2]:
let fieldNameIdent = fieldDef[0][0]
# error: Expected one of {nnkSym, nnkType, nnkBracketExpr, nnkDotExpr, nnkCheckedFieldExpr, nnkTypeOfExpr}, got nnkCall
if nnkDotExpr.newTree(a, fieldNameIdent).hasCustomPragma(opt):
result.add nnkOfBranch.newTree(
newLit(fieldNameIdent.strVal),
nnkStmtList.newTree(
nnkAsgn.newTree(nnkDotExpr.newTree(a, fieldNameIdent), newLit("π€"))
),
)
result.add nnkElse.newTree(
nnkStmtList.newTree(nnkDiscardStmt.newTree(newEmptyNode()))
)
static: echo A.getTypeImpl().treeRepr()
result is:
ObjectTy
Empty
Empty
RecList
IdentDefs
Sym "name"
Sym "string"
Empty
IdentDefs
Sym "age"
Sym "int"
Empty
i try to use fieldPairs(), but unfortunately
macro parse(a: typed; k: untyped): untyped =
result = nnkCaseStmt.newTree(k)
for fk, fv in a.fieldPairs():
when fv.hasCustomPragma(opt):
result.add nnkOfBranch.newTree(
newLit(fk), nnkStmtList.newTree(nnkAsgn.newTree(fv, newLit("π€")))
)
result.add nnkElse.newTree(
nnkStmtList.newTree(nnkDiscardStmt.newTree(newEmptyNode()))
)
Expression: fieldPairs(a) [1] a: NimNode Expected one of (first mismatch at [position]): [1] iterator fieldPairs[S: tuple | object; T: tuple | object](x: S; y: T): tuple[ key: string, a, b: RootObj] [1] iterator fieldPairs[T: tuple | object](x: T): tuple[key: string, val: RootObj]
You can use getTypeInst combined with getImpl to get the type's AST including pragmas on its fields, that's how hasCustomPragma does it (but as a macro it can't be used from inside a macro AFAIK), see customPragmaNode in https://github.com/nim-lang/Nim/blob/version-2-0/lib/core/macros.nim#L1571
Example:
import std/macros
template opt1() {.pragma.}
template opt2() {.pragma.}
type
A = object
foo {.opt1, opt2.}, bar: string
baz: int
var a = A(foo: "foo", bar: "bar", baz: 1)
macro parse(a: typed): untyped =
let
typ = a.getTypeInst
typImpl = typ.getImpl
for idef in typImpl[2][2]:
for field in idef[0..^3]:
if field.kind == nnkPragmaExpr:
for pragma in field[1]:
if pragma.eqident"opt1":
echo field.treeRepr
parse a
Note that in case the type is a generic or variant or some other non-trivial object, you might need to tweak the code, either to find the type symbol before using getImpl, or to get the complete list of fields, since the typed AST can vary in those cases.