设计并实现一个简单Pascal语言的编译器。主要包括几个模块:
词法分析模块
-------------------2020/05/29 备注: 本菜鸟只实现了这一个模块----------------------------------------
链接:https://pan.baidu.com/s/1Z2-O-3S4q0CFMfeI5uIzfg
提取码:256x
------------------------------------------------------------------------------------------------------------
语法分析模块
语义分析模块
1.Simple-PASCAL语言语法
源程序设计语言 G[<语句表>]
参考编译原理教材179 上机实习题
<语句>→<赋值语句>|<条件语句>|<WHILE语句>|<复合语句>
<赋值语句>→<变量>:=<算术表达式>
<条件语句>→IF<关系表达式>THEN<语句>ELSE<语句>
<WHILE语句>→WHILE<关系表达式>DO<语句>
<复合语句>→BEGIN<语句表>END
<算术表达式>→<项>|<算术表达式>+<项>|<算术表达式>-<项>
<项>→<因式>|<项>*<因式>|<项>/<因式>
<因式>→<变量>|<常数>|(<算术表达式>)
<关系表达式>→<算术表达式><关系符><算术表达式>
<变量>→<标识符>
<标识符>→<标识符><字母>|<标识符><数字>|<字母>
<常数>→<整数>
<整数>→0|<非零数字><泛整数>
<泛整数>→<数字>|<数字><泛整数>|ε
<关系符>→<|<=|==|>|>=|<>
<字母>
→A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z
<非零数字>→1|2|3|4|5|6|7|8|9
<数字>→<非零数字>|0
注释:从接连出现的/*到下一次接连出现的*/之间的任何文字都是注释。
从某行接连出现的//到该行的结尾的任何文字都是注释。
白空格:两个单词之间的任何空格,制表符,回车,换行都是白空格,除了用来分隔单词以外,没有意义。
限制条件:标识符的长度不大于8,整数不大于65535。
练习1:写出符合上面文法的两个不同源程序,要求基本覆盖所有产生式,包含两种注释, 包含若干白空格, 满足限制条件。
1)什么是源程序?
需要被处理的程序叫做源程序。
转换程序的输入时源程序。
在这里,源程序就是使用语句表文法编写的程序
2)什么是文本文件?
文件每个字节或者若干个字节表示一个字符的文件。
众所周知,一般情况下,源文件编写完毕以后,保存为特定后缀名的文本文件。
本次课程设计,所有的源文件后缀名约定为:YJB
这里强调 本次课程设计要求处理保存在文本文件中的源程序。
练习2:将练习1写好的源程序输入计算机,用1.YJB作为文件名保存
2. 词法分析
词法分析任务
主要设计内容(二选一):
(1)利用给出的类PASCAL文法,写出符合文法的两个不同源程序样例;设计相应单词的有限自动机并进行化简;根据简化的有限自动机设计并实现词法分析器,将写好的源程序作为输入,进行词法分析并给出分析结果。
(2)利用给出的类PASCAL文法,写出符合文法的两个不同源程序样例;以Flex、bison及C语言等工具编写的可被正确编译运行的源程序,将写好的源程序作为输入,进行词法分析并给出分析结果。(参考教材93页)
1、词法分析的“处理”做什么事情?
1)处理文件
读取源文件的内容
标准套路,(参考C语言的文件操作函数,fopen函数等)
2)把源文件的内容分解为词法单元
词法单元就是单词。
单词有类别(本次课程设计,请设计好单词的分类,参考教材79页):
标识符为1类:如ABC,XYZ,C
整数设为2类:如123,900
IF为3类
THEN为4类
ELSE, WHILE, DO, BEGIN, END…….
运算符+, -, *, /,>, <, =, >= , <=, <>, :=为101/102……
分隔符类: ;,(,)为201/201…….
白空格不是单词
注释以及注释标记不是单词
练习3 手工把练习1写好的源程序分解为单词类别和单词词文组成的二元式流
2、怎样描述词法单元:正规文法,正规表达式
上面给出的语句表文法实际上是该语言的BNF,其中包了单词描述和文法描述。需要从中提取出描述单词的正规文法或者正规表达式。
采用正规文法:
<保留字>→IF|THEN|……
<整数>→ 0|1<泛整数>|2<泛整数>
采用正规表达式:
(A|…|Z)(A|…|Z|0|…|9)*|(0|…|9)|(1|…|9)(0|…|9)*|IF|THEN|………….
练习4 写出描述语句表语言的词法单元的正规文法或者正规表达式(参考教材80页)
3、怎样分解词法单元:有穷自动机,递归分析程序
怎样编写词法分析程序:
1)预处理:把源文件一个字符一个字符的读入词法分析程序设置的输入字符结构体数组中(输入缓冲区),读入过程要删除注释,删除多余的白空格.
2)输入缓冲区要记录每个符号的ASCII码,以及该符号在源程序中的坐标值(行号和列号)
输入符号结构体数组:
struct inchar
{
char input;
int x,y;
}INCHAR[];
练习5 编写预处理函数,从源文件得到输入符号流,要求按照上面的形式保留每个符号的坐标
3)从源程序字符数组中获得单词, 编码为二元式.
二元式采用结构体数组存储, 把单词类型和词元记录下来
例如:
struct dual
{ int dual_type;
union {
char lexeme_text[];
int lexeme_num[];
} lexeme
} DUAL[]
4)分解单词的方法:
- 利用case多路转换语句根据单词的特点直接编写。
- 通过描述单词的正规文法得到相应的有穷自动机,通过case多路转换语句完成有穷自动机的处理流程。参考课本P78的例子
- 将有穷自动机转换为状态矩阵,构造状态矩阵驱动程序完成有穷自动机的流程。参考课本p55例子。
4、编写词法分析程序要注意的问题:
1)检查词法是否有错误
检查是否有非法字符:如a, aBC, @, &, !
检查是否有非法单词:如12A, BC_1,
检查标志符和数字是否满足限制条件
检查注释符号是否配对
2)界符分隔单词
能够区分两个单词的符号为界符
有些界符不是单词:如白空格
有些界符仅仅用来分隔:如;
有些界符本身还是源程序不可缺少的单词,如(, ), +, /, 等等
有些界符包含两个字符:如<>, >=等等
3)输出词法错误
如果没有错误,返回0
如果有错误,需要报告词法错误在源程序中的位置。并且,要能够越过错误,分解下一个单词,直到源程序结束。
4) 输出的二元式流保存在二元式结构体数组中,供语法分析使用。
为了方便接下来的语法分析能够定位语法错误在源程序中的位置,需要在二元式结构体数组中保留每个单词在源程序中的坐标(单词的首字母所在的行号列号)。
实际的二元式结构体数组应该是:
struct dual
{ int dual_type;
union {
char lexeme_text[];
int lexeme_num[];
} lexeme
int x, y;
} DUAL[]
我们还是把它叫做二元式
练习6 编写词法分析程序,从输入符号流得到二元式流(参考教材81页算法)
词法出错提示格式:
Error type [错误类型] at line[行号]:说明文字
错误类型:A(词法错误)
5、词法分析程序的总结
1)预处理函打开源文件,读取源程序内容到设置好的字符数组(输入缓冲区),这个过程要删除注释,删除多余的白空格,所有的白空格最好都转为单个空格。预处理得到了字符流。
2)预处理后关闭源文件,对输入缓冲区进行词法处理:分解输入字符流,得到的二元式存入二元式结构体。词法处理以后得到二元式流
3)错误处理要完善,特别是坐标准确
以上内容是课程设计指导书的部分内容(我只实现了编译器的词法分析部分)
词法分析程序设计及实现:
1、单词符号类别码
单词符号 | 类别码 | 助记符 | 说明 |
$ | 0 | FINISH | 结束符 |
BEGIN | 1 | BEGIN | 保留字 |
IF | 2 | IF | 保留字 |
WHILE | 3 | WHILE | 保留字 |
THEN | 4 | THEN | 保留字 |
DO | 5 | DO | 保留字 |
END | 6 | END | 保留字 |
ELSE | 7 | ELSE | 保留字 |
VAR | 8 | VAR | 保留字 |
INTEGER | 9 | INTEGER | 保留字 |
整数 | 11 | INT | 整数不大于 65535 |
标识符 | 10 | ID | 标识符长度 不大于8 |
+ | 101 | ADD |
|
- | 102 | SUB |
|
* | 103 | MUL |
|
/ | 10 | DIV |
|
: | 105 | COLON |
|
:= | 106 | COL_EQ | 赋值符号 |
< | 107 | LT |
|
<> | 108 | NE | 不等于符号 |
<= | 109 | LE |
|
> | 110 | GT |
|
>= | 111 | GE |
|
= | 112 | EQ |
|
; | 201 | SEMI |
|
( | 202 | LP |
|
) | 203 | RP |
|
, | 204 | COMMA |
|
2、源程序
写出符合上面文法G[<语句表>] 的两个不同源程序,要求基本覆盖所有产生式,包含两种注释, 包含若干白空格, 满足限制条件。
源程序test1.txt:
BEGIN
1A := 200; /*Program Passed1;Var A,B,B3Y,C12A1,DD : integer;*/
B3Y := 100 ;
IF@ A > B3Y THEN
A := A + B3Y
ELSE
C12A1 := 2 * A;
DD := C12A1 + 100;
IF 2 < B THEN
IF DD <> C12A1 THEN
C12A1 := C12A1 + C12A1 * 4;
ELSE
A := 0;
ELSE
BEGIN
A := 2 * 2 * 4;
B := 300 ;
END
END $
源程序test2.txt:
BEGIN
VAR A,B,C : INTEGER;//A,B,C is int
A := 10;
BDEFGHHIJ := 800000;
IF A > B3 THEN
A := A + B;
ELSE
B := aB + A;
DO
C_1 := A + 1;
WHILE C <= 100;
END $
3、数据结构
从源程序获取的单词,编码为二元式。二元式采用结构体数组进行存储,把单词类型,词元和坐标都记录下来
4、单词识别方法和识别流程
使用C标准库 ctype.h中的字符分类函数来识别从文件读取的每一个字符,保存到临时的存储数组,然后利用case多路转换语句根据单词的特点识别单词类别,最后将合法的单词存储到二元式结构体数组中。
5、单词符号分类
符号 | 编号 | 助记符 | |
结束符 | 0 | FINISH | |
BEGIN | 1 | GEGIN | |
IF | 2 | IF | |
WHILE | 3 | WHILE | |
THEN | 4 | THEN | |
DO | 5 | DO | |
END | 6 | END | |
ELSE | 7 | ELSE | |
VAR | 8 | VAR | |
INTEGER | 9 | INTEGER | |
整数 | 11 | INT | |
标识符 ID | 10 | ID | |
+ | 101 | ADD | |
- | 102 | SUB | |
* | 103 | MUL | |
/ | 104 | DIV | |
: | 105 | COLON | |
:= | 106 | COL_EQ | |
< | 107 | LT | |
<> | 108 | NE | |
<= | 109 | LE | |
> | 110 | GT | |
>= | 111 | GE | |
= | 112 | EQ |
6、附上代码吧。
pascal.h
//二元组结构体数组
struct pascal{
int type;//类别码
union {
char text[10];//值
int num;
}lexeme;
int x, y;//行号、列号
};
extern pascal P[200];
extern int num2;//二元组结构体数组下标
int TESTscan();//词法分析函数
int TESTparse();//语法分析函数
scan.cpp
#include <iostream>
#include "pascal.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
//临时二元组结构体数组
struct dual{
int dual_type;//类别码
union {
char lexeme_text[10];//值
char lexeme_num[10];
}lexeme;
int x, y;//行号、列号
}DUAL[200];
int num=0; //临时二元组结构体数组下标
extern pascal P[200];
extern int num2;
int num2=0;
//保留字表
const char *keyword[] = {"BEGIN","IF","WHILE","THEN","DO","END","ELSE","VAR","INTEGER"};
//单分界符
char singleword[50]="+-*/();,=";
//双分界符首字符
char doubleword[10]="><:";
//接收输入文件名
char Scanin[100];
using namespace std;
//指向输入文件的指针
FILE *fin;
//词法分析函数
int TESTscan() {
char ch,temp;//ch:每次读入的字符,token
int j,n;//j数组下标,n 中间变量
int row=1;//行号
int clumn=1;//列号
int es=0;//es错误代码,0表示无错误
cout<<"请输入源程序文件名(包括路径):";
cin>>Scanin;
fin=fopen(Scanin,"r");
if(fin==NULL){//判断输入文件名是否正确
cout<<"\n打开词法分析输入文件出错!"<<endl;
return(1); //返回错误代码1
}
cout<<"单词 助记符 类别码 坐标值(行号,列号):"<<endl;
ch=getc(fin);
while(ch!=EOF){
while(ch=='\n'){//换行处理:行号自增,列号置1
row++;
clumn=1;
ch=getc(fin); //取下一个字符
}
while(ch==' '||ch=='\t'){//过滤空格
clumn++;
ch=getc(fin);
}
if(isalpha(ch)){//如果ch是字母字符,则进行标识符处理
/*设定大写字母与数字组合是合法的,如果有小写字母或其他符号则是非法的*/
DUAL[num].lexeme.lexeme_text[0]=ch;
j=1;
DUAL[num].y=clumn;
DUAL[num].x=row;
if(islower(ch))//是小写字母
DUAL[num].dual_type=-8;
ch=getc(fin);//取下一个字符
clumn++;
int k=1;
while(isgraph(ch)&&ch!=' '&&ch!=';'&&ch!=','&&ch!='\t'){//ch是字母或数字字符或不包含空格、逗号、分号的标点符号
DUAL[num].lexeme.lexeme_text[j++]=ch;
k++;
if(ispunct(ch)||islower(ch))//存在非法字符
{
DUAL[num].dual_type=-8;
}
ch=getc(fin);
clumn++;
}
if(DUAL[num].dual_type!=-8){
DUAL[num].dual_type=10;//标识符
//查保留字
for(n=0;n<9;n++){
if(strcmp(DUAL[num].lexeme.lexeme_text,keyword[n])==0){
DUAL[num].dual_type=n+1;
break;
}
}
if(k<=8){
P[num2].type=DUAL[num].dual_type;
strcpy( P[num2].lexeme.text,DUAL[num].lexeme.lexeme_text);
P[num2].x=DUAL[num].x;
P[num2].y=DUAL[num].y;
num2++;
}
}
DUAL[num].lexeme.lexeme_text[j]='\0';//标识符组合结束
int type=DUAL[num].dual_type;
switch(type){
case 1:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tBEGIN,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 2:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tIF,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 3:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tWHILE,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 4:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tTHEN,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 5:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tDO,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 6:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tEND,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 7:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tELSE,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 8:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tVAR,\t""("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 9:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tINTEGER,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 10:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tID,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
default:
cout<<"Error type ["<<DUAL[num].lexeme.lexeme_text<<"]at line:"<<DUAL[num].x<<endl<<"存在非法字符\t错误类型:非法单词"<<endl;
break;
}
if(k>8){
cout<<"WARNING:标识符超长"<<endl;
}
num++;
}
else if(isdigit(ch))//ch是数字(0-9),进行数字处理
{
int sum;
DUAL[num].lexeme.lexeme_num[0]=ch;
j=1;
DUAL[num].x=row;
DUAL[num].y=clumn++;
ch=getc(fin);
while(isalnum(ch)){//ch是字母或数字字符
DUAL[num].lexeme.lexeme_num[j++]=ch;
clumn++;
if(!isdigit(ch))//存在非法字符
DUAL[num].dual_type=-8;
ch=getc(fin);
}
if(DUAL[num].dual_type!=-8)
{
sum=0;
char *test=DUAL[num].lexeme.lexeme_num;
sum=atoi(test);
if(sum>65535)
DUAL[num].dual_type=-7;
else
DUAL[num].dual_type=11;
}
if(DUAL[num].dual_type==11)
{
cout<<"["<<DUAL[num].lexeme.lexeme_num<<",\t"<<DUAL[num].dual_type<<",\tINT,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
P[num2].type=DUAL[num].dual_type;
P[num2].lexeme.num=sum;
P[num2].x=DUAL[num].x;
P[num2].y=DUAL[num].y;
num2++;
}
if(DUAL[num].dual_type==-7)
cout<<"Error type["<<DUAL[num].lexeme.lexeme_num<<"]at line:"<<DUAL[num].x<<endl<<"整数值大于65535\t错误类型:整数越界"<<endl;
if(DUAL[num].dual_type==-8)
cout<<"Error type["<<DUAL[num].lexeme.lexeme_num<<"]at line:"<<DUAL[num].x<<endl<<"存在非法字符\t错误类型:非法单词"<<endl;
num++;
}
else if(strchr(singleword,ch)>0)//单分界符处理
{
int isZhuShi=0;
switch(ch){
case '+' :
DUAL[num].dual_type=101;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tADD,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case '-':
DUAL[num].dual_type=102;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tSUM,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case '*':
DUAL[num].dual_type=103;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tMUL,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case '/':
temp=ch;
ch=getc(fin);
if(ch=='*')//注释识别
{
isZhuShi=1;
char ch2=ch;
ch=getc(fin);
do
{
ch2=ch;
ch=getc(fin);
}while((ch!='/'||ch2!='*')&&ch!=EOF);
}else{
DUAL[num].dual_type=104;
DUAL[num].lexeme.lexeme_text[0]=temp;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tDIV,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
}
break;
case '(':
DUAL[num].dual_type=202;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tLP,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case ')':
DUAL[num].dual_type=102;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tRP,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case ';':
DUAL[num].dual_type=201;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tSEMI,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case ',':
DUAL[num].dual_type=204;
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tCOMMA,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case '=':
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].x=row;
DUAL[num].y=clumn++;DUAL[num].dual_type=112;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tEQ,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
}
if(!isZhuShi){
P[num2].type=DUAL[num].dual_type;
strcpy( P[num2].lexeme.text,DUAL[num].lexeme.lexeme_text);
P[num2].x=DUAL[num].x;
P[num2].y=DUAL[num].y;
num2++;
}
num++;
ch=getc(fin);
}
else if(strchr(doubleword,ch)>0){//双分界符处理
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].y=clumn++;
DUAL[num].x=row;
char ch2=ch;
ch=getc(fin);
switch(ch2) {
case ':':
if(ch=='='){// 是双分界符:=
DUAL[num].lexeme.lexeme_text[1]=ch;
DUAL[num].lexeme.lexeme_text[2]='\0';
DUAL[num].dual_type=106;
clumn++;
}else{// 是单分界符:
DUAL[num].dual_type=105;
DUAL[num].lexeme.lexeme_text[1]='\0';
}break;
case '>':
if(ch=='='){// 是双分界符>=
DUAL[num].lexeme.lexeme_text[1]=ch;
DUAL[num].lexeme.lexeme_text[2]='\0';
DUAL[num].dual_type=111;
clumn++;
}else{//单分界符>
DUAL[num].dual_type=110;
DUAL[num].lexeme.lexeme_text[1]='\0';
}break;
case '<':
if(ch=='='){//是双分界符<=
DUAL[num].lexeme.lexeme_text[1]=ch;
DUAL[num].lexeme.lexeme_text[2]='\0';
DUAL[num].dual_type=109;
clumn++;
}
else if(ch=='>')
{//是双分界符<>
DUAL[num].lexeme.lexeme_text[1]=ch;
DUAL[num].lexeme.lexeme_text[2]='\0';
DUAL[num].dual_type=108;
clumn++;
}
else{//单分界符<
DUAL[num].dual_type=107;
DUAL[num].lexeme.lexeme_text[1]='\0';
}
}
int type=DUAL[num].dual_type;
switch(type){
case 105:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tCOLON,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 106:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tCOL_EQ,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 107:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tLT,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 108:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tNE,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 109:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tLE,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 110:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tGT,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
case 111:
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tGE,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
break;
}
P[num2].type=DUAL[num].dual_type;
strcpy( P[num2].lexeme.text,DUAL[num].lexeme.lexeme_text);
P[num2].x=DUAL[num].x;
P[num2].y=DUAL[num].y;
num2++;
num++;
ch=getc(fin);
}
else if(ch=='$')//结束符处理
{
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].dual_type=0;
DUAL[num].x=row;
DUAL[num].y=clumn;
cout<<"["<<DUAL[num].lexeme.lexeme_text<<",\t"<<DUAL[num].dual_type<<",\tFINISH,\t("<<DUAL[num].x<<","<<DUAL[num].y<<")]"<<endl;
P[num2].type=DUAL[num].dual_type;
strcpy( P[num2].lexeme.text,DUAL[num].lexeme.lexeme_text);
P[num2].x=DUAL[num].x;
P[num2].y=DUAL[num].y;
num++;
num2++;
fclose(fin);
break;
}
else //非法字符处理
{
DUAL[num].lexeme.lexeme_text[0]=ch;
DUAL[num].lexeme.lexeme_text[1]='\0';
DUAL[num].dual_type=-8;
DUAL[num].x=row;
DUAL[num].y=clumn++;
cout<<"Error type["<<DUAL[num].lexeme.lexeme_num<<"]at line:"<<DUAL[num].x<<endl<<"存在非法字符\t错误类型:非法字符"<<endl;
num++;
ch=getc(fin);
}
}
fclose(fin);
return(es);
}
main.cpp
#include <iostream>
#include "pascal.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
extern pascal P[200];
extern int num2;
int main(int argc, char** argv) {
int i;
int es=0;
es=TESTscan();
if(es>0)
cout<<"词法分析出错!编译停止。"<<endl;
else
cout<<"词法分析成功!"<<endl;
cout<<"分析器内容:"<<endl;
for(i=0;i<num2;i++)
{
cout<<"i="<<i<<"\t";
if(P[i].type==11)
cout<<"["<<P[i].lexeme.num<<",";
else cout<<"["<<P[i].lexeme.text<<",";
cout<<P[i].type<<"("<<P[i].x<<","<<P[i].y<<")]"<<endl;
}
return 0;
}
比起词法,语法分析更难了一点。(没有完成整个语法模块,就不发出来了。只做了词法分析,本渣渣的课设也过了。浓浓师生情啊),扩广文法,求FOLLOW集还是比较好做的,麻烦的是自动机的构造。要是哪一步写错,改起来要改动多个项目集。构造出了自动机,构造SLR分析表的时候就需要仔细的填写对应的值了。