Как да създадете параметризиран инфиксен макрос в sweet.js

В статията на bluebird wiki за убийците на JavaScript оптимизация, авторът споменава, че предаването на ключовата дума arguments към която и да е функция (с изключение на apply) ще накара родителската функция да не бъде оптимизирана. Бих искал да създам sweet.js макрос, който ми позволява да пиша стандартен идиоматичен JavaScript, но ще се погрижи за убиеца на оптимизацията .

В идеалния случай бих искал макрос, който да изпълнява следната функция:

function foo() {
    var args = [].slice.call(arguments);
    return args;
}

И извежда нещо като това:

function foo() {
    var args = [];
    for(var i, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }
    return args;
}

Имам обаче проблеми с правилния синтаксис на макроса sweet.js. Ето какво имам досега:

пример.sjs

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;
}

Което извежда следното:

function toArray() {
    var args$2 = [];
    for (var i = 0, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    }
    return args$2;
}

Опитах се да накарам моя макрос да има скоби около ключовата дума arguments и също да включа декларацията var, но без никакъв успех. Опитах нещо подобно:

невалиден макрос

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])
        }
    }
}

Това води до следната грешка:

SyntaxError: [syntaxCase] Infix macros require a `|` separator
414: 
                                ^

person kzh    schedule 24.10.2014    source източник
comment
всъщност първата ви функция е наред, тъй като връщате масив, а не аргументи. също така не се нуждаете от цикъла или временната променлива.   -  person dandavis    schedule 25.10.2014
comment
@dandavis За моите цели искам цикъла. Имахте ли възможност да прочетете свързаната статия? Това обяснява защо човек не би искал да премине arguments към Array.prototype.slice.call.   -  person kzh    schedule 25.10.2014


Отговори (2)


Добре, така че има няколко начина да направите това. Поставянето на аргументи вътре в скоби не работи, тъй като макросите infix не могат да съвпадат извън обхващащите разделители, така че когато макросът arguments бъде извикан, той вижда нулеви токени преди или след него (грешката трябва да е по-ясна).

Другото ви решение се сблъсква с хигиенни проблеми, тъй като макросът arguments се нуждае от достъп до идентификатора args, но макросите с инфикс не могат да съвпадат преди знака за равенство, когато е в оператор var, така че всъщност не може да съответства на идентификатора args.

И така, няколко решения. Най-лесният е просто да направите нещо като това, което предлага bluebird wiki:

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;
}

Можете също така да отидете по нехигиеничния път (наистина не се препоръчва, но arguments вече е доста нехигиеничен, така че...):

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;
}

Редактиране:

Току-що се сетих за друго решение, което ще ви позволи да запазите текущия си синтаксис чрез замяна на 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);
}
person timdisney    schedule 25.10.2014
comment
Е, неприятно е да не можеш да съпоставиш извън ограждащите разделители. Би било хубаво да не променяме никоя от нашата кодова база (или навиците на програмиста) и просто да имаме sweetjs като част от стъпката на изграждане. - person kzh; 25.10.2014
comment
Току-що редактиран в друго потенциално решение, което би трябвало да ви позволи да не променяте нищо от кода си. - person timdisney; 25.10.2014
comment
Благодаря! Трябваше да помисля дали да тръгна по този път. Бях по средата на поставянето на заявка за функция във вашия инструмент за проследяване на проблеми. - person kzh; 25.10.2014

Това не е съвсем същият резултат, тъй като има обвивка на функция (въпреки че се извиква с apply), но не изисква да замените var и може да се използва във всяка позиция на израз.

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;
}

се разширява до

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;
}

Не знам обаче дали това ще убие оптимизацията...

person Nathan Faubion    schedule 25.10.2014
comment
Apply е единственото нещо, което не убива оптимизацията. Array.apply(null, arguments) е почти толкова бърз, колкото for цикъла, но прекъсва, ако arguments има само един елемент. - person kzh; 26.10.2014