實驗二 詞法分析器的實現
阿新 • • 發佈:2019-02-02
這裡定義了一個程式語言稱作C-M i n u s (或簡稱為C-),這是一種適合編譯器設計方案的
語言,它比T I N Y語言更復雜,包括函式和陣列。本質上它是C的一個子集,但省去了一些重要
的部分,因此得名。這個附錄由5小節組成。首先,我們列出了語言慣用的詞法,包括語言標
記的描述。其次,給出了每個語言構造的B N F描述,同時還有相關語義的英語描述。在A . 3節,
給出了C-的兩個示例程式。再者,描述了C-的一個Tiny Machine執行時環境。最後一節描述
了一些使用C-和T M的程式設計設計方案,適合於一個編譯器教程。
C-慣用的詞法
- 下面是語言的關鍵字:
else if int return void while
所有的關鍵字都是保留字,並且必須是小寫。
2. 下面是專用符號:
+ - * / < <= > >= == != = ; , ( ) [ ] { } /* */
- 其他標記是I D和N U M,通過下列正則表示式定義:
ID = letter letter*
NUM = digit digit*
letter = a|..|z|A|..|Z
digit = 0|..|9
小寫和大寫字母是有區別的。
4. 空格由空白、換行符和製表符組成。空格通常被忽略,除了它必須分開I D、N U M關鍵字。
5. 註釋用通常的C語言符號/ * … * /圍起來。註釋可以放在任何空白出現的位置(即註釋不能放在標記內)上,且可以超過一行。註釋不能巢狀。
詞法分析器的DFA:
原始碼實現:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<iostream>
#define intputfile "test.c"
#define outputfile "target.c"
typedef int sInt;
using namespace std;
FILE *source;
FILE *target;
long beginp[5]; //記錄檔案指標的開始位置
long endp[5]; //記錄檔案指標的結束位置
char idStr[80]=""; //儲存識別符號中間值
int state[5]={0}; //不同記號型別的狀態
char unaryOP[16]={'+','-','*','/','<','>','=',';',',','(',')','[',']','{','}','!'}; //儲存一元運算子
char *p[6]={"if","else","int","return","void","while"}; //系統保留字
char *strToken[7]={"ID","NUM","OP","FUCN","RESW","ERROR","COM"};
typedef enum {ID,NUM,OP,FUCN,RESW,ERROR,COM}tokenType; //記號型別
void clearState()
{
memset(state,0,sizeof(state));
memset(idStr,'\0',sizeof(idStr));
memset(beginp,0,sizeof(beginp));
memset(endp,0,sizeof(endp));
}
void strPrintf(long begin,long end,tokenType t)
{
bool isComment = false;
int k;
char s[200]="";
long len=end-begin;
fseek(source,-len,1);//檔案指標回退
for(int i=0;i<len;i++) s[i]=fgetc(source);
switch(t)
{
case ID:
k=0;break;
case NUM:
k=1;break;
case OP:
k=2;break;
case FUCN:
k=3;break;
case RESW:
k=4;break;
case ERROR:
k=5;break;
case COM:
k=6;
isComment=true;break;
default:cout<<"輸入有誤!\n";
}
//printf("%s\n",s);
if(isComment)
{
fprintf(target,"%s\n",s);
printf("%s\n",s);
}
else
{
fprintf(target,"<%s,%s>\n",strToken[k],s);
printf("<%s,%s>\n",strToken[k],s);
}
}
void unaryPrintf(char s,tokenType t)
{
int k;
switch(t)
{
case ID:
k=0;break;
case NUM:
k=1;break;
case OP:
k=2;break;
case FUCN:
k=3;break;
case RESW:
k=4;break;
case ERROR:
k=5;break;
case COM:
k=6;break;
default:cout<<"輸入有誤!\n";
}
//cout<<strToken[k]<<endl;
fprintf(target,"<%s,%c>\n",strToken[k],s);
printf("<%s,%c>\n",strToken[k],s);
}
bool isOperator(char ch)
{
for(int i=0;i<16;i++)
{
if( ch==unaryOP[i] )
return true;
}
return false;
}
void number(char ch,int i)
{
beginp[i]=ftell(source)-1;
while(!feof(source))
{
if(ch>='0' && ch<='9')
{
state[i]=1;
endp[i]=ftell(source);
}else
{
fseek(source,-1L,1); //回退,讀到了下一個字元
strPrintf(beginp[i],endp[i],NUM);
return;
}
ch = fgetc(source);
}
}
void comment(char ch,int i)
{
bool isexit = false; //當不是註釋時用於跳出迴圈
while(!feof(source))
{
switch(state[i])
{
case 0:
if(ch=='/')
{
state[i]=1;
beginp[i]=ftell(source)-1;
}
break;
case 1:
if(ch=='*')
{
state[i]=2;
}
else
{
isexit = true; //說明不是註釋,請求退出
unaryPrintf('/',OP);
fseek(source,-1L,1); //回退一個位元組,因為向後判斷移了一位
}
break;
case 2:
if(ch=='*')
{
state[i]=3;
}else
{
state[i]=2;
}
break;
case 3:
if(ch=='/')
{
state[i]=4;
endp[i]= ftell(source);
strPrintf( beginp[i],endp[i],COM);
isexit = true; //back
}else state[i]=2;
break;
}
if(isexit) return;//back;
ch = fgetc(source);
}
}
void myOperator(char ch,int i)
{
bool isReturn = false;
while(!feof(source))
{
if(ch=='+' ||ch=='-'||ch=='*'||ch==';'||ch==','||ch=='('||ch==')'||ch=='['||ch==']'||ch=='{'||ch=='}')
{
state[i]=1;
unaryPrintf(ch,OP);
isReturn = true;
}else
{
switch(state[i])
{
case 0:
beginp[i]=ftell(source)-1;
switch(ch)
{
case '<':
state[i]=2;
break;
case '>':
state[i]=4;
break;
case '=':
state[i]=6;
break;
case '!':
state[i]=8;
break;
default:cout<<"data error!\n";
}
break;
case 2:
if(ch=='=')
{
state[i]=3;
endp[i]=ftell(source);
strPrintf(beginp[i],endp[i],OP);
isReturn = true;
}else
{
//屬於一元操作符
state[i]=2;
fseek(source,-1L,1);//回退一個字元
unaryPrintf('<',OP);
isReturn = true;
}
break;
case 4:
if(ch=='=')
{
state[i]=5;
endp[i]=ftell(source);
strPrintf(beginp[i],endp[i],OP);
isReturn = true;
}else
{
//屬於一元操作符
state[i]=4;
fseek(source,-1L,1);//回退一個字元
unaryPrintf('>',OP);
isReturn = true;
}
break;
case 6:
if(ch=='=')
{
state[i]=7;
endp[i]=ftell(source);
strPrintf(beginp[i],endp[i],OP);
isReturn = true;
}else
{
//屬於一元操作符
state[i]=6;
fseek(source,-1L,1);//回退一個字元
unaryPrintf('=',OP);
isReturn = true;
}
break;
case 8:
if(ch=='=')
{
state[i]=9;
endp[i]=ftell(source);
strPrintf(beginp[i],endp[i],OP);
isReturn = true;
}else
{
//屬於一元操作符
state[i]=8;
fseek(source,-1L,1);//回退一個字元
unaryPrintf('!',OP);
isReturn = true;
}
break;
default:cout<<"data error!\n";
}
}
if(isReturn) return;
ch = fgetc(source);
}
}
bool isLiter(char ch)
{
if((ch>='A' && ch<='Z')||( ch>= 'a' && ch<='z') || ch=='_')
{
return true;
}else
return false;
}
bool isResw(char *s)
{
for(int i=0;i<6;i++)
{
if( strcmp(s,p[i])==0 )
return true;
}
return false;
}
void identifier(char ch,int i)
{
beginp[i] = ftell(source)-1;
long len =0;
bool isQuit = false;
bool isFucn = false;
while(!feof(source))
{
if( isLiter(ch) )
{
state[i]=1;
endp[i]=ftell(source);
}
else
{
long enter = 1; //
isQuit = true;
if(ch=='\n') enter = 2;
fseek(source,-enter,1); //回退一或2個字元
//cout<<"pos="<<ftell(source)<<endl;
//printf("%d %d\n",beginp[i],endp[i]);
len = endp[i]-beginp[i];
fseek(source,-len,1);
//cout<<"pos="<<ftell(source)<<endl;
for(int j=0;j<len;j++)
{
idStr[j] = fgetc(source);
}
if( isResw(idStr) ) //如果是保留字,就儲存退出
{
strPrintf(beginp[i],endp[i],RESW);
}else
{
char temps;
long cout=1;
temps = fgetc(source);
while(!feof(source))
{
if(temps==' ' ||temps=='\n' || temps=='\t')
{
; //jump it;
}else
{
if(temps=='(')
{
isFucn = true; //表明是函式名
break;
}
else
{
isFucn = false; //不是函式名
break;
}
}
temps = fgetc(source);
cout++;
}
fseek(source,-cout,1); //回退檔案指標
//printf("back=%ld\n",ftell(source));
if(isFucn)
{
strPrintf(beginp[i],endp[i],FUCN);
}else
{
strPrintf(beginp[i],endp[i],ID);
}
}
}
if(isQuit) return;
ch = fgetc(source);
}
}
void Scanner()
{
char ch=fgetc(source);
while( !feof(source) )
{
if(ch==' ' ||ch=='\n' || ch=='\t')
{
;//nothing jump it!
}else if(ch=='/')
{
comment(ch,0);
clearState(); //清楚狀態資訊
}else if(ch>='0'&&ch<='9')
{
number(ch,1); //處理數字
clearState();
}else if( isOperator(ch) )
{
myOperator(ch,2); //處理操作符
clearState();
}else if( isLiter(ch) )
{
identifier(ch,3); //處理標誌符
clearState();
}
else
{
unaryPrintf(ch,ERROR);
}
ch = fgetc(source);
}
}
bool read(){
if(((source=fopen(intputfile,"r"))==NULL)||((target=fopen(outputfile,"w"))==NULL))
return 0;
return 1;
}
int main()
{
if(!read()){
printf("檔案開啟失敗!");
exit(0);
}
Scanner(); //開始掃描檔案 entrance
fclose(source);
fclose(target);
return 0;
}
輸入檔案:
int gcd (int u, int v)
{
if (v == 0)
return u ;
else
return gcd(v,u-u/v*v);
/* u-u/v*v == u mod v */
}
void main(void)
{
int x;int y;
x = input();
y = input();
output(gcd(x,y));
}
輸出檔案:
<RESW,int>
<FUCN,gcd>
<OP,(>
<RESW,int>
<ID,u>
<OP,,>
<RESW,int>
<ID,v>
<OP,)>
<OP,{>
<RESW,if>
<OP,(>
<ID,v>
<OP,==>
<NUM,0>
<OP,)>
<RESW,return>
<ID,u>
<OP,;>
<RESW,else>
<RESW,return>
<FUCN,gcd>
<OP,(>
<ID,v>
<OP,,>
<ID,u>
<OP,->
<ID,u>
<OP,/>
<ID,v>
<OP,*>
<ID,v>
<OP,)>
<OP,;>
/* u-u/v*v == u mod v */
<OP,}>
<RESW,void>
<FUCN,main>
<OP,(>
<RESW,void>
<OP,)>
<OP,{>
<RESW,int>
<ID,x>
<OP,;>
<RESW,int>
<ID,y>
<OP,;>
<ID,x>
<OP,=>
<FUCN,input>
<OP,(>
<OP,)>
<OP,;>
<ID,y>
<OP,=>
<FUCN,input>
<OP,(>
<OP,)>
<OP,;>
<FUCN,output>
<OP,(>
<FUCN,gcd>
<OP,(>
<ID,x>
<OP,,>
<ID,y>
<OP,)>
<OP,)>
<OP,;>
<OP,}>