## **词法分析**
### 正规式
- letter -> a|b|c|...|z
- digit -> 0|1|...|9
- number -> digit digit*
- id -> letter(letter|digit)*
### 确定有限自动机(DFA)
```mermaid
graph LR
A[start]-->S0((0))
S0-->|/|S1((1))
S1-->|b|S8((8))
S8-->|l|S9((9))
S9-->|a|S10((10))
S10-->|n|S11((11))
S11-->|k|S12["return /blank"]
S1-->|i|S2((2))
S2-->|n|S3((3))
S3-->|t|S4["return /int"]
S1-->|s|S5((5))
S5-->|u|S6((6))
S6-->|m|S7["return /sum"]
S0-->|"("|S13["return ("]
S0-->|")"|S14["return )"]
S0-->|"{"|S15["return {"]
S0-->|"}"|S16["return }"]
S0-->|_|S17["return _"]
S0-->|^|S18["return ^"]
S0-->|$|S19["return $"]
S0-->|num|S20["return number"]
S0-->|letter|S21((21))
S21-->|"letter/num"|S21
S21-->|other|S22[return id *]
```
## 语法分析
我的语法分析使用的是递归下降的预测分析(LL(1))
### 语法分析器代码
```
int match_B() {
while(syn != 13)
{
if(syn == 4 || syn == 5)
{
if(syn == 4) printf("B -> id\n") ;
if(syn == 5) printf("B -> num\n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 10 || syn == 13 || syn == 8) return true; // B -> id/num + }/$
if(syn == 11) //B_
{
scaner();
if(syn == 12) // B -> B_^
{
scaner();
if(syn == 9) // B_^{
{
scaner();
if(match_B()) // B_^{B
{
//scaner();
if(syn == 10) //B_^{B}
{
scaner();
if(syn == 9) { //B_^{B}{
scaner();
if(match_B()) //B_^{B}{B
{
// scaner();
if(syn == 10) //B_^{B}{B}
{
printf("B -> B_^{B}{B} \n");
scaner() ;
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; //B_^{B}{B} + }/)/$
}
}
}
}
}
}
}
else if(syn == 9) // B -> B_{
{
scaner();
if(match_B()) // B -> B_{B
{
//scaner();
if(syn == 10) // B -> B_{B}
{
printf("B -> B_{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3 )printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B_{B} + }/)/$
}
}
}
}
else if(syn == 12) // B -> B^
{
scaner();
if(syn == 9) // B -> B^{
{
scaner();
if(match_B()) // B -> B^{B
{
//scaner();
if(syn == 10) // B -> B^{B}
{
printf("B -> B^{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B^{B} + }/)/$
}
}
}
}
// else {
// printf("B -> id \n"); // B -> ID
// scaner();
// if(syn == 13 || syn == 10) return true; // B -> ID + $/}
// }
}
else if(syn == 2) { // B -> /int
scaner();
if(syn == 9) { // B -> /int{
scaner();
if(match_B()) { // B -> /int{B
//scaner();
if(syn == 10) { // B -> /int{B}
scaner();
if(syn == 9) { // B -> /int{B}{
scaner() ;
if(match_B()) { // B -> /int{B}{B
//scaner();
if(syn == 10) { // B -> /int{B}{B}
scaner();
if(syn == 9) { // B -> /int{B}{B}{
scaner();
if(match_B()){ // B -> /int{B}{B}{B
if(syn != 10)
scaner(); // B -> /int{B}{B}{B}
if(syn == 10) {
printf("B -> \\int{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /int{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 3) { // B -> /sum
scaner();
if(syn == 9) { // B -> /sum{
scaner();
if(match_B()) { // B -> /sum{B
if(syn == 10) { // B -> /sum{B}
scaner();
if(syn == 9) { // B -> /sum{B}{
scaner();
if(match_B()) { // B -> /sum{B}{B
if(syn == 10) { // B -> /sum{B}{B}
scaner();
if(syn == 9) { // B -> /sum{B}{B}{
scaner();
if(match_B()){ // B -> /sum{B}{B}{B
if(syn != 10)
scaner();
if(syn == 10) { // B -> /sum{B}{B}{B}
printf("B -> \\sum{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /sum{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 5) {
// scaner();
printf("B -> num \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 1) {
//scaner();
printf("B -> \\blank \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 7) { //B -> (
scaner();
if(match_B()) { //B -> (B
//scaner();
if(syn == 8) { //B -> (B)
// scaner();
printf("B -> (B)\n");
scaner();
return true; //B -> (B) + $/}
}
else match_B();
}
}
else if(syn == -1) {
return false;
}
}
}
```
## 语法制导翻译
因为我使用了递归下降的LL(1)语法,因此语法执导翻译采用L属性定义的自上而下计算 ,具体内容见源代码文件。
## 运行结果
过程描述:输入文件名,输出归约及编译过程,最后自动打开生成的html文件。若是正确样例,则命令行中输出"success",若是错误样例,命令行中输出"illegal",并在html文件中输出ERROR at ...(错误位置)。
### 测试样例(test文件)
#### test01.txt
![1525937972153](C:/Users/Boatman/AppData/Local/Temp/1525937972153.png)
![1525938041252](C:/Users/Boatman/AppData/Local/Temp/1525938041252.png)
#### test02.txt
![1525938132700](C:/Users/Boatman/AppData/Local/Temp/1525938132700.png)
![1525938175489](C:/Users/Boatman/AppData/Local/Temp/1525938175489.png)
#### test03.txt
![1525938299929](C:/Users/Boatman/AppData/Local/Temp/1525938299929.png)
![1525938320595](C:/Users/Boatman/AppData/Local/Temp/1525938320595.png)
#### test04.txt
![1525938538946](C:/Users/Boatman/AppData/Local/Temp/1525938538946.png)
![1525938559881](C:/Users/Boatman/AppData/Local/Temp/1525938559881.png)
#### test05.txt
![1525951182833](C:/Users/Boatman/AppData/Local/Temp/1525951182833.png)
![1525951260523](C:/Users/Boatman/AppData/Local/Temp/1525951260523.png)
#### test06.txt
![1525938780778](C:/Users/Boatman/AppData/Local/Temp/1525938780778.png)
![1525938792544](C:/Users/Boatman/AppData/Local/Temp/1525938792544.png)
#### test07.txt
![1525938919673](C:/Users/Boatman/AppData/Local/Temp/1525938919673.png)
![1525938978496](C:/Users/Boatman/AppData/Local/Temp/1525938978496.png)
#### test08.txt
![1525939048356](C:/Users/Boatman/AppData/Local/Temp/1525939048356.png)
![1525939062153](C:/Users/Boatman/AppData/Local/Temp/1525939062153.png)
#### test09.txt
![1525939148239](C:/Users/Boatman/AppData/Local/Temp/1525939148239.png)
![1525939165126](C:/Users/Boatman/AppData/Local/Temp/1525939165126.png)
#### test10.txt
![1525939254694](C:/Users/Boatman/AppData/Local/Temp/1525939254694.png)
![1525939266949](C:/Users/Boatman/AppData/Local/Temp/1525939266949.png)
## 软件设计
### 程序流程
```mermaid
graph TB
s1("开始")-->s2[输入待编译文件名]
s2-->|读文件|s3[词法分析器]
s3-->|合法记号|s4["语法分析器(递归下降的预测分析)"]
s4-->|取下一个记号|s3
s4-->s5["L属性定义的自上而下翻译"]
s5-->s6{"完成所有编译?"}
s6-->|否|s4
s6-->|是|s7[生成html文件]
s7-->s8(结束)
```
### 函数关系
#### 词法分析函数
##### 主要思想
对文本文件中读入的字符串,从第一个字符开始按照DFA进行匹配,返回每个记号对应的编号(syn)。
```
void scaner()
{
cishu++;
for(n=0;n<8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
{
ch=prog[p];
p++;
}
if(ch == '\\') {
ch = prog[p++];
m=1;
token[0] = '\\';
while(ch>='a'&&ch<='z') {
token[m++]=ch;
ch=prog[p++];
}
p--;
for(n=0;n<3;n++)
if(strcmp(token,rwtab[n])==0)
{
syn=n+1;
return;
}
}
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
m=0;
while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
token[m++]=ch;
ch=prog[p++];
}
token[m++]='\0';
p--;
syn=4;
}
else if((ch>='0'&&ch<='9'))
{
{
sum=0;
while((ch>='0'&&ch<='9'))
{
sum=sum*10+ch-'0';
ch=prog[p++];
}
}
p--;
syn=5;
if(sum>32767)
syn=-1;
}
else switch(ch)
{
case '(':syn=7;token[0]=ch;break;
case ')':syn=8;token[0]=ch;break;
case '{':syn=9;token[0]=ch;break;
case '}':syn=10;token[0]=ch;break;
case '_':syn=11;token[0]=ch;break;
case '^':syn=12;token[0]=ch;break;
case '$':syn=13;token[0]=ch;break;
case '\n':syn=-2;break;
case '\0':syn=0;break;
default: syn=-1;break;
}
}
```
##### 运行截图
![1525941777480](C:/Users/Boatman/AppData/Local/Temp/1525941777480.png)
#### 语法分析函数
##### 主要思想
每从词法分析函数返回一个序号,便进行匹配,若与某一状态相符,则进入该状态,取下一个序号进行匹配,当完全匹配到某一产生式时,进行归约。
```
int match_B() {
while(syn != 13)
{
if(syn == 4 || syn == 5)
{
if(syn == 4) printf("B -> id\n") ;
if(syn == 5) printf("B -> num\n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 10 || syn == 13 || syn == 8) return true; // B -> id/num + }/$
if(syn == 11) //B_
{
scaner();
if(syn == 12) // B -> B_^
{
scaner();
if(syn == 9) // B_^{
{
scaner();
if(match_B()) // B_^{B
{
//scaner();
if(syn == 10) //B_^{B}
{
scaner();
if(syn == 9) { //B_^{B}{
scaner();
if(match_B()) //B_^{B}{B
{
// scaner();
if(syn == 10) //B_^{B}{B}
{
printf("B -> B_^{B}{B} \n");
scaner() ;
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; //B_^{B}{B} + }/)/$
}
}
}
}
}
}
}
else if(syn == 9) // B -> B_{
{
scaner();
if(match_B()) // B -> B_{B
{
//scaner();
if(syn == 10) // B -> B_{B}
{
printf("B -> B_{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3 )printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B_{B} + }/)/$
}
}
}
}
else if(syn == 12) // B -> B^
{
scaner();
if(syn == 9) // B -> B^{
{
scaner();
if(match_B()) // B -> B^{B
{
//scaner();
if(syn == 10) // B -> B^{B}
{
printf("B -> B^{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B^{B} + }/)/$
}
}
}
}
// else {
// printf("B -> id \n"); // B -> ID
// scaner();
// if(syn == 13 || syn == 10) return true; // B -> ID + $/}
// }
}
else if(syn == 2) { // B -> /int
scaner();
if(syn == 9) { // B -> /int{
scaner();
if(match_B()) { // B -> /int{B
//scaner();
if(syn == 10) { // B -> /int{B}
scaner();
if(syn == 9) { // B -> /int{B}{
scaner() ;
if(match_B()) { // B -> /int{B}{B
//scaner();
if(syn == 10) { // B -> /int{B}{B}
scaner();
if(syn == 9) { // B -> /int{B}{B}{
scaner();
if(match_B()){ // B -> /int{B}{B}{B
if(syn != 10)
scaner(); // B -> /int{B}{B}{B}
if(syn == 10) {
printf("B -> \\int{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /int{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 3) { // B -> /sum
scaner();
if(syn == 9) { // B -> /sum{
scaner();
if(match_B()) { // B -> /sum{B
if(syn == 10) { // B -> /sum{B}
scaner();
if(syn == 9) { // B -> /sum{B}{
scaner();
if(match_B()) { // B -> /sum{B}{B
if(syn == 10) { // B -> /sum{B}{B}
scaner();
if(syn == 9) { // B -> /sum{B}{B}{
scaner();
if(match_B()){ // B -> /sum{B}{B}{B
if(syn != 10)
scaner();
if(syn == 10) { // B -> /sum{B}{B}{B}
printf("B -> \\sum{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /sum{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 5) {
// scaner();
printf("B -> num \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 1) {
//scaner();
printf("B -> \\blank \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 7) { //B -> (
scaner();
if(match_B()) { //B -> (B
//scaner();
if(syn == 8) { //B -> (B)
// scaner();
printf("B -> (B)\n");
scaner();
return true; //B -> (B) + $/}
}
else match_B();
}
}
else if(syn == -1) {
return false;
}
}
}
```
##### 运行截图
![1525942065526](C:/Users/Boatman/AppData/Local/Temp/1525942065526.png)
#### 关系
语法分析函数是建立在词法分析函数之上的,在语法分析过程中,每次都要先运行词法分析函数,使用其返回值syn来进行语法分析。
### 接口定义
用户只需在任意文本文件中输入代码,运行该程序,输入文本文件名即可编译。
## 体会与建议
通过本次大作业,我深刻体会到了“编译”二字的真正含义,理解了编译器构造的一般原理和基本实现方法,特别是词法分析、语法分析和语法制导翻译三个阶段,同时还大大提高了编程能力。本次大作业让我知道一段源代码是通过哪些过程,使计算机能够读懂,并表达给前端用户。首先是词法分析,词法分析器把构成源程序的字符流翻译成记号流,还完成一些和用户接口的任务,是整个编译器框架的基石,有了词法分析器,编译器才能分辨出每个字符都属于哪一类,这是编译器进行“看懂”源程序的前提;拿到词法分析器的记号后,开始进行语法分析,我采用的是LL(1)文法来进行语法分析,因此需要注意消除左递归,语法分析能够让编译器“看懂”源程序;编译器“看懂”程序了,如何把程序翻译为用户能够看懂的形式呢?这就是语法制导翻译的作用。本次大作业通过使用html脚本语言,随着语法分析器的归约过程,使用L属性定义的自上而下翻译,将抽象难懂的源代码文本翻译为简洁直观的html文件,这就是该编译器的作用。
虽然这个编译器只实现了一个很小的功能,但在这个实现过程中,我们可以理解那些大型的编程语言编译器和排版语言编译器的实现原理,能够以一个更底层的视角去了解一个编译器,这能改变我们的编程思维,提升编程能力,我觉得是这次大作业最重要的意义。
没有想到什么实质性的建议,要是能够不限编程语言的或许可以有更多的编译器形式?比如做成一个网页端的数学公式排版语言。
总之感谢老师和助教的帮助,这次大作业实实在在地提升了我非常薄弱的编程能力,让我具备了一些编程思维。
### 正规式
- letter -> a|b|c|...|z
- digit -> 0|1|...|9
- number -> digit digit*
- id -> letter(letter|digit)*
### 确定有限自动机(DFA)
```mermaid
graph LR
A[start]-->S0((0))
S0-->|/|S1((1))
S1-->|b|S8((8))
S8-->|l|S9((9))
S9-->|a|S10((10))
S10-->|n|S11((11))
S11-->|k|S12["return /blank"]
S1-->|i|S2((2))
S2-->|n|S3((3))
S3-->|t|S4["return /int"]
S1-->|s|S5((5))
S5-->|u|S6((6))
S6-->|m|S7["return /sum"]
S0-->|"("|S13["return ("]
S0-->|")"|S14["return )"]
S0-->|"{"|S15["return {"]
S0-->|"}"|S16["return }"]
S0-->|_|S17["return _"]
S0-->|^|S18["return ^"]
S0-->|$|S19["return $"]
S0-->|num|S20["return number"]
S0-->|letter|S21((21))
S21-->|"letter/num"|S21
S21-->|other|S22[return id *]
```
## 语法分析
我的语法分析使用的是递归下降的预测分析(LL(1))
### 语法分析器代码
```
int match_B() {
while(syn != 13)
{
if(syn == 4 || syn == 5)
{
if(syn == 4) printf("B -> id\n") ;
if(syn == 5) printf("B -> num\n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 10 || syn == 13 || syn == 8) return true; // B -> id/num + }/$
if(syn == 11) //B_
{
scaner();
if(syn == 12) // B -> B_^
{
scaner();
if(syn == 9) // B_^{
{
scaner();
if(match_B()) // B_^{B
{
//scaner();
if(syn == 10) //B_^{B}
{
scaner();
if(syn == 9) { //B_^{B}{
scaner();
if(match_B()) //B_^{B}{B
{
// scaner();
if(syn == 10) //B_^{B}{B}
{
printf("B -> B_^{B}{B} \n");
scaner() ;
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; //B_^{B}{B} + }/)/$
}
}
}
}
}
}
}
else if(syn == 9) // B -> B_{
{
scaner();
if(match_B()) // B -> B_{B
{
//scaner();
if(syn == 10) // B -> B_{B}
{
printf("B -> B_{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3 )printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B_{B} + }/)/$
}
}
}
}
else if(syn == 12) // B -> B^
{
scaner();
if(syn == 9) // B -> B^{
{
scaner();
if(match_B()) // B -> B^{B
{
//scaner();
if(syn == 10) // B -> B^{B}
{
printf("B -> B^{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B^{B} + }/)/$
}
}
}
}
// else {
// printf("B -> id \n"); // B -> ID
// scaner();
// if(syn == 13 || syn == 10) return true; // B -> ID + $/}
// }
}
else if(syn == 2) { // B -> /int
scaner();
if(syn == 9) { // B -> /int{
scaner();
if(match_B()) { // B -> /int{B
//scaner();
if(syn == 10) { // B -> /int{B}
scaner();
if(syn == 9) { // B -> /int{B}{
scaner() ;
if(match_B()) { // B -> /int{B}{B
//scaner();
if(syn == 10) { // B -> /int{B}{B}
scaner();
if(syn == 9) { // B -> /int{B}{B}{
scaner();
if(match_B()){ // B -> /int{B}{B}{B
if(syn != 10)
scaner(); // B -> /int{B}{B}{B}
if(syn == 10) {
printf("B -> \\int{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /int{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 3) { // B -> /sum
scaner();
if(syn == 9) { // B -> /sum{
scaner();
if(match_B()) { // B -> /sum{B
if(syn == 10) { // B -> /sum{B}
scaner();
if(syn == 9) { // B -> /sum{B}{
scaner();
if(match_B()) { // B -> /sum{B}{B
if(syn == 10) { // B -> /sum{B}{B}
scaner();
if(syn == 9) { // B -> /sum{B}{B}{
scaner();
if(match_B()){ // B -> /sum{B}{B}{B
if(syn != 10)
scaner();
if(syn == 10) { // B -> /sum{B}{B}{B}
printf("B -> \\sum{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /sum{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 5) {
// scaner();
printf("B -> num \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 1) {
//scaner();
printf("B -> \\blank \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 7) { //B -> (
scaner();
if(match_B()) { //B -> (B
//scaner();
if(syn == 8) { //B -> (B)
// scaner();
printf("B -> (B)\n");
scaner();
return true; //B -> (B) + $/}
}
else match_B();
}
}
else if(syn == -1) {
return false;
}
}
}
```
## 语法制导翻译
因为我使用了递归下降的LL(1)语法,因此语法执导翻译采用L属性定义的自上而下计算 ,具体内容见源代码文件。
## 运行结果
过程描述:输入文件名,输出归约及编译过程,最后自动打开生成的html文件。若是正确样例,则命令行中输出"success",若是错误样例,命令行中输出"illegal",并在html文件中输出ERROR at ...(错误位置)。
### 测试样例(test文件)
#### test01.txt
![1525937972153](C:/Users/Boatman/AppData/Local/Temp/1525937972153.png)
![1525938041252](C:/Users/Boatman/AppData/Local/Temp/1525938041252.png)
#### test02.txt
![1525938132700](C:/Users/Boatman/AppData/Local/Temp/1525938132700.png)
![1525938175489](C:/Users/Boatman/AppData/Local/Temp/1525938175489.png)
#### test03.txt
![1525938299929](C:/Users/Boatman/AppData/Local/Temp/1525938299929.png)
![1525938320595](C:/Users/Boatman/AppData/Local/Temp/1525938320595.png)
#### test04.txt
![1525938538946](C:/Users/Boatman/AppData/Local/Temp/1525938538946.png)
![1525938559881](C:/Users/Boatman/AppData/Local/Temp/1525938559881.png)
#### test05.txt
![1525951182833](C:/Users/Boatman/AppData/Local/Temp/1525951182833.png)
![1525951260523](C:/Users/Boatman/AppData/Local/Temp/1525951260523.png)
#### test06.txt
![1525938780778](C:/Users/Boatman/AppData/Local/Temp/1525938780778.png)
![1525938792544](C:/Users/Boatman/AppData/Local/Temp/1525938792544.png)
#### test07.txt
![1525938919673](C:/Users/Boatman/AppData/Local/Temp/1525938919673.png)
![1525938978496](C:/Users/Boatman/AppData/Local/Temp/1525938978496.png)
#### test08.txt
![1525939048356](C:/Users/Boatman/AppData/Local/Temp/1525939048356.png)
![1525939062153](C:/Users/Boatman/AppData/Local/Temp/1525939062153.png)
#### test09.txt
![1525939148239](C:/Users/Boatman/AppData/Local/Temp/1525939148239.png)
![1525939165126](C:/Users/Boatman/AppData/Local/Temp/1525939165126.png)
#### test10.txt
![1525939254694](C:/Users/Boatman/AppData/Local/Temp/1525939254694.png)
![1525939266949](C:/Users/Boatman/AppData/Local/Temp/1525939266949.png)
## 软件设计
### 程序流程
```mermaid
graph TB
s1("开始")-->s2[输入待编译文件名]
s2-->|读文件|s3[词法分析器]
s3-->|合法记号|s4["语法分析器(递归下降的预测分析)"]
s4-->|取下一个记号|s3
s4-->s5["L属性定义的自上而下翻译"]
s5-->s6{"完成所有编译?"}
s6-->|否|s4
s6-->|是|s7[生成html文件]
s7-->s8(结束)
```
### 函数关系
#### 词法分析函数
##### 主要思想
对文本文件中读入的字符串,从第一个字符开始按照DFA进行匹配,返回每个记号对应的编号(syn)。
```
void scaner()
{
cishu++;
for(n=0;n<8;n++) token[n]=NULL;
ch=prog[p++];
while(ch==' ')
{
ch=prog[p];
p++;
}
if(ch == '\\') {
ch = prog[p++];
m=1;
token[0] = '\\';
while(ch>='a'&&ch<='z') {
token[m++]=ch;
ch=prog[p++];
}
p--;
for(n=0;n<3;n++)
if(strcmp(token,rwtab[n])==0)
{
syn=n+1;
return;
}
}
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
m=0;
while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
{
token[m++]=ch;
ch=prog[p++];
}
token[m++]='\0';
p--;
syn=4;
}
else if((ch>='0'&&ch<='9'))
{
{
sum=0;
while((ch>='0'&&ch<='9'))
{
sum=sum*10+ch-'0';
ch=prog[p++];
}
}
p--;
syn=5;
if(sum>32767)
syn=-1;
}
else switch(ch)
{
case '(':syn=7;token[0]=ch;break;
case ')':syn=8;token[0]=ch;break;
case '{':syn=9;token[0]=ch;break;
case '}':syn=10;token[0]=ch;break;
case '_':syn=11;token[0]=ch;break;
case '^':syn=12;token[0]=ch;break;
case '$':syn=13;token[0]=ch;break;
case '\n':syn=-2;break;
case '\0':syn=0;break;
default: syn=-1;break;
}
}
```
##### 运行截图
![1525941777480](C:/Users/Boatman/AppData/Local/Temp/1525941777480.png)
#### 语法分析函数
##### 主要思想
每从词法分析函数返回一个序号,便进行匹配,若与某一状态相符,则进入该状态,取下一个序号进行匹配,当完全匹配到某一产生式时,进行归约。
```
int match_B() {
while(syn != 13)
{
if(syn == 4 || syn == 5)
{
if(syn == 4) printf("B -> id\n") ;
if(syn == 5) printf("B -> num\n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 10 || syn == 13 || syn == 8) return true; // B -> id/num + }/$
if(syn == 11) //B_
{
scaner();
if(syn == 12) // B -> B_^
{
scaner();
if(syn == 9) // B_^{
{
scaner();
if(match_B()) // B_^{B
{
//scaner();
if(syn == 10) //B_^{B}
{
scaner();
if(syn == 9) { //B_^{B}{
scaner();
if(match_B()) //B_^{B}{B
{
// scaner();
if(syn == 10) //B_^{B}{B}
{
printf("B -> B_^{B}{B} \n");
scaner() ;
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; //B_^{B}{B} + }/)/$
}
}
}
}
}
}
}
else if(syn == 9) // B -> B_{
{
scaner();
if(match_B()) // B -> B_{B
{
//scaner();
if(syn == 10) // B -> B_{B}
{
printf("B -> B_{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3 )printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B_{B} + }/)/$
}
}
}
}
else if(syn == 12) // B -> B^
{
scaner();
if(syn == 9) // B -> B^{
{
scaner();
if(match_B()) // B -> B^{B
{
//scaner();
if(syn == 10) // B -> B^{B}
{
printf("B -> B^{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> B^{B} + }/)/$
}
}
}
}
// else {
// printf("B -> id \n"); // B -> ID
// scaner();
// if(syn == 13 || syn == 10) return true; // B -> ID + $/}
// }
}
else if(syn == 2) { // B -> /int
scaner();
if(syn == 9) { // B -> /int{
scaner();
if(match_B()) { // B -> /int{B
//scaner();
if(syn == 10) { // B -> /int{B}
scaner();
if(syn == 9) { // B -> /int{B}{
scaner() ;
if(match_B()) { // B -> /int{B}{B
//scaner();
if(syn == 10) { // B -> /int{B}{B}
scaner();
if(syn == 9) { // B -> /int{B}{B}{
scaner();
if(match_B()){ // B -> /int{B}{B}{B
if(syn != 10)
scaner(); // B -> /int{B}{B}{B}
if(syn == 10) {
printf("B -> \\int{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /int{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 3) { // B -> /sum
scaner();
if(syn == 9) { // B -> /sum{
scaner();
if(match_B()) { // B -> /sum{B
if(syn == 10) { // B -> /sum{B}
scaner();
if(syn == 9) { // B -> /sum{B}{
scaner();
if(match_B()) { // B -> /sum{B}{B
if(syn == 10) { // B -> /sum{B}{B}
scaner();
if(syn == 9) { // B -> /sum{B}{B}{
scaner();
if(match_B()){ // B -> /sum{B}{B}{B
if(syn != 10)
scaner();
if(syn == 10) { // B -> /sum{B}{B}{B}
printf("B -> \\sum{B}{B}{B} \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10 || syn == 8) return true; // B -> /sum{B}{B}{B} + }/)/$
}
}
}
}
}
}
}
}
}
}
else if(syn == 5) {
// scaner();
printf("B -> num \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 1) {
//scaner();
printf("B -> \\blank \n");
scaner();
if(syn == 1 || syn == 2 || syn ==3) printf("B -> BB\n") ;
if(syn == 13 || syn == 10) return true;
}
else if(syn == 7) { //B -> (
scaner();
if(match_B()) { //B -> (B
//scaner();
if(syn == 8) { //B -> (B)
// scaner();
printf("B -> (B)\n");
scaner();
return true; //B -> (B) + $/}
}
else match_B();
}
}
else if(syn == -1) {
return false;
}
}
}
```
##### 运行截图
![1525942065526](C:/Users/Boatman/AppData/Local/Temp/1525942065526.png)
#### 关系
语法分析函数是建立在词法分析函数之上的,在语法分析过程中,每次都要先运行词法分析函数,使用其返回值syn来进行语法分析。
### 接口定义
用户只需在任意文本文件中输入代码,运行该程序,输入文本文件名即可编译。
## 体会与建议
通过本次大作业,我深刻体会到了“编译”二字的真正含义,理解了编译器构造的一般原理和基本实现方法,特别是词法分析、语法分析和语法制导翻译三个阶段,同时还大大提高了编程能力。本次大作业让我知道一段源代码是通过哪些过程,使计算机能够读懂,并表达给前端用户。首先是词法分析,词法分析器把构成源程序的字符流翻译成记号流,还完成一些和用户接口的任务,是整个编译器框架的基石,有了词法分析器,编译器才能分辨出每个字符都属于哪一类,这是编译器进行“看懂”源程序的前提;拿到词法分析器的记号后,开始进行语法分析,我采用的是LL(1)文法来进行语法分析,因此需要注意消除左递归,语法分析能够让编译器“看懂”源程序;编译器“看懂”程序了,如何把程序翻译为用户能够看懂的形式呢?这就是语法制导翻译的作用。本次大作业通过使用html脚本语言,随着语法分析器的归约过程,使用L属性定义的自上而下翻译,将抽象难懂的源代码文本翻译为简洁直观的html文件,这就是该编译器的作用。
虽然这个编译器只实现了一个很小的功能,但在这个实现过程中,我们可以理解那些大型的编程语言编译器和排版语言编译器的实现原理,能够以一个更底层的视角去了解一个编译器,这能改变我们的编程思维,提升编程能力,我觉得是这次大作业最重要的意义。
没有想到什么实质性的建议,要是能够不限编程语言的或许可以有更多的编译器形式?比如做成一个网页端的数学公式排版语言。
总之感谢老师和助教的帮助,这次大作业实实在在地提升了我非常薄弱的编程能力,让我具备了一些编程思维。