用C语言编写一个PL/0词法分析器,为语法语义分析提供单词,使之能把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析,并把分析结果(基本字,运算符,标识符,常数以及界符)输出。
PL/0的词法分析程序GETSYM是一个独立的过程,其功能是为语法语义分析提供单词,把输入的字符串形式的源程序分割成一个个单词符号传递给语法语义分析。其主要任务为:1、滤空格;2、识别基本字;3、识别标识符;4、拼数;5、拼复合词;6、输出源程序。
PL/0编译程序一般设置3个全程变量:
SYM:存放每个单词的类别,用内部编码形式表示;
ID: 存放用户所定义的标识别符的值;
NUM:存放用户定义的数。
PL/0语言的单词的种类分成基本字(亦称保留字)、运算符、标识符、常数、界符5个大类,以下是针对这5类单词的一种EBNF描叙:
<无符号整数>::=<数字>{<数字>}
<标识符> ::=<字母>{<字母>|<数字>}
<字母> ::=a|b|……|X|Y|Z
<数字> ::=0|1|2|……|8|9
<保留字> ::= const | var | procedure | begin | end | odd | if | then | call | while | do | read |write
<运算符> ::= + | - | * | / | = | # | < | <= | > | >= | :=
<界符> ::= ( | ) | , | ; | .
保留字、运算符和界符这几类在实践中一般将每个单词符号设计为独立的词法单元,即每个单词符号拥有独立的类别。这样PL/0编译程序所设计的单词符号就对应到31个单词种别,这31个单词种别采用枚举类型表示。
例:PL/0程序片段:
const a<=10;
var b,c;
procedure p;
begin
……
end.
**************************************************************
词法分析器运行结果:
const 基本字/constsym
a 标识符
<= 运算符/小于等于号
10 常数
; 界符/分号
var 基本字/varsym
b 标识符
c 标识符
; 界符/分号
……
end 基本字/endsym
. 界符/结束符
//Analysis.h
#ifndef ANALYSIS_H_INCLUDED
#define ANALYSIS_H_INCLUDED
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<cstring>
char stayWord[13][15]={"const","var","procedure","begin","end","odd","if","then","call"," while","do","read","write"};
Char enumStayWord[13][15]={"constsym","varsym","proceduresym","beginsym","endsym",
"oddsym"," ifsym","thensym","callsym"," whilesym","dosym","readsym","writesym"};
bool isNumber(char ch);
bool isCase(char ch);
bool isCaculationSymbol(char ch);
bool isBandSymbol(char ch);
int isStayWord(char *str);
void getInputStreamFromFile(char *fileName, char *str);
void calulationString(char *str);
void bandString(char *str);
void Analysis(char *InputFileName, char *str, char *outputFileName);
#endif // ANALYSIS_H_INCLUDED
//main.cpp
#include <iostream>
#include"Analysis.h"
using namespace std;
int main()
{
char str[100000],input[15]="input.txt",output[15]="output.txt";
Analysis(input, str,output);
return 0;
}
bool isNumber(char ch){
//判断该位置的符号是否是数字
if(ch>='0'&&ch<='9') return true;
else return false;
}
bool isCase(char ch){
//判断该位置的符号是否是字母
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
return true;
else return false;
}
bool isCaculationSymbol(char ch){
判断该位置是否是运算符的基本单位
if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='>'||ch=='<'||ch=='='||ch=='#'||ch==’:’)
return true;
else return false;
}
bool isBandSymbol(char ch){
//判断该位置是否是边界符
if(ch=='('||ch==')'||ch==','||ch==';'||ch=='.')
return true;
else return false;
}
int isStayWord(char *str){
//判断该位置是否是保留字
int aa;
for(aa=0;aa<13;aa++){ if(!strcmp(str,stayWord[aa])) break;}
return aa;
}
void getInputStreamFromFile(char *fileName, char *str){
//从文件中获取将被分析的代码段
char ch;
int i=0;
FILE *fp;
fp=fopen(fileName,"r");
while((ch=fgetc(fp))!=EOF){
if(ch!='\n'&&(int)ch==9) str[i++]=ch;
//去掉换行、Tab
else str[i++]=' ';
}
str[i]='\0';
fclose(fp);
}
void calulationString(char *str){//连续的运算符处理
int aa=strlen(str);
for(int i=0;i<aa;i++){
if(str[i]=='+'){ printf("+\t\t运算符/加号\n");}
else if(str[i]=='-'){ printf("-\t\t运算符/减号\n");}
else if(str[i]=='*'){ printf("*\t\t运算符/乘号\n");}
else if(str[i]=='/'){ printf("/\t\t运算符/除号\n");}
else if(str[i]=='='){ printf("=\t\t逻辑运算符/等于号\n");}
else if(str[i]==':'){
//如果是i位置是 ’:’,若i+1位置不是 ‘=’,那么该符号非法
if(i+1<aa&&str[i+1]=='='){ printf(":=\t\t运算符/赋值号\n");i++;}
else{ printf("%c\t\t非法字符!!!\n",str[i]); }
}
else if(str[i]=='#'){ printf("#\t\t运算符/不等于号\n");}
else if(str[i]=='>'){
if(i+1<aa&&str[i+1]=='='){ printf(">=\t\t逻辑运算符/大于等于号\n"); i++;}
else{ printf(">\t\t逻辑运算符/大于号\n");}
}
else if(str[i]=='<'){
if(i+1<aa&&str[i+1]=='='){ printf("<=\t\t逻辑运算符/小于等于号\n"); i++;}
else{ printf("<\t\t逻辑运算符/小于号\n");}
}
else ;
}
}
void bandString(char *str){
//获取一段连续的边界符号后,依次将其分解开
int i,k=strlen(str);
for( i=0;i<k;i++){
switch(str[i]){
case '(': printf("(\t\t界符/左刮胡\n"); break;
case ')': printf(")\t\t界符/右刮胡\n"); break;
case ',': printf(",\t\t界符/逗号\n"); break;
case ';': printf(";\t\t界符/分号\n"); break;
case '.': printf(".\t\t界符/结束号\n"); break;
default: break;
}
}
}
void Analysis(char *InputFileName, char *str, char *outputFileName){
//词法分析函数
getInputStreamFromFile(InputFileName,str);
int lenth=strlen(str),i,j,bb,cc,dd,ee;
char tempStr[100],smallStr[100];
//每种类型的符号 其连续的个数是有限的……
i=0;
while(i<lenth){
//进入大循环……
j=0;
while(str[i]==' '&&i<lenth) { i++; }//去掉开头的空格符号
while(str[i]!=' '&&i<lenth) { tempStr[j++]=str[i++]; }//截取一段代码字符串
tempStr[j]='\0';
bb=strlen(tempStr);
cc=0;
while(cc<bb){
if(isCase(tempStr[cc])){//如果以字母开头,则执行…… while(( !isCaculationSymbol(tempStr[cc]))&&(!isBandSymbol(tempStr[cc]))&&cc<bb){
smallStr[dd++]=tempStr[cc++]; }//截取全是字母的一段
smallStr[dd]='\0';
ee=isStayWord(smallStr);
if(ee<13) {//如果字母串是否是保留字,则……
printf("%s\t\t基本字/%s\n",stayWord[ee],enumStayWord[ee]);
strcpy(smallStr,"");
dd=0;}
else{否则……
printf("%s\t\t标识符\n",smallStr);
strcpy(smallStr,"");
dd=0; }
//所截取的短串没处理完,回到 while(cc<bb){} 的开始,有当前所指向位置的
//符号作为判断条件(即什么样的开始决定什么样的处理)……
}
else if(isNumber(tempStr[cc])){//如果以数字开头,则执行……
while(isNumber(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
printf("%s\t\t常数\n",smallStr);
strcpy(smallStr,"");
dd=0;
}else if(isCaculationSymbol(tempStr[cc])){//如果以运算符开头,则执行……
while(isCaculationSymbol(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
calulationString(smallStr); //连续的运算符处理……
strcpy(smallStr,"");
dd=0;
}else if(isBandSymbol(tempStr[cc])){//如果以边界符开头,则执行……
while(isBandSymbol(tempStr[cc])&&cc<bb){
smallStr[dd++]=tempStr[cc++];}
smallStr[dd]='\0';
bandString(smallStr); //l连续的界限符的处理……
strcpy(smallStr,"");
dd=0;
}else{
printf("无法识别的符号……\n");cc++;
}
}
strcpy(tempStr,"");
}
fclose(stdout);
}
输入文件:
const a<=10;
var b,c;
procedure p;
begin:
var aa:=100;
bb#cc;
dfghfhg<hj=gh;
end.
运行结果:
, 界符/逗号
c 标识符
; 界符/分号
procedure 基本字/proceduresym
p 标识符
; 界符/分号
begin 基本字/beginsym
: 非法字符!!!
var 基本字/varsym
aa 标识符
:= 运算符/赋值号
100 常数
; 界符/分号
bb 标识符
# 运算符/不等于号
cc 标识符
; 界符/分号
dfghfhg 标识符
< 逻辑运算符/小于号
hj 标识符
= 逻辑运算符/等于号
gh 标识符
; 界符/分号
end 基本字/endsym
. 界符/结束号
Process returned 0 (0x0) execution time : 0.172 s
Press any key to continue.
来源:https://www.cnblogs.com/jaychen/p/6738129.html