Search code examples
sweet.js

Is it possible for sweet.js macros to define other macros?


I have attempted to define a sweet.js macro that allows other repeating macros to be defined more easily, but I have found a syntax error here:

SyntaxError: [patterns] Ellipses level does not match in the template
11:                 { $($b...)... }

This is the macro that produced this syntax error:

macro repeatingMacro{
    rule{
        $a {
            $b...
        } {
            $c...
        }
    } => {
        //the output of this macro should be another macro with repeating patterns
        macro $a {
            rule{
                { $($b...)... }
            } => {
                { $($c...)... }
            }
        }
    }
}

If this macro were correctly defined, then it would allow other macros to be created, like this one:

repeatingMacro cond {
    $a... { $b... }
}
{
    if($a...){
        $b...
    }
}

var x = 1;
var y = 2;
cond {
(x > y) {
      alert(x);
}
(x < y) {
      alert(y)
}
}

This code can be edited online here.

In other words, is it possible to define a macro that will automatically transform this macro:

macro cond {
  rule {
    $x... { $y... }
} => {
  if($x...){
  $y...
}
}
}

...into this macro?

macro cond {
  rule {
    { $($x... { $y... })... }
} => {
  $(if($x...){
  $y...
})...
}
}

Solution

  • The immediate problem you are running into is that if you need to emit literal ellipses ... in the template of a macro you need to escape it by doing $[...].

    But at a higher level, are you sure you need to go to the effort of macro defining macros here? Sweet.js actually has a nice declarative feature called macroclass (documented here) that makes doing things like this really simple. In fact, cond is the example we use:

    // define the cond_clause pattern class
    macroclass cond_clause {
      pattern {
        rule { $check:expr => $body:expr }
      }
    }
    
    macro cond {
      rule { $first:cond_clause $rest:cond_clause ... } => {
        // sub-pattern variables in the custom class are 
        // referenced by concatenation
        if ($first$check) {
          $first$body
        } $(else if ($rest$check) {
          $rest$body
        }) ...
      }
    }
    
    cond
      x < 3  => console.log("less than 3")
      x == 3 => console.log("3")
      x > 3  => console.log("greater than 3")