📄 pl_0_word.cpp
字号:
/*
南京航空航天大学 计算机系 吴潇
欢迎与我交流, QQ: 706883830
*/
#include<stdio.h>
#include<memory.h>
#include<conio.h>
//#include<string.h>
//存储单词符号的结构
struct plword{
int sort;//种别
int value;//属性
};
int line_count;//行数
//全局变量
// 输入处理:从指定的文件(TXT)中获取一定长度的内容,存入指定的内存中
// 在读取内容的过程中,不隔断单词
// skip为跳过的字符数 max 为输入缓冲区最大的字符个数
// 返回已读取的字符个数 错误则返回-1
int GetText(char* filename, char* buffer,unsigned int skip,int max)
{
FILE *fp=NULL;
int i=0;
char c;
if(NULL==filename || NULL==buffer)//错误检测
{
printf("Error\n未指定正确的文件名或缓冲区指针\n");
return(0);
}
if(max<2)
{
printf("缓冲区太小!\n");
return(-1);
}
fp=fopen(filename,"r");//只读方式打开文件
if(NULL==fp)
{
printf("Error\n文件%s不存在,请创建该文件并将PL/0语言的源代码存入该文件\n",filename);
return(0);
}
while(1!=feof(fp))//跳过一定字符,然后读取
{
c=fgetc(fp);
if(skip>0)
{
//文件已读完,skip>0
if(feof(fp))
{
printf("字符长度错误!\n");
return(-1);
}
skip--;
}
else
{
//feof返回1时,本次读取的字符无效
if(feof(fp))
break;
buffer[i]=c;
if(i==max-2)//(预留最后一个单元存储 \0 )
{
//如果读进的不是空白字符,则退还字符,直到读取的为空白字符为止
while(buffer[i]!=' ' &&
buffer[i]!='\t' &&
buffer[i]!='\n' &&
buffer[i]!='\0')
i--;
break;
}
i++;
}
}
buffer[i]='\0';
fclose(fp);
return(i);
}
//预处理:从输入缓冲区中取数据(不隔断单词),预处理后存入指定的扫描缓冲区
// 在从缓冲区中取数据的时候,head 总是指向非空白字符;tail总是指向空白字符或者\0(head最大为 maxlength
// tail最大可取 maxlength+1)
//maxlength 为 input 中字符串的长度 (不包括\0) //input[maxlength]为最后一个字符——不是 \0
//reset 为1,则表明输入缓冲区已重置
//错误返回 -1 全部完成返回 0 , 输入缓冲区未扫描完则返回 1
int Prepare(char* input, char* buffer,int maxlength,int reset)
{
static int head=0;//head, tail 指示 buffer 中的位置
static int tail=120;//head 和 tail 指示输入缓冲区
int head0,tail0; //用于检测
int i=head;
if(NULL==input || NULL==buffer)//错误检测
{
printf("Error\n未指定正确的缓冲区指针\n");
return(0);
}
if(reset!=0)//输入缓冲区被更新
{
head=0;
//tail总是指向一个空白字符
if(maxlength<120)
tail=maxlength;
else
tail=120;
}
//检查是否截断了末尾的单词,并使tail指向空白字符
while(input[tail]!=' '&&
input[tail]!='\t'&&
input[tail]!='\n' &&
input[tail]!='\0')
{
tail--;
if(tail<=head)
{
printf("Error: 未知错误 Prepare 函数\n");
return(0);
}
}
head0=head;
tail0=tail;
memcpy(buffer,input+head,tail-head);
buffer[tail-head]='\0';
//合并界符
for(i=0;i<tail-head;i++)
{
if(buffer[i]==' '||
buffer[i]=='\t')// \n作为特殊的界符(涉及到行号,留到后面处理)
{
buffer[i]=' ';
if(buffer[i+1]==' '||
buffer[i+1]=='\t')
{
for(int j=i;j<tail-head;j++)
buffer[j]=buffer[j+1];
i--;
}
}
}
//确定下次的head,tail,使head总不指向空白字符
head=tail;
if('\0'==input[head])
return(0);
while(input[head]==' '||
input[head]=='\t')
{
head++;
if(head>maxlength)
{
printf("未知错误!\n");
return(-1);
}
}
if(head>maxlength)
return(-1);
else
{
tail=head+120;
if(tail>maxlength)
tail=maxlength;
}
return(1);
}
//标识符 状态转换器
int id_detect(int last_status, char inpt)
{
int status=-1;// -1 表示无法识别 2* 表示分析成功
switch(last_status)
{
case 0:
if(inpt==' ')
status=0;
if(inpt>=97 &&
inpt<=122)
status=1;
if(inpt>=65 &&
inpt<=90)
status=1;
break;
case 1:
if(inpt>=97 &&
inpt<=122)
status=1;
else if(inpt>=65 &&
inpt<=90)
status=1;
else if(inpt>=48 &&
inpt<=57)
status=1;
else
status=2;
break;
default:
break;
}
return(status);
}
//常数识别 状态转换器
int integer_detect(int last_status, char inpt)
{
int status=-1;// -1 表示无法识别 2* 表示分析成功
switch(last_status)
{
case 0:
if(inpt==' ')
status=0;
if(inpt>=48 &&
inpt<=57)
status=1;
break;
case 1:
if(inpt>=48 &&
inpt<=57)
status=1;
else
{
status=2;
//常数识别中出现字母
if(inpt>=97 && inpt<=122)
status=3;
if(inpt>=65 && inpt<=90)
status=3;
}
break;
default:
break;
}
return(status);
}
//单字符算符识别 状态转换器 (\n也被当作算符处理)
int ssign_detect(int last_status, char inpt)
{
int status=-1;// -1 表示无法识别 1 表示分析成功
switch(last_status)
{
case 0:
if(inpt==' ')
status=0;
if(inpt==';' ||
inpt==',' ||
inpt=='(' ||
inpt==')' ||
inpt=='+' ||
inpt=='-' ||
inpt=='=' ||
inpt=='*' ||
inpt=='/')
status=1;
if(inpt=='\n')
status=1;
break;
case 1:
break;
default:
break;
}
return(status);
}
//双字符算符识别 状态转换器
int dsign_detect(int last_status, char inpt)
{
int status=-1;//-1表示无法识别 6* 表示已识别
switch(last_status)
{
case 0:
if(inpt==' ')
status=0;
else if(inpt==':')
status=1;
else if(inpt=='<')
status=3;
else if(inpt=='>')
status=7;
break;
case 1:
if(inpt=='=')
status=2;
break;
case 2:
status=6;
break;
case 3:
if(inpt=='>')
status=4;
else if(inpt=='=')
status=5;
else
status=6;
break;
case 4:
case 5:
status=6;
break;
case 7:
if(inpt=='=')
status=8;
else
status=6;
break;
case 8:
status=6;
break;
}
return(status);
}
int strlen(char* s)
{
int count=0;
if(s==NULL)
return(0);
while(*(s+count)!='\0')
count++;
return(count);
}
//分析器:返回值 0 表示当前扫描缓冲区未完成,1 表示完成(本次单词无效),-1 表示错误
//通过 buffer 末尾的 \0 判断结束
//该模块每当被调用一次,返回一个单词符号的种别码和属性码
int Analysis(char* buffer, int &sort, int &value,int reset)
{
static int head=0;//【下标】每次调用承接上次的结果
int offset=0;
int status;
char keywords[][10]={"program","const","var","procedure","begin",
"end","if","then","else","while","call",
"read","write","odd","do"};
if(NULL==buffer)
{
printf("Error:未指定正确的缓冲区地址!\n");
return(-1);
}
if(reset!=0)//已经更新了扫描缓冲区
head=0;
if(buffer[head]=='\0')
return(1);//扫描缓冲区已全部分析,需要新的输入串
///////////////检查是否是算符(单字符)/////////////
offset=0;
status=0;
while(status!=1 && status!=-1)
{
status=ssign_detect(0,buffer[head+offset]);
offset++;
}
offset--;
if(status==1)
{
int i=0;
switch(buffer[head+offset])
{
case ';':
i=1;
value=';';
break;
case ',':
i=2;
value=',';
break;
case '(':
i=3;
value='(';
break;
case ')':
i=4;
value=')';
break;
case '+':
i=5;
value='+';
break;
case '-':
i=6;
value='-';
break;
case '=':
i=7;
value='=';
break;
case '*':
i=8;
value='*';
break;
case '/':
i=9;
value='/';
break;
}
sort=99+i;
if(buffer[head+offset]=='\n')
{
line_count++;
sort=16;
}
// printf("(%d,%c)\t",sort,value);
offset++;
head+=offset;
while(buffer[head]==' ')
{
if(buffer[head]=='\0')
return(1);
head++;
}
return(0);
}
///////////检查是否是双字符算符/////////////
status=0;
offset=0;
while(status!=6 && status!=-1)
{
status=dsign_detect(status,buffer[head+offset]);
offset++;
}
offset--;
if(status==6)
{
int i=0;
switch(buffer[head])
{
case ':':
i=10;
value=':';
break;
case '<':
if(buffer[head+1]=='>')
{
i=11;
value='X';
}
else if(buffer[head+1]=='=')
{
i=12;
value='l';
}
else
{
i=13;
value='<';
}
break;
case '>':
if(buffer[head+1]=='=')
{
i=14;
value='m';
}
else
{
i=15;
value='>';
}
break;
}
sort=99+i;
// printf("(%d,%c)\t",sort,value);
head+=offset;
while(buffer[head]==' ')
{
if(buffer[head]=='\0')
return(1);
head++;
}
return(0);
}/////////////////////////////////////////////
/////////////检查是否是标志符/////////////
status=0;
offset=0;
while(status!=2 && status!=-1)
{
status=id_detect(status,buffer[head+offset]);
offset++;
}
offset--;
if(status==2)
{
int j;//关键字下标
for(j=0;j<15;j++)
{
int tag=0;
char *s=NULL;
int len;
s=keywords[j];
len=strlen(s);
for(int i=0;i<len;i++)
{
if(keywords[j][i]!=buffer[head+i])
{
tag=1;
break;
}
}
if(tag==1)
continue;
else if(tag==0)
{
sort=j+1;
value=0;
break;
}
}
if(j>=15)
{
sort=0;//未找到关键字,识别为标志符
value=0;
}
// printf("(%d,%d)\t",sort,value);
head+=offset;
while(buffer[head]==' ')
{
if(buffer[head]=='\0')
return(1);
head++;
}
return(0);
}
/////////////// 检查是否是常数 /////////////
status=0;
offset=0;
bool d=false;
while(status!=2 && status!=-1)
{
if(d==false)
status=integer_detect(status,buffer[head+offset]);
else
status=id_detect(status,buffer[head+offset]);
offset++;
if(status==3)//常数识别到了字母
{
d=true;
status=0;
offset--;
}
}
offset--;
if(status==2)
{
sort=200;//常数种别码
value=0;
if(d==true)
{
char temp[50];
memcpy(temp,buffer+head,offset);
temp[offset]='\0';
printf("第 %d 行错误,标志符(%s)不能以数字开头!\n",line_count,temp);
}
head+=offset;
while(buffer[head]==' ')
{
if(buffer[head]=='\0')
return(1);
head++;
}
return(0);
}
//未识别出
if(status==-1)
printf("第 %d 行包含非法字符:\t%c\n",line_count,buffer[head]);
head++;
return(0);
}
//输出处理: 返回0/1来表示状态 0 表示缓冲区未满, 1表示满
//参数 bufferlength 为剩余的缓冲区长度
// sort 种别码 max 缓冲区最大长度
int Output(struct plword* buffer,int sort, int value,int max, int bufferlength)
{
plword thisword;
char* filename="C:\\pl_0_tempfile.txt";
FILE* fp=NULL;
if(NULL==buffer)
{
printf("错误:参数指定不正确!\n");
return(1);
}
if(bufferlength<=0)
return(1);
// 缓冲区已满!
thisword.sort=sort;
thisword.value=value;
//把当前这个单词拷入缓冲区
memcpy(buffer+max-bufferlength,&thisword,sizeof(plword));
// 如果这是最后一个单元,则把整个缓冲区的内容输出到文件
if(1==bufferlength)
{
fp=fopen(filename,"ab");
fseek(fp,0,SEEK_END);
fwrite(buffer,sizeof(plword),max,fp);
fclose(fp);
return(1);
}
return(0);
}
void main()
{
char buffer[1024]; //输入缓冲区(从文件中直接提取的一部分字符)
int bufferlen;
char pre[121]; //扫描缓冲区(从输入缓冲区中选取的不割裂单词的字符串)
int sort,value; //种别码和属性
plword pbuffer[100];//单词符号缓冲区(经过扫描识别后的单词符号)
int tag1=0;
int tag2=0;
int tag3=0;
int flag=0,rst=0;//rst 表示输入缓冲区是否已更新 1 为是, 0 为否
int skip=0; //输入缓冲区中已读进的字符数
int count=sizeof(pbuffer)/sizeof(plword);//单词缓冲区中剩余空间
FILE* fp=NULL;
int read_count=0;
line_count=1;//行号赋初值
printf("开始进行词法分析:\n\n");
//如果临时文件存在,则清空原文件的内容
fp=fopen("C:\\pl_0_tempfile.txt","w");
fclose(fp);
do
{
//读取原始的字符串(保证不割裂单词)
bufferlen=GetText("C:\\code.txt",buffer,skip,1024);
if(bufferlen<=0)
break;
//记录已读取的字符数,以避免下次重复读取
skip+=bufferlen;
rst=1;
do{
//tag=1表示输入缓冲区未读完
tag1=Prepare(buffer,pre,bufferlen,rst);
rst=0;
flag=1;
//逐个识别出单词符号,将识别出的单词送Output处理
do{
tag2=Analysis(pre,sort,value,flag);
if(tag2==1)
break;
tag3=Output(pbuffer,sort,value,sizeof(pbuffer)/sizeof(plword),count);
read_count++;
//tag3=1表示pbuffer中已无剩余空间
if(tag3==1)
{
count=sizeof(pbuffer)/sizeof(plword);
count++;
}
count--;//按字节计算
flag=0;
}while(tag2==0);
}while(tag1==1);
}while(bufferlen>0);
//把剩余的单词符号输出
if(0==tag3)
{
char* filename="C:\\pl_0_tempfile.txt";
fp=fopen(filename,"a");
fseek(fp,0,SEEK_END);
fwrite(pbuffer,sizeof(plword),sizeof(pbuffer)/sizeof(plword)-count,fp);
fclose(fp);
}
printf("词法分析完成!\n\n共 %d 个单词,共 %d 行\n产生文件:C:\\pl_0_tempfile.txt\n",read_count,line_count);
printf("按任意键继续......\n");
getch();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -