解析四則運算式的編譯過程及產生彙編代碼

來源:互聯網
上載者:User

1、前序
這是編譯原理的實驗,自認為是上大學以來做過的最難的一個實驗。
實驗用到的基礎知識:C語言、資料結構、彙編(只需簡單的瞭解)。
開發工具:VC

2、問題描述
編譯整數四則運算運算式,將整數四則運算運算式翻譯為組合語言代碼。
消除左遞迴後的文法:
E→TE'
E'→+TE' |ε
T→FT'
T'→*FT' |ε
F→(E) | i
消除左遞迴後的翻譯模式:
E ::=     T    {E'.i:=T.nptr}
E'    {E.nptr:=E'.s}
E'::=      + T  {E'1.i:=mknode(‘+',E'.i,T.nptr)}
E'1   {E'.s:=E1.s}
E'::=       - T   {E'1.i:=mknode(‘-',E'.i,T.nptr)}
E'1   {E'.s:=E1.s}
E'::=      ε     {E'.s:= E'.i}
T ::=      F    {T'.i:=F.nptr}
T'    {T.nptr:=T'.s}
T'::=      * F  {T'1.i:=mknode(‘*',T'.i,F.nptr)}
T'1   {T'.s:=T1.s}
T'::=      / F   {T'1.i:=mknode(‘/',T'.i,F.nptr)}
T'1   {T'.s:=T1.s}
T' ::= ε  {T'.s:= T'.i}
F ::= (E) {F.nptr:=E.nptr}
F ::= num {F.nptr:=mkleaf(num,num.val)}

3、全域定義
test.c檔案
複製代碼 代碼如下:#ifndef TEST_C
#define TEST_C
/**
  * 全域變數和全域函數檔案
  **/
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
/************************* 以下是全域變數(函數)的定義 *******************/
//輸入的運算式最大長度,可以看做是緩衝區的長度
#define MAX_EXPRESSION_LENGTH 50
//存放輸入的運算式
char expression[MAX_EXPRESSION_LENGTH];
//運算式字元數組的下標
int expression_index=0;
//存放一個單詞符號
char strToken[MAX_EXPRESSION_LENGTH/2];
//判斷是否是數字
int isNum(char * strToken)
{
 int i=0;
 while(strToken[i]){
  if(!isdigit(strToken[i]))
   break;
  i++;
 }
 return strToken[i]==0;
}
//錯誤處理程式
void error(char* errerMessage)
{
 printf("\nERROR:%s\n",errerMessage);
 exit(0);
}
/************************* 以上是全域變數(函數)的定義 ******************/
#endif

4、詞法分析
詞法分析的要求是:接受一個運算式,輸出該運算式中的各類單詞符號
一般有兩種方法來進行詞法分析,一種是用狀態圖來實現,一種是用狀態轉換表。下面採用狀態圖實現
首先定義單詞符號的種類和所屬類型
typedef enum Symbol { ERR = -1, END, NUM, PLUS, MINUS, TIMES, SLASH, LPAREN, RPAREN } Symbol;

然後轉態轉換圖如下所示:

test1.c檔案用代碼錶示如下:

複製代碼 代碼如下:#ifndef TEST1_C
#define TEST1_C
/**
  * 採用狀態圖進行詞法分析以及測試詞法分析
  *
  **/
