# Example
Here's your code shortened:
import macros
macro iter(i, c1, c2, stm: untyped): untyped =
var stmList = nnkStmtList.newTree(
newCall(ident("write"), ident("stdout"), i, newLit("- ")))
stmList.add(stm)
result = nnkStmtList.newTree(
nnkForStmt.newTree(
i, infix(c1, "..", c2), stmList))
iter(i, 0, 3):
echo "Hello, world."
echo "End"
Run
Basically, 3 lines when not split for prettiness, or 4 if you include macro
header.
You can make it even simpler and more readable with `quote do`, I saw you use
it in an earlier example, so I understand not doing it here is a conscious
choice for some reason. Actually, this can be simplified even more with a
template instead of a macro:
template iter(i, c1, c2, stm: untyped): untyped =
for `i` in `c1` .. `c2`:
stdout.write(i, "- ")
`stm`
iter(i, 0, 3):
echo "Hello, world."
echo "End"
Run
# Suggested reading
I've written some macros recently, so I'm relatively fresh with the topic. Some
cheatsheet of what I found most useful and not always obvious in this area:
* The [macros library reference](https://nim-lang.org/docs/macros.html) is a
page I keep open and regularly consult about various stuff, esp. the references
for the fields of various nnk types. Especially the intro is a must-read.
Though I recommend going through the list of functions at least once; you may
not (yet) understand all of them, but there are some hidden gems, and with
time, more and more will become understandable.
* `.treeRepr` tends to be useful when debugging
* `.repr` is also useful to verify what gets rendered by the macro — it dumps
your AST as a syntactically correct Nim code (alternatively, you may prefer to
use `expandMacros`, which has a similar role)
* `dumpTree` is what I usually start with — to see how particular input will
be seen by my macro, or what I need to emit as a macro's result
* `quote do` is another basic tool — whenever I can use it, I do, for
improved readability.
# Macromatch package
Finally, I've written a helper module
[macromatch](https://github.com/akavel/dali/blob/master/src/dali/utils/macromatch.nim),
which helps me match/compare nodes (nnk enums) in macros. It's not yet
documented or officially released, as I'm focusing on actually building stuff
with it. But to show you some examples, it lets me write code [like
this](https://github.com/akavel/dali/blob/18341fefa071c4582b44f4e3b7e07f4ec944faee/src/dali/macros.nim#L331):
if header =~ Infix(Ident("of"), [], []):
...
Run
or [like
this](https://github.com/akavel/dali/blob/18341fefa071c4582b44f4e3b7e07f4ec944faee/src/dali/macros.nim#L336-L340):
if rest =~ PragmaExpr(_):
if rest !~ PragmaExpr([], []):
error "encountered unexpected syntax (too many words)", rest
if rest !~ PragmaExpr([], Pragma(_)):
error "expected pragmas list", rest[1]
Run
where:
* `[]` represents a single node of unknown type (like a `.` in regexp),
* `_` represents unknown number of nodes (like `.*` in regexp),
* node types are specified without `nnk`, i.e. `Ident` instead of `nnkIdent`
* `=~` means "matches"
* `!~` means "does not match".
Warning:
* this code is most certainly slower than checking `.kind`;
* there may be bugs — I didn't manage to invent a way how I could write tests
for those macros;
* not all constructions may be implemented, e.g. I didn't add numeric
literals because I didn't need them yet.
I'd **really love** if someone showed me an example how I could **write tests
for those macros** — one example is enough, I'll quickly write the rest myself,
it's just that I didn't seem to be able to make _unittest_ work at compile time
:( :( :(