You are supposed to return another nnkTypeDef. Before this is changed in favor of some new system, if ever, a small change in the meantime for convenience could be to accept a typesection from the macro and inject all the typedefs inside it into the original typesection where the typedef was (or behave as such).
import macros
macro test(s): untyped =
let name = $s[0].basename
result = newTree(nnkTypeSection,
newTree(nnkTypeDef, ident(name & "1"), s[1], s[2]),
newTree(nnkTypeDef, ident(name & "2"), s[1], s[2]))
type
Foo = object
Bar {.test.} = object
x, y, z: int
Baz = object
# becomes
type
Foo = object
Bar1 = object
x, y, z: int
Bar2 = object
x, y, z: int
Baz = object
Also maybe accept any expression from the macro if the typedef is the only one in its typesection (so cyclic references can be respected) and replace the entire typesection with that expression, but this might not be as easy.
import macros
macro test(s): untyped =
let name = $s[0].basename
result = newCall("echo", newLit("type name is " & name))
type Bar {.test.} = object
x, y, z: int
# becomes
echo("type name is Bar")
# invalid:
type
Foo = object
Bar {.test.} = object
x, y, z: int
type
Bar {.test.} = object
x, y, z: int
Foo = object
Bonus: Maybe accept statement lists where the first statement is a typesection, inject that typesection, then add the remaining statements after the FULL original typesection. Not sure if this makes sense.
This should be completely non breaking, I don't think anyone relies on "illformed AST" errors.
Extras (not part of proposal):
The reason for basename in these examples is because typedef macros do not erase the remaining {..} like proc macro pragmas do, which maybe should be changed.
If you're wondering "why not make your own DSL for type construction like all the OOP libraries" it's because when and case in objects and postfixes are impossible to recreate without it looking very ugly. Enums also have their own unique syntax privileges. It sucks because type sections are also the least flexible syntax in the language (object does not even accept semicolons).
If you're wondering "wouldn't solving cyclic references make this a lot easier of a problem" yeah it would, but it doesn't really warrant the wait, it will only be satisfying to simplify it when we get there anyway. Plus macro pragmas for variables aren't even a proper feature yet and they're completely sequential.
This currently fails with "illformed AST":
You are supposed to return another
nnkTypeDef
. Before this is changed in favor of some new system, if ever, a small change in the meantime for convenience could be to accept a typesection from the macro and inject all the typedefs inside it into the original typesection where the typedef was (or behave as such).Also maybe accept any expression from the macro if the typedef is the only one in its typesection (so cyclic references can be respected) and replace the entire typesection with that expression, but this might not be as easy.
Bonus: Maybe accept statement lists where the first statement is a typesection, inject that typesection, then add the remaining statements after the FULL original typesection. Not sure if this makes sense.
This should be completely non breaking, I don't think anyone relies on "illformed AST" errors.
Extras (not part of proposal):
basename
in these examples is because typedef macros do not erase the remaining{..}
like proc macro pragmas do, which maybe should be changed.when
andcase
in objects and postfixes are impossible to recreate without it looking very ugly. Enums also have their own unique syntax privileges. It sucks because type sections are also the least flexible syntax in the language (object
does not even accept semicolons).