如何扩展JavaScript语言以支持新的操作符?

如何扩展JavaScript语言以支持新的操作符?,javascript,node.js,operators,language-extension,Javascript,Node.js,Operators,Language Extension,问题的答案是尚未,但可以使用第三方工具添加新的操作员: 可以使用第三方工具(如sweet.js)添加自定义运算符,但这需要额外的编译步骤 我会举同样的例子,就像上一个问题一样: (ℝ, ∘), x∘ y=x+2y 对于任意两个实数x和y:x∘ y是x+2y这也是一个实数。如何在扩展JavaScript语言中添加此运算符 之后,将运行以下代码: var x = 2 , y = 3 , z = x ∘ y; console.log(z); 输出将包含 8 (因为8是2+2*3) 我如

问题的答案是尚未,但可以使用第三方工具添加新的操作员:

可以使用第三方工具(如sweet.js)添加自定义运算符,但这需要额外的编译步骤

我会举同样的例子,就像上一个问题一样:

(ℝ, ∘), x∘ y=x+2y 对于任意两个实数x和y:x∘ yx+2y这也是一个实数。如何在扩展JavaScript语言中添加此运算符

之后,将运行以下代码:

var x = 2
  , y = 3
  , z = x ∘ y;

console.log(z);
输出将包含

8
(因为
8
2+2*3



我如何扩展JavaScript语言以支持新的操作员?

正如我在您的问题的评论中所说的,sweet.js还没有。您可以自由地fork sweet.js并自己添加它,或者您只是简单地使用SOL

老实说,实现自定义中缀运算符还不值得。Sweet.js是一个受良好支持的工具,也是我所知道的唯一一个尝试在js中实现宏的工具。使用自定义预处理器添加自定义中缀运算符可能不值得

这就是说,如果你是为了非专业的工作而独自工作,做你想做的任何事

编辑


sweet.js现在支持。

是的,这是可能的,甚至不是很难:)


我们需要讨论几件事:

  • 什么是语法和语义
  • 编程语言是如何解析的?什么是语法树
  • 扩展语言语法
  • 扩展语言语义
  • 如何向JavaScript语言添加运算符
  • 如果你很懒,只想看到它的行动-

    1.什么是语法和语义? 一般来说,一门语言由两部分组成

    • 语法-这些是语言中的符号,如一元运算符,如
      ++
      ,以及表示“内联”函数的表达式。语法仅表示所使用的符号,而不是其含义。简而言之,语法只是字母和符号的图画,它没有固有的意义

    • 语义将意义与这些符号联系起来。语义学是说
      ++
      意味着“增加一个”,实际上是这样的。它将意义与我们的语法联系在一起,如果没有它,语法只是一个有顺序的符号列表

    2.编程语言是如何解析的?什么是语法树? 在某种程度上,当某些东西用JavaScript或任何其他编程语言执行代码时,它需要理解这些代码。其中一部分称为词法分析(或标记化,我们不要在这里讨论细微的区别)意味着分解代码,如:

    function foo(){ return 5;}
    
    进入其有意义的部分-也就是说这里有一个
    函数
    关键字,后面是标识符、空参数列表,然后是一个块打开
    {
    ,其中包含一个返回关键字,带有文本
    5
    ,然后是分号,然后是一个结束块

    这一部分完全是语法,它所做的只是将其分解为
    函数,foo,(,),{,return,5,;,}
    。它仍然不理解代码

    然后-构建一个
    语法树
    。语法树更了解语法,但仍然完全是语法树。例如,语法树将看到以下标记:

    function foo(){ return 5;}
    
    然后算出“嘿!这里有一个!”

    它之所以被称为树,是因为树允许筑巢

    例如,上面的代码可以生成如下内容:

                                            Program
                                      FunctionDeclaration (identifier = 'foo')
                                         BlockStatement
                                         ReturnStatement
                                         Literal (5)
    
    这相当简单,只是为了告诉你它并不总是那么线性,让我们检查一下
    5+5

                                            Program
                                      ExpressionStatement
                                   BinaryExpression (operator +)
                                Literal (5)       Literal(5)   // notice the split her
    
    这种分裂可能发生

    基本上,语法树允许我们表达语法

    这里是
    x∘ y
    失败-它看到
    并且不理解语法

    3.扩展语言语法。 这只需要一个解析语法的项目。我们在这里要做的是阅读“我们”语言的语法,它与JavaScript不同(并且不符合规范),并用JavaScript语法可以接受的东西替换我们的运算符

    我们要做的不是JavaScript。它不遵循JavaScript规范,JS解析器会对其抛出异常

    4.扩展语言语义 不管怎样,我们一直在做:)这里我们要做的就是定义一个函数,在调用操作符时调用它

    5.如何向JavaScript语言添加运算符。 让我先在这个前缀后面说,我们不会在这里向JS添加操作符,而是-我们定义自己的语言-让我们称它为“CakeLanguage”或其他什么,然后添加操作符。这是因为
    不是JS语法的一部分,JS语法不允许像这样的任意运算符

    为此,我们将使用两个开源项目:

    • 它接受JS代码并为其生成语法树
    • 这是另一个方向,从语法树esprima spits生成JS代码
    如果你密切注意,你就会知道我们不能直接使用esprima,因为我们会给它语法,它不懂

    为了好玩,我们将添加一个
    #
    操作符,它执行
    x#y==2x+y
    。我们将赋予它多重性的优先级(因为运算符具有运算符优先级)

    因此,在您获得Esprima.js副本后,我们需要更改以下内容:

    fexprtokens
    ——这是我们需要添加
    #
    的表达式,以便它能够识别它。之后,它看起来是这样的:

    FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
                        'return', 'case', 'delete', 'throw', 'void',
                        // assignment operators
                        '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
                        '&=', '|=', '^=', ',',
                        // binary/unary operators
                        '+', '-', '*', '/', '%','#', '++', '--', '<<', '>>', '>>>', '&',
                        '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
                        '<=', '<', '>', '!=', '!=='];
    
    而不是:

        if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
    
    就这样!我们刚刚扩展了语言语法以支持
    操作符

    我们没有被绑架
        if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
    
    case '*':
    case '/':
    case '#': // put it elsewhere if you want to give it another precedence
    case '%':
       prec = 11;
       break;
    
    function visitor(tree,visit){
        for(var i in tree){
            visit(tree[i]);
            if(typeof tree[i] === "object" && tree[i] !== null){
                visitor(tree[i],visit);
            }
        }
    }
    
    visitor(syntax,function(el){ // for every node in the syntax
        if(el.type === "BinaryExpression"){ // if it's a binary expression
    
            if(el.operator === "#"){ // with the operator #
            el.type = "CallExpression"; // it is now a call expression
            el.callee = {name:"operator_sharp",type:"Identifier"}; // for the function operator_#
            el.arguments = [el.left, el.right]; // with the left and right side as arguments
            delete el.operator; // remove BinaryExpression properties
            delete el.left;
            delete el.right;
            }
        }
    });
    
    var syntax = esprima.parse("5 # 5");
    
    visitor(syntax,function(el){ // for every node in the syntax
        if(el.type === "BinaryExpression"){ // if it's a binary expression
    
            if(el.operator === "#"){ // with the operator #
            el.type = "CallExpression"; // it is now a call expression
            el.callee = {name:"operator_sharp",type:"Identifier"}; // for the function operator_#
            el.arguments = [el.left, el.right]; // with the left and right side as arguments
            delete el.operator; // remove BinaryExpression properties
            delete el.left;
            delete el.right;
            }
        }
    });
    
    var asJS = escodegen.generate(syntax); // produces operator_sharp(5,5);
    
    function operator_sharp(x,y){
        return 2*x + y;
    }