#include"test.c"
//枚舉類型
typedef enum Symbol { ERR = -1, END, NUM, PLUS, MINUS, TIMES,
SLASH, LPAREN, RPAREN } Symbol;
//擷取一個單詞符號,該單詞符號存放在strToken中。返回該單詞符號的枚舉類型
Symbol getToken();
//根據傳入的枚舉類型輸出對應的單詞符號
void printToken(Symbol i);
//測試詞法分析
void testLexAnalyse();
//擷取一個單詞符號,該單詞符號存放在strToken中。返回該單詞符號的枚舉類型
Symbol getToken()
{
 char ch;
 int state=0;//每次都是從狀態0開始
 int j=0;
 //運算式遍曆完成,單詞符號為'#'
 if(expression[expression_index]=='\0'){
        strToken[0]='#';
        strToken[1]='\0';
  return END;
    }
    while(1){
        switch(state){
  case 0:
   //讀取一個字元
   ch=strToken[j++]=expression[expression_index++];
   if(isspace(ch)){
    j--;//注意退格
    state=0; 
   }
   else if(isdigit(ch))
    state=1;     
   else if(ch=='+')
    state=2;     
   else if(ch=='-')
    state=3;     
   else if(ch=='*')
    state=4;   
   else if(ch=='/')
    state=5;     
   else if(ch=='(')
    state=6;     
   else if(ch==')')
    state=7;
   else
    return ERR;
   break;
  case 1:
   ch=strToken[j++]=expression[expression_index++];
   if(isdigit(ch))
    state=1;
   else{
    expression_index--;
    strToken[--j]=0;
    return NUM;
   }
   break;
  case 2:
   strToken[j]=0;   
   return PLUS;
  case 3:
   strToken[j]=0;
   return MINUS;
  case 4:
   strToken[j]=0;
   return TIMES;
  case 5:
   strToken[j]=0;
   return SLASH;
  case 6:
   strToken[j]=0;
      return LPAREN;
  case 7:
   strToken[j]=0;
   return RPAREN;     
  }
 }
}
//根據傳入的枚舉類型輸出對應的單詞符號
void printToken(Symbol i){
 switch(i){
 case -1:printf("ERR\n");break;
 case 0:printf("END\n");break;
 case 1:printf("NUM    %s\n",strToken);break;
 case 2:printf("PLUS   %s\n",strToken);break;
 case 3:printf("MINUS  %s\n",strToken);break;
 case 4:printf("TIMES  %s\n",strToken);break;
 case 5:printf("SLASH  %s\n",strToken);break;
 case 6:printf("LPAREN %s\n",strToken);break;
 case 7:printf("RPAREN %s\n",strToken);break;
 }
}
//測試詞法分析
void testLexAnalyse()
{
   Symbol tokenStyle;//單詞符號類型
   expression_index=0;
   puts("\n詞法分析結果如下:");
   while(1){
    tokenStyle=getToken();
    printToken(tokenStyle);
    if(tokenStyle == ERR){   
     error("詞法分析錯誤!");
    }
    if(tokenStyle == END){
     break;
    }
   }
}

//主函數
int main()
{
 gets(expression);
 testLexAnalyse();
 return 0;
}

#endif

運行結果

5、文法分析
要求:接受一個運算式,分析該運算式,並根據輸入正確與否給出相應資訊
主要是根據無左遞迴文法寫出對應的各個子程式
test2.c複製代碼 代碼如下:#ifndef TEST_2
#define TEST_2
/**
  * 文法分析以及測試文法分析
  **/
#include"test1.c"
/*
消除左遞迴後的文法:
E→TE'
E'→+TE' |ε
T→FT'
T'→*FT' |ε
F→(E) | i
*/
//每個非終結符有對應的子程式函式宣告
void E();
void E1();
void T();
void T1();
void F();
//測試文法分析
void testSyntaxAnalyse();
//每個非終結符有對應的子程式
void E()
{
 T();
 E1();
}
void E1()
{
 if(strcmp(strToken,"+")==0 || strcmp(strToken,"-")==0){
  getToken();
  T();
  E1();
 }
 //Follow(E1)={#,)}
 else if(strcmp(strToken,"#")!=0 && strcmp(strToken,")")!=0){
  error("文法分析錯誤!");
 }
}
void T()
{
 F();
 T1();
}
void T1()
{
 if(strcmp(strToken,"*")==0 || strcmp(strToken,"/")==0){
        getToken();
     F();
     T1();
 }
 //Follow(T1)={+,#,)},如果考慮-號的話要加上-號
 else if(strcmp(strToken,"-")!=0 &&strcmp(strToken,"+")!=0 && strcmp(strToken,"#")!=0 && strcmp(strToken,")")!=0){
  error("文法分析錯誤!");
 }
}
void F()
{
 if(isNum(strToken)){
        getToken();
 }
 else{
  if(strcmp(strToken,"(")==0){
    getToken();
    E();
    if(strcmp(strToken,")")==0)
                 getToken();
    else
     error("文法分析錯誤!");
  }
  else
   error("文法分析錯誤!");
 }
}
//測試文法分析
void testSyntaxAnalyse()
{
   expression_index=0;
   getToken();
   E();
   puts("\n文法分析結果如下:");
   if(strcmp(strToken,"#")!=0)
    error("文法分析錯誤!");
   else{
    puts("文法分析正確!");
   }
}
//主函數
int main()
{
 gets(expression);
 testLexAnalyse();
 testSyntaxAnalyse();
 return 0;
}
#endif

運行時要刪掉test1.c中的主函數,運行結果

6、語義分析
要求:需要實現的語義剖析器的功能是,接受一個運算式,分析該運算式,並在分析的過程中建立該運算式的抽象文法樹。由於四則運算運算式的抽象文法樹可基本上看作是二叉樹,因此中序遍曆序列應該和輸入的運算式一樣——除了沒有括弧之外。可輸出中序遍曆序列檢測程式功能是否正確。如果每個分支節點用一個臨時變數標記,則對四則運算運算式的抽象文法樹進行後序遍曆,可以得到輸入運算式所對應的四元式序列
test3.c檔案
複製代碼 代碼如下:#ifndef TEST3_C
#define TEST3_C
/**
  * 語義分析以及測試語義分析
  * 其實這個實驗是在test2的代碼上進行修改
  **/
#include"test1.c"
/*
消除左遞迴的翻譯模式:
E ::=  T  {E'.i:=T.nptr}
E' {E.nptr:=E'.s}
E'::=  + T {E'1.i:=mknode('+',E'.i,T.nptr)}
E'1 {E'.s:=E1.s}
E'::= - T {E'1.i:=mknode('-',E'.i,T.nptr)}
E'1 {E'.s:=E1.s}
E'::=  ε {E'.s:= E'.i}
T ::=  F  {T'.i:=F.nptr}
T' {T.nptr:=T'.s}
T'::=  * F {T'1.i:=mknode('*',T'.i,F.nptr)}
T'1 {T'.s:=T1.s}
T'::=  / F {T'1.i:=mknode('/',T'.i,F.nptr)}
T'1 {T'.s:=T1.s}
T' ::= ε {T'.s:= T'.i}
F ::= (E) {F.nptr:=E.nptr}
F ::= num {F.nptr:=mkleaf(num,num.val)}
*/
#define MAX_LENGTH 20 //四元式中運算元的最大長度
typedef int ValType;
//結點類型
typedef struct ASTNode {
 Symbol sym;//類型
 ValType val;//值
 struct ASTNode * left, *right;//左、右孩子
}ASTNode, *AST;
//四元式類型定義如下
typedef struct Quaternion{
 char op;
 char arg1[MAX_LENGTH];
 char arg2[MAX_LENGTH];
    char result[MAX_LENGTH];
}Quaternion;
//四元式數組,存放產生的四元式
Quaternion quaternion[MAX_LENGTH*2];
//統計四元式的個數
int count=0;
//後序遍曆抽象文法樹時存放運算元和臨時變數,這裡當作一個棧來使用
char stack[MAX_LENGTH*2][MAX_LENGTH];
//stack棧的下標
int index=0;
//記憶體中臨時資料存放區地址的位移量
int t=-4;
//函式宣告
ASTNode* E();
ASTNode* E1(ASTNode* E1_i);
ASTNode* T();
ASTNode* T1(ASTNode* T1_i);
ASTNode* F();
void error(char* errerMessage);
ASTNode *mknode(Symbol op, ASTNode *left, ASTNode *right);
ASTNode *mkleaf(Symbol sym, ValType val);
void yuyi_analyse();
void print_node(ASTNode *root);
void middle_list(ASTNode *root);
void last_list(ASTNode *root);
//測試語義分析
void testYuyiAnalyse();
//建立運算子結點
ASTNode *mknode(Symbol op, ASTNode *left, ASTNode *right);
//建立運算元結點
ASTNode *mkleaf(Symbol sym, ValType val);
//輸出結點
void printNode(ASTNode *root);
//中序遍曆二叉樹
void middle_list(ASTNode *root);
//後序遍曆二叉樹
void last_list(ASTNode *root);
/*
E ::=  T  {E'.i:=T.nptr}
E' {E.nptr:=E'.s}
*/
//為右邊的每個非終結符定義一個屬性,返回E的綜合屬性
ASTNode* E()
{
    ASTNode * E_nptr;
    ASTNode * E1_i;
 E1_i=T();
    E_nptr=E1(E1_i);
 return E_nptr;
}
/*
E'::=  + T {E'1.i:=mknode('+',E'.i,T.nptr)}
E'1 {E'.s:=E1.s}
E'::= - T {E'1.i:=mknode('-',E'.i,T.nptr)}
E'1 {E'.s:=E1.s}
E'::=  ε {E'.s:= E'.i}
*/
//返回的是綜合屬性,傳遞的是繼承屬性
ASTNode * E1(ASTNode *E1_i)
{
    ASTNode * E11_i;
 ASTNode * E1_s;
    ASTNode * T_nptr;
 char oper;
 if(strcmp(strToken,"+")==0 || strcmp(strToken,"-")==0){
     oper=strToken[0];
  getToken();
  T_nptr=T();
  if(oper=='+')
      E11_i=mknode(PLUS,E1_i,T_nptr);
  else
   E11_i=mknode(MINUS,E1_i,T_nptr);
  E1_s=E1(E11_i);  
 }
 //Follow(E1)={#,)},可以匹配空串
 else if(strcmp(strToken,"#")==0 || strcmp(strToken,")")==0){
  E1_s=E1_i;
 }else{
        error("文法分析錯誤!");
 }
 return E1_s;
}
/*
T ::=  F  {T'.i:=F.nptr}
T' {T.nptr:=T'.s}
*/
ASTNode* T()
{
    ASTNode * T_nptr;
    ASTNode * T1_i;
 T1_i=F();
    T_nptr=T1(T1_i);
 return T_nptr;
}
/*
T'::=  * F {T'1.i:=mknode('*',T'.i,F.nptr)}
T'1 {T'.s:=T1.s}
T'::=  / F {T'1.i:=mknode('/',T'.i,F.nptr)}
T'1 {T'.s:=T1.s}
T' ::= ε {T'.s:= T'.i}
*/
ASTNode* T1(ASTNode* T1_i)
{
    ASTNode* F_nptr;
 ASTNode* T11_i;
 ASTNode* T1_s;
 char oper;
 if(strcmp(strToken,"*")==0 || strcmp(strToken,"/")==0){
  oper=strToken[0];
  getToken();     
      F_nptr=F();
  if(oper=='*')
      T11_i=mknode(TIMES,T1_i,F_nptr);
  else
   T11_i=mknode(SLASH,T1_i,F_nptr);
     T1_s=T1(T11_i);    
 }
 //Follow(T1)={+,#,)},如果考慮-號的話要加上-號
 else if(strcmp(strToken,"-")==0 || strcmp(strToken,"+")==0 || strcmp(strToken,"#")==0 || strcmp(strToken,")")==0){
  T1_s=T1_i;
 }else{
        error("文法分析錯誤!");
 }
 return T1_s;
}
/*
F ::= (E) {F.nptr:=E.nptr}
F ::= num {F.nptr:=mkleaf(num,num.val)}
*/
ASTNode* F()
{
    ASTNode* F_nptr;
    ASTNode* E_nptr;
 if(isNum(strToken)){
  F_nptr=mkleaf(NUM,atoi(strToken));
        getToken();
 }
 else{
  if(strcmp(strToken,"(")==0){
    getToken();
    E_nptr=E();
    if(strcmp(strToken,")")==0)
                 getToken();
    else
     error("文法分析錯誤!");
             F_nptr=E_nptr;
  }
  else {
   error("文法分析錯誤!");
  }
 }
 return F_nptr;
}
//建立運算子結點
ASTNode *mknode(Symbol op, ASTNode *left, ASTNode *right)
{
    ASTNode* p=(ASTNode*)malloc(sizeof(ASTNode));
 p->left=left;
 p->right=right;
 p->sym=op;
 p->val=0;
 return p;
}
//建立運算元結點
ASTNode *mkleaf(Symbol sym, ValType val)
{
    ASTNode* p=(ASTNode*)malloc(sizeof(ASTNode));
 p->sym=sym;
 p->val=val;
 p->left=NULL;
 p->right=NULL;
 return p;
}
//輸出結點
void printNode(ASTNode *root)
{
 if(root->sym==NUM)
  printf("%d ",root->val);
 else if(root->sym==PLUS)
  printf("+ ");
 else if(root->sym==MINUS)
  printf("- ");
 else if(root->sym==TIMES)
  printf("* ");
 else if(root->sym==SLASH)
  printf("/ ");
}
//中序遍曆二叉樹
void middle_list(ASTNode *root)
{
 if(root==NULL)
  return ;
    middle_list(root->left);
 printNode(root);
    middle_list(root->right);
}
//後序遍曆二叉樹
void last_list(ASTNode *root)
{
 char temp[MAX_LENGTH];
 if(root==NULL)
  return ;
    last_list(root->left);
    last_list(root->right);
 if(root->sym == NUM){//如果是數字,則直接存入棧中
        sprintf(temp,"%d\0",root->val);
  strcpy(stack[index++],temp);
 }
 else if(root->sym == PLUS){//如果是+號,產生一個四元式
  //給四元式賦值
  quaternion[count].op='+';
  strcpy(quaternion[count].arg2,stack[--index]);
  strcpy(quaternion[count].arg1,stack[--index]);
     sprintf(quaternion[count].result,"t+%d\0",t+=4);
  strcpy(stack[index++],quaternion[count].result);
  //輸出該四元式
   printf("%-4c%-8s%-8s%-8s\n",quaternion[count].op,quaternion[count].arg1,quaternion[count].arg2,quaternion[count].result);
  count++;
 }else if(root->sym == MINUS){//如果是+號,產生一個四元式
        quaternion[count].op='-';
  strcpy(quaternion[count].arg2,stack[--index]);
  strcpy(quaternion[count].arg1,stack[--index]);
     sprintf(quaternion[count].result,"t+%d\0",t+=4);
  strcpy(stack[index++],quaternion[count].result);
   printf("%-4c%-8s%-8s%-8s\n",quaternion[count].op,quaternion[count].arg1,quaternion[count].arg2,quaternion[count].result);
  count++;
 }else if(root->sym == TIMES){//如果是*號,產生一個四元式
        quaternion[count].op='*';
  strcpy(quaternion[count].arg2,stack[--index]);
  strcpy(quaternion[count].arg1,stack[--index]);
     sprintf(quaternion[count].result,"t+%d\0",t+=4);
  strcpy(stack[index++],quaternion[count].result);
   printf("%-4c%-8s%-8s%-8s\n",quaternion[count].op,quaternion[count].arg1,quaternion[count].arg2,quaternion[count].result);
  count++;
 }else if(root->sym == SLASH){
        quaternion[count].op='/';
  strcpy(quaternion[count].arg2,stack[--index]);
  strcpy(quaternion[count].arg1,stack[--index]);
     sprintf(quaternion[count].result,"t+%d\0",t+=4);
  strcpy(stack[index++],quaternion[count].result);
   printf("%-4c%-8s%-8s%-8s\n",quaternion[count].op,quaternion[count].arg1,quaternion[count].arg2,quaternion[count].result);
  count++;
 }
}
//測試語義分析
void testYuyiAnalyse()
{
   ASTNode *root;
   expression_index=0;
   getToken();
   root=E();
   puts("\n語義分析結果如下:");
   printf("中序遍曆:");
   middle_list(root);
   putchar('\n');
   printf("後序遍曆得到的四元式:\n");
   last_list(root);
   putchar('\n');
}
//主函數
int main()
{
 gets(expression);
    testYuyiAnalyse();
 return 0;
}
#endif

運行結果

7、代碼產生
要求:以實驗3的語義剖析器的四元式輸出作為輸入,輸出組合語言程式。
test4.c複製代碼 代碼如下:#ifndef TEST4_C
#define TEST4_C
/**
  * 生產彙編代碼
  **/
#include"test3.c"
//傳人一個四元式,輸出對應的彙編代碼
void print_code(Quaternion qua)
{
 putchar('\n');
 /*
  mov eax, 3
     add eax, 4
     mov t+0, eax
 */
 if(qua.op == '+'){
  printf("        mov eax,%s\n",qua.arg1);
        printf("        add eax,%s\n",qua.arg2);
        printf("        mov %s,eax\n",qua.result);
 }else if(qua.op == '-'){
  printf("        mov eax,%s\n",qua.arg1);
        printf("        sub eax,%s\n",qua.arg2);
        printf("        mov %s,eax\n",qua.result);
 }
 /*
 mov eax, 2
    mov ebx, t+0
    mul ebx
    mov t+4, eax
 */
 else if(qua.op == '*'){
  printf("        mov eax,%s\n",qua.arg1);
  printf("        mov ebx,%s\n",qua.arg2);
  printf("        mul ebx\n");
  printf("        mov %s,eax\n",qua.result);
 }else if(qua.op == '/'){//除法的時候不考慮餘數
  printf("        mov eax,%s\n",qua.arg1);
  printf("        mov ebx,%s\n",qua.arg2);
  printf("        div ebx\n");
  printf("        mov %s,eax\n",qua.result);
 }
}
//輸出全部彙編代碼
void testCode()
{
 int i=0;
 puts("產生的彙編代碼如下:\n");
 puts(".386");
 puts(".MODEL FLAT");
 puts("ExitProcess PROTO NEAR32 stdcall, dwExitCode:DWORD");
 puts("INCLUDE io.h            ; header file for input/output");
 puts("cr      EQU     0dh     ; carriage return character");
 puts("Lf      EQU     0ah     ; line feed");
 puts(".STACK  4096            ; reserve 4096-byte stack");
 puts(".DATA                   ; reserve storage for data");
 puts("t       DWORD   40 DUP (?)");
 puts("label1   BYTE    cr, Lf, \"The result is \"");
 puts("result  BYTE    11 DUP (?)");
 puts("        BYTE    cr, Lf, 0");
 puts(".CODE                           ; start of main program code");
 puts("_start:");
 //遍曆實驗3中的四元式,輸出對應的彙編代碼
 for(;i<count;i++)
     print_code(quaternion[i]);
 puts("        dtoa    result, eax     ; convert to ASCII characters");
 puts("        output  label1          ; output label and sum");
 puts("        INVOKE  ExitProcess, 0  ; exit with return code 0");
 puts("PUBLIC _start                   ; make entry point public");
 puts("END                             ; end of source code");
}
//主函數
int main()
{
 gets(expression);
 testLexAnalyse();
    testYuyiAnalyse();
 testCode();
 return 0;
}
#endif

運行結果

8、點擊下載原始碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.