Trying to write accesors to get a value of an object with a kind member, I get invalid indentation error in a macro I am not sure why
I imagine I might be building macros wrong but if there is a better way to abstract type kinds in objects it would be great to know.
Here is the implementation I am working:
import macros,strutils,tables
type Types = enum Integer,Float,String,Array
type StyleName = enum X,Y,W,H,Left,Margin,Padding,Color,BorderColor
type Value=object
case kind : Types
of Integer: ival:int
of Float: fval:float
of String: sval:string
of Array: cval:array[4,int]
proc toValue(x:float):Value=Value(kind:Float,fval:x)
proc toValue(x:int):Value=Value(kind:Integer,ival:x)
proc toValue(x:string):Value=Value(kind:String,sval:x)
proc toValue(x:array[4,int]):Value=Value(kind:Array,cval:x)
#Construct {stylename:value,...} to {stylename:Value(kind,value),...}
macro style(args: varargs[untyped]): Table[StyleName,Value] =
#echo repr(args), args.treeRepr
var s:seq[string]
for arg in args:
s.add arg[0].repr & ":" & "toValue(" & arg[1].repr & ")"
result = parseStmt("{" & s.join(",") & "}.toTable")
Macro accesors for the variant object "Value"
macro getWithKind(style:Table[StyleName,Value], prop:StyleName, kind:Types):untyped=
var accesor:string
case kind.repr:
of "Integer": accesor="ival"
of "Float": accesor="fval"
of "String": accesor="sval"
of "Array": accesor="cval"
result = parseStmt(style.repr & "[" & prop.repr & "]." & accesor)
#Transforms simple get(style,prop) call to getWithKind(style,prop,kind)
macro get(style:Table[StyleName,Value], prop:StyleName):untyped=
result = parseStmt(style.repr & ".getWithKind(" & prop.repr & ", kind=" & style.repr & "[" & prop.repr & "].kind)")
This is the output I get: Is it not possible to call a macro inside another macro?
let style1 = style(X=1,Y=2.0,Color=[0,0,0,0]) #build a table of (StyleName:Value)
echo style1 #output: {X: (kind: Integer, ival: 1), Y: (kind: Float, fval: 2.0), Color: (kind: Array, cval: [0, 0, 0, 0])}
echo style1[X].ival # output:1
echo style1[X].kind # output:Integer
echo style1.getWithKind(X,kind=Integer) # output:1
echo style1.get(X) #(should get 1), output:
C:\Users\cravs\Desktop\test.nim(109, 21) getWithKind
C:\nim-1.4.8\lib\core\macros.nim(558, 17) parseStmt
C:\Users\cravs\Desktop\test.nim(119, 12) template/generic instantiation of `get` from here
C:\nim-1.4.8\lib\core\macros.nim(557, 7) template/generic instantiation of `getWithKind` from here
C:\nim-1.4.8\lib\core\macros.nim(558, 17) Error: unhandled exception: C:\nim-1.4.8\lib\core\macros.nim(557, 11) Error: invalid indentation [ValueError]
Edit: Here is another attempt
macro getWithKind(style:Table[StyleName,Value], prop:StyleName, kind:Types):untyped=
var accesor:string
case kind.repr
of "Integer": accesor="ival"
of "Float": accesor="fval"
of "String": accesor="sval"
of "Array": accesor="cval"
result = newDotExpr(newTree(nnkBracketExpr,style,prop), newIdentNode(accesor))
#echo parseStmt(style.repr & "[" & prop.repr & "]." & accesor)
macro get(style:Table[StyleName,Value], prop:StyleName):untyped=
#result = parseStmt(style.repr & ".getWithKind(" & prop.repr & ", kind=" & style.repr & "[" & prop.repr & "].kind)")
let kind = newDotExpr(newTree(nnkBracketExpr,style,prop), newIdentNode("kind"))
echo treeRepr kind
quote do:
echo `kind` #prints Integer
getWithKind(`style`,`prop`,kind=`kind`)
let style1 = style(X=1,Y=2.0,Color=[0,0,0,0]) #build a table of (StyleName:Value)
echo style1 #output: {X: (kind: Integer, ival: 1), Y: (kind: Float, fval: 2.0), Color: (kind: Array, cval: [0, 0, 0, 0])}
echo style1[X].ival # output:1
echo style1[X].kind # output:Integer
echo style1.getWithKind(X,kind=Integer) # output:1
echo style1.get(X) #(should get 1), output:
C:\Users\...\test.nim(127, 3) Error: undeclared field: '' for type test.Value [declared in C:\Users\...\test.nim(80, 6)]
Here is how you should make macros:
import macros, strutils, tables
type Types = enum Integer, Float, String, Array
type StyleName = enum X, Y, W, H, Left, Margin, Padding, Color, BorderColor
type Value = object
case kind: Types
of Integer: ival: int
of Float: fval: float
of String: sval: string
of Array: cval: array[4, int]
proc toValue(x: float): Value = Value(kind: Float, fval: x)
proc toValue(x: int): Value = Value(kind: Integer, ival: x)
proc toValue(x: string): Value = Value(kind: String, sval: x)
proc toValue(x: array[4, int]): Value = Value(kind: Array, cval: x)
macro style(properties: untyped): Table[StyleName, Value] =
var table = newNimNode(nnkTableConstr)
properties.expectKind nnkStmtList
for property in properties:
property.expectKind nnkCall
property.expectLen 2
property[0].expectKind nnkIdent
property[1].expectKind nnkStmtList
property[1].expectLen 1
let
name = property[0]
value = property[1][0]
table.add(newTree(nnkExprColonExpr, name , quote do: `value`.toValue()))
quote do:
`table`.toTable()
let table = style:
X: 10.0
Y: 20.0
Color: "ff00ff"
Margin: [10, 20, 30, 40]
echo table
I had no idea you can use strings in macro but its match better to manipulate AST instead. This way you can also verify what user is inputting.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.