I would like to generate an enum and a matching array of strings in a macro, as in this example but with quote instead of direct tree nodes like nnkTypeSection.newTree(...) in order to keep the code more readable in simple macros. Is it possible to declare an empty enum under a quote at all?
macro defineSoA_with_enum(enumName, .., definitionsAoS: untyped): untyped =
var
enumDef = quote do:
type
`enumName` = enum <make it empty>
# maybe something like:
#`enumName` = enum discard
# or something like this:
#`enumName` = `nnkEnumTy.newTree(newEmptyNode())`
# but it does not work: enumDef.repr is `enumName = nnkEnumTy` not `enumName = enum`
# the rest of the macro populates enumDef
for i in countup(0, definitionsAoS.len - 1):
...
enumDef[0][2].add nnkEnumFieldDef.newtree(...)
Other objects can be empty, like strings and arrays etc. But it seems an empty enum might not make sense for the language. Or is there a way to do it? Probably, it is possible to add a dummy node under quote and then remove it in the macro?
On the same topic, is there a quote-base way to define a completely branch-less case statement? Some sort of the same quote do: case ...: discard?
Although, it may be OK for many situations to define the case statement with the else branch only, then the macro could insert the other branches:
caseSwitch = quote do:
case parseEnum[`enumName`](`templateArgument`):
else:
stack.push parseFloat(`templateArgument`)
# insert the new nodes instead of adding, so that the else branch is the last one, as the case order demands
# insert at position 1, because 0 is the case parsing expression
caseSwitch.insert 1, nnkOfBranch.newTree(
enumInfo[0],
commandInfo[1])
nim is not lisp, that it has a more complicated grammar beyond sexps means ultimately that there are AST substitutions you cannot do in quote do. this is why when building macros you should always keep astGenRepr close by in case you do have to get dirtier.
as your concern is with readability, what i would probably go for is populating the enum with a dummy field and then deleting/replacing the fields from there
It's not hard to create the enum without quote do:
#
var enumValues = nnkEnumTy.newTree()
enumValues.add newEmptyNode()
for subtype in class.subtypes:
let kind = ident("k" & $subtype) # kSphere, kLambertian
enumValues.add kind
# Create the type section
result = nnkStmtList.newTree(
nnkTypeSection.newTree(
# type MaterialKind* = enum
# kLambertian, kMetal, ...
nnkTypeDef.newTree(
exported($className & "Kind"),
newEmptyNode(),
enumValues
),
It looks like newEnum also requires the enum to have at least one field:
macro ... =
enumDef = newEnum(`enumName`, [], true, true)
$ nim c -r -d:release basic_macro.nim
...
/.../.local/nim/lib/core/macros.nim(1272, 10) Error: Enum must contain at least one field
Thanks for the links!
I think it may actually be a good idiom to declare dummy nodes and delete them in the macro. The dummy nodes can serve as a demo for other developers of what you intend to do in the macro.