# 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 
:( :( :(

Reply via email to