C語言實現一個簡單的詞法分析器

此程序是針對Pascal語言文集所做的一個詞法分析器,也是蘭州大學編譯原理實驗課所要求的。

由於Pascal語言結構嚴謹,層次清晰,語法與C語言接近,也便於理解,因此本實驗抽取Pascal語言的一個子集,稍加改造,作爲源語言,姑且命名爲LittleP。

1. LittleP的文法:
〈程序〉→〈程序首部〉〈程序體〉.
〈程序首部〉→ program〈程序名〉; 
  〈程序體〉→〈變量聲明〉〈複合語句〉
〈變量聲明〉→ var〈變量定義列表〉|〈空〉
<變量定義列表> → 〈變量定義〉〈變量定義列表〉|〈變量定義〉;
〈變量定義〉→〈變量名列表〉: <類型> ; 
<變量名列表> → 〈變量名〉|〈變量名〉,〈變量名列表〉
<類型> →  integer  
〈複合語句〉→ begin〈語句塊〉end 
〈語句塊〉→〈語句〉|〈語句〉 ;〈語句塊〉
〈語句〉→〈賦值語句〉|〈條件語句〉|〈循環語句〉|〈複合語句〉|〈空〉
〈賦值語句〉→〈左部〉:= 〈右部〉 
〈左部〉→〈變量名〉
〈右部〉→〈算術表達式〉
〈條件語句〉→ if〈關係表達式〉then〈語句〉else〈語句〉
〈循環語句〉→ while〈關係表達式〉do〈語句〉
<關係表達式> →〈算術表達式〉〈關係運算符〉〈算術表達式〉
<算術表達式> → 〈項〉| 〈算術表達式〉〈加運算符〉〈項〉
<項> → 〈因子〉| 〈項〉〈乘運算符〉〈因子〉
〈因子〉→〈變量名〉|(〈算術表達式〉) |〈整數〉
〈程序名〉→〈標識符〉
〈變量名〉→〈標識符〉
〈標識符〉→〈字母〉|〈標識符〉〈字母〉|〈標識符〉〈數字〉
〈整數〉→〈數字〉|〈整數〉〈數字〉
〈關係運算符〉→ < | <= | = | >= | > | <>
〈加運算符〉→ + | -  
〈乘運算符〉→ * | /  
〈字母〉→ a|b|…|x|y|z 
〈數字〉→ 1|2|3|4|5|6|7|8|9|0

2、程序代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_WD_LEN 255//word長度
#define MAX_INT 32767
#define MAX_SRC_LEN 1000//源文件source長度
#define MAX_WD_CNT 100
#define KWD_CNT 6
/************************************************/

union value_type{
       int d;
       char c;
       char s[MAX_WD_LEN];
};

typedef struct{
       int syn;
       union value_type value;
}word_type;

/********************關鍵字 source 棧數組和指針等****************************/
char *keywords[20]={"begin","if","then","while","do","end"};
char source[MAX_SRC_LEN];
word_type word_queue[MAX_WD_CNT];
int line=1,wip=0,ip=0;

