Reputation: 305
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!
Upvotes: 2
Views: 96
Reputation: 1528
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.
Upvotes: 2