出于兴趣,我想学习如何为一种简单的语言编写解析器,最终为我自己的小代码高尔夫语言编写一个解释器,只要我了解了这些东西的一般工作原理。
所以我开始阅读 Douglas Crockfords 的文章Top Down Operator Precedence。
注意:如果您想更深入地了解以下代码片段的上下文,您可能应该阅读这篇文章
我很难理解var
语句和赋值运算符=
应该如何协同工作。
DC 定义了一个赋值运算符,例如
var assignment = function (id) {
return infixr(id, 10, function (left) {
if (left.id !== "." && left.id !== "[" &&
left.arity !== "name") {
left.error("Bad lvalue.");
}
this.first = left;
this.second = expression(9);
this.assignment = true;
this.arity = "binary";
return this;
});
};
assignment("=");
注意:[[value]] 指代币,简化为它的值
现在如果表达式函数达到 eg [[t]],[[=]],[[2]]
,结果[[=]].led
是这样的。
{
"arity": "binary",
"value": "=",
"assignment": true, //<-
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "2"
}
}
DC使assignment
功能,因为
我们希望它做两件额外的事情:检查左操作数以确保它是正确的左值,并设置一个赋值成员,以便我们稍后可以快速识别赋值语句。
这对我来说是有意义的,直到他介绍了
var
声明,定义如下。
var 语句定义当前块中的一个或多个变量。每个名称可以选择后跟 = 和一个初始化表达式。
stmt("var", function () {
var a = [], n, t;
while (true) {
n = token;
if (n.arity !== "name") {
n.error("Expected a new variable name.");
}
scope.define(n);
advance();
if (token.id === "=") {
t = token;
advance("=");
t.first = n;
t.second = expression(0);
t.arity = "binary";
a.push(t);
}
if (token.id !== ",") {
break;
}
advance(",");
}
advance(";");
return a.length === 0 ? null : a.length === 1 ? a[0] : a;
});
现在,如果解析器到达一组标记,比如[[var]],[[t]],[[=]],[[1]]
生成的树,它会看起来像这样。
{
"arity": "binary",
"value": "=",
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "1"
}
}
我的问题的关键部分是if (token.id === "=") {...}
部分。
我不明白为什么我们打电话
t = token;
advance("=");
t.first = n;
t.second = expression(0);
t.arity = "binary";
a.push(t);
而不是
t = token;
advance("=");
t.led (n);
a.push(t);
在...
部分。
这将调用我们的[[=]]
操作符led
函数(赋值函数),它确实
确保它是一个正确的左值,并设置一个赋值成员,以便我们以后可以快速识别赋值语句。 例如
{
"arity": "binary",
"value": "=",
"assignment": true,
"first": {
"arity": "name",
"value": "t"
},
"second": {
"arity": "literal",
"value": "1"
}
}
由于没有lbp
介于 0 和 10 之间的运算符,因此调用expression(0) vs. expression (9)
没有区别。( !(0<0) && !(9<0) && 0<10 && 9<10)
)
并且该token.id === "="
条件阻止了对对象成员的分配,无论token.id
是被调用'['
还是不会被调用。'.'
t.led
简而言之,我的问题是:
为什么我们不可选地在变量声明后调用赋值运算符的可用led
函数。而是手动设置语句的first
andsecond
成员而不是assignment
成员?