I am attempting to use SweetJS in my project. In order to better understand and learn SweetJS I thought I would start with a simple "class" macro (I know a few exist, just playing around here...). I can NOT seem to get SweetJS to stop messing with my local variables "self" and "superCall" however. Any ideas what I am doing wrong? I would like var self=this
to remain var self=this
instead of being mangled.
macro class {
case { _ $name extends $parent {
constructor $cargs { $cbody ... }
$($mname $margs { $mbody ... } ) ...
} } => {
return #{
function $name $cargs { var self=this,superCall=$parent.prototype; $cbody ... }
$name.prototype = Object.create($parent.prototype);
($name.prototype.$mname = function $margs {var self=this,superCall=$parent.prototype; $mbody ... } ) ...;
}
}
case { _ $name { $body ...} } => {
return #{ class $name extends test2 { $body ... } };
}
}
macro super {
case { $macroName.$name( $($args (,) ...) ) } => {
letstx $s = [makeIdent("self", #{ $macroName })];
letstx $sC = [makeIdent("superCall", #{ $macroName })];
return #{
$sC.$name.call($s)
};
}
case { $macroName( $args ... ) } => {
letstx $s = [makeIdent("self", #{ $macroName })];
letstx $sC = [makeIdent("superCall", #{ $macroName })];
return #{
superCall.constructor.call($s);
};
}
}
class test extends cow {
constructor(arg1, arg2) {
console.log('Hello world!');
}
method1(arg1, arg2) {
super.method1();
}
}
This expands to:
function test(arg1, arg2) {
var self$2 = this, superCall$2 = cow.prototype;
console.log('Hello world!');
}
test.prototype = Object.create(cow.prototype);
test.prototype.method1 = function (arg1, arg2) {
var self$2 = this, superCall$2 = cow.prototype;
superCall.method1.call(self);
};
As you can see, var self=this
has been turned into var self$2 = this
. How can I prevent this? I have attempted to use makeIdent
, but I think I am doing something wrong. Any ideas? Thanks!
In order to break hygiene you need to provide the lexical context that is outside the scope of the macro you are in. In this case, by using the $name
binding, you are actually referencing scope outside of your macro rather than from within; this makes breaking hygiene possible in this case.
As a result, the following seems to work:
macro class {
case { _ $name extends $parent {
constructor $cargs { $cbody ... }
$($mname $margs { $mbody ... } ) ...
} } => {
letstx $self = [makeIdent("self", #{ $name })];
return #{
function $name $cargs { var $self=this,superCall=$parent.prototype; $cbody ... }
$name.prototype = Object.create($parent.prototype);
($name.prototype.$mname = function $margs {var $self=this,superCall=$parent.prototype; $mbody ... } ) ...;
}
}
case { _ $name { $body ...} } => {
return #{ class $name extends test2 { $body ... } };
}
}
Notice that I created an identifier named $self
and used the name of the class as my syntax object.
Read more about breaking hygiene here.