/**********************打印隊列**************************/
void p_word_queue(){
       int i;word_type w;
       printf("(syn,value)\n");
       printf("________________\n");
       for(i=0;i<wip;i++){
               w=word_queue[i];
               if( (w.syn>=1 && w.syn<=10) || w.syn==18 || w.syn==21 || w.syn==22
|| w.syn==24)
                       printf("(%d,%s)\n",w.syn,w.value.s);//開頭是字母(關鍵字或標識符)
               else if(w.syn==11)
                       printf("(%d,%d)\n",w.syn,w.value.d);//數字
               else
                       printf("(%d,%c)\n",w.syn,w.value.c);//符號
       }
       return ;
}
/*************報告錯誤******************/
void tell_err(){
       printf("error in line %d\n",line);
       exit(1);
       return ;
}
/**************掃描器**********************/
void scan(){
       word_type w;
       char c;
       int j=0;

       /**********對數字進行處理***********/
       if(isdigit(c=source[ip])){
               w.syn=11;   /* dd*  */
               w.value.d=c-'0';//變成int型
               while(isdigit(c=source[++ip]))//下一位也是數字時候的處理方法
                       w.value.d=w.value.d*10+c-'0';
      if(!isalpha(c))
      word_queue[wip++]=w;
      else
      tell_err();
               return;
       }


       /*********開頭是字母(關鍵字或者標識符)**********/
       if(isalpha(c=source[ip])){
               w.syn=10;    /*  (ll|d) */
               w.value.s[0]=c;//存入緩衝區數組之中
               while(isalpha(c=source[++ip]) || isdigit(c))//下一位是數字或者字母
                       w.value.s[++j]=c;
               w.value.s[j+1]='\0';//以此符號作爲最終態
               for(j=0;j<KWD_CNT;j++){//判斷是否是關鍵字
                       if(strcmp(keywords[j],w.value.s)==0)
                               w.syn=j+1;
               }
               word_queue[wip++]=w;
               return ;
       }

       /*************對符號進行處理******************/
       switch(c=source[ip]){
               case '+' :
                       w.syn=14;  /* '+' */
                       w.value.c='+';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case '-' :
                       w.syn=15; /* '-' */
                       w.value.c='-';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case '*' :
                       w.syn=16;  /* '*' */
                       w.value.c='*';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case '/' :
                       w.syn=17;
                       w.value.c='/';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case ':' :
                       w.syn=19;
                       w.value.c=':';
                       if( (c=source[++ip]) !='='){
                               word_queue[wip++]=w;

                       }
                       else if(c=='='){
                               strcpy(w.value.s,":=");
                               w.syn=18;
                               word_queue[wip++]=w;
                               ip++;
                       }
                       break;
               case '<' :
                       w.syn=20;
                       w.value.c='<';
                       if( (c=source[++ip]) !='>' && c!='='){
                               word_queue[wip++]=w;
                       }
                       else if(c=='>'){
                               w.syn=21;
                               strcpy(w.value.s,"<>");
                               word_queue[wip++]=w;
                               ip++;
                       }
                       else if(c=='='){
                               w.syn=22;
                               strcpy(w.value.s,"<=");
                               word_queue[wip++]=w;
                               ip++;
                       }
                       break;
               case  '>' :
                       w.syn=23;
                       w.value.c='>';
                       if( (c=source[++ip]) !='='){
                               word_queue[wip++]=w;
                       }
                       else if(c=='='){
                               w.syn=24;
                               strcpy(w.value.s,">=");
                               word_queue[wip++]=w;
                               ip++;
                       }
                       break;
               case '=' :
                       w.syn=25;
                       w.value.c='=';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case ';' :
                       w.syn=26;
                       w.value.c=';';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case '(' :
                       w.syn=27;
                       w.value.c='(';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case ')' :
                       w.syn=28;
                       w.value.c=')';
                       word_queue[wip++]=w;
                       ip++;
                       break;
               case ' ' :
                       while(source[++ip]==' ');
                       break;
               case '\n' :
                       line++;
                       while(source[++ip]=='\n')line++;
                       break;

               case '\t' :
                       while(source[++ip]=='\t');
                       break;
               case '\r' :
                       while(source[++ip]=='\r');
                       break;
               default:
                       tell_err();
       }
       return;
}
int main(){
       FILE* fp;
       int i=0;
       word_type w;
       fp=fopen("input.txt","r");
       /****讀取所有字符到source數組中*****/
       while(!feof(fp))
               source[i++]=getc(fp);
       fclose(fp);
       /****檢測是否已經達到源文件末尾*****/
       while(source[ip]!='#')
               scan(ip);//對單個字符進行掃描、處理
       w.syn=0;//此時已經掃描完畢
       w.value.c='#';//對#單獨賦值
       word_queue[wip++]=w;//加入到打印隊列裏
       p_word_queue();//調用打印函數
}

3、詞法分析器要分析的littleP對象,即input文件


begin
 ab2a:=9;
 if x>=0 then x:=x+1;
 while a=0 do
  b:=a*x/33455;
 end
#

4、運行效果





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章