Ion*_*zău 22 javascript operators language-extension node.js
问题的答案是否可以在JavaScript中创建自定义运算符?还没有,但@Benjamin建议可以使用第三方工具添加新的运算符:
虽然需要额外的编译步骤,但可以使用像sweet.js这样的第三方工具来添加自定义运算符.
我会采用相同的例子,就像上一个问题一样:
对于任何两个实数x和y:x∘y是x + 2y,也是实数.如何在我的扩展JavaScript语言中添加此运算符?
运行以下代码后:
var x = 2
, y = 3
, z = x ? y;
console.log(z);
Run Code Online (Sandbox Code Playgroud)
输出将包含
8
Run Code Online (Sandbox Code Playgroud)
(因为8是2 + 2 * 3)
我如何扩展JavaScript语言以支持新的运算符?
Ben*_*aum 43
是的,这是可能的,甚至不是很难:)
我们需要讨论一些事情:
如果你很懒,只是想看看它的实际效果 - 我把工作代码放在GitHub上
一般来说 - 一种语言由两件事组成.
语法 -这是在像元运算符一样的语言符号++,以及Expression像是一个FunctionExpression表示"内联"的功能.语法仅代表使用的符号,而不代表其含义.简而言之,语法只是字母和符号的图形 - 它没有固有的含义.
语义与这些符号有关.语义就是所谓的++"递增一",实际上这就是确切的定义.它与我们的语法有意义,没有它,语法只是带有顺序的符号列表.
在某些时候,当某些东西用JavaScript或任何其他编程语言执行你的代码时 - 它需要理解那些代码.这个名为lexing的一部分(或标记化,我们不会在这里进行细微差别)意味着分解如下代码:
function foo(){ return 5;}
Run Code Online (Sandbox Code Playgroud)
进入其有意义的部分 - 也就是说function这里有一个关键字,后跟一个标识符,一个空参数列表,然后是一个{包含带有文字的return关键字的块开头5,然后是一个分号,然后是一个结束块}.
这部分完全在语法中,它所做的就是将它分解为类似的部分function,foo,(,),{,return,5,;,}.它仍然不了解代码.
在那之后 - 一个Syntax Tree建成.语法树更了解语法,但仍然完全是语法.例如,语法树会看到以下标记:
function foo(){ return 5;}
Run Code Online (Sandbox Code Playgroud)
并弄清楚"嘿!这里有一个函数声明!".
它被称为树,因为它只是 - 树允许嵌套.
例如,上面的代码可以产生如下内容:
Program
FunctionDeclaration (identifier = 'foo')
BlockStatement
ReturnStatement
Literal (5)
Run Code Online (Sandbox Code Playgroud)
这很简单,只是为了告诉你它并不总是如此线性,让我们检查一下5 +5:
Program
ExpressionStatement
BinaryExpression (operator +)
Literal (5) Literal(5) // notice the split her
Run Code Online (Sandbox Code Playgroud)
可能发生这种分裂.
基本上,语法树允许我们表达语法.
这是x ? y失败的地方 - 它看到?并且不理解语法.
这只需要一个解析语法的项目.我们在这里要做的是阅读"我们的"语言的语法,它与JavaScript不同(并且不符合规范),并用JavaScript语法可以替换我们的运算符.
我们要做的不是 JavaScript.它不遵循JavaScript规范和标准投诉JS解析器将抛出异常.
我们总是这样做:)我们在这里所做的只是定义一个在调用运算符时调用的函数.
让我先说一下这个前缀后我们不会在这里添加一个运算符,而是 - 我们正在定义我们自己的语言 - 让我们称它为"CakeLanguage"或其他东西并添加运算符吧.这是因为?它不是JS语法的一部分,并且JS语法不允许像其他语言那样的任意运算符.
我们将使用两个开源项目:
你密切注意你知道我们不能直接使用esprima因为我们会给它不懂的语法.
我们将添加一个有趣的#运算符x # y === 2x + y.我们将赋予它多重性的优先级(因为运算符具有运算符优先级).
因此,在您获得Esprima.js副本后 - 我们需要更改以下内容:
要FnExprTokens- 这是我们需要添加的表达式,#以便识别它.之后,它看起来像这样:
FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
'return', 'case', 'delete', 'throw', 'void',
// assignment operators
'=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
'&=', '|=', '^=', ',',
// binary/unary operators
'+', '-', '*', '/', '%','#', '++', '--', '<<', '>>', '>>>', '&',
'|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
'<=', '<', '>', '!=', '!=='];
Run Code Online (Sandbox Code Playgroud)
为了scanPunctuator我们将添加它和它的字符代码作为可能的情况:case 0x23: // #
然后进行测试,看起来像:
if ('<>=!+-*#%&|^/'.indexOf(ch1) >= 0) {
Run Code Online (Sandbox Code Playgroud)
代替:
if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
Run Code Online (Sandbox Code Playgroud)
然后binaryPrecedence让它给它与多重性相同的优先级:
case '*':
case '/':
case '#': // put it elsewhere if you want to give it another precedence
case '%':
prec = 11;
break;
Run Code Online (Sandbox Code Playgroud)
而已!我们刚刚扩展了语言语法以支持#运算符.
我们尚未完成,我们需要将其转换回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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这只是通过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;
}
}
});
Run Code Online (Sandbox Code Playgroud)
简而言之:
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);
Run Code Online (Sandbox Code Playgroud)
我们需要做的最后一件事是定义函数本身:
function operator_sharp(x,y){
return 2*x + y;
}
Run Code Online (Sandbox Code Playgroud)
并将其包含在我们的代码之上.
这里的所有都是它的!如果你读到目前为止 - 你应该得到一个cookie :)
| 归档时间: |
|
| 查看次数: |
3005 次 |
| 最近记录: |