当前位置: 首页 > 面试题库 >

如何扩展JavaScript语言以支持新的运算符?

容磊
2023-03-14
问题内容

问题的答案
是否可以在JavaScript中创建自定义运算符?
没有的,但@Benjamin建议,这将有可能使用添加一个新的运营商
的第三方工具

可以使用sweet.js之类的第三方工具来添加自定义运算符,尽管这需要额外的编译步骤。

我将采用相同的示例,就像上一个问题一样:

(ℝ,∘),x y = x + 2y

对于任何两个实数 xyx yx + 2y ,这也是一个实数。如何在扩展的JavaScript语言中添加此运算符?

之后,将运行以下代码:

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

console.log(z);

输出将包含

8

(因为82 + 2 * 3

如何扩展JavaScript语言以支持新的运算符?


问题答案:

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

我们需要讨论一些事情:

  1. 什么是语法和语义。
  2. 程序语言如何解析?什么是语法树?
  3. 扩展语言语法。
  4. 扩展语言语义。
  5. 如何将运算符添加到JavaScript语言。

如果您很懒,只是想看看它在运行- 我将工作代码放在GitHub上

1.什么是语法和语义?

通常,一种语言由两部分组成。

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

  • 语义 与这些符号联系在一起。语义学的++意思是“一个增量”,实际上这里是确切的定义。它把含义与我们的语法联系在一起,没有它,语法只是带有顺序的符号列表。

2.如何解析编程语言?什么是语法树?

在某些时候,当某人用JavaScript或任何其他编程语言执行您的代码时-它需要了解该代码。其中一部分叫做 词法分析 (或
词法化
,在这里不要做细微的区别)意味着分解代码,例如:

function foo(){ return 5;}

对其有意义的部分进行说明-
也就是说,这里有一个function关键字,后跟一个标识符,一个空的参数列表,然后是一个{包含带有文字的return关键字的块开头5,然后是一个分号,然后一个end块}

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

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

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语法不允许像某些其他语言一样使用任意运算符。

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

  • esprima ,它将JS代码并为其生成语法树。
  • escodegen 朝另一个方向发展,从语法树esprima spits生成JS代码。

如果您密切注意,您会知道我们 不能 直接使用esprima,因为我们将提供它不了解的语法。

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

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

FnExprTokens-这是我们需要添加的 表达式#以便可以识别它。然后,它看起来像这样:

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

scanPunctuator添加它及其字符代码(可能的话):case 0x23: // #

然后进行测试,如下所示:

 if ('<>=!+-*#%&|^/'.indexOf(ch1) >= 0) {

代替:

    if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {

然后binaryPrecedence让我们赋予它与多重性相同的优先级:

case '*':
case '/':
case '#': // put it elsewhere if you want to give it another precedence
case '%':
   prec = 11;
   break;

而已!我们刚刚扩展了语言语法以支持#运算符。

我们尚未完成,我们需要将其转换回JS。

首先,visitor为树定义一个简短函数,该函数递归访问其所有节点。

function visitor(tree,visit){
    for(var i in tree){
        visit(tree[i]);
        if(typeof tree[i] === "object" && tree[i] !== null){
            visitor(tree[i],visit);
        }
    }
}

这只是通过Esprima生成的树并进行访问。我们将其传递给函数,并在每个节点上运行该函数。

现在,让我们来对待我们特殊的新运算符:

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

并将其包含在我们的代码上方。

这里的所有都是它的!如果到目前为止,您应该得到一个cookie :)

这是GitHub上的代码,因此您可以使用它。



 类似资料:
  • 我尝试扩展ISOSTS XSD方案以支持SVG图像标记。我找到了用于SVG的XSD方案,并将其放在附近。现在我尝试扩展: 但是我在尝试加载方案时出错: 怎么啦?

  • 为什么一定要使用 ...path 才能正确的运行,在上面代码中测试的结果是一样的,而下面则一定要用 ... ?否则就会出现如图2所示的结果 这段代码是 解决 (给定两个整数 n 和 k,返回范围 [1,n] 中所有可能的 k 个数的组合。) 这个问题的 ,用的回溯

  • Electron 支持 Chrome 扩展API的子集, 主要是支持 DevTools 扩展和 Chromium-internal 扩展,但它同时也支持一些其他扩展能。 注意:Electron 不支持商店中的任意 Chrome 扩展,Electron 项目的目标不是与 Chrome 的扩展实现完全兼容。 加载扩展 Electron 只支持加载未打包的扩展 (即不能使用 .crx 文件)。 插件会被

  • 多亏这些改变,我们可以去创建自己的builder和代码块。我们已经在使用一些有趣的函数,比如with。如下简单的实现: inline fun <T> with(t: T, body: T.() -> Unit) { t.body() } 这个函数接收一个T类型的对象和一个被作为扩展函数的函数。它的实现仅仅是让这个对象去执行这个函数。因为第二个参数是一个函数,所以我们可以把它放在圆括号外面,所以我们

  • 问题内容: 我仍在寻求一种非常简单的语言,现在我知道没有任何语言。所以我是用ANTLR3自己写的。 我在这个答案中找到了一个非常好的例子: 克: Java代码: 使用这个ANTLR语法,我可以评估类似 结果是13 现在,我的用例唯一缺少的是一种向其中注入简单的double变量的方法,因此我可以通过提供{“ A”:12.0,“ B”:14.0}作为输入映射来评估以下内容: 有任何想法吗? 问题答案:

  • 本文向大家介绍ANTLR语言支持,包括了ANTLR语言支持的使用技巧和注意事项,需要的朋友参考一下 示例 ANTLR能够为多种编程语言生成解析器: C#目标 Python目标 JavaScript目标 Java目标 默认情况下,ANTLR将使用Java编程语言从命令行生成解析器: 要更改目标语言,可以从OS终端/命令行运行以下命令: 不必每次都在命令行/终端上使用“ -Dlanguage”参数来为