123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- <?xml version="1.0" encoding="utf-8" ?>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <!-- This file is generated by Nim. -->
- <html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Nim Tutorial (Part III)</title>
- <!-- Google fonts -->
- <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
- <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
- <!-- Favicon -->
- <link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
- <link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
- <!-- CSS -->
- <link rel="stylesheet" type="text/css" href="nimdoc.out.css?v=2.3.1">
- <!-- JS -->
- <script type="text/javascript" src="dochack.js?v=2.3.1"></script>
- </head>
- <body>
- <div class="document" id="documentId">
- <div class="container">
- <h1 class="title">Nim Tutorial (Part III)</h1>
- <div class="row">
- <div class="three columns">
- <div class="theme-select-wrapper">
- <label for="theme-select">Theme: </label>
- <select id="theme-select" onchange="setTheme(this.value)">
- <option value="auto">🌗 Match OS</option>
- <option value="dark">🌑 Dark</option>
- <option value="light">🌕 Light</option>
- </select>
- </div>
- <div id="global-links">
- <ul class="simple-boot">
- <li><a href="manual.html">Manual</a></li>
- <li><a href="lib.html">Standard library</a></li>
- <li> <a id="indexLink" href="theindex.html">Index</a></li>
- <li><a href="compiler/theindex.html">Compiler docs</a></li>
- <li><a href="https://nim-lang.github.io/fusion/theindex.html">Fusion docs</a></li>
- <li><a href="https://nim-lang.github.io/Nim/">devel</a>, <a href="https://nim-lang.org/documentation.html">stable</a></li>
- </ul>
- </div>
- <div id="searchInputDiv">
- Search: <input type="search" id="searchInput"
- oninput="search()" />
- </div>
- <div class="search-groupby">
- Group by:
- <select onchange="groupBy(this.value)">
- <option value="section">Section</option>
- <option value="type">Type</option>
- </select>
- </div>
- <ul class="simple simple-toc" id="toc-list">
- <li><a class="reference" id="introduction_toc" href="#introduction">Introduction</a></li>
- <ul class="simple"><li><a class="reference" id="introduction-macro-arguments_toc" href="#introduction-macro-arguments">Macro Arguments</a></li>
- <li><a class="reference" id="introduction-untyped-arguments_toc" href="#introduction-untyped-arguments">Untyped Arguments</a></li>
- <li><a class="reference" id="introduction-typed-arguments_toc" href="#introduction-typed-arguments">Typed Arguments</a></li>
- <li><a class="reference" id="introduction-static-arguments_toc" href="#introduction-static-arguments">Static Arguments</a></li>
- <li><a class="reference" id="introduction-code-blocks-as-arguments_toc" href="#introduction-code-blocks-as-arguments">Code Blocks as Arguments</a></li>
- <li><a class="reference" id="introduction-the-syntax-tree_toc" href="#introduction-the-syntax-tree">The Syntax Tree</a></li>
- <li><a class="reference" id="introduction-custom-semantic-checking_toc" href="#introduction-custom-semantic-checking">Custom Semantic Checking</a></li>
- <li><a class="reference" id="introduction-generating-code_toc" href="#introduction-generating-code">Generating Code</a></li>
- <li><a class="reference" id="introduction-building-your-first-macro_toc" href="#introduction-building-your-first-macro">Building Your First Macro</a></li>
- <li><a class="reference" id="introduction-going-further_toc" href="#introduction-going-further">Going further</a></li>
- <li><a class="reference" id="introduction-with-power-comes-responsibility_toc" href="#introduction-with-power-comes-responsibility">With Power Comes Responsibility</a></li>
- <li><a class="reference" id="introduction-limitations_toc" href="#introduction-limitations">Limitations</a></li>
- </ul><li><a class="reference" id="more-examples_toc" href="#more-examples">More Examples</a></li>
- <ul class="simple"><li><a class="reference" id="more-examples-strformat_toc" href="#more-examples-strformat">Strformat</a></li>
- <li><a class="reference" id="more-examples-ast-pattern-matching_toc" href="#more-examples-ast-pattern-matching">Ast Pattern Matching</a></li>
- <li><a class="reference" id="more-examples-opengl-sandbox_toc" href="#more-examples-opengl-sandbox">OpenGL Sandbox</a></li>
- </ul>
- </ul>
- </div>
- <div class="nine columns" id="content">
- <a href="https://github.com/nim-lang/Nim/tree/devel/doc/tut3.md#L1" class="link-seesrc" target="_blank">Source</a>
- <a href="https://github.com/nim-lang/Nim/edit/devel/doc/tut3.md#L1" class="link-seesrc" target="_blank" >Edit</a>
- <div id="tocRoot"></div>
-
- <p class="module-desc"><table class="docinfo" frame="void" rules="none"><col class="docinfo-name" /><col class="docinfo-content" /><tbody valign="top"><tr><th class="docinfo-name">Author:</th><td>Arne Döring</td></tr>
- <tr><th class="docinfo-name">Version:</th><td>2.3.1</td></tr>
- </tbody></table>
- <h1><a class="toc-backref" id="introduction" href="#introduction">Introduction</a></h1><blockquote class="markdown-quote"><p>"With Great Power Comes Great Responsibility." -- Spider Man's Uncle</p></blockquote>
- <p>This document is a tutorial about Nim's macro system. A macro is a function that is executed at compile-time and transforms a Nim syntax tree into a different tree.</p>
- <p>Examples of things that can be implemented in macros:</p>
- <ul class="simple"><li>An assert macro that prints both sides of a comparison operator, if the assertion fails. <tt class="docutils literal"><span class="pre"><span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">a</span> <span class="Operator">==</span> <span class="Identifier">b</span><span class="Punctuation">)</span></span></tt> is converted to <tt class="docutils literal"><span class="pre"><span class="Keyword">if</span> <span class="Identifier">a</span> <span class="Operator">!=</span> <span class="Identifier">b</span><span class="Punctuation">:</span> <span class="Identifier">quit</span><span class="Punctuation">(</span><span class="Operator">$</span><span class="Identifier">a</span> <span class="StringLit">" != "</span> <span class="Operator">$</span><span class="Identifier">b</span><span class="Punctuation">)</span></span></tt></li>
- <li>A debug macro that prints the value and the name of the symbol. <tt class="docutils literal"><span class="pre"><span class="Identifier">myDebugEcho</span><span class="Punctuation">(</span><span class="Identifier">a</span><span class="Punctuation">)</span></span></tt> is converted to <tt class="docutils literal"><span class="pre"><span class="Identifier">echo</span> <span class="StringLit">"a: "</span><span class="Punctuation">,</span> <span class="Identifier">a</span></span></tt></li>
- <li>Symbolic differentiation of an expression. <tt class="docutils literal"><span class="pre"><span class="Identifier">diff</span><span class="Punctuation">(</span><span class="Identifier">a</span><span class="Operator">*</span><span class="Identifier">pow</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">,</span><span class="DecNumber">3</span><span class="Punctuation">)</span> <span class="Operator">+</span> <span class="Identifier">b</span><span class="Operator">*</span><span class="Identifier">pow</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">,</span><span class="DecNumber">2</span><span class="Punctuation">)</span> <span class="Operator">+</span> <span class="Identifier">c</span><span class="Operator">*</span><span class="Identifier">x</span> <span class="Operator">+</span> <span class="Identifier">d</span><span class="Punctuation">,</span> <span class="Identifier">x</span><span class="Punctuation">)</span></span></tt> is converted to <tt class="docutils literal"><span class="pre"><span class="DecNumber">3</span><span class="Operator">*</span><span class="Identifier">a</span><span class="Operator">*</span><span class="Identifier">pow</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">,</span><span class="DecNumber">2</span><span class="Punctuation">)</span> <span class="Operator">+</span> <span class="DecNumber">2</span><span class="Operator">*</span><span class="Identifier">b</span><span class="Operator">*</span><span class="Identifier">x</span> <span class="Operator">+</span> <span class="Identifier">c</span></span></tt></li>
- </ul>
- <h2><a class="toc-backref" id="introduction-macro-arguments" href="#introduction-macro-arguments">Macro Arguments</a></h2><p>The types of macro arguments have two faces. One face is used for the overload resolution and the other face is used within the macro body. For example, if <tt class="docutils literal"><span class="pre"><span class="Keyword">macro</span> <span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Identifier">int</span><span class="Punctuation">)</span></span></tt> is called in an expression <tt class="docutils literal"><span class="pre"><span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">)</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> has to be of a type compatible to int, but <em>within</em> the macro's body <tt class="docutils literal"><span class="pre"><span class="Identifier">arg</span></span></tt> has the type <tt class="docutils literal"><span class="pre"><span class="Identifier">NimNode</span></span></tt>, not <tt class="docutils literal"><span class="pre"><span class="Identifier">int</span></span></tt>! Why it is done this way will become obvious later, when we have seen concrete examples.</p>
- <p>There are two ways to pass arguments to a macro, an argument can be either <tt class="docutils literal"><span class="pre"><span class="Identifier">typed</span></span></tt> or <tt class="docutils literal"><span class="pre"><span class="Identifier">untyped</span></span></tt>.</p>
- <h2><a class="toc-backref" id="introduction-untyped-arguments" href="#introduction-untyped-arguments">Untyped Arguments</a></h2><p>Untyped macro arguments are passed to the macro before they are semantically checked. This means the syntax tree that is passed down to the macro does not need to make sense for Nim yet, the only limitation is that it needs to be parsable. Usually, the macro does not check the argument either but uses it in the transformation's result somehow. The result of a macro expansion is always checked by the compiler, so apart from weird error messages, nothing bad can happen.</p>
- <p>The downside for an <tt class="docutils literal"><span class="pre"><span class="Identifier">untyped</span></span></tt> argument is that these do not play well with Nim's overloading resolution.</p>
- <p>The upside for untyped arguments is that the syntax tree is quite predictable and less complex compared to its <tt class="docutils literal"><span class="pre"><span class="Identifier">typed</span></span></tt> counterpart.</p>
- <h2><a class="toc-backref" id="introduction-typed-arguments" href="#introduction-typed-arguments">Typed Arguments</a></h2><p>For typed arguments, the semantic checker runs on the argument and does transformations on it, before it is passed to the macro. Here identifier nodes are resolved as symbols, implicit type conversions are visible in the tree as calls, templates are expanded, and probably most importantly, nodes have type information. Typed arguments can have the type <tt class="docutils literal"><span class="pre"><span class="Identifier">typed</span></span></tt> in the arguments list. But all other types, such as <tt class="docutils literal"><span class="pre"><span class="Identifier">int</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">float</span></span></tt> or <tt class="docutils literal"><span class="pre"><span class="Identifier">MyObjectType</span></span></tt> are typed arguments as well, and they are passed to the macro as a syntax tree.</p>
- <h2><a class="toc-backref" id="introduction-static-arguments" href="#introduction-static-arguments">Static Arguments</a></h2><p>Static arguments are a way to pass values as values and not as syntax tree nodes to a macro. For example for <tt class="docutils literal"><span class="pre"><span class="Keyword">macro</span> <span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Keyword">static</span><span class="Punctuation">[</span><span class="Identifier">int</span><span class="Punctuation">]</span><span class="Punctuation">)</span></span></tt> in the expression <tt class="docutils literal"><span class="pre"><span class="Identifier">foo</span><span class="Punctuation">(</span><span class="Identifier">x</span><span class="Punctuation">)</span></span></tt>, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> needs to be an integer constant, but in the macro body <tt class="docutils literal"><span class="pre"><span class="Identifier">arg</span></span></tt> is just like a normal parameter of type <tt class="docutils literal"><span class="pre"><span class="Identifier">int</span></span></tt>.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">myMacro</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Keyword">static</span><span class="Punctuation">[</span><span class="Identifier">int</span><span class="Punctuation">]</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
- <span class="Identifier">echo</span> <span class="Identifier">arg</span> <span class="Comment"># just an int (7), not `NimNode`</span>
- <span class="Identifier">myMacro</span><span class="Punctuation">(</span><span class="DecNumber">1</span> <span class="Operator">+</span> <span class="DecNumber">2</span> <span class="Operator">*</span> <span class="DecNumber">3</span><span class="Punctuation">)</span></pre></p>
- <h2><a class="toc-backref" id="introduction-code-blocks-as-arguments" href="#introduction-code-blocks-as-arguments">Code Blocks as Arguments</a></h2><p>It is possible to pass the last argument of a call expression in a separate code block with indentation. For example, the following code example is a valid (but not a recommended) way to call <tt class="docutils literal"><span class="pre"><span class="Identifier">echo</span></span></tt>:</p>
- <p><pre class="listing"><span class="Identifier">echo</span> <span class="StringLit">"Hello "</span><span class="Punctuation">:</span>
- <span class="Keyword">let</span> <span class="Identifier">a</span> <span class="Operator">=</span> <span class="StringLit">"Wor"</span>
- <span class="Keyword">let</span> <span class="Identifier">b</span> <span class="Operator">=</span> <span class="StringLit">"ld!"</span>
- <span class="Identifier">a</span> <span class="Operator">&</span> <span class="Identifier">b</span></pre></p>
- <p>For macros this way of calling is very useful; syntax trees of arbitrary complexity can be passed to macros with this notation.</p>
- <h2><a class="toc-backref" id="introduction-the-syntax-tree" href="#introduction-the-syntax-tree">The Syntax Tree</a></h2><p>In order to build a Nim syntax tree one needs to know how Nim source code is represented as a syntax tree, and how such a tree needs to look like so that the Nim compiler will understand it. The nodes of the Nim syntax tree are documented in the <a class="reference external" href="macros.html">macros</a> module. But a more interactive way to explore the Nim syntax tree is with <tt class="docutils literal"><span class="pre"><span class="Identifier">macros</span><span class="Operator">.</span><span class="Identifier">treeRepr</span></span></tt>, it converts a syntax tree into a multi-line string for printing on the console. It can be used to explore how the argument expressions are represented in tree form and for debug printing of generated syntax tree. <tt class="docutils literal"><span class="pre"><span class="Identifier">dumpTree</span></span></tt> is a predefined macro that just prints its argument in a tree representation, but does nothing else. Here is an example of such a tree representation:</p>
- <p><pre class="listing"><span class="Identifier">dumpTree</span><span class="Punctuation">:</span>
- <span class="Keyword">var</span> <span class="Identifier">mt</span><span class="Punctuation">:</span> <span class="Identifier">MyType</span> <span class="Operator">=</span> <span class="Identifier">MyType</span><span class="Punctuation">(</span><span class="Identifier">a</span><span class="Punctuation">:</span><span class="FloatNumber">123.456</span><span class="Punctuation">,</span> <span class="Identifier">b</span><span class="Punctuation">:</span><span class="StringLit">"abcdef"</span><span class="Punctuation">)</span>
- <span class="Comment"># output:</span>
- <span class="Comment"># StmtList</span>
- <span class="Comment"># VarSection</span>
- <span class="Comment"># IdentDefs</span>
- <span class="Comment"># Ident "mt"</span>
- <span class="Comment"># Ident "MyType"</span>
- <span class="Comment"># ObjConstr</span>
- <span class="Comment"># Ident "MyType"</span>
- <span class="Comment"># ExprColonExpr</span>
- <span class="Comment"># Ident "a"</span>
- <span class="Comment"># FloatLit 123.456</span>
- <span class="Comment"># ExprColonExpr</span>
- <span class="Comment"># Ident "b"</span>
- <span class="Comment"># StrLit "abcdef"</span></pre></p>
- <h2><a class="toc-backref" id="introduction-custom-semantic-checking" href="#introduction-custom-semantic-checking">Custom Semantic Checking</a></h2><p>The first thing that a macro should do with its arguments is to check if the argument is in the correct form. Not every type of wrong input needs to be caught here, but anything that could cause a crash during macro evaluation should be caught and create a nice error message. <tt class="docutils literal"><span class="pre"><span class="Identifier">macros</span><span class="Operator">.</span><span class="Identifier">expectKind</span></span></tt> and <tt class="docutils literal"><span class="pre"><span class="Identifier">macros</span><span class="Operator">.</span><span class="Identifier">expectLen</span></span></tt> are a good start. If the checks need to be more complex, arbitrary error messages can be created with the <tt class="docutils literal"><span class="pre"><span class="Identifier">macros</span><span class="Operator">.</span><span class="Identifier">error</span></span></tt> proc.</p>
- <p><pre class="listing"><span class="Keyword">macro</span> <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
- <span class="Identifier">arg</span><span class="Operator">.</span><span class="Identifier">expectKind</span> <span class="Identifier">nnkInfix</span></pre></p>
- <h2><a class="toc-backref" id="introduction-generating-code" href="#introduction-generating-code">Generating Code</a></h2><p>There are two ways to generate the code. Either by creating the syntax tree with expressions that contain a lot of calls to <tt class="docutils literal"><span class="pre"><span class="Identifier">newTree</span></span></tt> and <tt class="docutils literal"><span class="pre"><span class="Identifier">newLit</span></span></tt>, or with <tt class="docutils literal"><span class="pre"><span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span></span></tt> expressions. The first option offers the best low-level control for the syntax tree generation, but the second option is much less verbose. If you choose to create the syntax tree with calls to <tt class="docutils literal"><span class="pre"><span class="Identifier">newTree</span></span></tt> and <tt class="docutils literal"><span class="pre"><span class="Identifier">newLit</span></span></tt> the macro <tt class="docutils literal"><span class="pre"><span class="Identifier">macros</span><span class="Operator">.</span><span class="Identifier">dumpAstGen</span></span></tt> can help you with the verbosity.</p>
- <p><tt class="docutils literal"><span class="pre"><span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span></span></tt> allows you to write the code that you want to generate literally. Backticks are used to insert code from <tt class="docutils literal"><span class="pre"><span class="Identifier">NimNode</span></span></tt> symbols into the generated expression.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">a</span><span class="Punctuation">(</span><span class="Identifier">i</span><span class="Punctuation">)</span> <span class="Operator">=</span> <span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Keyword">let</span> <span class="Punctuation">`</span><span class="Identifier">i</span><span class="Punctuation">`</span> <span class="Operator">=</span> <span class="DecNumber">0</span>
- <span class="Identifier">a</span> <span class="Identifier">b</span>
- <span class="Identifier">doAssert</span> <span class="Identifier">b</span> <span class="Operator">==</span> <span class="DecNumber">0</span></pre></p>
- <p>A custom prefix operator can be defined whenever backticks are needed.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">a</span><span class="Punctuation">(</span><span class="Identifier">i</span><span class="Punctuation">)</span> <span class="Operator">=</span> <span class="Identifier">quote</span><span class="Punctuation">(</span><span class="StringLit">"@"</span><span class="Punctuation">)</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Identifier">assert</span> <span class="Operator">@</span><span class="Identifier">i</span> <span class="Operator">==</span> <span class="DecNumber">0</span>
- <span class="Keyword">let</span> <span class="Identifier">b</span> <span class="Operator">=</span> <span class="DecNumber">0</span>
- <span class="Identifier">a</span> <span class="Identifier">b</span></pre></p>
- <p>The injected symbol needs accent quoted when it resolves to a symbol.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">a</span><span class="Punctuation">(</span><span class="Identifier">i</span><span class="Punctuation">)</span> <span class="Operator">=</span> <span class="Identifier">quote</span><span class="Punctuation">(</span><span class="StringLit">"@"</span><span class="Punctuation">)</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Keyword">let</span> <span class="Punctuation">`</span><span class="Operator">@</span><span class="Identifier">i</span><span class="Punctuation">`</span> <span class="Operator">=</span> <span class="DecNumber">0</span>
- <span class="Identifier">a</span> <span class="Identifier">b</span>
- <span class="Identifier">doAssert</span> <span class="Identifier">b</span> <span class="Operator">==</span> <span class="DecNumber">0</span></pre></p>
- <p>Make sure to inject only symbols of type <tt class="docutils literal"><span class="pre"><span class="Identifier">NimNode</span></span></tt> into the generated syntax tree. You can use <tt class="docutils literal"><span class="pre"><span class="Identifier">newLit</span></span></tt> to convert arbitrary values into expressions trees of type <tt class="docutils literal"><span class="pre"><span class="Identifier">NimNode</span></span></tt> so that it is safe to inject them into the tree.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">type</span>
- <span class="Identifier">MyType</span> <span class="Operator">=</span> <span class="Keyword">object</span>
- <span class="Identifier">a</span><span class="Punctuation">:</span> <span class="Identifier">float</span>
- <span class="Identifier">b</span><span class="Punctuation">:</span> <span class="Identifier">string</span>
- <span class="Keyword">macro</span> <span class="Identifier">myMacro</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
- <span class="Keyword">var</span> <span class="Identifier">mt</span><span class="Punctuation">:</span> <span class="Identifier">MyType</span> <span class="Operator">=</span> <span class="Identifier">MyType</span><span class="Punctuation">(</span><span class="Identifier">a</span><span class="Punctuation">:</span><span class="FloatNumber">123.456</span><span class="Punctuation">,</span> <span class="Identifier">b</span><span class="Punctuation">:</span><span class="StringLit">"abcdef"</span><span class="Punctuation">)</span>
-
- <span class="Comment"># ...</span>
-
- <span class="Keyword">let</span> <span class="Identifier">mtLit</span> <span class="Operator">=</span> <span class="Identifier">newLit</span><span class="Punctuation">(</span><span class="Identifier">mt</span><span class="Punctuation">)</span>
-
- <span class="Identifier">result</span> <span class="Operator">=</span> <span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Identifier">echo</span> <span class="Punctuation">`</span><span class="Identifier">arg</span><span class="Punctuation">`</span>
- <span class="Identifier">echo</span> <span class="Punctuation">`</span><span class="Identifier">mtLit</span><span class="Punctuation">`</span>
- <span class="Identifier">myMacro</span><span class="Punctuation">(</span><span class="StringLit">"Hallo"</span><span class="Punctuation">)</span></pre></p>
- <p>The call to <tt class="docutils literal"><span class="pre"><span class="Identifier">myMacro</span></span></tt> will generate the following code:</p>
- <p><pre class="listing"><span class="Identifier">echo</span> <span class="StringLit">"Hallo"</span>
- <span class="Identifier">echo</span> <span class="Identifier">MyType</span><span class="Punctuation">(</span><span class="Identifier">a</span><span class="Punctuation">:</span> <span class="FloatNumber">123.456'f64</span><span class="Punctuation">,</span> <span class="Identifier">b</span><span class="Punctuation">:</span> <span class="StringLit">"abcdef"</span><span class="Punctuation">)</span></pre></p>
- <h2><a class="toc-backref" id="introduction-building-your-first-macro" href="#introduction-building-your-first-macro">Building Your First Macro</a></h2><p>To give a starting point to writing macros we will show now how to implement the <tt class="docutils literal"><span class="pre"><span class="Identifier">myAssert</span></span></tt> macro mentioned earlier. The first thing to do is to build a simple example of the macro usage, and then just print the argument. This way it is possible to get an idea of what a correct argument should look like.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
- <span class="Identifier">echo</span> <span class="Identifier">arg</span><span class="Operator">.</span><span class="Identifier">treeRepr</span>
- <span class="Keyword">let</span> <span class="Identifier">a</span> <span class="Operator">=</span> <span class="DecNumber">1</span>
- <span class="Keyword">let</span> <span class="Identifier">b</span> <span class="Operator">=</span> <span class="DecNumber">2</span>
- <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">a</span> <span class="Operator">!=</span> <span class="Identifier">b</span><span class="Punctuation">)</span></pre></p>
- <p><pre class="listing">Infix
- Ident "!="
- Ident "a"
- Ident "b"</pre></p>
- <p>From the output, it is possible to see that the argument is an infix operator (node kind is "Infix"), as well as that the two operands are at index 1 and 2. With this information, the actual macro can be written.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">arg</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span><span class="Punctuation">)</span><span class="Punctuation">:</span> <span class="Identifier">untyped</span> <span class="Operator">=</span>
- <span class="Comment"># all node kind identifiers are prefixed with "nnk"</span>
- <span class="Identifier">arg</span><span class="Operator">.</span><span class="Identifier">expectKind</span> <span class="Identifier">nnkInfix</span>
- <span class="Identifier">arg</span><span class="Operator">.</span><span class="Identifier">expectLen</span> <span class="DecNumber">3</span>
- <span class="Comment"># operator as string literal</span>
- <span class="Keyword">let</span> <span class="Identifier">op</span> <span class="Operator">=</span> <span class="Identifier">newLit</span><span class="Punctuation">(</span><span class="StringLit">" "</span> <span class="Operator">&</span> <span class="Identifier">arg</span><span class="Punctuation">[</span><span class="DecNumber">0</span><span class="Punctuation">]</span><span class="Operator">.</span><span class="Identifier">repr</span> <span class="Operator">&</span> <span class="StringLit">" "</span><span class="Punctuation">)</span>
- <span class="Keyword">let</span> <span class="Identifier">lhs</span> <span class="Operator">=</span> <span class="Identifier">arg</span><span class="Punctuation">[</span><span class="DecNumber">1</span><span class="Punctuation">]</span>
- <span class="Keyword">let</span> <span class="Identifier">rhs</span> <span class="Operator">=</span> <span class="Identifier">arg</span><span class="Punctuation">[</span><span class="DecNumber">2</span><span class="Punctuation">]</span>
-
- <span class="Identifier">result</span> <span class="Operator">=</span> <span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Keyword">if</span> <span class="Keyword">not</span> <span class="Punctuation">`</span><span class="Identifier">arg</span><span class="Punctuation">`</span><span class="Punctuation">:</span>
- <span class="Keyword">raise</span> <span class="Identifier">newException</span><span class="Punctuation">(</span><span class="Identifier">AssertionDefect</span><span class="Punctuation">,</span><span class="Operator">$</span><span class="Punctuation">`</span><span class="Identifier">lhs</span><span class="Punctuation">`</span> <span class="Operator">&</span> <span class="Punctuation">`</span><span class="Identifier">op</span><span class="Punctuation">`</span> <span class="Operator">&</span> <span class="Operator">$</span><span class="Punctuation">`</span><span class="Identifier">rhs</span><span class="Punctuation">`</span><span class="Punctuation">)</span>
- <span class="Keyword">let</span> <span class="Identifier">a</span> <span class="Operator">=</span> <span class="DecNumber">1</span>
- <span class="Keyword">let</span> <span class="Identifier">b</span> <span class="Operator">=</span> <span class="DecNumber">2</span>
- <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">a</span> <span class="Operator">!=</span> <span class="Identifier">b</span><span class="Punctuation">)</span>
- <span class="Identifier">myAssert</span><span class="Punctuation">(</span><span class="Identifier">a</span> <span class="Operator">==</span> <span class="Identifier">b</span><span class="Punctuation">)</span></pre></p>
- <p>This is the code that will be generated. To debug what the macro actually generated, the statement <tt class="docutils literal"><span class="pre"><span class="Identifier">echo</span> <span class="Identifier">result</span><span class="Operator">.</span><span class="Identifier">repr</span></span></tt> can be used, in the last line of the macro. It is also the statement that has been used to get this output.</p>
- <p><pre class="listing"><span class="Keyword">if</span> <span class="Keyword">not</span> <span class="Punctuation">(</span><span class="Identifier">a</span> <span class="Operator">!=</span> <span class="Identifier">b</span><span class="Punctuation">)</span><span class="Punctuation">:</span>
- <span class="Keyword">raise</span> <span class="Identifier">newException</span><span class="Punctuation">(</span><span class="Identifier">AssertionDefect</span><span class="Punctuation">,</span> <span class="Operator">$</span><span class="Identifier">a</span> <span class="Operator">&</span> <span class="StringLit">" != "</span> <span class="Operator">&</span> <span class="Operator">$</span><span class="Identifier">b</span><span class="Punctuation">)</span></pre></p>
- <h2><a class="toc-backref" id="introduction-going-further" href="#introduction-going-further">Going further</a></h2><p>It is possible to create more complex macros by combining different <tt class="docutils literal"><span class="pre"><span class="Identifier">NimNode</span></span></tt> symbols with <tt class="docutils literal"><span class="pre"><span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span></span></tt> expressions. For example, you may use <tt class="docutils literal"><span class="pre"><span class="Identifier">newStmtList</span></span></tt> to build your macro iteratively, and <tt class="docutils literal"><span class="pre"><span class="Identifier">ident</span></span></tt> in cases in which you wish to create an identifier from a string, as shown below.</p>
- <p><pre class="listing"><span class="Keyword">import</span> <span class="Identifier">std</span><span class="Operator">/</span><span class="Identifier">macros</span>
- <span class="Keyword">macro</span> <span class="Identifier">createProcedures</span><span class="Punctuation">(</span><span class="Punctuation">)</span> <span class="Operator">=</span>
- <span class="Identifier">result</span> <span class="Operator">=</span> <span class="Identifier">newStmtList</span><span class="Punctuation">(</span><span class="Punctuation">)</span>
-
- <span class="Keyword">for</span> <span class="Identifier">i</span> <span class="Keyword">in</span> <span class="FloatNumber">0.</span><span class="Operator">.<</span><span class="DecNumber">10</span><span class="Punctuation">:</span>
- <span class="Keyword">let</span> <span class="Identifier">name</span> <span class="Operator">=</span> <span class="Identifier">ident</span><span class="Punctuation">(</span><span class="StringLit">"myProc"</span> <span class="Operator">&</span> <span class="Operator">$</span><span class="Identifier">i</span><span class="Punctuation">)</span>
- <span class="Keyword">let</span> <span class="Identifier">content</span> <span class="Operator">=</span> <span class="Identifier">newLit</span><span class="Punctuation">(</span><span class="StringLit">"I am procedure number #"</span> <span class="Operator">&</span> <span class="Operator">$</span><span class="Identifier">i</span><span class="Punctuation">)</span>
-
- <span class="Identifier">result</span><span class="Operator">.</span><span class="Identifier">add</span> <span class="Identifier">quote</span> <span class="Keyword">do</span><span class="Punctuation">:</span>
- <span class="Keyword">proc</span> <span class="Punctuation">`</span><span class="Identifier">name</span><span class="Punctuation">`</span><span class="Punctuation">(</span><span class="Punctuation">)</span> <span class="Operator">=</span>
- <span class="Identifier">echo</span> <span class="Punctuation">`</span><span class="Identifier">content</span><span class="Punctuation">`</span>
- <span class="Identifier">createProcedures</span><span class="Punctuation">(</span><span class="Punctuation">)</span>
- <span class="Identifier">myProc7</span><span class="Punctuation">(</span><span class="Punctuation">)</span></pre></p>
- <p>The call to <tt class="docutils literal"><span class="pre"><span class="Identifier">myProc7</span></span></tt> will echo <tt class="docutils literal"><span class="pre"><span class="Identifier">I</span> <span class="Identifier">am</span> <span class="Identifier">procedure</span> <span class="Identifier">number</span> <span class="Comment">#7</span></span></tt>.</p>
- <h2><a class="toc-backref" id="introduction-with-power-comes-responsibility" href="#introduction-with-power-comes-responsibility">With Power Comes Responsibility</a></h2><p>Macros are very powerful. A piece of good advice is to use them as little as possible, but as much as necessary. Macros can change the semantics of expressions, making the code incomprehensible for anybody who does not know exactly what the macro does with it. So whenever a macro is not necessary and the same logic can be implemented using templates or generics, it is probably better not to use a macro. And when a macro is used for something, the macro should better have a well-written documentation. For all the people who claim to write only perfectly self-explanatory code: when it comes to macros, the implementation is not enough for documentation.</p>
- <h2><a class="toc-backref" id="introduction-limitations" href="#introduction-limitations">Limitations</a></h2><p>Since macros are evaluated in the compiler in the NimVM, macros share all the limitations of the NimVM. They have to be implemented in pure Nim code. Macros can start external processes on the shell, but they cannot call C functions except those that are built in the compiler.</p>
- <h1><a class="toc-backref" id="more-examples" href="#more-examples">More Examples</a></h1><p>This tutorial can only cover the basics of the macro system. There are macros out there that could be an inspiration for you of what is possible with it.</p>
- <h2><a class="toc-backref" id="more-examples-strformat" href="#more-examples-strformat">Strformat</a></h2><p>In the Nim standard library, the <tt class="docutils literal"><span class="pre"><span class="Identifier">strformat</span></span></tt> library provides a macro that parses a string literal at compile time. Parsing a string in a macro like here is generally not recommended. The parsed AST cannot have type information, and parsing implemented on the VM is generally not very fast. Working on AST nodes is almost always the recommended way. But still <tt class="docutils literal"><span class="pre"><span class="Identifier">strformat</span></span></tt> is a good example for a practical use case for a macro that is slightly more complex than the <tt class="docutils literal"><span class="pre"><span class="Identifier">assert</span></span></tt> macro.</p>
- <p><a class="reference external" href="https://github.com/nim-lang/Nim/blob/devel/lib/pure/strformat.nim">Strformat</a></p>
- <h2><a class="toc-backref" id="more-examples-ast-pattern-matching" href="#more-examples-ast-pattern-matching">Ast Pattern Matching</a></h2><p>Ast Pattern Matching is a macro library to aid in writing complex macros. This can be seen as a good example of how to repurpose the Nim syntax tree with new semantics.</p>
- <p><a class="reference external" href="https://github.com/nim-lang/ast-pattern-matching">Ast Pattern Matching</a></p>
- <h2><a class="toc-backref" id="more-examples-opengl-sandbox" href="#more-examples-opengl-sandbox">OpenGL Sandbox</a></h2><p>This project has a working Nim to GLSL compiler written entirely in macros. It scans recursively through all used function symbols to compile them so that cross library functions can be executed on the GPU.</p>
- <p><a class="reference external" href="https://github.com/krux02/opengl-sandbox">OpenGL Sandbox</a> </p>
- </p>
-
- </div>
- </div>
- <div class="twelve-columns footer">
- <span class="nim-sprite"></span>
- <br>
- <small style="color: var(--hint);">Made with Nim. Generated: 2025-02-03 14:56:36 UTC</small>
- </div>
- </div>
- </div>
- <script defer data-domain="nim-lang.org" src="https://plausible.io/js/plausible.js"></script>
-
- </body>
- </html>
|