Reputation: 20598
In the bluebird wiki article about JavaScript optimization killers, the author mentions that passing the arguments
keyword to any function (except apply
) will cause the parent function to not be optimizable. I would like to create a sweet.js macro that allows me to write standard idiomatic JavaScript but will take care of the optimization killer.
Ideally, I would like a macro that would take the following function:
function foo() {
var args = [].slice.call(arguments);
return args;
}
And output something like this:
function foo() {
var args = [];
for(var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args;
}
I am having trouble with getting the sweet.js macro syntax correct, however. This is what I have so far:
let arguments = macro {
rule infix {
[].slice.call |
} => {
[];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
function toArray() {
var args = [].slice.call arguments
return args;
}
Which outputs the following:
function toArray() {
var args$2 = [];
for (var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
return args$2;
}
I tried making my macro have parenthesis around the arguments
keyword and also include the var
declaration, but without any success. I tried something like this:
let arguments = macro {
rule infix {
var $var = [].slice.call ( | )
} => {
var $var = [];
for(var i = 0, len = arguments.length; i < len; i++) {
args.push(arguments[i])
}
}
}
This produces the following error:
SyntaxError: [syntaxCase] Infix macros require a `|` separator
414:
^
Upvotes: 2
Views: 386
Reputation: 161
This isn't quite the same result, since it has a function wrapper (though it's invoked with apply
), but it doesn't require you to override var
and can be used in any expression position.
macro copy_args {
rule {} => {
function() {
var len = arguments.length;
var args = Array(len);
for (var i = 0; i < len; i++) {
args[i] = arguments[i];
}
return args;
}.apply(this, arguments)
}
}
let slice = macro {
rule infix { []. | .call(arguments) } => { copy_args }
rule infix { []. | .apply(arguments) } => { copy_args }
rule infix { Array.prototype. | .call(arguments) } => { copy_args }
rule infix { Array.prototype. | .apply(arguments) } => { copy_args }
rule { } => { slice }
}
function go() {
var args = Array.prototype.slice.call(arguments);
return args;
}
expands to
function go() {
var args = function () {
var len = arguments.length;
var args$2 = Array(len);
for (var i = 0; i < len; i++) {
args$2[i] = arguments[i];
}
return args$2;
}.apply(this, arguments);
return args;
}
Don't know if that would kill optimization though...
Upvotes: 2
Reputation: 5337
Right, so there are a couple of ways to do this. Putting arguments
inside of parens doesn't work because infix macros can't match outside
of enclosing delimiters so when the arguments
macro gets invoked it
sees zero tokens before or after it (the error should be clearer).
Your other solution is running into hygiene problems since the
arguments
macro needs access to the args
identifier but infix
macros are not allowed to match before the equals sign when it's in a
var
statement so it can't actually match the args
identifier.
So couple of solutions. The easiest is to just do something like what the bluebird wiki suggested:
macro argify {
rule { ( $arg ) } => {
var $arg;
for (var i, len = arguments.length; i < len; i++) {
args.push(arguments[i]);
}
}
}
function foo() {
argify(args)
return args;
}
You could also go the unhygienic route (not really recommended but
arguments
is already kinda unhygienic so…):
let function = macro {
case {$mname $name ( $parens ...) { $body ... } } => {
letstx $args = [makeIdent("args", #{$mname})];
return #{
function $name ( $parens ...) {
var $args = [];
for (var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
$body ...
}
}
}
}
function foo() {
return args;
}
Edit:
I just thought of another solution that would allow you to keep your current syntax by overriding var
:
let var = macro {
rule { $args = [].slice.call(arguments) } => {
var $args = [];
for(var i, len = arguments.length; i < len; i++) {
$args.push(arguments[i]);
}
}
rule { $rest ... } => { var $rest ... }
}
function foo() {
var args = [].slice.call(arguments);
}
Upvotes: 2