<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi Clang folks,<div class=""><br class=""></div><div class="">Recently I have been working on a side project, Nacro (<a href="https://github.com/mshockwave/nacro" class="">https://github.com/mshockwave/nacro</a>), that you might find interesting.</div><div class=""><br class=""></div><div class=""><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">Nacro is a small DSL aim to provide a better C/C++ macro experience. Here is an example:</span></div><div class=""><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">```</span></div><div class=""><pre style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 0px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(36, 41, 46); font-variant-ligatures: normal; orphans: 2; widows: 2;" class="">#<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">pragma</span> nacro rule myPrint
(val:$expr) -> $stmt {
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">printf</span>(<span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);"><span class="pl-pds" style="box-sizing: border-box;">"</span>%d<span class="pl-cce" style="box-sizing: border-box;">\n</span><span class="pl-pds" style="box-sizing: border-box;">"</span></span>, val * <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">2</span>)
}
#<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">pragma</span> nacro rule genColors
(cases:$expr*) -> {
$<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">loop</span>(c in cases) {
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">case</span> c:
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">printf</span>(<span class="pl-s" style="box-sizing: border-box; color: rgb(3, 47, 98);"><span class="pl-pds" style="box-sizing: border-box;">"</span>the color is %s<span class="pl-cce" style="box-sizing: border-box;">\n</span><span class="pl-pds" style="box-sizing: border-box;">"</span></span>, $<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">str</span>(c));
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">break</span>;
}
}
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">enum</span> Color { RED, BLUE, YELLOW };
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">void</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">printColor</span>(<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">enum</span> Color color) {
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">switch</span>(color) <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">genColors</span>(RED, BLUE, YELLOW)
}
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">int</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">main</span>() {
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">myPrint</span>(<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">1</span> + <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">3</span>) <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">//</span> print out '8'</span>
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">printColor</span>(RED); <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">//</span> print 'the color is RED'</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">return</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">0</span>;
}</pre></div><div class=""><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">```</span></div><div class=""><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">You can create a nacro rule with a pragma directive and follows with definitions written a simple DSL. The defined nacro rule works just like a normal macro function (i.e. expanded during preprocessing and called like a function), but with some extra features:</span></div><div class=""><font color="#000000" class=""> - Multiline definitions without ‘\’.</font></div><div class=""><font color="#000000" class=""> - Expression Protection: As shown in the ‘myPrint’ nacro above, you don’t need to add parens around expressions anymore, the nacro preprocessor will do that for you.</font></div><div class=""><font color="#000000" class=""> - Loops, one of the most exciting features in nacro: You can use “$loop” directive to ‘unroll’ a list of argument (argument type trailing with ‘*’, like ‘$expr*’. It’s equivalent to VA_ARGS) during preprocessing.</font></div><div class=""><font color="#000000" class=""> - Detecting invalid capturing. This is still an unstable feature, but basically it’s aiming to detect issues like this:</font></div><div class=""><font color="#000000" class="">```</font></div><div class=""><pre style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 0px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(36, 41, 46); font-variant-ligatures: normal; orphans: 2; widows: 2;" class="">#<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">define</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">foo</span>(<span class="pl-v" style="box-sizing: border-box; color: rgb(227, 98, 9);">arg</span>) {\
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">int</span> x = <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">0</span>; \
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">return</span> arg + x; \
}
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">//</span> ERROR: caller will always return 0 </span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">//</span> regardless the argument value</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">int</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">caller</span>(<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">int</span> x) {
<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">foo</span>(x)
}</pre><div class=""><br class=""></div></div><div class=""><font color="#000000" class="">```</font></div><div class=""><font color="#000000" class="">Please checkout the its wiki page (</font><a href="https://github.com/mshockwave/nacro/wiki/Invalid-Capture-Detection" class="">https://github.com/mshockwave/nacro/wiki/Invalid-Capture-Detection</a><span style="color: rgb(0, 0, 0);" class="">) for more info.</span></div><div class=""><span style="color: rgb(0, 0, 0);" class=""><br class=""></span></div><div class=""><font color="#000000" class="">Another important thing is that nacro _doesn’t require a custom Clang / LLVM_. All of the features are implemented in a Clang plugin. So all you need is a Clang 10 by your hand and the plugin.</font></div><div class=""><font color="#000000" class=""><br class=""></font></div><div class=""><font color="#000000" class="">Though not modifying Clang actually put a tons of restrictions and force me to create many hacky workarounds that might be broken in newer version of Clang. For example, as a preprocessor plugin, all i can do is mutating and injecting tokens. But the preprocessor doesn’t like it, or in other words, current preprocessor doesn’t have a good support on this kind of actions. Especially while I was dealing with SourceLocation: there are many time i need to use an imprecise one or even fake one.</font></div><div class=""><font color="#000000" class=""><br class=""></font></div><div class=""><font color="#000000" class="">Another hard part is the Invalid Capture Detection. My original plan was to </font><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">automatically </span><font color="#000000" class="">fix it rather than throwing an error message. However, that will require symbol table and scope info that merely exist in AST. Though Parser/Sema has those info but currently we don’t have any plugin support on Parser/Sema. </font></div><div class=""><font color="#000000" class=""><br class=""></font></div><div class=""><font color="#000000" class="">Hope you find this project interesting, or at least find it amusing. </font></div><div class=""><font color="#000000" class=""><br class=""></font></div><div class=""><font color="#000000" class="">-Min</font></div></body></html>