Commit 78e208f5 authored by 李晓奇's avatar 李晓奇

Merge branch 'master' of 202.38.79.174:compiler_staff/2022fall-compiler_cminus

parents 5b1dfa22 49c4e204
...@@ -34,130 +34,147 @@ ...@@ -34,130 +34,147 @@
30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$ 30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$
31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$ 31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$
## cminus-f 的语义 ## cminus-f 的语义
在上述语法规则中,我们定义了 `cminus-f` 语言的语法,接着,我们对照语法规则,给出相关的语义和解释。 在上述语法规则中,我们定义了 `cminus-f` 语言的语法,接着,我们对照语法规则,给出相关的语义和解释。
在阅读前,需要理解 `cminus-f` 主要源自于 C 语言,因此它的行为都会接近 C 语言。 在阅读前,需要理解 `cminus-f` 主要源自于 C 语言,因此它的行为都会接近 C 语言。
1. $`\text{program} \rightarrow \text{declaration-list}`$ 1. $`\text{program} \rightarrow \text{declaration-list}`$
2. $`\text{declaration-list} \rightarrow \text{declaration-list}\ \text{declaration}\ |\ \text{declaration}`$
3. $`\text{declaration} \rightarrow \text{var-declaration}\ |\ \text{fun-declaration}`$
一个`程序`由一系列`声明`组成,声明包括了`函数声明``变量声明`,它们可以以任意顺序排列。
全局变量需要初始化为全 0 2. $`\text{declaration-list} \rightarrow \text{declaration-list}\ \text{declaration}\ |\ \text{declaration}`$
所有的变量必须在使用前先进行声明,所有的函数必须在使用前先进行定义
一个`程序`中至少要有一个`main`函数的`声明`
因为没有原型这个概念, `cminus-f` 不区分函数的声明和定义。 3. $`\text{declaration} \rightarrow \text{var-declaration}\ |\ \text{fun-declaration}`$
一个`程序`由一系列`声明`组成,声明包括了`函数声明``变量声明`,它们可以以任意顺序排列。
全局变量需要初始化为全 0
所有的变量必须在使用前先进行声明,所有的函数必须在使用前先进行定义
一个`程序`中至少要有一个`main`函数的`声明`
因为没有原型这个概念, `cminus-f` 不区分函数的声明和定义。
4. $`\text{var-declaration}\ \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{;}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[}\ \textbf{INTEGER}\ \textbf{]}\ \textbf{;}`$ 4. $`\text{var-declaration}\ \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{;}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[}\ \textbf{INTEGER}\ \textbf{]}\ \textbf{;}`$
5. $`\text{type-specifier} \rightarrow \textbf{int}\ |\ \textbf{float}\ |\ \textbf{void}`$
`cminus-f` 的基础类型只有整型(`int`)、浮点型(`float`)和 `void`。而在变量声明中,只有整型和浮点型可以使用,`void` 仅用于函数声明。
一个`变量声明`定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。
数组变量在声明时,$`\textbf{INTEGER}`$ 应当大于0。
一次只能声明一个变量。 5. $`\text{type-specifier} \rightarrow \textbf{int}\ |\ \textbf{float}\ |\ \textbf{void}`$
`cminus-f` 的基础类型只有整型(`int`)、浮点型(`float`)和 `void`。而在变量声明中,只有整型和浮点型可以使用,`void` 仅用于函数声明。
一个`变量声明`定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。
数组变量在声明时,$`\textbf{INTEGER}`$ 应当大于0。
一次只能声明一个变量。
6. $`\text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt}`$ 6. $`\text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt}`$
7. $`\text{params} \rightarrow \text{param-list}\ |\ \textbf{void}`$
8. $`\text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param}`$
9. $`\text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]}`$
`函数声明`包含了返回类型,标识符,由逗号分隔的`形参`列表,还有一个`复合语句`
当函数的返回类型是 `void` 时,函数不返回任何值。
函数的参数可以是 `void` ,也可以是一个列表。当函数的`形参``void`时,调用该函数时不用传入任何参数。
`形参`中跟着中括号代表数组参数,它们可以有不同长度。 7. $`\text{params} \rightarrow \text{param-list}\ |\ \textbf{void}`$
整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。
函数的`形参`拥有和`函数声明``复合语句`相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致) 8. $`\text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param}`$
函数可以递归调用。 9. $`\text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]}`$
`函数声明`包含了返回类型,标识符,由逗号分隔的`形参`列表,还有一个`复合语句`
当函数的返回类型是 `void` 时,函数不返回任何值。
函数的参数可以是 `void` ,也可以是一个列表。当函数的`形参``void`时,调用该函数时不用传入任何参数。
`形参`中跟着中括号代表数组参数,它们可以有不同长度。
整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。
函数的`形参`拥有和`函数声明``复合语句`相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致)
函数可以递归调用。
10. $`\text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}}`$ 10. $`\text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}}`$
一个`复合语句`由一对大括号和其中的`局部声明``语句列表`组成 一个`复合语句`由一对大括号和其中的`局部声明``语句列表`组成
`复合语句`的执行时,对包含着的语句按照`语句列表`中的顺序执行 `复合语句`的执行时,对包含着的语句按照`语句列表`中的顺序执行
`局部声明`拥有和`复合语句`中的`语句列表`一样的作用域,且其优先级高于任何同名的全局声明(常见的静态作用域) `局部声明`拥有和`复合语句`中的`语句列表`一样的作用域,且其优先级高于任何同名的全局声明(常见的静态作用域)
11. $`\text{local-declarations} \rightarrow \text{local-declarations var-declaration}\ |\ \text{empty}`$ 11. $`\text{local-declarations} \rightarrow \text{local-declarations var-declaration}\ |\ \text{empty}`$
12. $`\text{statement-list} \rightarrow \text{statement-list}\ \text{statement}\ |\ \text{empty}`$
`局部声明``语句列表`都可以为空(empty表示空字符串,即$`\varepsilon`$) 12. $`\text{statement-list} \rightarrow \text{statement-list}\ \text{statement}\ |\ \text{empty}`$
`局部声明``语句列表`都可以为空(empty表示空字符串,即$`\varepsilon`$)
13. $`\begin{aligned}\text{statement} \rightarrow\ &\text{expression-stmt}\\ &|\ \text{compound-stmt}\\ &|\ \text{selection-stmt}\\ &|\ \text{iteration-stmt}\\ &|\ \text{return-stmt}\end{aligned}`$ 13. $`\begin{aligned}\text{statement} \rightarrow\ &\text{expression-stmt}\\ &|\ \text{compound-stmt}\\ &|\ \text{selection-stmt}\\ &|\ \text{iteration-stmt}\\ &|\ \text{return-stmt}\end{aligned}`$
14. $`\text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;}`$
`表达式语句`由一个可选的`表达式`(即可以没有`表达式`)和一个分号组成
我们通常使用`表达式语句`中的`表达式`计算时产生的副作用,所以这种`语句`用于赋值和函数调用 14. $`\text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;}`$
`表达式语句`由一个可选的`表达式`(即可以没有`表达式`)和一个分号组成
我们通常使用`表达式语句`中的`表达式`计算时产生的副作用,所以这种`语句`用于赋值和函数调用
15. $`\begin{aligned}\text{selection-stmt} \rightarrow\ &\textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\\ &|\ \textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\ \textbf{else}\ \text{statement}\end{aligned}`$ 15. $`\begin{aligned}\text{selection-stmt} \rightarrow\ &\textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\\ &|\ \textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\ \textbf{else}\ \text{statement}\end{aligned}`$
`if`语句中的`表达式`将被求值,若结果的值等于0,则第二个`语句`执行(如果存在的话),否则第一个`语句`会执行。 `if`语句中的`表达式`将被求值,若结果的值等于0,则第二个`语句`执行(如果存在的话),否则第一个`语句`会执行。
为了避免歧义,$`\textbf{else}`$将会匹配最近的$`\textbf{if}`$ 为了避免歧义,$`\textbf{else}`$将会匹配最近的$`\textbf{if}`$
16. $`\text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}`$ 16. $`\text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}`$
`while`语句是 `cminus-f` 中唯一的`迭代语句`。它执行时,会不断对`表达式`进行求值,并且在对`表达式`的求值结果等于 0 前,循环执行执下面的`语句` `while`语句是 `cminus-f` 中唯一的`迭代语句`。它执行时,会不断对`表达式`进行求值,并且在对`表达式`的求值结果等于 0 前,循环执行执下面的`语句`
17. $`\text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;}`$ 17. $`\text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;}`$
`return`语句可以返回值,也可以不返回值。 `return`语句可以返回值,也可以不返回值。
未声明为$`\textbf{void}`$类型的函数必须返回和函数返回类型相同的值 未声明为$`\textbf{void}`$类型的函数必须返回和函数返回类型相同的值
`return`会将程序的控制转移给当前函数的调用者,而$`\textbf{main}`$函数的`return`会使得程序终止 `return`会将程序的控制转移给当前函数的调用者,而$`\textbf{main}`$函数的`return`会使得程序终止
18. $`\text{expression} \rightarrow \text{var}\ \textbf{=}\ \text{expression}\ |\ \text{simple-expression}`$ 18. $`\text{expression} \rightarrow \text{var}\ \textbf{=}\ \text{expression}\ |\ \text{simple-expression}`$
19. $`\text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]}`$
一个`表达式`可以是一个变量引用(即`var`)接着一个赋值符号(=)以及一个表达式,也可以是一个`简单表达式` 19. $`\text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]}`$
`var` 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。 一个`表达式`可以是一个变量引用(即`var`)接着一个赋值符号(=)以及一个表达式,也可以是一个`简单表达式`
数组的下标值为整型,作为数组下标值的表达式计算结果可能需要类型转换变成整型值 `var` 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。
一个负的下标会导致程序终止,需要调用框架中的内置函数`neg_idx_except` (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。 数组的下标值为整型,作为数组下标值的表达式计算结果可能需要类型转换变成整型值
赋值语义为:先找到 `var` 代表的变量地址(如果是数组,需要先对下标表达式求值),然后对右侧的表达式进行求值,求值结果将在转换成变量类型后存储在先前找到的地址中。同时,存储在 `var` 中的值将作为赋值表达式的求值结果。 一个负的下标会导致程序终止,需要调用框架中的内置函数`neg_idx_except` (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。
`C` 中,赋值对象(即 `var` )必须是左值,而左值可以通过多种方式获得。`cminus-f`中,唯一的左值就是通过 `var` 的语法得到的,因此 `cminus-f` 通过语法限制了 `var` 为左值,而不是像 `C` 中一样通过类型检查,这也是为什么 `cminus-f` 中不允许进行指针算数。 赋值语义为:先找到 `var` 代表的变量地址(如果是数组,需要先对下标表达式求值),然后对右侧的表达式进行求值,求值结果将在转换成变量类型后存储在先前找到的地址中。同时,存储在 `var` 中的值将作为赋值表达式的求值结果。
`C` 中,赋值对象(即 `var` )必须是左值,而左值可以通过多种方式获得。`cminus-f`中,唯一的左值就是通过 `var` 的语法得到的,因此 `cminus-f` 通过语法限制了 `var` 为左值,而不是像 `C` 中一样通过类型检查,这也是为什么 `cminus-f` 中不允许进行指针算数。
20. $`\text{simple-expression} \rightarrow \text{additive-expression}\ \text{relop}\ \text{additive-expression}\ |\ \text{additive-expression}`$ 20. $`\text{simple-expression} \rightarrow \text{additive-expression}\ \text{relop}\ \text{additive-expression}\ |\ \text{additive-expression}`$
21. $`\text{relop}\ \rightarrow \textbf{<=}\ |\ \textbf{<}\ |\ \textbf{>}\ |\ \textbf{>=}\ |\ \textbf{==}\ |\ \textbf{!=}`$ 21. $`\text{relop}\ \rightarrow \textbf{<=}\ |\ \textbf{<}\ |\ \textbf{>}\ |\ \textbf{>=}\ |\ \textbf{==}\ |\ \textbf{!=}`$
22. $`\text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term}`$ 22. $`\text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term}`$
23. $`\text{addop} \rightarrow \textbf{+}\ |\ \textbf{-}`$
24. $`\text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor}`$
25. $`\text{mulop} \rightarrow \textbf{*}\ |\ \textbf{/}`$
一个`简单表达式`是一个`加法表达式`或者两个`加法表达式`的关系运算。当它是`加法表达式`时,它的值就是`加法表达式`的值。而当它是关系运算时,如果关系运算结果为真则值为整型值 1,反之则值为整型值 0。 23. $`\text{addop} \rightarrow \textbf{+}\ |\ \textbf{-}`$
`加法表达式`表现出了四则运算的结合性质与优先级顺序,四则运算的含义和`C`中的整型运算一致。 24. $`\text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor}`$
浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型 25. $`\text{mulop} \rightarrow \textbf{*}\ |\ \textbf{/}`$
一个`简单表达式`是一个`加法表达式`或者两个`加法表达式`的关系运算。当它是`加法表达式`时,它的值就是`加法表达式`的值。而当它是关系运算时,如果关系运算结果为真则值为整型值 1,反之则值为整型值 0。
`加法表达式`表现出了四则运算的结合性质与优先级顺序,四则运算的含义和`C`中的整型运算一致。
浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型
26. $`\text{factor} \rightarrow \textbf{(}\ \text{expression}\ \textbf{)}\ |\ \text{var}\ |\ \text{call}\ |\ \text{integer}\ |\ \text{float}`$ 26. $`\text{factor} \rightarrow \textbf{(}\ \text{expression}\ \textbf{)}\ |\ \text{var}\ |\ \text{call}\ |\ \text{integer}\ |\ \text{float}`$
`因数`可以是一个括号包围的`表达式`(此时它的值是`表达式`的值),或者是一个`变量`(此时它的值是`变量`的值),或者是一个`函数调用`(此时它的值是`函数调用`的返回值),或者是一个`数字字面量`(此时它的值为该字面量的值)。当`因数`是数组变量时,除非此时它被用作一个`函数调用`中的数组参数,否则它必须要带有下标。 `因数`可以是一个括号包围的`表达式`(此时它的值是`表达式`的值),或者是一个`变量`(此时它的值是`变量`的值),或者是一个`函数调用`(此时它的值是`函数调用`的返回值),或者是一个`数字字面量`(此时它的值为该字面量的值)。当`因数`是数组变量时,除非此时它被用作一个`函数调用`中的数组参数,否则它必须要带有下标。
27. $`\text{integer} \rightarrow \textbf{INTEGER}`$ 27. $`\text{integer} \rightarrow \textbf{INTEGER}`$
28. $`\text{float} \rightarrow \textbf{FLOATPOINT}`$ 28. $`\text{float} \rightarrow \textbf{FLOATPOINT}`$
29. $`\text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)}`$ 29. $`\text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)}`$
30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$ 30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$
31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$ 31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$
`函数调用`由一个函数的`标识符`与一组括号包围的`实参`组成。`实参`可以为空,也可以是由逗号分隔的的`表达式`组成的列表,这些表达式代表着函数调用时,传给`形参`的值。`函数调用时``实参`数量和类型必须与`函数声明`中的`形参`一致,必要时需要进行类型转换。 `函数调用`由一个函数的`标识符`与一组括号包围的`实参`组成。`实参`可以为空,也可以是由逗号分隔的的`表达式`组成的列表,这些表达式代表着函数调用时,传给`形参`的值。`函数调用时``实参`数量和类型必须与`函数声明`中的`形参`一致,必要时需要进行类型转换。
...@@ -176,10 +193,10 @@ void neg_idx_except(void) {...} ...@@ -176,10 +193,10 @@ void neg_idx_except(void) {...}
* `outputFloat` 函数接受一个浮点参数,然后将它的值打印到标准输出,并输出换行符。 * `outputFloat` 函数接受一个浮点参数,然后将它的值打印到标准输出,并输出换行符。
* `neg_idx_except` 函数没有形参,执行后报错并退出 * `neg_idx_except` 函数没有形参,执行后报错并退出
除此之外,其它规则和 C 中类似,比如同一个作用域下不允许定义重名变量或函数(本次实验中不做要求) 除此之外,其它规则和 C 中类似,比如同一个作用域下不允许定义重名变量或函数(本次实验中不做要求)
## 提醒与补充 ## 提醒与补充
1. 本次实验存在五种情况下的类型转换 1. 本次实验存在五种情况下的类型转换
* 赋值时 * 赋值时
* 返回值类型和函数签名中的返回类型不一致时 * 返回值类型和函数签名中的返回类型不一致时
......
...@@ -134,6 +134,12 @@ PB20111654 李晓奇 ...@@ -134,6 +134,12 @@ PB20111654 李晓奇
问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。 问题出在:我为if_else语句设置了三个测试块,分别负责if,else,和跳出。但是有一处的块是空的,就会导致生成默认返回。
后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。 后来发这里其实并没有问题,如果有返回值,在if或者else内部就返回了,并不会跳出去被默认返回扰乱。
- 最后一个测试文件能够正常执行,但是输出有错,
发现一个好的debug方法:
利用裁剪定位问题,即注释掉一部分,分别利用clang、cminusfc生成可执行文件,查看输出是否一致。
## 实验设计 ## 实验设计
......
...@@ -104,17 +104,18 @@ int main(int argc, char **argv) { ...@@ -104,17 +104,18 @@ int main(int argc, char **argv) {
output_stream.close(); output_stream.close();
if (!emit) { if (!emit) {
std::string lib_path = argv[0]; std::string lib_path = argv[0];
lib_path.erase(lib_path.rfind('/')) += "/libcminus_io.a"; auto idx = lib_path.rfind('/');
auto command_string = "clang -O0 -w "s + target_path + ".ll -o " + target_path + " " + lib_path; if (idx != std::string::npos)
std::cout << command_string << std::endl; lib_path.erase(lib_path.rfind('/'));
int re_code0 = std::system(command_string.c_str()); auto cmd_str = "clang -O0 -w "s + target_path + ".ll -o " + target_path + " -L" + lib_path + " -lcminus_io";
command_string = "rm "s + target_path + ".ll"; std::cout << cmd_str << std::endl;
int re_code1 = std::system(command_string.c_str()); int re_code0 = std::system(cmd_str.c_str());
cmd_str = "rm "s + target_path + ".ll";
int re_code1 = std::system(cmd_str.c_str());
if (re_code0 == 0 && re_code1 == 0) if (re_code0 == 0 && re_code1 == 0)
return 0; return 0;
else else
return 1; return 1;
} }
return 0; return 0;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment