[cfe-dev] Nacro: A better C/C++ macro extension implemented in Clang plugins

Min-Yih Hsu via cfe-dev cfe-dev at lists.llvm.org
Thu Jun 11 11:32:43 PDT 2020

Hi Clang folks,

Recently I have been working on a side project, Nacro (https://github.com/mshockwave/nacro <https://github.com/mshockwave/nacro>), that you might find interesting.

Nacro is a small DSL aim to provide a better C/C++ macro experience. Here is an example:
#pragma nacro rule myPrint
(val:$expr) -> $stmt {
    printf("%d\n", val * 2)

#pragma nacro rule genColors
(cases:$expr*) -> {
    $loop(c in cases) {
        case c:
        printf("the color is %s\n", $str(c));

enum Color { RED, BLUE, YELLOW };
void printColor(enum Color color) {
    switch(color) genColors(RED, BLUE, YELLOW)

int main() {
    myPrint(1 + 3)   // print out '8'
    printColor(RED); // print 'the color is RED'
    return 0;
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:
 - Multiline definitions without ‘\’.
 - 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.
 - 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.
 - Detecting invalid capturing. This is still an unstable feature, but basically it’s aiming to detect issues like this:
#define foo(arg) {\
    int x = 0;  \
    return arg + x; \

// ERROR: caller will always return 0 
// regardless the argument value
int caller(int x) {

Please checkout the its wiki page (https://github.com/mshockwave/nacro/wiki/Invalid-Capture-Detection <https://github.com/mshockwave/nacro/wiki/Invalid-Capture-Detection>) for more info.

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.

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.

Another hard part is the Invalid Capture Detection. My original plan was to automatically 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. 

Hope you find this project interesting, or at least find it amusing. 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200611/fafd5875/attachment-0001.html>

More information about the cfe-dev mailing list