【问题标题】:How to create a parameterized infix macro in sweet.js如何在 sweet.js 中创建参数化中缀宏
【发布时间】:2014-12-20 17:53:59
【问题描述】:

bluebird wiki article about JavaScript optimization killers中,作者提到将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 宏语法。 This is what I have so far:

example.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: 
                                ^

【问题讨论】:

  • 实际上,您的第一个函数很好,因为您返回的是一个数组,而不是一个参数。您也不需要循环或临时变量。
  • @dandavis 出于我的目的,我想要循环。您有机会阅读链接的文章吗?它解释了为什么不想将arguments 传递给Array.prototype.slice.call

标签: javascript macros infix-notation sweet.js


【解决方案1】:

是的,所以有几种方法可以做到这一点。提出论点 括号内部不起作用,因为中缀宏无法匹配外部 封闭分隔符,所以当 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);
}

【讨论】:

  • 嗯,不能在封闭分隔符之外进行匹配真是令人遗憾。如果不改变我们的任何代码库(或开发人员的习惯),而只是将 sweetjs 作为构建步骤的一部分,那就太好了。
  • 刚刚在另一个潜在的解决方案中进行了编辑,应该让您不会更改任何代码。
  • 谢谢!我应该考虑走那条路。我正在向您的问题跟踪器提出功能请求。
【解决方案2】:

这不是完全相同的结果,因为它有一个函数包装器(尽管它是用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;
}

不知道这是否会扼杀优化...

【讨论】:

  • 应用是唯一不会扼杀优化的东西。 Array.apply(null, arguments) 几乎和 for 循环一样快,但如果 arguments 只有一个元素就会中断。
猜你喜欢
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 2016-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多