08 识别过程调用 | 《Let’s Build A Simple Interpreter》笔记

本系列是《Let’s Build A Simple Interpreter》的阅读笔记。

本文的目标是确保当解释器读取一个带有过程调用的程序时,parser 会构造一个 AST,并为过程调用构建一个新的树节点。

Parser

  1. 新增 AST 节点
1
2
3
4
5
class ProcedureCall(AST):
def __init__(self, proc_name, actual_params, token):
self.proc_name = proc_name
self.actual_params = actual_params # a list of AST nodes
self.token = token
  1. 扩展语法:添加过程调用语句语法 proccall_statement
1
proccall_statement : ID LPAREN (expr (COMMA expr)*)? RPAREN

下面是几条符合上述语法规则的语句:

1
2
3
Alpha();
Alpha(1);
Alpha(3 + 5, 7);

对应语法的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def proccall_statement(self):
"""proccall_statement : ID LPAREN (expr (COMMA expr)*)? RPAREN"""
token = self.current_token

proc_name = self.current_token.value
self.eat(TokenType.ID)
self.eat(TokenType.LPAREN)
actual_params = []
if self.current_token.type != TokenType.RPAREN:
node = self.expr()
actual_params.append(node)

while self.current_token.type == TokenType.COMMA:
self.eat(TokenType.COMMA)
node = self.expr()
actual_params.append(node)

self.eat(TokenType.RPAREN)

node = ProcedureCall(
proc_name=proc_name,
actual_params=actual_params,
token=token,
)
return node
  1. 扩展语法:将过程调用语句语法 proccall_statement 添加到 statement 中
1
2
3
4
statement : compound_statement
| proccall_statement
| assignment_statement
| empty

proccall_statement 和 assignment_statement 都是以 ID token 开头的,例如:

1
2
foo();     { procedure call }
foo := 5; { assignment }

通过下面的方式可以区分这两者:

1
2
3
4
5
6
if (self.current_token.type == TokenType.ID and
self.lexer.current_char == '('
):
node = self.proccall_statement()
elif self.current_token.type == TokenType.ID:
node = self.assignment_statement()

SemanticAnalyzer

SemanticAnalyzer 的更改仅仅是添加 visit_ProcedureCall 方法:

1
2
3
def visit_ProcedureCall(self, node):
for param_node in node.actual_params:
self.visit(param_node)

Interpreter

对于解释器,由于目前还不用执行过程调用,所以函数没有内容:

1
2
def visit_ProcedureCall(self, node):
pass

AST 示例

下面是本文用到的例子:

1
2
3
4
5
6
7
8
9
10
program Main;
procedure Alpha(a : integer; b : integer);
var x : integer;
begin
x := (a + b) * 2;
end;

begin { Main }
Alpha(3 + 5, 7); { procedure call }
end. { Main }

其对应的 AST 如下:

08 识别过程调用 | 《Let’s Build A Simple Interpreter》笔记

http://www.zh0ngtian.tech/posts/a5304580.html

作者

zhongtian

发布于

2021-08-28

更新于

2023-12-16

许可协议

评论