?? 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 + -