In a macro, I want to use the result of an expression (which is compile-time evaluatable), in order to get the name of a function for which I am constructing a call:
nim
import macros
macro callIt(name: typed) : typed =
result = newTree(nnkStmtList, newTree(
nnkVarSection, newTree(nnkIdentDefs, newIdentNode("x"), newEmptyNode(), newTree(
nnkCall, newIdentNode(name.strVal))
)
))
(A modified snippet from a larger macro to illustrate the problem.)
The problem is that if name is an expression, the expression's ast is passed in. Can I evaluate it?
Thanks!
When you want to add name to a new tree, don't you just use "name" by itself, since it is already a nnkIdent? callIt("name") would pass name as a nnkStrLit, which would have a .strVal accessor, that you could create an identifier from, but callit(name) would pass name as a nnkIdent, which would have an .ident accessor, and can be used as an identifier.
To decide what to do with name in a macro, I usually go like: case name.kind
looking at macros.nim, that can lead to a whole... lot of conditions, considering how many node types there are. But, that's one of the drawbacks of Nim really, is irreducible complexity in the syntax. f/i you can't treat nnkIncludeStmt the same as nnkExportStmt, nor even nnkIncludeStmt. But it's not too much trouble to have your code require an extreme subset of the full Nim pile of syntax on evaluation, like just an identifier for instance, or just a statement list.
Dunno if any of that helps. Yes you can walk through expressions. Yes there are about 155 different cases you have to deal with individually, in order to do so in a general sense. If you only want to pick out a few cases, every node can be treated as an array of children, so just walk through that recursively and seek out nodes of the kind you want.
The concrete case in which I noticed the problem was a call like callIt(foo.bar) where field bar of object foo contained the name I wanted to call. foo is available at compile time, so one would think I could get the name out. However, its not something I can just read from the node -- it requires evaluation of the node to get at.
I was thinking I could write out an AST tree that:
Would something like that work, or is one not allowed to build an AST tree to call a macro?
Hmm... cant seem to construct a static object. Is this a bug? Should I try a tuple instead of an object?
type
Obj = ref object
f: static[string]
var x: Obj = Obj(f: "foo")
gives me
testamac.nim(19, 5) Error: invalid type: 'static[string]' in this context: 'Obj'
type
Obj = ref object
f: string
var x: static[Obj] = Obj(f: "foo")
gives me
testamac.nim(19, 5) Error: invalid type: 'static[Obj](Obj(f: "foo"))'
type
Obj = ref object
f: string
var x: static[Obj] = static[Obj](f: "foo")
gives me
testamac.nim(19, 28) Error: type expected
I do need to have some static... if I redefine macro to have declaration:
macro callit(fct: static[string]) ...
then
callit(x.f)
will fail without some static somewhere.Hmm... cant seem to construct a static object. Is this a bug?
No, static is a constraint for parameters, not a first class type. And your type constructions do not make sense to me.
will fail without some static somewhere.
That might be a bug but it also might mean that it's not actually statically computable.
My overall purpose: I decided to learn nim I would write a version of "cucumber" -- the BDD tool. This requires that parameters be taken out of strings and passed to functions. I have built a "step definition" macro that builds steps with appropriate parameters. I wanted to write a block of casts using a macro... I'd look up in a statically created datastructure using the name of the type, get the name of the function needed to parse the string and write a block of code in a wrapper around the step that converts the string.
However, even if all the datastructures are known at compile time, I'm having trouble using them to write code. Any advice?
No, static is a constraint for parameters, not a first class type. And your type constructions do not make sense to me.
Just searching around... don't make sense to me, either. I was treating it equivalent to a {.compile.} pragma on a type -- "any value that goes here must be known at compile time.
That might be a bug but it also might mean that it's not actually statically computable.
What datastructure can I use for "things" hashed by strings build statically with all static attributes? How can I tell the difference between the bug and the "feature" here?
PS --- incidentally, one quirk I noticed is that when a macro has a static[T] parameter, it gets the value as a T, not as a node representing a T literal. No big deal, as its easy to convert back and forth -- just that its not consistent, so you might want to consider documenting this if that is the way it will work. (If you do allow "larger" types to be passed in, its probably better to have the actual object of type T, and you can always use quote if if want the nodes; just its then not consistent with macro documentation.)