PL0編譯器擴展

實驗四 pl0編譯器的擴展

一、實驗目的和內容
1. 理解語法、語義分析程序爲中心的單遍編譯程序組織方法;
2. 理解編譯程序的基本邏輯過程(詞法分析、語法分析、語義分析及目標代碼的生成;
3. 理解編譯過程中的符號表、內存管理、錯誤處理的基本方法;

二、擴展要求(每項50分)
修改PL/0編譯程序和類P-code解釋程序的源代碼,以支持對PL/0語言所進行的如下擴充,並調試通過:
1. 給PL/0語言增加帶else子句的條件語句
<條件語句>::=IF <條件>THEN <語句>[ELSE <語句>]
問題:按照你的修改方式,如下PL/0代碼執行後變量x的值是什麼?你認爲這樣的結果是否合理?爲什麼?

  1. 增加參數
    爲函數或過程增加參數,實現傳值方式。
    PROCEDURE<過程名>(<形式參數表>);

    FUNCTION<函數名>(<形式參數表>);
    形式參數表的語法格式和變量聲明完全一致。
/*
 * PL/0 complier program for win32 platform (implemented in C)
 *
 * The program has been test on Visual C++ 6.0,  Visual C++.NET and
 * Visual C++.NET 2003,  on Win98,  WinNT,  Win2000,  WinXP and Win2003
 *
 */

typedef enum {
    false,
    true
} bool;


#define norw 14     /* 關鍵字個數 */
#define txmax 100   /* 名字表容量 */
#define nmax 14     /* number的最大位數 */
#define al 10       /* 符號的最大長度 */
#define amax 2047   /* 地址上界*/
#define levmax 3    /* 最大允許過程嵌套聲明層數 [0,  levmax]*/
#define cxmax 500   /* 最多的虛擬機代碼數 */

/* 符號 */
enum symbol {
    nul,         ident,     number,     plus,      minus,
    times,       slash,     oddsym,     eql,       neq,
    lss,         leq,       gtr,        geq,       lparen,
    rparen,      comma,     semicolon,  period,    becomes,
    beginsym,    endsym,    ifsym,      thensym,   whilesym,
    writesym,    readsym,   dosym,      callsym,   constsym,
    varsym,      procsym, elsesym
};
#define symnum 33

/* 名字表中的類型 */
enum object {
    constant,
    variable,
    procedur,
    parametre,//參數類

};
struct prod   //臨時記錄語法分析時函數參數的個數和名字
{
    char id[al+1];
};
struct prod pnow[15];
int  prodn=0;


/* 虛擬機代碼 */
enum fct {
    lit,     opr,     lod,
    sto,     cal,     inte,
    jmp,     jpc,
};
#define fctnum 81

/* 虛擬機代碼結構 */
struct instruction
{
    enum fct f; /* 虛擬機代碼指令 */
    int l;      /* 引用層與聲明層的層次差 */
    int a;      /* 根據f的不同而不同 */
};

FILE* fas;  /* 輸出名字表 */
FILE* fa;   /* 輸出虛擬機代碼 */
FILE* fa1;  /* 輸出源文件及其各行對應的首地址 */
FILE* fa2;  /* 輸出結果 */
bool listswitch;    /* 顯示虛擬機代碼與否 */
bool tableswitch;   /* 顯示名字表與否 */
char ch;            /* 獲取字符的緩衝區,getch 使用 */
enum symbol sym;    /* 當前的符號 */
char id[al+1];      /* 當前ident, 多出的一個字節用於存放0 */
int num;            /* 當前number */
int cc, ll;          /* getch使用的計數器,cc表示當前字符(ch)的位置 */
int cx;             /* 虛擬機代碼指針, 取值範圍[0, cxmax-1]*/
char line[81];      /* 讀取行緩衝區 */
char a[al+1];       /* 臨時符號, 多出的一個字節用於存放0 */
struct instruction code[cxmax]; /* 存放虛擬機代碼的數組 */
char word[norw][al];        /* 保留字 */
enum symbol wsym[norw];     /* 保留字對應的符號值 */
enum symbol ssym[256];      /* 單字符的符號值 */
char mnemonic[fctnum][5];   /* 虛擬機代碼指令名稱 */
bool declbegsys[symnum];    /* 表示聲明開始的符號集合 */
bool statbegsys[symnum];    /* 表示語句開始的符號集合 */
bool facbegsys[symnum];     /* 表示因子開始的符號集合 */

/* 名字表結構 */
struct tablestruct
{
    char name[al];      /* 名字 */
    enum object kind;   /* 類型:const, var, array or procedure */
    int val;            /* 數值,僅const使用 */
    int level;          /* 所處層,僅const不使用 */
    int adr;            /* 地址,僅const不使用 */
    int size;           /* 需要分配的數據區空間, 僅procedure使用 */
    int n;             /*參數個數*/
};

struct tablestruct table[txmax]; /* 名字表 */

FILE* fin;
FILE* fout;
char fname[al];
int err; /* 錯誤計數器 */

/* 當函數中會發生fatal error時,返回-1告知調用它的函數,最終退出程序 */
#define getsymdo                      if(-1 == getsym()) return -1
#define getchdo                       if(-1 == getch()) return -1
#define testdo(a, b, c)               if(-1 == test(a, b, c)) return -1
#define gendo(a, b, c)                if(-1 == gen(a, b, c)) return -1
#define expressiondo(a, b, c)         if(-1 == expression(a, b, c)) return -1
#define factordo(a, b, c)             if(-1 == factor(a, b, c)) return -1
#define termdo(a, b, c)               if(-1 == term(a, b, c)) return -1
#define conditiondo(a, b, c)          if(-1 == condition(a, b, c)) return -1
#define statementdo(a, b, c)          if(-1 == statement(a, b, c)) return -1
#define constdeclarationdo(a, b, c)   if(-1 == constdeclaration(a, b, c)) return -1
#define vardeclarationdo(a, b, c)     if(-1 == vardeclaration(a, b, c)) return -1

void error(int n);
int getsym();
int getch();
void init();
int gen(enum fct x, int y, int z);
int test(bool* s1, bool* s2, int n);
int inset(int e, bool* s);
int addset(bool* sr, bool* s1, bool* s2, int n);
int subset(bool* sr, bool* s1, bool* s2, int n);
int mulset(bool* sr, bool* s1, bool* s2, int n);
int block(int lev, int tx, bool* fsys);
void interpret();
int factor(bool* fsys, int* ptx, int lev);
int term(bool* fsys, int* ptx, int lev);
int condition(bool* fsys, int* ptx, int lev);
int expression(bool* fsys, int* ptx, int lev);
int statement(bool* fsys, int* ptx, int lev);
void listcode(int cx0);
int vardeclaration(int* ptx, int lev, int* pdx);
int constdeclaration(int* ptx, int lev, int* pdx);
int position(char* idt, int tx);
void enter(enum object k, int* ptx, int lev, int* pdx);
int base(int l, int* s, int b);
/*
 * PL/0 complier program for win32 platform (implemented in C)
 *
 * The program has been test on Visual C++ 6.0, Visual C++.NET and
 * Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003
 *
 * 使用方法:
 * 運行後輸入PL/0源程序文件?
 * 回答是否輸出虛擬機代碼
 * 回答是否輸出名字表
 * fa.tmp輸出虛擬機代碼
 * fa1.tmp輸出源文件及其各行對應的首地址
 * fa2.tmp輸出結?
 * fas.tmp輸出名字表
 */

#include <stdio.h>

#include "pl0.h"
#include "string.h"

/* 解釋執行時使用的棧 */
#define stacksize 500


int main()
{
    bool nxtlev[symnum];

    printf("Input pl/0 file?   ");
    scanf("%s", fname);     /* 輸入文件名 */

    fin = fopen(fname, "r");

    if (fin)
    {
        printf("List object code?(Y/N)");   /* 是否輸出虛擬機代碼 */
        scanf("%s", fname);
        listswitch = (fname[0]=='y' || fname[0]=='Y');

        printf("List symbol table?(Y/N)");  /* 是否輸出名字表 */
        scanf("%s", fname);
        tableswitch = (fname[0]=='y' || fname[0]=='Y');

        fa1 = fopen("fa1.tmp", "w");
        fprintf(fa1,"Input pl/0 file?   ");
        fprintf(fa1,"%s\n",fname);

        init();     /* 初始化 */

        err = 0;
        cc = cx = ll = 0;
        ch = ' ';

        if(-1 != getsym())
        {
            fa = fopen("fa.tmp", "w");
            fas = fopen("fas.tmp", "w");
            addset(nxtlev, declbegsys, statbegsys, symnum);
            nxtlev[period] = true;

            if(-1 == block(0, 0, nxtlev))   /* 調用編譯程序 */
            {
                fclose(fa);
                fclose(fa1);
                fclose(fas);
                fclose(fin);
                printf("\n");
                return 0;
            }
            fclose(fa);
            fclose(fa1);
            fclose(fas);

            if (sym != period)
            {
                error(9);
            }

            if (err == 0)
            {
                fa2 = fopen("fa2.tmp", "w");
                interpret();    /* 調用解釋執行程序 */
                fclose(fa2);
            }
            else
            {
                printf("Errors in pl/0 program");
            }
        }

        fclose(fin);
    }
    else
    {
        printf("Can't open file!\n");
    }

    printf("\n");
    return 0;
}

/*
* 初始化
*/
void init()
{
    int i;

    /* 設置單字符符號 */
    for (i=0; i<=255; i++)
    {
        ssym[i] = nul;
    }
    ssym['+'] = plus;
    ssym['-'] = minus;
    ssym['*'] = times;
    ssym['/'] = slash;
    ssym['('] = lparen;
    ssym[')'] = rparen;
    ssym['='] = eql;
    ssym[','] = comma;
    ssym['.'] = period;
    ssym['#'] = neq;
    ssym[';'] = semicolon;

    /* 設置保留字名字,按照字母順序,便於折半查找 */
    strcpy(&(word[0][0]), "begin");
    strcpy(&(word[1][0]), "call");
    strcpy(&(word[2][0]), "const");
    strcpy(&(word[3][0]), "do");
    strcpy(&(word[4][0]), "else");//增加保留字else
    strcpy(&(word[5][0]), "end");
    strcpy(&(word[6][0]), "if");
    strcpy(&(word[7][0]), "odd");
    strcpy(&(word[8][0]), "procedure");
    strcpy(&(word[9][0]), "read");
    strcpy(&(word[10][0]), "then");
    strcpy(&(word[11][0]), "var");
    strcpy(&(word[12][0]), "while");
    strcpy(&(word[13][0]), "write");

    /* 設置保留字符號 */
    wsym[0] = beginsym;
    wsym[1] = callsym;
    wsym[2] = constsym;
    wsym[3] = dosym;
    wsym[4] = elsesym;
    wsym[5] = endsym;
    wsym[6] = ifsym;
    wsym[7] = oddsym;
    wsym[8] = procsym;
    wsym[9] = readsym;
    wsym[10] = thensym;
    wsym[11] = varsym;
    wsym[12] = whilesym;
    wsym[13] = writesym;
    /* 設置指令名稱 */
    strcpy(&(mnemonic[lit][0]), "lit");
    strcpy(&(mnemonic[opr][0]), "opr");
    strcpy(&(mnemonic[lod][0]), "lod");
    strcpy(&(mnemonic[sto][0]), "sto");
    strcpy(&(mnemonic[cal][0]), "cal");
    strcpy(&(mnemonic[inte][0]), "int");
    strcpy(&(mnemonic[jmp][0]), "jmp");
    strcpy(&(mnemonic[jpc][0]), "jpc");

    /* 設置符號集 */
    for (i=0; i<symnum; i++)
    {
        declbegsys[i] = false;
        statbegsys[i] = false;
        facbegsys[i] = false;
    }

    /* 設置聲明開始符號集 */
    declbegsys[constsym] = true;
    declbegsys[varsym] = true;
    declbegsys[procsym] = true;

    /* 設置語句開始符號集 */
    statbegsys[beginsym] = true;
    statbegsys[callsym] = true;
    statbegsys[ifsym] = true;
    statbegsys[whilesym] = true;

    /* 設置因子開始符號集 */
    facbegsys[ident] = true;
    facbegsys[number] = true;
    facbegsys[lparen] = true;
}

/*
* 用數組實現集合的集合運算
*/
int inset(int e, bool* s)
{
    return s[e];
}

int addset(bool* sr, bool* s1, bool* s2, int n)
{
    int i;
    for (i=0; i<n; i++)
    {
        sr[i] = s1[i]||s2[i];
    }
    return 0;
}

int subset(bool* sr, bool* s1, bool* s2, int n)
{
    int i;
    for (i=0; i<n; i++)
    {
        sr[i] = s1[i]&&(!s2[i]);
    }
    return 0;
}

int mulset(bool* sr, bool* s1, bool* s2, int n)
{
    int i;
    for (i=0; i<n; i++)
    {
        sr[i] = s1[i]&&s2[i];
    }
    return 0;
}

/*
*   出錯處理,打印出錯位置和錯誤編碼
*/
void error(int n)
{
    char space[81];
    memset(space,32,81);

    space[cc-1]=0; //出錯時當前符號已經讀完,所以cc-1

    printf("****%s!%d\n", space, n);
    fprintf(fa1,"****%s!%d\n", space, n);

    err++;
}

/*
* 漏掉空格,讀取一個字符。
*
* 每次讀一行,存入line緩衝區,line被getsym取空後再讀一行
*
* 被函數getsym調用。
*/
int getch()
{
    if (cc == ll)
    {
        if (feof(fin))
        {
            printf("program incomplete");
            return -1;
        }
        ll=0;
        cc=0;
        printf("%d ", cx);
        fprintf(fa1,"%d ", cx);
        ch = ' ';
        while (ch != 10)
        {
            //fscanf(fin,"%c", &ch)
            //richard
            if (EOF == fscanf(fin,"%c", &ch))
            {
                line[ll] = 0;
                break;
            }
            //end richard
            printf("%c", ch);
            fprintf(fa1, "%c", ch);
            line[ll] = ch;
            ll++;
        }
        printf("\n");
        fprintf(fa1, "\n");
    }
    ch = line[cc];
    cc++;
    return 0;
}

/*
* 詞法分析,獲取一個符號
*/
int getsym()
{
    int i,j,k;

    /* the original version lacks "\r", thanks to foolevery */
    while (ch==' ' || ch==10 || ch==13 || ch==9)  /* 忽略空格、換行、回車和TAB */
    {
        getchdo;
    }
    if (ch>='a' && ch<='z')
    {
        /* 名字或保留字以a..z開頭 */
        k = 0;
        do
        {
            if(k<al)
            {
                a[k] = ch;
                k++;
            }
            getchdo;
        }
        while (ch>='a' && ch<='z' || ch>='0' && ch<='9');
        a[k] = 0;
        strcpy(id, a);
        i = 0;
        j = norw-1;
        do      /* 搜索當前符號是否爲保留字 */
        {
            k = (i+j)/2;
            if (strcmp(id,word[k]) <= 0)
            {
                j = k - 1;
            }
            if (strcmp(id,word[k]) >= 0)
            {
                i = k + 1;
            }
        }
        while (i <= j);
        if (i-1 > j)
        {
            sym = wsym[k];
        }
        else
        {
            sym = ident; /* 搜索失敗則,是名字或數字 */
        }
    }
    else
    {
        if (ch>='0' && ch<='9')
        {
            /* 檢測是否爲數字:以0..9開頭 */
            k = 0;
            num = 0;
            sym = number;
            do
            {
                num = 10*num + ch - '0';
                k++;
                getchdo;
            }
            while (ch>='0' && ch<='9');   /* 獲取數字的值 */
            k--;
            if (k > nmax)
            {
                error(30);
            }
        }
        else
        {
            if (ch == ':')      /* 檢測賦值符號 */
            {
                getchdo;
                if (ch == '=')
                {
                    sym = becomes;
                    getchdo;
                }
                else
                {
                    sym = nul;  /* 不能識別的符號 */
                }
            }
            else
            {
                if (ch == '<')      /* 檢測小於或小於等於符號 */
                {
                    getchdo;
                    if (ch == '=')
                    {
                        sym = leq;
                        getchdo;
                    }
                    else
                    {
                        sym = lss;
                    }
                }
                else
                {
                    if (ch=='>')        /* 檢測大於或大於等於符號 */
                    {
                        getchdo;
                        if (ch == '=')
                        {
                            sym = geq;
                            getchdo;
                        }
                        else
                        {
                            sym = gtr;
                        }
                    }
                    else
                    {
                        sym = ssym[ch];     /* 當符號不滿足上述條件時,全部按照單字符符號處理 */
                        //getchdo;
                        //richard
                        if (sym != period)
                        {
                            getchdo;
                        }
                        //end richard
                    }
                }
            }
        }
    }
    return 0;
}

/*
* 生成虛擬機代碼
*
* x: instruction.f;
* y: instruction.l;
* z: instruction.a;
*/
int gen(enum fct x, int y, int z )
{
    if (cx >= cxmax)
    {
        printf("Program too long"); /* 程序過長 */
        return -1;
    }
    code[cx].f = x;
    code[cx].l = y;
    code[cx].a = z;
    cx++;
    return 0;
}


/*
* 測試當前符號是否合法
*
* 在某一部分(如一條語句,一個表達式)將要結束時時我們希望下一個符號屬於某集?
* (該部分的後跟符號),test負責這項檢測,並且負責當檢測不通過時的補救措施,
* 程序在需要檢測時指定當前需要的符號集合和補救用的集合(如之前未完成部分的後跟
* 符號),以及檢測不通過時的錯誤號。
*
* s1:   我們需要的符號
* s2:   如果不是我們需要的,則需要一個補救用的集?
* n:    錯誤號
*/
int test(bool* s1, bool* s2, int n)
{
    if (!inset(sym, s1))
    {
        error(n);
        /* 當檢測不通過時,不停獲取符號,直到它屬於需要的集合或補救的集合 */
        while ((!inset(sym,s1)) && (!inset(sym,s2)))
        {
            getsymdo;
        }
    }
    return 0;
}

/*
* 編譯程序主?
*
* lev:    當前分程序所在層
* tx:     名字表當前尾指針
* fsys:   當前模塊後跟符號集?
*/
int block(int lev, int tx, bool* fsys)
{
    int i;

    int dx;                 /* 名字分配到的相對地址 */
    int tx0;                /* 保留初始tx */
    int cx0;              /* 保留初始cx */
    bool nxtlev[symnum];    /* 在下級函數的參數中,符號集合均爲值參,但由於使用數組實現,
                            傳遞進來的是指針,爲防止下級函數改變上級函數的集合,開闢新的空?
                            傳遞給下級函數*/

    dx = 3;
    tx0 = tx;               /* 記錄本層名字的初始位置 */
    table[tx].adr = cx;
    cx0=cx;
    gendo(jmp, 0, 0);
    table[tx].n=prodn;
    table[tx].size=dx+prodn;
    int j;
    for(j=0; j<prodn; j++)
    {
        tx++;
        strcpy(table[tx].name,pnow[j].id);
        table[tx].kind=variable;
        table[tx].level=lev;
        table[tx].size=1;
        table[tx].adr=dx;
        dx++;

    }


    if (lev > levmax)
    {
        error(32);
    }

    do
    {

        if (sym == constsym)    /* 收到常量聲明符號,開始處理常量聲明 */
        {
            getsymdo;

            /* the original do...while(sym == ident) is problematic, thanks to calculous */
            /* do { */
            constdeclarationdo(&tx, lev, &dx);  /* dx的值會被constdeclaration改變,使用指針 */
            while (sym == comma)
            {
                getsymdo;
                constdeclarationdo(&tx, lev, &dx);
            }
            if (sym == semicolon)
            {
                getsymdo;
            }
            else
            {
                error(5);   /*漏掉了逗號或者分號*/
            }
            /* } while (sym == ident); */
        }

        if (sym == varsym)      /* 收到變量聲明符號,開始處理變量聲明 */
        {
            getsymdo;

            /* the original do...while(sym == ident) is problematic, thanks to calculous */
            /* do {  */
            vardeclarationdo(&tx, lev, &dx);
            while (sym == comma)
            {
                getsymdo;
                vardeclarationdo(&tx, lev, &dx);
            }
            if (sym == semicolon)
            {
                getsymdo;
            }
            else
            {
                error(5);
            }
            /* } while (sym == ident);  */
        }

        while (sym == procsym) /* 收到過程聲明符號,開始處理過程聲明 */
        {
            getsymdo;
            prodn=0;
            if (sym == ident)
            {
                enter(procedur, &tx, lev, &dx); /* 記錄過程名字 */

                getsymdo;
                if(sym==lparen)
                {
                    getsymdo;
                    if(sym==varsym)
                    {
                        while(sym==varsym)
                        {
                            getsymdo;
                            if(sym==ident)
                            {
                                strcpy(pnow[prodn].id,id);
                                prodn++;
                                getsymdo;
                                if(sym==comma||sym==rparen)
                                {
                                    getsymdo;
                                }
                                 //getsymdo;
                                else
                                {
                                    error(5);
                                }
                            }
                            else
                            {
                                error(5);

                            }

                        }
                    }
                    else if(sym==rparen)
                    {
                        getsymdo;
                    }
                    //getsymdo;
                    else
                    {
                        error(5);
                    }
                }
            }
            else
            {
                error(4);   /* procedure後應爲標識符 */
            }
            getsymdo;
            if (sym == semicolon)
            {
                getsymdo;
            }
            else
            {
                error(5);   /* 漏掉了分號 */
            }
            //getsymdo;
            memcpy(nxtlev, fsys, sizeof(bool)*symnum);
            nxtlev[semicolon] = true;
            if (-1 == block(lev+1, tx, nxtlev))
            {
                return -1;  /* 遞歸調用 */
            }

            if(sym == semicolon)
            {
                getsymdo;
                memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
                nxtlev[ident] = true;
                nxtlev[procsym] = true;
                testdo(nxtlev, fsys, 6);
            }
            else
            {
                error(5);   /* 漏掉了分號 */
            }
        }
        memcpy(nxtlev, statbegsys, sizeof(bool)*symnum);
        nxtlev[ident] = true;
        nxtlev[period] = true;
        testdo(nxtlev, declbegsys, 7);
    }
    while (inset(sym, declbegsys));     /* 直到沒有聲明符號 */

    code[table[tx0].adr].a = cx;    /* 開始生成當前過程代碼 */

    table[tx0].adr = cx;            /* 當前過程代碼地址 */
    table[tx0].size = dx;           /* 聲明部分中每增加一條聲明都會給dx增加1,聲明部分已經結束,dx就是當前過程數據的size */
    cx0 = cx;
    gendo(inte, 0, dx);             /* 生成分配內存代碼 */

    if (tableswitch)        /* 輸出名字表 */
    {
        printf("TABLE:\n");
        if (tx0+1 > tx)
        {
            printf("    NULL\n");
        }
        for (i=tx0+1; i<=tx; i++)
        {
            switch (table[i].kind)
            {
            case constant:
                printf("    %d const %s ", i, table[i].name);
                printf("val=%d\n", table[i].val);
                fprintf(fas, "    %d const %s ", i, table[i].name);
                fprintf(fas, "val=%d\n", table[i].val);
                break;
            case variable:
                printf("    %d var   %s ", i, table[i].name);
                printf("lev=%d addr=%d\n", table[i].level, table[i].adr);
                fprintf(fas, "    %d var   %s ", i, table[i].name);
                fprintf(fas, "lev=%d addr=%d\n", table[i].level, table[i].adr);
                break;
            case procedur:
                printf("    %d proc  %s ", i, table[i].name);
                printf("lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);
                fprintf(fas,"    %d proc  %s ", i, table[i].name);
                fprintf(fas,"lev=%d addr=%d size=%d\n", table[i].level, table[i].adr, table[i].size);
                break;
            }
        }
        printf("\n");
    }

    /* 語句後跟符號爲分號或end */
    memcpy(nxtlev, fsys, sizeof(bool)*symnum);  /* 每個後跟符號集和都包含上層後跟符號集和,以便補救 */
    nxtlev[semicolon] = true;
    nxtlev[endsym] = true;
    statementdo(nxtlev, &tx, lev);
    gendo(opr, 0, 0);                       /* 每個過程出口都要使用的釋放數據段指令 */
    memset(nxtlev, 0, sizeof(bool)*symnum); /*分程序沒有補救集合 */
    testdo(fsys, nxtlev, 8);                /* 檢測後跟符號正確性 */
    listcode(cx0);                          /* 輸出代碼 */
    return 0;
}

/*
* 在名字表中加入一項
*
* k:      名字種類const,var or procedure
* ptx:    名字表尾指針的指針,爲了可以改變名字表尾指針的值
* lev:    名字所在的層次,,以後所有的lev都是這樣
* pdx:    dx爲當前應分配的變量的相對地址,分配後要增加1
*/
void enter(enum object k, int* ptx, int lev, int* pdx)
{
    (*ptx)++;
    strcpy(table[(*ptx)].name, id); /* 全局變量id中已存有當前名字的名字 */
    table[(*ptx)].kind = k;
    switch (k)
    {
    case constant:  /* 常量名字 */
        if (num > amax)
        {
            error(31);  /* 數越界 */
            num = 0;
        }
        table[(*ptx)].val = num;
        break;
    case variable:  /* 變量名字 */
        table[(*ptx)].level = lev;
        table[(*ptx)].adr = (*pdx);
        (*pdx)++;
        break;
    case procedur:  /* 過程名字 */
        table[(*ptx)].level = lev;
        break;
    }
}

/*
* 查找名字的位置.
* 找到則返回在名字表中的位置,否則返回0.
*
* idt:    要查找的名字
* tx:     當前名字表尾指針
*/
int position(char* idt, int tx)
{
    int i;
    strcpy(table[0].name, idt);
    i = tx;
    while (strcmp(table[i].name, idt) != 0)
    {
        i--;
    }
    return i;
}

/*
* 常量聲明處理
*/
int constdeclaration(int* ptx, int lev, int* pdx)
{
    if (sym == ident)
    {
        getsymdo;
        if (sym==eql || sym==becomes)
        {
            if (sym == becomes)
            {
                error(1);   /* 把=寫成了:= */
            }
            getsymdo;
            if (sym == number)
            {
                enter(constant, ptx, lev, pdx);
                getsymdo;
            }
            else
            {
                error(2);   /* 常量說明=後應是數字 */
            }
        }
        else
        {
            error(3);   /* 常量說明標識後應是= */
        }
    }
    else
    {
        error(4);   /* const後應是標識 */
    }
    return 0;
}

/*
* 變量聲明處理
*/
int vardeclaration(int* ptx,int lev,int* pdx)
{
    if (sym == ident)
    {
        enter(variable, ptx, lev, pdx); // 填寫名字表
        getsymdo;
    }
    else
    {
        error(4);   /* var後應是標識 */
    }
    return 0;
}

/*
* 輸出目標代碼清單
*/
void listcode(int cx0)
{
    int i;
    if (listswitch)
    {
        for (i=cx0; i<cx; i++)
        {
            printf("%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
            fprintf(fa,"%d %s %d %d\n", i, mnemonic[code[i].f], code[i].l, code[i].a);
        }
    }
}

/*
* 語句處理
*/
int statement(bool* fsys, int* ptx, int lev)
{
    int i, cx1, cx2;
    bool nxtlev[symnum];

    if (sym == ident)   /* 準備按照賦值語句處理 */
    {
        i = position(id, *ptx);
        if (i == 0)
        {
            error(11);  /* 變量未找到 */
        }
        else
        {
            if(table[i].kind != variable)
            {
                error(12);  /* 賦值語句格式錯誤 */
                i = 0;
            }
            else
            {
                getsymdo;
                if(sym == becomes)
                {
                    getsymdo;
                }
                else
                {
                    error(13);  /* 沒有檢測到賦值符號 */
                }
                memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                expressiondo(nxtlev, ptx, lev); /* 處理賦值符號右側表達式 */
                if(i != 0)
                {
                    /* expression將執行一系列指令,但最終結果將會保存在棧頂,執行sto命令完成賦值 */
                    gendo(sto, lev-table[i].level, table[i].adr);
                }
            }
        }//if (i == 0)
    }
    else
    {
        if (sym == readsym) /* 準備按照read語句處理 */
        {
            getsymdo;
            if (sym != lparen)
            {
                error(34);  /* 格式錯誤,應是左括號 */
            }
            else
            {
                do
                {
                    getsymdo;
                    if (sym == ident)
                    {
                        i = position(id, *ptx); /* 查找要讀的變量 */
                    }
                    else
                    {
                        i=0;
                    }

                    if (i == 0)
                    {
                        error(35);  /* read()中應是聲明過的變量名 */
                    }
                    else if (table[i].kind != variable)
                    {
                        error(32);  /* read()參數表的標識符不是變量, thanks to amd */
                    }
                    else
                    {
                        gendo(opr, 0, 16);  /* 生成輸入指令,讀取值到棧頂 */
                        gendo(sto, lev-table[i].level, table[i].adr);   /* 儲存到變量 */
                    }
                    getsymdo;

                }
                while (sym == comma);   /* 一條read語句可讀多個變量 */
            }
            if(sym != rparen)
            {
                error(33);  /* 格式錯誤,應是右括號 */
                while (!inset(sym, fsys))   /* 出錯補救,直到收到上層函數的後跟符號 */
                {
                    getsymdo;
                }
            }
            else
            {
                getsymdo;
            }
        }
        else
        {
            if (sym == writesym)    /* 準備按照write語句處理,與read類似 */
            {
                getsymdo;
                if (sym == lparen)
                {
                    do
                    {
                        getsymdo;
                        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                        nxtlev[rparen] = true;
                        nxtlev[comma] = true;       /* write的後跟符號爲) or , */
                        expressiondo(nxtlev, ptx, lev); /* 調用表達式處理,此處與read不同,read爲給變量賦值 */
                        gendo(opr, 0, 14);  /* 生成輸出指令,輸出棧頂的值 */
                    }
                    while (sym == comma);
                    if (sym != rparen)
                    {
                        error(33);  /* write()中應爲完整表達式 */
                    }
                    else
                    {
                        getsymdo;
                    }
                }
                gendo(opr, 0, 15);  /* 輸出換行 */
            }
            else
            {
                if (sym == callsym) /* 準備按照call語句處理 */
                {
                    int k=0;
                    getsymdo;
                    if (sym != ident)
                    {
                        error(14);  /* call後應爲標識符 */
                    }
                    else
                    {
                        i = position(id, *ptx);
                        if (i == 0)
                        {
                            error(11);  /* 過程未找到 */
                        }
                        else
                        {
                            if (table[i].kind == procedur)
                            {
                                //gendo(cal, lev-table[i].level, table[i].adr);   // 生成call指令
                                getsymdo;
                                if(sym == lparen)
                                {
                                    do
                                    {
                                        getsymdo;
                                        expressiondo(nxtlev,ptx,lev);
                                        k++;
                                    }
                                    while(sym==comma);
                                    if(sym==rparen)
                                    {
                                        getsymdo;
                                    }
                                    else
                                    {
                                        error(45);

                                    }
                                }
                                if(k!=table[i].n)
                                {
                                    printf("參數個數不對");
                                    error(46);
                                }
                                gendo(cal,lev-table[i].level,i);
                                gen(opr,0,7);
                                for(k=0; k<table[i].n; k++)
                                {
                                    gen(opr,0,7);
                                }
                            }
                            else
                            {
                                error(15);  //call後標識符應爲過程
                            }
                        }
                    }
                }
                else
                {

                    if (sym == ifsym)   /* 準備按照if語句處理 */
                    {
                        getsymdo;
                        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                        nxtlev[thensym] = true;
                        nxtlev[dosym] = true;   /* 後跟符號爲then或do */
                        conditiondo(nxtlev, ptx, lev); /* 調用條件處理(邏輯運算)函數 */
                        if (sym == thensym)
                        {
                            getsymdo;
                        }
                        else
                        {
                            error(16);  /* 缺少then */
                        }
                        cx1 = cx;   /* 保存當前指令地址 */
                        gendo(jpc, 0, 0);  /* 生成條件跳轉指令,跳轉地址未知,暫時寫0 */
                        statementdo(fsys, ptx, lev);    /* 處理then後的語句 */
                        cx2=cx;
                        gendo(jmp,0,0);
                        //code[cx1].a=cx;
                        if(sym==elsesym)
                        {

                            getsymdo;
                            //cx1=cx;
                            code[cx1].a=cx;//出現else則可回填if的假鏈
                            statementdo(fsys,ptx,lev);//處理else後的語句,處理完則cx也會改變
                            code[cx2].a=cx;//回填else的跳轉地址
                        }//add-end
                        else
                        {
                            code[cx1].a = cx;   //經statement處理後,cx爲then後語句執行完的位置,它正是前面未定的跳轉地址
                            code[cx2].a = cx;

                        }
                    }

                    else
                    {
                        if (sym == beginsym)    /* 準備按照複合語句處理 */
                        {
                            getsymdo;
                            memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                            nxtlev[semicolon] = true;
                            nxtlev[endsym] = true;  /* 後跟符號爲分號或end */
                            /* 循環調用語句處理函數,直到下一個符號不是語句開始符號或收到end */
                            statementdo(nxtlev, ptx, lev);

                            while (inset(sym, statbegsys) || sym==semicolon)
                            {
                                if (sym == semicolon)
                                {
                                    getsymdo;
                                }
                                else
                                {
                                    error(10);  /* 缺少分號 */
                                }
                                statementdo(nxtlev, ptx, lev);
                            }
                            if(sym == endsym)
                            {
                                getsymdo;
                            }
                            else
                            {
                                error(17);  /* 缺少end或分號 */
                            }
                        }
                        else
                        {
                            if (sym == whilesym)    /* 準備按照while語句處理 */
                            {
                                cx1 = cx;   /* 保存判斷條件操作的位置 */
                                getsymdo;
                                memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                                nxtlev[dosym] = true;   /* 後跟符號爲do */
                                conditiondo(nxtlev, ptx, lev);  /* 調用條件處理 */
                                cx2 = cx;   /* 保存循環體的結束的下一個位置 */
                                gendo(jpc, 0, 0);   /* 生成條件跳轉,但跳出循環的地址未知 */
                                if (sym == dosym)
                                {
                                    getsymdo;
                                }
                                else
                                {
                                    error(18);  /* 缺少do */
                                }
                                statementdo(fsys, ptx, lev);    /* 循環體 */
                                gendo(jmp, 0, cx1); /* 回頭重新判斷條件 */
                                code[cx2].a = cx;   /* 反填跳出循環的地址,與if類似 */
                            }
                            else
                            {
                                memset(nxtlev, 0, sizeof(bool)*symnum); /* 語句結束無補救集合 */
                                testdo(fsys, nxtlev, 19);   /* 檢測語句結束的正確性 */
                            }
                        }
                    }
                }
            }
        }
    }
    return 0;
}

/*
* 表達式處理
*/
int expression(bool* fsys, int* ptx, int lev)
{
    enum symbol addop;  /* 用於保存正負號 */
    bool nxtlev[symnum];

    if(sym==plus || sym==minus) /* 開頭的正負號,此時當前表達式被看作一個正的或負的項 */
    {
        addop = sym;    /* 保存開頭的正負號 */
        getsymdo;
        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
        nxtlev[plus] = true;
        nxtlev[minus] = true;
        termdo(nxtlev, ptx, lev);   /* 處理項 */
        if (addop == minus)
        {
            gendo(opr,0,1); /* 如果開頭爲負號生成取負指令 */
        }
    }
    else    /* 此時表達式被看作項的加減 */
    {
        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
        nxtlev[plus] = true;
        nxtlev[minus] = true;
        termdo(nxtlev, ptx, lev);   /* 處理項 */
    }
    while (sym==plus || sym==minus)
    {
        addop = sym;
        getsymdo;
        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
        nxtlev[plus] = true;
        nxtlev[minus] = true;
        termdo(nxtlev, ptx, lev);   /* 處理項 */
        if (addop == plus)
        {
            gendo(opr, 0, 2);   /* 生成加法指令 */
        }
        else
        {
            gendo(opr, 0, 3);   /* 生成減法指令 */
        }
    }
    return 0;
}

/*
* 項處理
*/
int term(bool* fsys, int* ptx, int lev)
{
    enum symbol mulop;  /* 用於保存乘除法符號 */
    bool nxtlev[symnum];

    memcpy(nxtlev, fsys, sizeof(bool)*symnum);
    nxtlev[times] = true;
    nxtlev[slash] = true;
    factordo(nxtlev, ptx, lev); /* 處理因子 */
    while(sym==times || sym==slash)
    {
        mulop = sym;
        getsymdo;
        factordo(nxtlev, ptx, lev);
        if(mulop == times)
        {
            gendo(opr, 0, 4);   /* 生成乘法指令 */
        }
        else
        {
            gendo(opr, 0, 5);   /* 生成除法指令 */
        }
    }
    return 0;
}

/*
* 因子處理
*/
int factor(bool* fsys, int* ptx, int lev)
{
    int i;
    bool nxtlev[symnum];
    testdo(facbegsys, fsys, 24);    /* 檢測因子的開始符號 */
    /* while(inset(sym, facbegsys)) */  /* 循環直到不是因子開始符號 */
    if(inset(sym,facbegsys))    /* BUG: 原來的方法var1(var2+var3)會被錯誤識別爲因子 */
    {
        if(sym == ident)    /* 因子爲常量或變量 */
        {
            i = position(id, *ptx); /* 查找名字 */
            if (i == 0)
            {
                error(11);  /* 標識符未聲明 */
            }
            else
            {
                switch (table[i].kind)
                {
                case constant:  /* 名字爲常量 */
                    gendo(lit, 0, table[i].val);    /* 直接把常量的值入棧 */
                    break;
                case variable:  /* 名字爲變量 */
                    gendo(lod, lev-table[i].level, table[i].adr);   /* 找到變量地址並將其值入棧 */
                    break;
                case procedur:  /* 名字爲過程 */
                    error(21);  /* 不能爲過程 */
                    break;
                }
            }
            getsymdo;
        }
        else
        {
            if(sym == number)   /* 因子爲數 */
            {
                if (num > amax)
                {
                    error(31);
                    num = 0;
                }
                gendo(lit, 0, num);
                getsymdo;
            }
            else
            {
                if (sym == lparen)  /* 因子爲表達式 */
                {
                    getsymdo;
                    memcpy(nxtlev, fsys, sizeof(bool)*symnum);
                    nxtlev[rparen] = true;
                    expressiondo(nxtlev, ptx, lev);
                    if (sym == rparen)
                    {
                        getsymdo;
                    }
                    else
                    {
                        error(22);  /* 缺少右括號 */
                    }
                }
                testdo(fsys, facbegsys, 23);    /* 因子後有非法符號 */
            }
        }
    }
    return 0;
}

/*
* 條件處理
*/
int condition(bool* fsys, int* ptx, int lev)
{
    enum symbol relop;
    bool nxtlev[symnum];

    if(sym == oddsym)   /* 準備按照odd運算處理 */
    {
        getsymdo;
        expressiondo(fsys, ptx, lev);
        gendo(opr, 0, 6);   /* 生成odd指令 */
    }
    else
    {
        /* 邏輯表達式處理 */
        memcpy(nxtlev, fsys, sizeof(bool)*symnum);
        nxtlev[eql] = true;
        nxtlev[neq] = true;
        nxtlev[lss] = true;
        nxtlev[leq] = true;
        nxtlev[gtr] = true;
        nxtlev[geq] = true;
        expressiondo(nxtlev, ptx, lev);
        if (sym!=eql && sym!=neq && sym!=lss && sym!=leq && sym!=gtr && sym!=geq)
        {
            error(20);
        }
        else
        {
            relop = sym;
            getsymdo;
            expressiondo(fsys, ptx, lev);
            switch (relop)
            {
            case eql:
                gendo(opr, 0, 8);
                break;
            case neq:
                gendo(opr, 0, 9);
                break;
            case lss:
                gendo(opr, 0, 10);
                break;
            case geq:
                gendo(opr, 0, 11);
                break;
            case gtr:
                gendo(opr, 0, 12);
                break;
            case leq:
                gendo(opr, 0, 13);
                break;
            }
        }
    }
    return 0;
}

/*
* 解釋程序
*/
void interpret()
{
    int p, b, t;    /* 指令指針,指令基址,棧頂指針 */
    struct instruction i;   /* 存放當前指令 */
    int s[stacksize];   /* 棧 */

    printf("start pl0\n");
    t = 0;
    b = 0;
    p = 0;
    s[0] = s[1] = s[2] = 0;
    do
    {
        i = code[p];    /* 讀當前指令 */
        p++;
        switch (i.f)
        {
        case lit:   /* 將a的值取到棧頂 */
            s[t] = i.a;
            t++;
            break;
        case opr:   /* 數學、邏輯運算 */
            switch (i.a)
            {
            case 0:
                t = b;
                p = s[t+2];
                b = s[t+1];
                break;
            case 1:
                s[t-1] = -s[t-1];
                break;
            case 2:
                t--;
                s[t-1] = s[t-1]+s[t];
                break;
            case 3:
                t--;
                s[t-1] = s[t-1]-s[t];
                break;
            case 4:
                t--;
                s[t-1] = s[t-1]*s[t];
                break;
            case 5:
                t--;
                s[t-1] = s[t-1]/s[t];
                break;
            case 6:
                s[t-1] = s[t-1]%2;
                break;
            case 7:   //返回參數彈出
                t--;
                break;
            case 8:
                t--;
                s[t-1] = (s[t-1] == s[t]);
                break;
            case 9:
                t--;
                s[t-1] = (s[t-1] != s[t]);
                break;
            case 10:
                t--;
                s[t-1] = (s[t-1] < s[t]);
                break;
            case 11:
                t--;
                s[t-1] = (s[t-1] >= s[t]);
                break;
            case 12:
                t--;
                s[t-1] = (s[t-1] > s[t]);
                break;
            case 13:
                t--;
                s[t-1] = (s[t-1] <= s[t]);
                break;
            case 14:
                printf("%d", s[t-1]);
                fprintf(fa2, "%d", s[t-1]);
                t--;
                break;
            case 15:
                printf("\n");
                fprintf(fa2,"\n");
                break;
            case 16:
                printf("?");
                fprintf(fa2, "?");
                scanf("%d", &(s[t]));
                fprintf(fa2, "%d\n", s[t]);
                t++;
                break;
            }
            break;
        case lod:   /* 取相對當前過程的數據基地址爲a的內存的值到棧頂 */
            s[t] = s[base(i.l,s,b)+i.a];
            t++;
            break;
        case sto:   /* 棧頂的值存到相對當前過程的數據基地址爲a的內存 */
            t--;
            s[base(i.l, s, b) + i.a] = s[t];
            break;
        case cal:   /* 調用子過程 */
            s[t] = base(i.l, s, b); // 將父過程基地址入棧
            s[t+1] = b; // 將本過程基地址入棧,此兩項用於base函數
            s[t+2] = p; //將當前指令指針入棧
            int w=0;
            for( w=0; w<table[i.a].n; w++)
            {
                s[t+3+w]=s[t-table[i.a].n+w];
            }

            b = t;  // 改變基地址指針值爲新過程的基地址
            p = i.a;    //跳轉
            break;
        case inte:  /* 分配內存 */
            t += i.a;
            break;
        case jmp:   /* 直接跳轉 */
            p = i.a;
            break;
        case jpc:   /* 條件跳轉 */
            t--;
            if (s[t] == 0)
            {
                p = i.a;
            }
            break;
        }
    }
    while (p != 0);
}

/* 通過過程基址求上l層過程的基址 */
int base(int l, int* s, int b)
{
    int b1;
    b1 = b;
    while (l > 0)
    {
        b1 = s[b1];
        l--;
    }
    return b1;
}

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