请帮助我理解 craftinginterpreters.com 中的解析树示例
Please, help me to understand parsing trees example from craftinginterpreters.com
我正在尝试从头开始构建 C 编译器。谷歌搜索将我带到 https://craftinginterpreters.com/ site. There are a lot of explanations how source code parsers work in detail. I've reached "Parsing expressions" 章节并卡在了这一章。
他们提供了创建解析树的算法(递归下降)
Expr expression() {
Expr left = equality();
...
}
Expr equality() {
Expr left = comparison();
...
}
Expr comparison() {
Expr left = term();
...
}
Expr term() {
Expr left = factor();
...
}
Expr factor() {
Expr left = unary();
...
}
Expr unary() {
if (WeFoundToken('-')) {
return Expr.Unary(prev(), unary());
}
return primary; // if there's no '-'
}
但是当我们解析这个简单的表达式时会发生什么:1 * 2 - 3
我无法理解这将如何工作,因为顶级 expression()
在被调用时会通过所有低优先级运算符解析函数到最低优先级 unary()
函数,该函数将遍历标记,找到 (-
) 和 return 它作为一元 (-
) 操作 (-3
) 表达式
在那种情况下,将无法构建像这样的有效树:
-
* 3
1 2
如果没有根节点那将是无效的:
*
1 2 (-3)
让我们逐步完成示例表达式 1 * 2 - 3
。解析器从头开始。
- 第一个标记匹配
primary
中的 1
。消费 1
.
- 控制returns为
factor
,expr
设置为1
。然后 while
条件匹配 *
。消耗*
。
- 在
while
循环中,它尝试接下来消费一个unary
,这成功匹配了primary
2
。消费 2
.
- 表达式设置为
1 * 2
。不再有 [*/]
消耗,循环终止。 Return 这个表达式为 term
.
term
进入while循环,看到-
。已消耗 -
(意味着下一个令牌是 3
,而不是 -
)
- 尝试使用
factor
,成功匹配 primary
中的 3
。消费 3
.
- 表达式设置为
1 * 2 - 3
。
这导致树:
-
* 3
1 2
也就是说,因为term
已经消耗了1 * 2
作为一个factor
,所以term
会进入while
循环,不会调用factor
再次。这成功地将 -
识别为 term
中的运算符,而不是 unary
表达式的一部分。
unary() function which will iterate through tokens
不,不是。它只查看下一个输入标记,发现它不是 -
。所以它 returns primary()
.
您在问题中链接的页面的实际代码是:
private Expr unary() {
if (match(BANG, MINUS)) {
Token operator = previous();
Expr right = unary();
return new Expr.Unary(operator, right);
}
return primary();
}
该函数调用 match
,而不是 WeFoundToken
。 (我不确定那是从哪里来的)。 match
在页面前面定义:
private boolean match(TokenType... types) {
for (TokenType type : types) {
if (check(type)) {
advance();
return true;
}
}
return false;
}
该函数中的循环 (for (TokenType type : types)
) 遍历调用的参数,依次将每个参数与下一个输入标记进行比较。它从不查看任何其他输入令牌。
我正在尝试从头开始构建 C 编译器。谷歌搜索将我带到 https://craftinginterpreters.com/ site. There are a lot of explanations how source code parsers work in detail. I've reached "Parsing expressions" 章节并卡在了这一章。
他们提供了创建解析树的算法(递归下降)
Expr expression() {
Expr left = equality();
...
}
Expr equality() {
Expr left = comparison();
...
}
Expr comparison() {
Expr left = term();
...
}
Expr term() {
Expr left = factor();
...
}
Expr factor() {
Expr left = unary();
...
}
Expr unary() {
if (WeFoundToken('-')) {
return Expr.Unary(prev(), unary());
}
return primary; // if there's no '-'
}
但是当我们解析这个简单的表达式时会发生什么:1 * 2 - 3
我无法理解这将如何工作,因为顶级 expression()
在被调用时会通过所有低优先级运算符解析函数到最低优先级 unary()
函数,该函数将遍历标记,找到 (-
) 和 return 它作为一元 (-
) 操作 (-3
) 表达式
在那种情况下,将无法构建像这样的有效树:
-
* 3
1 2
如果没有根节点那将是无效的:
*
1 2 (-3)
让我们逐步完成示例表达式 1 * 2 - 3
。解析器从头开始。
- 第一个标记匹配
primary
中的1
。消费1
. - 控制returns为
factor
,expr
设置为1
。然后while
条件匹配*
。消耗*
。- 在
while
循环中,它尝试接下来消费一个unary
,这成功匹配了primary
2
。消费2
. - 表达式设置为
1 * 2
。不再有[*/]
消耗,循环终止。 Return 这个表达式为term
.
- 在
term
进入while循环,看到-
。已消耗-
(意味着下一个令牌是3
,而不是-
)- 尝试使用
factor
,成功匹配primary
中的3
。消费3
. - 表达式设置为
1 * 2 - 3
。
- 尝试使用
这导致树:
-
* 3
1 2
也就是说,因为term
已经消耗了1 * 2
作为一个factor
,所以term
会进入while
循环,不会调用factor
再次。这成功地将 -
识别为 term
中的运算符,而不是 unary
表达式的一部分。
unary() function which will iterate through tokens
不,不是。它只查看下一个输入标记,发现它不是 -
。所以它 returns primary()
.
您在问题中链接的页面的实际代码是:
private Expr unary() {
if (match(BANG, MINUS)) {
Token operator = previous();
Expr right = unary();
return new Expr.Unary(operator, right);
}
return primary();
}
该函数调用 match
,而不是 WeFoundToken
。 (我不确定那是从哪里来的)。 match
在页面前面定义:
private boolean match(TokenType... types) {
for (TokenType type : types) {
if (check(type)) {
advance();
return true;
}
}
return false;
}
该函数中的循环 (for (TokenType type : types)
) 遍历调用的参数,依次将每个参数与下一个输入标记进行比较。它从不查看任何其他输入令牌。