Hi y'all!
I wrote a macro for conveniently declaring widget types when using
[traitor](https://github.com/beef331/traitor), it is used to create a type that
conforms to the interface built with traitor with the corresponding member
variables. The widget type is generic, where the parameter is the type of the
display that is used, and with the display also comes the type of color that is
used. In the type definition I use
fgcolor*: typeof(T.color)
Run
which works fine when used directly in the code, but does cause `Error: no
field symbol` when used within a macro.
Here is the reduced code with the macro:
import std/macros
import std/genasts
import traitor
type
AnyDisplay*[T] = concept display
display.color is T
TestDisplay = ref object
color: int
type
Widgetable* = distinct tuple[
setPos: proc(a: Atom, x,y: int) {.nimcall.},
getx: proc(a: Atom): int {.nimcall.},
]
implTrait Widgetable
macro implementWidget*(ast: untyped): untyped =
var
name = ast[0][0][0][1]
self = newIdentNode("self")
result = newNimNode(nnkStmtList)
result.add(ast)
var
base = genAst(name, self) do:
type
Widget*[T: AnyDisplay] = ref object
fgcolor*: typeof(T.color)
display*: T
x: int
y: int
# add variables from base to resulting type
if result[0][0].kind == nnkTypeSection:
if result[0][0][0].kind == nnkTypeDef:
if result[0][0][0][2].kind == nnkRefTy:
if result[0][0][0][2][0].kind == nnkObjectTy:
if result[0][0][0][2][0][2].kind == nnkRecList:
for rec in base[0][2][0][2]:
result[0][0][0][2][0][2].add(rec)
var
fg = newIdentNode("fg")
bg = newIdentNode("bg")
x = newIdentNode("x")
y = newIdentNode("y")
# add procs needed for beeing valid "Widgetable" trait
result.add genAst(name, self, fg, x, y) do:
proc setfgColor*[T](self: var name[T], fg: auto) =
self.fgcolor = fg
proc setPos*[T](self: var name[T], x,y: int) =
self.x = x
self.y = y
proc getx*[T](self: name[T]): int =
return self.x
# call macro that generates trait converter
result.add genAst(name) do:
emitConverter name, Widgetable
echo result.repr
implementWidget:
type
Label*[T] = ref object
text: string
var
disp: TestDisplay
foo: Label[TestDisplay]
Run
And here is the code without the macro, using the code that is printed with
`echo result.repr` at the end of macro:
import std/macros
import std/genasts
import traitor
type
AnyDisplay*[T] = concept display
display.color is T
TestDisplay = ref object
color: int
type
Widgetable* = distinct tuple[
setPos: proc(a: Atom, x,y: int) {.nimcall.},
getx: proc(a: Atom): int {.nimcall.},
]
implTrait Widgetable
type
Label*[T] = ref object
text: string
fgcolor*: typeof(T.color)
display*: T
x: int
y: int
proc setfgColor*[T](self: var Label[T]; fg: auto) =
self.fgcolor = fg
proc setPos*[T](self: var Label[T]; x, y: int) =
self.x = x
self.y = y
proc getx*[T](self: Label[T]): int =
return self.x
emitConverter Label, Widgetable
var
disp: TestDisplay
foo: Label[TestDisplay]
Run
I don't understand why the code works when I just copy the output of the macro,
but not when macro itself is used ...