44
55## 环境配置
66
7- #### 1.WSL 环境下使用 ` systemctl ` 指令报错:` System has not been booted with systemd as init system (PID 1). Can’t operate. `
7+ #### 1.WSL 环境下使用 ` systemctl ` 指令报错: ` System has not been booted with systemd as init system (PID 1). Can’t operate. `
88
9- 由于部分 WSL 使用 ` SysV init ` 而非 ` systemd ` 管理服务,解决方法是使用 ` service ` 指令代替 ` systemctl ` 指令。
9+ 由于部分 WSL 使用 ` SysV init ` 而非 ` systemd ` 管理服务,解决方法是使用 ` service `
10+ 指令代替 ` systemctl ` 指令。
1011
1112#### 2.如何更新代码仓库: 请在终端依次输入以下指令
1213
1314(注意:你的终端所在目录应在实验代码文件夹根目录下,如果你不确定,请新建终端)
1415
15- ``` bash
16- git stash # 将当前未提交的修改暂时储藏
17- git pull # 从远程仓库拉取新的实验代码
18- git stash pop # 恢复先前暂时储藏的修改
16+ ``` shell
17+ git stash # 将当前未提交的修改暂时储藏
18+ git pull # 从远程仓库拉取新的实验代码
19+ git stash pop # 恢复先前暂时储藏的修改
1920```
2021
2122#### 3.MacOS + VSCode 如何进入单步调试
2223
23241 . 安装 VSCode 插件 ` CodeLLDB `
2425 ![ codeLLDB] ( images/QA/codelldb.png )
25- 2 . 在` .vscode/launch.json ` 中加入以下配置
26+ 2 . 在 ` .vscode/launch.json ` 中加入以下配置
2627
2728``` json
2829{
@@ -35,21 +36,22 @@ git stash pop # 恢复先前暂时储藏的修改
3536}
3637```
3738
38- ![ launch.json] ( images/QA/VSClaunch.png ) 3. 打上断点,在` 测试 ` 插件中对单个测例进行单步调试
39- ![ alt text] ( images/QA/debug.png )
39+ ![ launch.json] ( images/QA/VSClaunch.png ) 3. 打上断点,在 ` 测试 `
40+ 插件中对单个测例进行单步调试 ![ alt text] ( images/QA/debug.png )
4041
4142## 实验一
4243
4344#### 1.在 task1 中存在部分 Loc 要求输出相对路径,部分 Loc 要求输出绝对路径,是否需要针对不同要求编写不同方式的 print_token?
4445
45- ![ alt text] ( images/QA/task1.1_1.png )
46- ![ alt text] ( images/QA/task1.1_2.png )
46+ ![ alt text] ( images/QA/task1.1_1.png ) ![ alt text] ( images/QA/task1.1_2.png )
4747答:相对路径与绝对路径的区别并不是 print_token()函数导致的,而是由于 clang 预处理时输出格式不同所导致的(详见 task0-answer 生成的预处理源代码,路径为 build/test/task0/\* ),词法规则中对预处理信息的处理可以提供文件路径信息,可以关注预处理信息中的最后一条。
4848
4949#### 2.预处理文件中没有关于源文件的行号信息,如何识别出词法单元出现在源文件中的位置?是否需要额外编写脚本?
5050
5151答:关于源文件的行号信息,需要从预处理文件中的预处理信息中获取,无需额外编写脚本
52- 例如# 1 "./basic/000_main.sysu.c" 2:其中'#'右边的数字 1 即下一行有效内容出现在对应源文件中的行数(可自行比对预处理后的源文件与标准输出 answer.txt),此后的行号/列号信息需要自行维护
52+ 例如#
53+ 1 "./basic/000_main.sysu.c"
54+ 2:其中'#'右边的数字 1 即下一行有效内容出现在对应源文件中的行数(可自行比对预处理后的源文件与标准输出 answer.txt),此后的行号/列号信息需要自行维护
5355如图是 003 样例的源文件,预处理文件和答案,起始行并不是 1
5456![ alt text] ( images/QA/task1.2_1.png )
5557
@@ -65,7 +67,8 @@ git stash pop # 恢复先前暂时储藏的修改
6567答:
6668
67691 . 预处理信息应选取最后一个地址
68- 2 . 因为在"sylib.h"头文件中,"void \_ sysy_starttime..."为第 10 行;预处理信息第九行的意思是下一行的词法单元处于源文件中的行号
70+ 2 . 因为在"sylib.h"头文件中,"void
71+ \_ sysy_starttime..."为第 10 行;预处理信息第九行的意思是下一行的词法单元处于源文件中的行号
69723 . 地址后的数字与输出无关,可忽略
7073
7174## 实验二
@@ -76,61 +79,98 @@ git stash pop # 恢复先前暂时储藏的修改
7679
7780在解释这个问题前,需要说明如下事项。在 task2 中,我们既可以使用“递归”的文法,也可以使用“直接”的文法。例如 expression 的这两种写法都是可以的:
7881
79- ``` bash
82+ ``` shell
8083assignment_expression | expression Comma assignment_expression
8184assignment_expression (Comma assignment_expression)*
8285```
8386
84- 但是在 task2 中,需要注意,你的 g4 的文法会影响 ` ctx->assignment_expression() ` 的返回类型:
87+ 但是在 task2 中,需要注意,你的 g4 的文法会影响 ` ctx->assignment_expression() `
88+ 的返回类型:
8589
86- 文法是 ` assignment_expression | expression Comma assignment_expression ` 时,` ctx->assignment_expression() ` 是单个的指针;
90+ 文法是 ` assignment_expression | expression Comma assignment_expression ` 时,
91+ ` ctx->assignment_expression() ` 是单个的指针;
8792
88- 文法是 ` assignment_expression (Comma assignment_expression)* ` 时,` ctx->assignment_expression() ` 是一个 vector ,里面存放一堆指针。
93+ 文法是 ` assignment_expression (Comma assignment_expression)* ` 时,
94+ ` ctx->assignment_expression() ` 是一个 vector,里面存放一堆指针。
8995
9096怎么回事呢?这是因为助教们的脚本会根据你的文法,在 SYsUParser.h 中自动生成相应的类型。
9197
92- 比如 ` assignment_expression (Comma assignment_expression)* ` ,因为里面 assignment_expression 的数量是不确定的,所以需要用 vector ,` ctx->assignment_expression() ` 便是一个 vector ,与此同时, expression 将会有许多的孩子分支, ` auto children = ctx->children; ` 之后, ` children[0] ` 、 ` children[2] ` 、 ` children[4] ` ...便对应各个 assignment_expression 节点。
98+ 比如
99+ ` assignment_expression (Comma assignment_expression)* ` ,因为里面 assignment_expression 的数量是不确定的,所以需要用 vector,
100+ ` ctx->assignment_expression() `
101+ 便是一个 vector,与此同时,expression 将会有许多的孩子分支,
102+ ` auto children = ctx->children; ` 之后, ` children[0] ` 、 ` children[2] ` 、
103+ ` children[4] ` ...便对应各个 assignment_expression 节点。
93104
94- 另一个例子, ` assignment_expression | expression Comma assignment_expression ` ,里面 assignment_expression 的数量是确定的1,于是 ` ctx->assignment_expression() ` 便是单个的指针,与此同时, expression 的孩子分支要么只有一个,要么有三个(跟你写的文法是呼应的)。
105+ 另一个例子,
106+ ` assignment_expression | expression Comma assignment_expression ` ,里面 assignment_expression 的数量是确定的 1,于是
107+ ` ctx->assignment_expression() `
108+ 便是单个的指针,与此同时,expression 的孩子分支要么只有一个,要么有三个(跟你写的文法是呼应的)。
95109
96- 现在回到标题的问题,为什么一元表达式不能用非递归型的 ` (unary_operator)* postfix_expression ` 呢?这其实是助教的问题,因为助教在一些已实现的代码里,是按照 ` ctx->unary_operator() ` 是单个的指针写的代码,但是如果你把文法改成 ` (unary_operator)* postfix_expression ` ,就会使得 ` ctx->unary_operator() ` 变成一个 vector 了,于是助教们写的代码会炸。
110+ 现在回到标题的问题,为什么一元表达式不能用非递归型的
111+ ` (unary_operator)* postfix_expression `
112+ 呢?这其实是助教的问题,因为助教在一些已实现的代码里,是按照
113+ ` ctx->unary_operator() ` 是单个的指针写的代码,但是如果你把文法改成
114+ ` (unary_operator)* postfix_expression ` ,就会使得 ` ctx->unary_operator() `
115+ 变成一个 vector 了,于是助教们写的代码会炸。
97116
98- 就结果而言,对一元表达式,建议使用“递归”的文法 ` unary_expression : postfix_expression | unary_operator unary_expression; ` 。当然对于其他大多数文法规则,你用“递归”的文法或者“直接”的文法都可以!注意一下上述的“是否是vector”的问题即可。
117+ 就结果而言,对一元表达式,建议使用“递归”的文法
118+ ` unary_expression : postfix_expression | unary_operator unary_expression; ` 。当然对于其他大多数文法规则,你用“递归”的文法或者“直接”的文法都可以!注意一下上述的“是否是 vector”的问题即可。
99119
100120#### 2.关于 ` children[i] ` 与 ` ctx->assignment_expression() ` 的区别
101121
102- 以文法 ` expression : assignment_expression (Comma assignment_expression)* ; ` 为例,
122+ 以文法 ` expression : assignment_expression (Comma assignment_expression)* ; `
123+ 为例,
103124
104125``` cpp
105126auto children = ctx->children;
106127for (int i=0 ;i<children.size();i+=2 )children[i];
107128```
108129
109- 这样取出的偶数位置的 ` children[i] ` 便对应各个 ` assignment_expression ` 节点,但需注意类型转换,因为 ` children[i] ` 还不是 ` ast::Assignment_expressionContext* ` 类型,照葫芦画瓢可以写出类似的代码:
130+ 这样取出的偶数位置的 ` children[i] ` 便对应各个 ` assignment_expression `
131+ 节点,但需注意类型转换,因为 ` children[i] ` 还不是
132+ ` ast::Assignment_expressionContext* ` 类型,照葫芦画瓢可以写出类似的代码:
110133
111134``` cpp
112135node->rht = self(dynamic_cast <ast::Assignment_expressionContext*>(children[i]));
113136```
114137
115138这里稍微介绍一下这个 self 是做什么的:
116139
117- 在task2中,你会用到大量的 ` make<???>() ` ,这个 make 函数的功能是向系统申请一小块内存空间,然后返回一个指向该实空间的指针,例如 ` auto ret = make<CallExpr>(); ` 。
140+ 在 task2 中,你会用到大量的
141+ ` make<???>() ` ,这个 make 函数的功能是向系统申请一小块内存空间,然后返回一个指向该实空间的指针,例如
142+ ` auto ret = make<CallExpr>(); ` 。
118143
119- 我们的函数诸如 ` Expr* Ast2Asg::operator()(ast::Postfix_expressionContext* ctx) ` ,返回的便是这个 ret 指针,但重点是这个 ret 指针指向了一个实空间,这个实空间正是我们通过调用 ` make<???>() ` 申请得到的。
144+ 我们的函数诸如
145+ ` Expr* Ast2Asg::operator()(ast::Postfix_expressionContext* ctx) ` ,返回的便是这个 ret 指针,但重点是这个 ret 指针指向了一个实空间,这个实空间正是我们通过调用
146+ ` make<???>() ` 申请得到的。
120147
121- 我们用到的许多 self 就是诸如 ` Expr* Ast2Asg::operator()(ast::Postfix_expressionContext* ctx) ` 的函数中的某一种。总之, ` self(???) ` 返回一个指针,但具有实际价值的,是这个指针指向的实空间。
148+ 我们用到的许多 self 就是诸如
149+ ` Expr* Ast2Asg::operator()(ast::Postfix_expressionContext* ctx) `
150+ 的函数中的某一种。总之, ` self(???) `
151+ 返回一个指针,但具有实际价值的,是这个指针指向的实空间。
122152
123- 回归正题,介绍完 ` children[i] ` ,再来介绍 ` ctx->assignment_expression() ` 。
153+ 回归正题,介绍完 ` children[i] ` ,再来介绍 ` ctx->assignment_expression() ` 。
124154
125- 调用 ` auto list=ctx->assignment_expression() ` ,返回一个 vector , list 里面便装着一堆 ` assignment_expression ` 。通过 ` for(int i = 0; i < list.size(); i++)list[i]; ` 可以访问 ` expression ` 的每一个 ` assignment_expression ` 孩子。注意我们不需要像 children 那样访问0、2、4、6这样的位置,因为 ` ctx->assignment_expression() ` 返回一个 vector ,里面只装了 ` assignment_expression ` ,而不会装有 Comma。
155+ 调用
156+ ` auto list=ctx->assignment_expression() ` ,返回一个 vector,list 里面便装着一堆
157+ ` assignment_expression ` 。通过 ` for(int i = 0; i < list.size(); i++)list[i]; `
158+ 可以访问 ` expression ` 的每一个 ` assignment_expression `
159+ 孩子。注意我们不需要像 children 那样访问 0、2、4、6 这样的位置,因为
160+ ` ctx->assignment_expression() ` 返回一个 vector,里面只装了
161+ ` assignment_expression ` ,而不会装有 Comma。
126162
127- 在编写代码时,需注意 ` list[i] ` 和 ` self(list[i]) ` 的区别。前文已提到了 ` self(???) ` 的作用, ` self(???) ` 实际上会调用我们写的那些函数,返回一个指针,这个指针指向一个 make 出来的实空间。而 ` list[i] ` 仅是在遍历 ctx 的孩子。我们的 task2 要做的就是让指针们指向正确的 make 出来的实空间,这个实空间往往也具备一些指针成员,指向其他的实空间。这样指来指去,把实空间和指针安排好后,就构建好了 asg 语法图。
163+ 在编写代码时,需注意 ` list[i] ` 和 ` self(list[i]) ` 的区别。前文已提到了
164+ ` self(???) ` 的作用, ` self(???) `
165+ 实际上会调用我们写的那些函数,返回一个指针,这个指针指向一个 make 出来的实空间。而
166+ ` list[i] `
167+ 仅是在遍历 ctx 的孩子。我们的 task2 要做的就是让指针们指向正确的 make 出来的实空间,这个实空间往往也具备一些指针成员,指向其他的实空间。这样指来指去,把实空间和指针安排好后,就构建好了 asg 语法图。
128168
129169#### 3.关于函数定义的若干问题
130170
131- 一种能够减少你的工作量的g4文法如下 :
171+ 一种能够减少你的工作量的 g4 文法如下 :
132172
133- ``` bash
173+ ``` shell
134174parameter_list
135175 : parameter_declaration (Comma parameter_declaration)*
136176 ;
@@ -149,7 +189,8 @@ function_definition
149189
150190为了便于后文你理解“类型”与“限定”,此处稍作提及。
151191
152- 你可能在代码中见过一个叫 ` specs ` 的变量,是 ` SpecQual ` 类型。` SpecQual ` 类型包含“类型spec”与“是否限定qual”两项内容。
192+ 你可能在代码中见过一个叫 ` specs ` 的变量,是 ` SpecQual ` 类型。 ` SpecQual `
193+ 类型包含“类型 spec”与“是否限定 qual”两项内容。
153194
154195下文的函数定义的代码中含有详细的注释解析,可以帮助你理解这部分具有复杂层次的内容,代码仅供参考,同学们也可以有其他实现方式。
155196
@@ -158,13 +199,13 @@ FunctionDecl*
158199Ast2Asg::operator ()(ast::Function_definitionContext* ctx){
159200 auto ret = make<FunctionDecl >();
160201 mCurrentFunc = ret;
161-
202+
162203 auto type = make<Type >();
163204 ret->type = type;
164-
205+
165206 auto sq = self(ctx->declaration_specifiers());
166207 type->spec = sq.first, type->qual = sq.second;
167-
208+
168209 auto [ texp, name] = self(ctx->direct_declarator(), nullptr);//右侧调用的函数为 std::pair<TypeExpr* , std::string> Ast2Asg::operator()(ast::Direct_declaratorContext* ctx, TypeExpr* sub)
169210//这里解释一下texp的类型,即TypeExpr* ,你可以简单认为他是处理数组定义的,例如对于int a[ 5] [ 3 ] ,TypeExpr* 负责记录这些维度的长度
170211//那么texp作为一个指针类型,即TypeExpr* 类型,这个指针若非空,是要指向一个实空间的,一个例子便是指向 make<ArrayType >(); 的实空间
@@ -193,7 +234,7 @@ Ast2Asg::operator()(ast::Function_definitionContext* ctx){
193234//ret存放函数名name,函数实现body,以及一个参数列表(含有参数名字)
194235
195236//注:我们不考虑int a[ 2] [ 4 ] (int b=10,char c[ 3] );这种带默认值的
196-
237+
197238 Symtbl localDecls(self);
198239
199240 if(auto plist = ctx->parameter_list()){
@@ -220,7 +261,7 @@ Ast2Asg::operator()(ast::Function_definitionContext* ctx){
220261 ret->body = self(ctx->compound_statement());
221262 }
222263 }
223-
264+
224265 return ret;
225266}
226267```
0 commit comments