function bubbleSort(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
var tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
swap(arr[j], arr[j + 1]);
}
}
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
macro { swap($a, $b); }
=> { var tmp = $a; $a = $b; $b = tmp; }
macro
{ swap($a, $b);}
=> { var tmp = $a;
$a = $b;
$b = tmp; }
swap(arr[j], arr[j+1]);
⇩
{$a:`arr[j]`,$b:`arr[j+1]`}
⇩
var tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
macro
{ swap($a, $b);}
=> { var tmp = $a;
$a = $b;
$b = tmp; }
swap(point.x, point.y);
⇧
{$a:`point.x`,$b:`point.y`}
⇧
var tmp = point.x;
point.x = point.y;
point.y = tmp;
Github Repository: https://github.com/mozilla/sweet.js
Online Live Demo: http://sweetjs.org/browser/editor.html
Disney, Faubion, Herman and Flanagan.
Sweeten your JavaScript: Hygienic macros for ES5. (DLS'14).
LISP, Scheme, Racket, Rust, Template Haskell
Pombrio and Krishnamurthi.
Resugaring: Lifting Evaluation Sequences Through Syntactic Sugar. (PLDI'14).
macro
{ swap($a, $b);}
=> { var tmp = $a;
$a = $b;
$b = tmp; }
swap(x, y);
⇧
{$a:`x`,$b:`y`} ✗
⇧
var x = 12, y = 23;
var tmp = x;
x = yy + 1;
y = tmp;
class Person {
constructor(name) {
this.name = name;
}
sayName() {
alert(this.name);
}
}
function Person(name) {
this.name = name;
}
Person.prototype.sayName =
function() {
alert(this.name);
};
macro { class $cname {
constructor ($cparams...) $cbody
$($mname ($mparams...) $mbody)...
} }
=> { function $cname ($cparams...) $cbody
$($cname.prototype.$mname =
function ($mparams...) $mbody;)... }
macro { inc $x } => { $x + 1 }
macro { inc 1 } => { 3 }
macro { inc $x } => { $x + 1 }
macro { inc $x } => { $x + 1 }
macro { swap($a, $b) }
=> { var tmp = $a; $a = $b; $b = tmp; }
macro { swap($a, $b) }
=> { var tmp = $a; $a = $b; $b = tmp; }
Naive reverse expansion produces incorrect results
Correct refactoring depends on context and scoping
Solution: Macro-expand original and refactored code and enforce syntactic equivalence
expand(source) =α expand(refactor(source))
Syntactic equivalence may reject valid refactoring candiates
Can the macrofication refactoring be applied to real world code?
Task: Refactor BackboneJS, a MVC library for JavaScript, by replacing its prototype definitions with ES2015 class
definitions
Is it fast enough for interactive use?
Benchmark: Find all macrofication candidates in the ru-lang project
Project | LOC | Time to refactor | Macros | Macrofications |
---|---|---|---|---|
Backbone.js | 1633 | 0.9s | 1 | 5 |
ru-lang | 257 | 17.0s | 27 | 52 |
Advantages
Disadvantages
let macro = macro {
rule {
{ $name $pat ... } => { $tmpl ... }
} => {
macro $name {
rule { $pat ... } => { $tmpl ... }
}
}
}