程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> 解析四則表達式的編譯過程及生成匯編代碼

解析四則表達式的編譯過程及生成匯編代碼

編輯:更多關於編程

    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:%sn",errerMessage);
    exit(0);
    }
    /************************* 以上是全局變量(函數)的定義 ******************/
    #endif


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

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

    01.png

    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]==''){
    strToken[0]='#';
    strToken[1]='';
    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("ERRn");break;
    case 0:printf("ENDn");break;
    case 1:printf("NUM %sn",strToken);break;
    case 2:printf("PLUS %sn",strToken);break;
    case 3:printf("MINUS %sn",strToken);break;
    case 4:printf("TIMES %sn",strToken);break;
    case 5:printf("SLASH %sn",strToken);break;
    case 6:printf("LPAREN %sn",strToken);break;
    case 7:printf("RPAREN %sn",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


    運行結果
    02.jpg

    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中的主函數,運行結果
    03.jpg
    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",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",t+=4);
    strcpy(stack[index++],quaternion[count].result);
    //輸出該四元式
    printf("%-4c%-8s%-8s%-8sn",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",t+=4);
    strcpy(stack[index++],quaternion[count].result);
    printf("%-4c%-8s%-8s%-8sn",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",t+=4);
    strcpy(stack[index++],quaternion[count].result);
    printf("%-4c%-8s%-8s%-8sn",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",t+=4);
    strcpy(stack[index++],quaternion[count].result);
    printf("%-4c%-8s%-8s%-8sn",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


    運行結果
    04.jpg
    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,%sn",qua.arg1);
    printf(" add eax,%sn",qua.arg2);
    printf(" mov %s,eaxn",qua.result);
    }else if(qua.op == '-'){
    printf(" mov eax,%sn",qua.arg1);
    printf(" sub eax,%sn",qua.arg2);
    printf(" mov %s,eaxn",qua.result);
    }
    /*
    mov eax, 2
    mov ebx, t+0
    mul ebx
    mov t+4, eax
    */
    else if(qua.op == '*'){
    printf(" mov eax,%sn",qua.arg1);
    printf(" mov ebx,%sn",qua.arg2);
    printf(" mul ebxn");
    printf(" mov %s,eaxn",qua.result);
    }else if(qua.op == '/'){//除法的時候不考慮余數
    printf(" mov eax,%sn",qua.arg1);
    printf(" mov ebx,%sn",qua.arg2);
    printf(" div ebxn");
    printf(" mov %s,eaxn",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


    運行結果
    05.jpg

    8、點擊下載源代碼

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved