1. 程式人生 > >實驗二 詞法分析器的實現

實驗二 詞法分析器的實現

這裡定義了一個程式語言稱作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-慣用的詞法

  1. 下面是語言的關鍵字:
 else
if int return void while

所有的關鍵字都是保留字,並且必須是小寫。
2. 下面是專用符號:

+ - * / < <= > >= == != = ; , ( ) [ ] { } /* */
  1. 其他標記是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,}>