运行环境:Dev-C++ 5.4.0
一、前提了解
语法分析的作用是识别由词法分析给出的单词符号串是否是给定文法的正确句子(程序)。PL/0语言语法可用EBNF形式描述,其中的一个EBNF描述如下。
PL/0语言语法的EBNF描述
PL/0语法单位 | EBNF描述 |
<程序> | ::=<分程序>. |
<分程序> | ::=[<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句> |
<常量说明部分> | ::=const<常量定义>{,<常量定义>}; |
<常量定义> | ::=<标识符>=<无符号整数> |
<变量说明部分> | ::=var<标识符>{,<标识符>}; |
<过程说明部分> | ::=<过程首部><分程序>{;<过程说明部分>}; |
<过程首部> | ::=procedure<标识符>; |
<语句> | ::=<赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空语句> |
<赋值语句> | ::=<标识符>:=<表达式> |
<复合语句> | ::=begin<语句>{;<语句>}end |
<空语句> | ::=ɛ |
<条件> | ::=<表达式><关系运算符><表达式>|odd<表达式> |
<表达式> | ::=[+|-]<项>{<加减运算符><项>} |
<项> | ::=<因子>{<乘除运算符><因子>} |
<因子> | ::=<标识符>|<无符号整数>|’(’<表达式>’)’ |
<加减运算符> | ::=+|- |
<乘除运算符> | ::=*|/ |
<关系运算符> | ::==|#|<|<=|>|>= |
<条件语句> | ::=if<条件>then<语句> |
<过程调用语句> | ::=call<标识符> |
<当型循环语句> | ::=while<条件>do<语句> |
<读语句> | ::=read’(’<标识符>{,<标识符>}’)’ |
<写语句> | ::=write’(’<表达式>{,<表达式>}’)’ |
注:PL/0中变量的数据类型只有整型。
二、算法设计方法
语法分析常用的程序设计方法可分为自顶向下分析和自底向上分析两类。
其中,自顶向下分析包括预测分析法和递归下降分析法,自底向上分析包括LR分析法和算符优先分析法。此外,还可以通过语法分析器自动生成工具(例如yacc)编写语法分析程序。
由于本词法分析使用了每一行进行单词类型判断,故只可简单使用预测分析法和递归下降分析法的相结合。
在具体实现中,该语法分析是基于文件中的一行来判断是否语法出现问题,若出现问题,则输出语法错误以及所在文件中的行数;若整个文件皆无语法错误,则输出“语法正确”字样。而这样是通过每一行皆使用这7个函数进行语法分析判断,使用数组tmp[10]来进行存储每一行的语法对错,并将结果返回给上一个调用函数,直至分析完整个文件。
目前,仍存在着的不足有,条件语句中,表达式只可为标识符-运算符-标识符,并且亦无法判断begin-end语句的对错。需要继续加强改进。
三、结果展示
1、出现语法错误
(1)输入
const a := 10;
var b, c d;
//单行注释
/*
* 多行注释
*/
procedure procedure fun1;
if a <= 10
begin
c = b + a
end;
begin
read(b;
while b # 0
begin
call fun1;
write 2 * c);
read(b);
end;
end.
(2)输出
2、语法正确
(1)输入
const a = 10;
var b, c;
procedure p;
if a <= 10 then
begin
c := b + a;
end;
begin
read(b);
while b # 0 do
begin
call p;
write(2 * c);
read(b);
end;
end.
(2)输出
四、重要代码
(1)常数说明部分-常数定义-const
//常数说明部分-常数定义-const
int Is_constant(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"const")==0){
int tmp=-1;
for(int i=1;i<bobo-1;i++){
if(bo[i][0]=='4'){
if(bo[i+1][0]=='2'&&strcmp(jiangbo[i+1],"=")==0){
if(bo[i+2][0]=='3'){
tmp=1;
}
}
}
if(tmp==1){
break;
}
}
if(strcmp(jiangbo[bobo-1],";")!=0){
tmp=-1;
}
if(tmp!=1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(2)变量说明部分-var
//变量说明部分-var
int Is_varialble(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
//printf("-%c\n",bo[0][0]);
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"var")==0){
//printf("11111\n");
int tmp=-1;
for(int i=1;i<bobo;i++){
//printf("22222\n");
//printf("%d=%c\n",i,bo[i][0]);
if(bo[i][0]=='4'){
if(i==bo_n-1){//只有一个标识符或者最后一个标识符,对上面的补充
tmp=1;
break;
}
tmp=-1;
//printf("33333\n");
if(bobo-2==1){ //表示只有一个标识符
tmp=1;
break;
}else{
//printf("44444\n");
//printf("%d,%c\n",i,jiangbo[i][0]);
//printf("=%d=\n",strcmp(jiangbo[i+1],","));
if(strcmp(jiangbo[i+1],",")==0&&bo[i+2][0]=='4'){
//tmp=1;printf("====1====\n");
//break;
//printf("5555555\n");
}else{
tmp=-1;
//if(bo[bo_n-1][0]=='4'&&jiangbo[bo_n-2][0]==','){ //只有一个标识符或者最后一个标识符,对上面的补充
// tmp=1;
//}
break;
}
}
}
//printf("\n%d,%d,%c\n",i-1,tmp,jiangbo[i][0]);
}
if(strcmp(jiangbo[bobo-1],";")!=0){
tmp=-1;
}
if(tmp!=1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(3)过程首部
//过程首部
int Is_head(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"procedure")==0){
int tmp=-1;
for(int i=0;i<bobo;i++){
if(bo[i][0]=='0'&&strcmp(jiangbo[i],"procedure")==0){
//printf("--1--\n");
//for(int i=0;i<=bo_n;i++){
// printf("%c",bo[i][0]);
//}
if(bo[i+1][0]=='4'){
// printf("--2--\n");
tmp=1;
}else{
break;
}
}
}
if(strcmp(jiangbo[bobo-1],";")!=0){
tmp=-1;
}
if(tmp==-1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(4)条件语句-条件(+语句)-表达式+运算符+表达式
//条件语句-条件(+语句)-表达式+运算符+表达式
int Is_if(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"if")==0){
int tmp=-1;
for(int i=1;i<bobo;i++){
if(bo[i][0]=='0'&&strcmp(jiangbo[i],"then")==0){
tmp=1; //存在if-then
}
}
if(tmp==1){ //看条件是否符合规范
//表达式往小了说是标识符,往大了说是一个式子
//先弄标识符的
for(int i=1;i<bobo;i++){
if((bo[i][0]=='4'||bo[i][0]=='3')&&bo[i+1][0]=='2'&&(bo[i+2][0]=='4'||bo[i+2][0]=='3')){
tmp=1;//printf("1111111111\n");
break;
}
else{
tmp=-1;
}
}
if(tmp!=1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}else{
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(5)赋值语句-标识符+:=+表达式-->标识符+:=+标识符+运算符+标识符
//赋值语句-标识符+:=+表达式-->标识符+:=+标识符+运算符+标识符
int Is_boundary(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
int tmp=-1;
if(bo[0][0]=='4'){
for(int i=1;i<bobo;i++){
if(bo[i][0]=='2'&&strcmp(jiangbo[i],":=")==0){//赋值语句
if((bo[i+1][0]=='4'||bo[i+1][0]=='3')&&bo[i+2][0]=='2'&&(bo[i+3][0]=='4'||bo[i+3][0]=='3')){
tmp=1;
break;
}
}else if(bo[i][0]=='2'&&strcmp(jiangbo[i],"=")==0){//常量定义
if(bo[i][0]=='3'){
tmp=1;
break;
}
}
}
//printf("=%d,%s\n",bo_n,jiangbo[bobo-1]);
//printf("1.%d\n",tmp);
if(strcmp(jiangbo[bobo-1],";")!=0){
tmp=-1;
}
//printf("2.%d\n",tmp);
if(tmp==-1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(6)读、写语句 读-(+标识符+)
//读、写语句 读-(+标识符+)
int Is_RW(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"read")==0){
int x=-1;int y=-1;
for(int i=1;i<bobo;i++){
if(strcmp(jiangbo[i],"(")==0){
x=i;
}
if(strcmp(jiangbo[i],")")==0){
y=i;
}
}
for(int i=x+1;i<y;i++){
if(bo[i][0]!='4'){
if(strcmp(jiangbo[i],",")!=0){
printf("(语法错误,行号:%d)\n",line_num);
break;
return 1;
}
}
}
if(x==-1||y==-1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
if(strcmp(jiangbo[bobo-1],";")!=0){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}else if(bo[0][0]=='0'&&strcmp(jiangbo[0],"write")==0){
int tmp=-1;
int x=-1;int y=-1;
for(int i=1;i<bobo;i++){
if(strcmp(jiangbo[i],"(")==0){
x=i;
}
if(strcmp(jiangbo[i],")")==0){
y=i;
}
}
for(int i=x+1;i<y;i++){ //表达式
if((bo[i][0]=='4'||bo[i][0]=='3')
&&bo[i+1][0]=='2'
&&(bo[i+2][0]=='4'||bo[i+2][0]=='3')){
tmp=1;
break;
}
}
if(x==-1||y==-1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
if(tmp==-1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
if(strcmp(jiangbo[bobo-1],";")!=0){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
(7)循环语句 while-条件-do 条件-表达式+运算符+表达式
//循环语句 while-条件-do 条件-表达式+运算符+表达式
int Is_while(char jiangbo[][10],int bobo,int line_num,char bo[][1],int bo_n){
if(bo[0][0]=='0'&&strcmp(jiangbo[0],"while")==0){
int tmp=-1;
for(int i=1;i<bobo;i++){
if(bo[i][0]=='0'&&strcmp(jiangbo[i],"do")==0){
tmp=1;
}
}
if(tmp==1){ //看条件是否符合规范
//表达式往小了说是标识符,往大了说是一个式子
//先弄标识符的
for(int i=1;i<bobo;i++){
if((bo[i][0]=='4'||bo[i][0]=='3')&&bo[i+1][0]=='2'&&(bo[i+2][0]=='4'||bo[i+2][0]=='3')){
tmp=1;
break;
}
else{
tmp=-1;
}
}
if(tmp!=1){
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}else{
//printf("-----------\n");
printf("(语法错误,行号:%d)\n",line_num);
return 1;
}
}
return 0;
}
补充说明:
其中found函数在词法分析中已展示出了代码,其中包括了jiangbo数组、bo数组等(自己随便命名,并不规范)。
剩余代码若展示过于多,故不予展示。
需要私聊。