CPU0 處理器的架構及應用


CPU0 處理器的架構及應用

簡介

CPU0 是一個 32 位的處理器,包含 R0..R15, IR, MAR, MDR 等緩存器,結構如下圖所示。

 

 

 圖 1 :CPU0 處理器的結構

其中各個緩存器的用途如下所示:

IR

指令緩存器

R0

常數緩存器, 值永遠為 0。

R1~R11

通用型緩存器。

R12

狀態緩存器 (Status Word : SW)

R13

堆棧指針緩存器 (Stack Pointer : SP)

R14

鏈接緩存器 (Link Register : LR)

R15

程序計數器 (Program Counter : PC)

MAR

地址緩存器 (Memory Address Register)

MDR

數據緩存器 (Memory Data Register)

CPU0 的指令集

CPU0 的指令分為三種類型,L 型通常為加載儲存指令、A 型以算術指令為主、J 型則通常為跳躍指令,下圖顯示了這三種類型指令的編碼格式。

 

 

 圖 2:CPU0 的三種指令格式

以下是 CPU0 處理器的指令表格式

表 1 :CPU0 的指令表

 

 

 在第二版的 CPU0_v2 中,補上了以下指令:

類型

格式

指令

OP

說明

語法

語意

浮點運算

A

FADD

41

浮點加法

FADD Ra, Rb, Rc

Ra = Rb + Rc

浮點運算

A

FSUB

42

浮點減法

FSUB Ra, Rb, Rc

Ra = Rb + Rc

浮點運算

A

FMUL

43

浮點乘法

FMUL Ra, Rb, Rc

Ra = Rb * Rc

浮點運算

A

FADD

44

浮點除法

FDIV Ra, Rb, Rc

Ra = Rb / Rc

中斷處理

J

IRET

2D

中斷返回

IRET

PC = LR; INT 0

狀態緩存器

CPU0 的狀態緩存器,包含 N, Z, C, V 等狀態,以及 I, T 等中斷模式位。結構如下圖所示。

 

 

 圖 3:CPU0 的狀態緩存器

當 CMP Ra, Rb 指令執行時,狀態標志會因而改變。

假如 Ra > Rb, 則會設定狀態 N=0, Z=0
假如 Ra < Rb, 則會設定狀態 N=1, Z=0
假如 Ra = Rb, 則會設定狀態 N=0, Z=1

於是條件式跳躍的 JGT, JLT, JGE, JLE, JEQ, JNE 等指令,就可以根據狀態緩存器中的 N, Z 標志進行跳躍操作。

指令的執行步驟

CPU0在執行一個指令時,必須經過取指、譯碼與執行等三大階段。

  1. 提取階段
    • 操作1、提取指令 :IR = [PC]
    • 操作2、更新計數器 :PC = PC + 4
  2. 解碼階段
    • 操作3、解碼 :控制單元對IR進行譯碼后,設定數據流向開關與 ALU 的運算模式
  3. 運行時間
    • 操作4、執行 :數據流入 ALU,經過運算后,流回指定的緩存器

V-OS: 橫跨操作系統與硬件的虛擬機系統

  1. 設計一個虛擬機系統,可以將 CPU A, B, C, D, E … 模擬成另外任何一種 CPU,這樣是否能解決所有的跨平台問題呢?
    • QEMU 其實可以做到類似的操作,想法與 QEMU 不同點在於 QEMU 是在操作系統層次之上的,做法是在操作系統層次之下的。
    • 這樣子就可以將在任何一個 CPU 上,跑另一個操作系統的程序,但是,不知速度會比 QEMU 快還是慢呢?
    • 這種做法姑且可以想象為「雲端虛擬機」!
    • 不知大家覺得可能嗎?有用嗎?

 

 

 圖一:V-OS 系統的架構圖

CC1 編譯程序

為了說明編譯程序是如何設計出來的,在開放計算機計划中,設計了一個功能完備,簡化過的 C 語言,這個語言稱為 C1 語言,是 C0 語言的擴充版。

CC1 編譯程序是一個 C1 語言的編譯程序,具有完成的編譯程序功能。在程序設計上,CC1 又被進一步拆解為 1. 詞匯分析 2. 語法分析 3. 語意分析 4. 中間碼產生 5. 匯編語言產生 等階段,這所有的階段,都會存取一個共同的數據結構,就是符號表。

因此,整個 CC1 編譯程序,進一步分解為下列程序模塊。

模塊

核心對象

程序

詞匯分析 (Lexical Analysis)

Scanner

Scanner.c, Scanner.h

語法分析 (Syntax Analysis)

Parser

Parser.c, Parser.h

語意分析 (Semantic Analysis)

Semantic

Semantic.c, Semantic.h

中間碼產生 (Intermediate Code)

PCode

PCode.c, PCode.h

匯編語言產生 (Code Generation)

Generator

Generator.c, Generator.h

符號表 (Symbol Table)

SymTable

SymTable.c, SymTable.h

Lua

  1. http://zh.wikipedia.org/wiki/Lua

Lua 的 BNF

    chunk ::= {stat [`;´]} [laststat [`;´]]
 
    block ::= chunk
 
    stat ::=  varlist `=´ explist | 
         functioncall | 
         do block end | 
         while exp do block end | 
         repeat block until exp | 
         if exp then block {elseif exp then block} [else block] end | 
         for Name `=´ exp `,´ exp [`,´ exp] do block end | 
         for namelist in explist do block end | 
         function funcname funcbody | 
         local function Name funcbody | 
         local namelist [`=´ explist] 
 
    laststat ::= return [explist] | break
 
    funcname ::= Name {`.´ Name} [`:´ Name]
 
    varlist ::= var {`,´ var}
 
    var ::=  Name | prefixexp `[´ exp `]´ | prefixexp `.´ Name 
 
    namelist ::= Name {`,´ Name}
 
    explist ::= {exp `,´} exp
 
    exp ::=  nil | false | true | Number | String | `...´ | function | 
         prefixexp | tableconstructor | exp binop exp | unop exp 
 
    prefixexp ::= var | functioncall | `(´ exp `)´
 
    functioncall ::=  prefixexp args | prefixexp `:´ Name args 
 
    args ::=  `(´ [explist] `)´ | tableconstructor | String 
 
    function ::= function funcbody
 
    funcbody ::= `(´ [parlist] `)´ block end
 
    parlist ::= namelist [`,´ `...´] | `...´
 
    tableconstructor ::= `{´ [fieldlist] `}´
 
    fieldlist ::= field {fieldsep field} [fieldsep]
 
    field ::= `[´ exp `]´ `=´ exp | Name `=´ exp | exp
 
    fieldsep ::= `,´ | `;´
 
    binop ::= `+´ | `-´ | `*´ | `/´ | `^´ | `%´ | `..´ | 
         `<´ | `<=´ | `>´ | `>=´ | `==´ | `~=´ | 
         and | or
 
    unop ::= `-´ | not | `#´
  1. Lua 5.1 Reference Manual — http://www.lua.org/manual/5.1/manual.html
    • 最后有 Lua 的 BNF。
  2. Lua Interpreter in C — http://www.lua.org/source/5.1/lua.c.html
  3. Lua Compiler in Lua — http://lua-users.org/wiki/LuaCompilerInLua
  4. Lua Interpreter in Lua — http://lua-users.org/wiki/LuaInterpreterInLua
  5. http://luajit.org/ — The LuaJIT Project

CC1 編譯程序的符號表

#ifndef SYMTABLE_H
#define SYMTABLE_H
 
#include "lib.h"
#include "HashTable.h"
#include "Tree.h"
 
// 型態 Type 有:函數、結構與指針與基本型態
//   基本 : int x;
//   指標  : int *px;
//   函數  : int total(int a[]) {...};  
//   結構  : struct Person { ... };
 
typedef struct _Method {
    char *name;
    char *returnType;
    Array *params;
} Method;
 
typedef struct _Struct {
    char *name;
    Array *fields;
} Struct;
 
typedef union _Type {
    Method *pmethod;
    Struct *pstruct;
    char   *pbtype;
} Type;
 
// 符號的值可能是 byte, int, float 或 pointer (包含 struct, method, type*)
typedef union _Value {
    BYTE  bvalue;
    int   ivalue;
    float fvalue;
    void  *pvalue;
} Value;
 
// 變量符號: int x; Symbol(name=x, tag=VAR, type=int)
// 函數符號: int total(int a[]) {...};  Symbol(name=total, tag=METHOD, type=int)
// 結構符號: struct Person { ... }; Symbol(name=x, tag=ETYPE, type=int)
typedef struct _Symbol { // 符號記錄
    void  *scope; // 所屬領域 
    char  *name;  // 符號名稱 (x, px, Person, total) 
    char  *tag;   // 符號標記 (變量定義 VAR 函數定義 METHOD、結構定義 STRUCT)
    Type  type;   // 符號的形態 
    Value value;  // 符號的值 
} Symbol;
 
typedef HashTable SymTable;
 
Symbol *SymNew(void *scope, char *name, char *tag);
void SymFree(Symbol *s);
void TypeFree(Type *type);
 
SymTable *SymTableNew();
Symbol *SymTablePut(SymTable *table, Symbol *sym);
Symbol* SymTableGet(SymTable *table, void *scope, char *name);
void SymTableFree(SymTable *table);
void SymTableDebug(SymTable *table);
 
#endif

CC1 的詞匯分析 (Scanner) 程序

檔案Scanner.h

#ifndef SCANNER_H

#define SCANNER_H

 

#include "lib.h"

 

typedef struct {           // 掃描儀的對象結構

    char *text;            //   輸入的程序 (text)

    int len;               //   程序的總長度

    // 注意:以下的 xSave 都是在 ScannerStore() 與 ScannerRestore() 時使用的備份。

    int i, iSave;          //   目前詞匯的位置

    int line, lineSave;    //   目前詞匯的行號

    int pos, posSave;      //   目前詞匯的起始點

    char *tag, *tagSave;   //   詞匯的標記

    char token[100], tokenSave[100]; // 目前的詞匯

} Scanner;

 

void ScannerTest(char *fileName);   // Scanner 詞匯分析階段的測試程序。

Scanner* ScannerNew(char *pText);   // 建立新的詞匯分析 Scanner 對象

void ScannerFree(Scanner *s);       // 釋放 Scanner 對象

void ScannerStore(Scanner *s);      // 儲存 Scanner 的目前狀態

void ScannerRestore(Scanner *s);    // 恢復 Scanner 的儲存狀態

BOOL ScannerIsNext(Scanner *s, char *pTags); // 檢查下一個詞匯是否符合 tag 標記。

void ScannerNext(Scanner *s);       // 取得下一個詞匯 (token)

char ch(Scanner *s);                // 取得目前字符

void cnext(Scanner *s);             // 前進到下一個字符

char *tokenToTag(char *token);      // 對詞匯 (token) 進行標記 (tag)

 

// 宣告 Token 變量,包含關鍵詞 if, for, 運算符 ++, / 與 非終端項目 IF, FOR...

#define DEF(var, str) extern char var[];

#include "Token.h"

#undef DEF

 

#endif

檔案Scanner.c

#include <string.h>

#include "Scanner.h"

 

// 宣告關鍵詞的字符串變量,像是 char kIF[]="if"; ...char EXP[]="EXP";...

#define DEF(var, str) char var[]=str;

#include "Token.h"

#undef DEF

 

// 宣告關鍵詞數組, gTagList={...,"if", ...,"EXP", ... };

char *gTokenList[] = {

#define DEF(var, str) var,

#include "Token.h"

#undef DEF

};

 

// 功能:Scanner 詞匯分析階段的測試程序。

// 范例:ScannerTest("test.c1");

void ScannerTest(char *fileName) {

    debug("======================ScannerTest()=========================\n");   

    char *text = fileToStr(fileName); // 讀取整個程序文件,成為一個字符串 text

    Scanner *s = ScannerNew(text); // 建立 Scanner 對象

    while (TRUE) { // 不斷掃描詞匯,直到檔案結束

        ScannerNext(s); // 取得下一個詞匯

        debug("token=%-10s tag=%-10s line=%-4d pos=%-3d\n",

              s->token, s->tag, s->line, s->pos);

        if (s->tag == kEND) // 已經到程序結尾

            break; // 結束掃描

    }

    ScannerFree(s); // 釋放 Scanner 對象

    strFree(text); // 釋放字符串 text

    memCheck(); // 檢查內存

}

 

// 功能:建立新的詞匯分析 Scanner 對象

// 范例:Scanner *s = ScannerNew(text);

Scanner* ScannerNew(char *pText) {

    Scanner *s = ObjNew(Scanner, 1);

    s->text = pText;

    s->len = strlen(pText);

    s->i = 0;

    s->line = 1;

    s->pos = 1;

//    ScannerNext(s);

    return s;

}

 

// 功能:釋放 Scanner 對象

// 范例:ScannerFree(s);

void ScannerFree(Scanner *s) {

    ObjFree(s);

}

 

// 功能:儲存 Scanner 的目前狀態

// 說明:剖析時若「偷看」后面幾個 token,就必須使用 ScannerStore() 儲存,然后呼叫

//       ScannerNext() 偷看,之后再用 ScannerRestore() 恢復,以完成整個偷看過程。

// 范例:ScannerStore(s);

void ScannerStore(Scanner *s) {

    s->iSave = s->i;

    s->posSave = s->pos;

    s->lineSave = s->line;

    s->tagSave = s->tag;

    strcpy(s->tokenSave, s->token);

}

 

// 功能:恢復 Scanner 的儲存狀態

// 范例:ScannerRestore(s);

void ScannerRestore(Scanner *s) {

    s->i = s->iSave;

    s->pos = s->posSave;

    s->line = s->lineSave;

    s->tag = s->tagSave;

    strcpy(s->token, s->tokenSave);

}

 

// 功能:檢查下一個詞匯是否符合 tag 標記。

// 范例:if (ScannerIsNext(s, "+|-|*|/")) ScannerNext(s);

BOOL ScannerIsNext(Scanner *s, char *pTags) { // 檢查下一個詞匯的型態

    char tTags[MAX_LEN+1];

    sprintf(tTags, "|%s|", pTags);

    if (strPartOf(s->tag, tTags))

        return TRUE;

    else

        return FALSE;

}

 

// 功能:取得目前字符

// 范例:while (strMember(ch(s), DIGIT)) cnext(s);

char ch(Scanner *s) {

    return s->text[s->i];

}

 

// 功能:前進到下一個字符

// 范例:while (strMember(ch(s), DIGIT)) cnext(s);

void cnext(Scanner *s) {

    s->i++;s->pos++;

}

 

#define OP "+-*/%<=>!&|"        // 運算符號字符集 (用來取得 +,-,*,/, ++, ...)

 

// 功能:Scanner 詞匯分析階段的測試程序。

// 范例:ScannerTest("test.c1");

void ScannerNext(Scanner *s) { // 掃描下一個詞匯

    while (strMember(ch(s), SPACE)) { // 忽略空白

        if (ch(s)=='\n') {

            s->line++;

            s->pos = 1;

        }

        cnext(s);

    }

    if (s->i >= s->len) { // 如果超過程序結尾

        s->tag = kEND; // 傳回 tag = kEND

        s->token[0] = '\0'; // 傳回 token = 空字符串

        return;

    }

    char c = ch(s); // 取得下一個字符

    int begin = s->i; // 記住詞匯開始點

    if (c == '\"') { // 如果是 " 代表字符串開頭

        // 字符串常數 : string = ".."

        cnext(s); // 跳過 "

        while (ch(s) != '\"') cnext(s); // 一直讀到下一個 " 符號為止。

        cnext(s); // 跳過 "

    } else if (strMember(c, OP)) { // 如果是OP(+-*/<=>!等符號)

          // 運算符號 : OP = ++, --, <=, >=, ...

        while (strMember(ch(s), OP)) cnext(s); // 一直讀到不是OP為止

    } else if (strMember(c, DIGIT)) { // 如果是數字

           // 數字常數 : number = 312, 77568, ...

        while (strMember(ch(s), DIGIT)) cnext(s); // 一直讀到不是數字為止

        // 浮點常數 : float = 3.14, ...

        if (ch(s) == '.') cnext(s); // 取得小數點

        while (strMember(ch(s), DIGIT)) cnext(s); // 取得小數部分

    } else if (strMember(c, ALPHA)) { // 如果是英文字母

        // 基本詞匯 : token = int, sum, i, for, if, x1y2z, .... 

        while (strMember(ch(s), ALPHA) || strMember(ch(s), DIGIT))

            cnext(s); // 一直讀到不是英文字母 (或數字)為止

    } else // 其他符號,都解讀為單一字符

        cnext(s); // 傳回單一字符

 

    // 字符串掃描完了,設定 token 為(begin…textIdx) 之間的子字符串

    strSubstr(s->token, s->text, begin, (s->i) - begin);

 

    // 設定 token 的標記 tag

    s->tag = tokenToTag(s->token);

}

 

// 功能:Scanner 詞匯分析階段的測試程序。

// 范例:ScannerTest("test.c1");

char *tokenToTag(char *token) { // 判斷並取得 token的型態

    if (token[0] == '\"') // 如果以符號 " 開頭,則

        return CSTR; // 型態為 STRING

    else if (strMember(token[0], DIGIT)) {// 如果是數字開頭,則

        if (strMember('.', token))

            return CFLOAT;

        else

            return CINT;

    } else { // 否則 (像是 +,-,*,/,>,<,….)

        char *tag = NULL;

        // 若是 keyword (包含 關鍵詞 if, for 與 +, ->, {, ++ 等合法符號

        // 則傳回查表結果 (字符串指針)。

        int i;

        for (i=0; gTokenList[i] != kEND; i++) {

            if (strEqual(token, gTokenList[i])) // 找到該 token,傳回字符串指針。

               return gTokenList[i];

        }

        if (strMember(token[0], ALPHA)) // 如果是英文字母開頭

           return ID; // 則型態為 ID

        else

            ERROR();

    }

}

輸入范例

int x=1, y=2;

 

struct Date {

    int year, month, day;

}

 

struct Person {

    char *name;

    Date birthday;

}

 

int total(int* a) {

    int s = 0;

    for (int i=0; i<10; i++)

        s = s+a[i];

    return s;

}

 

char* getName(Person *p) {

    return p->name;

}

 

int main() {

    int b[10], a=3;

    int t = total(b);

    Person p;

    p.birthday.year = 1990;

    t = 3 + (5 * a);

    return t;

}

測試程序 ScannerTest() 的執行結果

======================ScannerTest()===================

token=int        tag=int        line=1    pos=4

token=x          tag=ID         line=1    pos=6

token==          tag==          line=1    pos=7

token=1          tag=CINT       line=1    pos=8

token=,          tag=,          line=1    pos=9

token=y          tag=ID         line=1    pos=11

token==          tag==          line=1    pos=12

token=2          tag=CINT       line=1    pos=13

token=;          tag=;          line=1    pos=14

token=struct     tag=struct     line=3    pos=8

token=Date       tag=ID         line=3    pos=13

token={          tag={          line=3    pos=15

token=int        tag=int        line=4    pos=9

token=year       tag=ID         line=4    pos=14

token=,          tag=,          line=4    pos=15

token=month      tag=ID         line=4    pos=21

token=,          tag=,          line=4    pos=22

token=day        tag=ID         line=4    pos=26

token=;          tag=;          line=4    pos=27

token=}          tag=}          line=5    pos=3

token=struct     tag=struct     line=7    pos=8

token=Person     tag=ID         line=7    pos=15

token={          tag={          line=7    pos=17

token=char       tag=char       line=8    pos=7

token=*          tag=*          line=8    pos=9

token=name       tag=ID         line=8    pos=13

token=;          tag=;          line=8    pos=14

token=Date       tag=ID         line=9    pos=7

token=birthday   tag=ID         line=9    pos=16

token=;          tag=;          line=9    pos=17

token=}          tag=}          line=10   pos=3

token=int        tag=int        line=12   pos=5

token=total      tag=ID         line=12   pos=11

token=(          tag=(          line=12   pos=12

token=int        tag=int        line=12   pos=15

token=*          tag=*          line=12   pos=16

token=a          tag=ID         line=12   pos=18

token=)          tag=)          line=12   pos=19

token={          tag={          line=12   pos=21

token=int        tag=int        line=13   pos=6

token=s          tag=ID         line=13   pos=8

token==          tag==          line=13   pos=10

token=0          tag=CINT       line=13   pos=12

token=;          tag=;          line=13   pos=13

token=for        tag=for        line=14   pos=6

token=(          tag=(          line=14   pos=8

token=int        tag=int        line=14   pos=11

token=i          tag=ID         line=14   pos=13

token==          tag==          line=14   pos=14

token=0          tag=CINT       line=14   pos=15

token=;          tag=;          line=14   pos=16

token=i          tag=ID         line=14   pos=18

token=<          tag=<          line=14   pos=19

token=10         tag=CINT       line=14   pos=21

token=;          tag=;          line=14   pos=22

token=i          tag=ID         line=14   pos=24

token=++         tag=++         line=14   pos=26

token=)          tag=)          line=14   pos=27

token=s          tag=ID         line=15   pos=5

token==          tag==          line=15   pos=7

token=s          tag=ID         line=15   pos=9

token=+          tag=+          line=15   pos=10

token=a          tag=ID         line=15   pos=11

token=[          tag=[          line=15   pos=12

token=i          tag=ID         line=15   pos=13

token=]          tag=]          line=15   pos=14

token=;          tag=;          line=15   pos=15

token=return     tag=return     line=16   pos=9

token=s          tag=ID         line=16   pos=11

token=;          tag=;          line=16   pos=12

token=}          tag=}          line=17   pos=3

token=char       tag=char       line=19   pos=6

token=*          tag=*          line=19   pos=7

token=getName    tag=ID         line=19   pos=15

token=(          tag=(          line=19   pos=16

token=Person     tag=ID         line=19   pos=22

token=*          tag=*          line=19   pos=24

token=p          tag=ID         line=19   pos=25

token=)          tag=)          line=19   pos=26

token={          tag={          line=19   pos=28

token=return     tag=return     line=20   pos=9

token=p          tag=ID         line=20   pos=11

token=->         tag=->         line=20   pos=13

token=name       tag=ID         line=20   pos=17

token=;          tag=;          line=20   pos=18

token=}          tag=}          line=21   pos=3

token=int        tag=int        line=23   pos=5

token=main       tag=ID         line=23   pos=10

token=(          tag=(          line=23   pos=11

token=)          tag=)          line=23   pos=12

token={          tag={          line=23   pos=14

token=int        tag=int        line=24   pos=6

token=b          tag=ID         line=24   pos=8

token=[          tag=[          line=24   pos=9

token=10         tag=CINT       line=24   pos=11

token=]          tag=]          line=24   pos=12

token=,          tag=,          line=24   pos=13

token=a          tag=ID         line=24   pos=15

token==          tag==          line=24   pos=16

token=3          tag=CINT       line=24   pos=17

token=;          tag=;          line=24   pos=18

token=int        tag=int        line=25   pos=6

token=t          tag=ID         line=25   pos=8

token==          tag==          line=25   pos=10

token=total      tag=ID         line=25   pos=16

token=(          tag=(          line=25   pos=17

token=b          tag=ID         line=25   pos=18

token=)          tag=)          line=25   pos=19

token=;          tag=;          line=25   pos=20

token=Person     tag=ID         line=26   pos=9

token=p          tag=ID         line=26   pos=11

token=;          tag=;          line=26   pos=12

token=p          tag=ID         line=27   pos=4

token=.          tag=.          line=27   pos=5

token=birthday   tag=ID         line=27   pos=13

token=.          tag=.          line=27   pos=14

token=year       tag=ID         line=27   pos=18

token==          tag==          line=27   pos=20

token=1990       tag=CINT       line=27   pos=25

token=;          tag=;          line=27   pos=26

token=t          tag=ID         line=28   pos=4

token==          tag==          line=28   pos=6

token=3          tag=CINT       line=28   pos=8

token=+          tag=+          line=28   pos=10

token=(          tag=(          line=28   pos=12

token=5          tag=CINT       line=28   pos=13

token=*          tag=*          line=28   pos=15

token=a          tag=ID         line=28   pos=17

token=)          tag=)          line=28   pos=18

token=;          tag=;          line=28   pos=19

token=return     tag=return     line=29   pos=9

token=t          tag=ID         line=29   pos=11

token=;          tag=;          line=29   pos=12

token=}          tag=}          line=30   pos=3

token=           tag=_?END?_    line=32   pos=3

Memory:newCount=438 freeCount=438

程序語言 C1 的語法規則

EBNF 語法

// =============== C1 語言的 EBNF 語法規則  ================================== 
// PROG = (STRUCT | METHOD | DECL ; )*
// METHOD = TYPE ** ID(PARAM_LIST?) BLOCK
// STRUCT = struct ID { DECL_LIST ; }
// BLOCK = { BASE* }
// BASE = IF | FOR | WHILE | BLOCK | STMT ;
// IF = if (EXP) BASE (else BASE)?
// FOR = for (STMT ; EXP ; STMT) BASE
// WHILE = while (EXP) BASE
// STMT = return EXP | DECL | PATH (EXP_LIST) | PATH = EXP | PATH OP1
// VAR = ** ID ([ integer ])* (= EXP)?
// EXP = TERM (OP2 TERM)?
// TERM = ( EXP (OP2 EXP)? ) | CINT | CFLOAT | CSTR | PATH
// PATH = ATOM ((.|->) ATOM)*
// ATOM = ID (([ EXP ])* |( EXP_LIST? ))
// DECL = TYPE VAR_LIST
// PARAM = TYPE VAR
// VAR_LIST = VAR (, VAR)*
// EXP_LIST = EXP (, EXP)*
// DECL_LIST = DECL (; DECL)*
// PARAM_LIST = PARAM (, PARAM)*
// TYPE = (byte | char | int | float | ID) // 最后一個 ID 必須是 TYPE [STRUCT]
// ID = [A-Za-z_][0-9A-Za-z_]*
// CINT = [0-9]+
// CFLOAT = [0-9]+.[0-9]+
// CSTR = ".*"
// OP2 = +|-|/|*|%|&|&&|^|<<|>>|<|>|<=|>=|==|!=|  與 | , ||
// OP1 = ++ | --

C1 語言的剖析器 -- CC1

開放計算機計划 — 最新版本下載

  1. ss1v0.50.zip — 包含虛擬機 VM1, 組譯器 AS1, 編譯程序 CC1 (剖析器完成,符號表完成,程序代碼產生修改中)

檔案:Parser.h

// =============== C1 語言的 EBNF 語法規則  ================================== 
// PROG = (STRUCT | METHOD | DECL ; )*
// METHOD = TYPE ** ID(PARAM_LIST?) BLOCK
// STRUCT = struct ID { DECL_LIST ; }
// BLOCK = { BASE* }
// BASE = IF | FOR | WHILE | BLOCK | STMT ;
// IF = if (EXP) BASE (else BASE)?
// FOR = for (STMT ; EXP ; STMT) BASE
// WHILE = while (EXP) BASE
// STMT = return EXP | DECL | PATH (EXP_LIST) | PATH = EXP | PATH OP1
// VAR = ** ID ([ integer ])* (= EXP)?
// EXP = TERM (OP2 TERM)?
// TERM = ( EXP (OP2 EXP)? ) | CINT | CFLOAT | CSTR | PATH
// PATH = ATOM ((.|->) ATOM)*
// ATOM = ID (([ EXP ])* |( EXP_LIST? ))
// DECL = TYPE VAR_LIST
// PARAM = TYPE VAR
// VAR_LIST = VAR (, VAR)*
// EXP_LIST = EXP (, EXP)*
// DECL_LIST = DECL (; DECL)*
// PARAM_LIST = PARAM (, PARAM)*
// TYPE = (byte | char | int | float | ID) // 最后一個 ID 必須是 TYPE [STRUCT]
// ID = [A-Za-z_][0-9A-Za-z_]*
// CINT = [0-9]+
// CFLOAT = [0-9]+.[0-9]+
// CSTR = ".*"
// OP2 = +|-|/|*|%|&|&&|^|<<|>>|<|>|<=|>=|==|!=|  與 | , ||
// OP1 = ++ | --
 
#ifndef PARSER_H
#define PARSER_H
 
#include "Scanner.h"
#include "Tree.h"
#include "Lib.h"
#include "Semantic.h"
 
typedef struct {           // 剖析器的對象結構      
    Array *nodeStack;      // 剖析過程用的節點 node 堆棧 (從樹根到目前節點間的所有節點形成的堆棧)。 
    Array *blockStack;     // 符號區塊堆棧,變量 id 的區塊范圍,像是 PROG, STRUCT, METHOD, BLOCK 等。
    Var   decl;            // 在 parseType 時用來記住型態的變量。 
    Scanner *scanner;      // 詞匯掃描儀 (Lexical Analysis)
    SymTable *symTable;    // 符號表 
    char  spaces[MAX_LEN]; // 用來暫存空白字符串的變量。 
} Parser;                                                     
 
Tree *parse(char *text, SymTable *symTable);// 剖析器的主程序
Parser *ParserNew(Scanner *scanner, SymTable *symTable); // 剖析器的建構函數
Tree *ParserParse(Parser *p, char *text);   // 剖析器的剖析函數
void ParserFree(Parser *parser);           // 釋放內存
 
Tree* parseProg(Parser *p);     // PROG = (STRUCT | METHOD | DECL ; )*
Tree* parseBase(Parser *p);     // BASE = IF | FOR | WHILE | BLOCK | STMT ;
Tree* parseStruct(Parser *p);   // STRUCT = struct ID { DECL_LIST ; }
Tree* parseMethod(Parser *p);   // METHOD = TYPE ** ID(PARAM_LIST?) BLOCK
Tree* parseDecl(Parser *p);     // DECL = TYPE VAR_LIST
Tree* parseIf(Parser *p);       // IF = if (EXP) BASE (else BASE)?
Tree* parseFor(Parser *p);      // FOR = for (STMT ; EXP ; STMT) BASE
Tree* parseWhile(Parser *p);    // WHILE = while (EXP) BASE
Tree* parseStmt(Parser *p);     // STMT = return EXP | DECL | PATH (EXP_LIST) | PATH = EXP | PATH OP1
Tree* parseBlock(Parser *p);    // BLOCK = { BASE* }
Tree* parseVar(Parser *p);      // VAR = ** ID ([ integer ])* (= EXP)?
Tree* parseExp(Parser *p);      // EXP = TERM (OP2 TERM)?
Tree* parseTerm(Parser *p);     // TERM = ( EXP (OP2 EXP)? ) | CINT | CFLOAT | CSTR | PATH
Tree* parsePath(Parser *p);     // PATH = ATOM ((.|->) ATOM)*
Tree* parseAtom(Parser *p);     // ATOM = ID (([ EXP ])* |( EXP_LIST? ))
Tree* parseDecl(Parser *p);     // DECL = TYPE VAR_LIST
Tree* parseParam(Parser *p);    // PARAM = TYPE VAR
Tree* parseVarList(Parser *p);  // VAR_LIST = VAR (, VAR)*
Tree* parseExpList(Parser *p);  // EXP_LIST = EXP (, EXP)*
Tree* parseDeclList(Parser *p); // DECL_LIST = DECL (; DECL)*
Tree* parseParamList(Parser *p);// PARAM_LIST = PARAM (, PARAM)*
Tree* parseType(Parser *p);     // TYPE = (byte | char | int | float | ID)
Tree* parseId(Parser *p);       // ID = [A-Za-z_][0-9A-Za-z_]*
 
BOOL isMethod(Parser *p); // 判斷接下來是否為 METHOD 程序區塊。 
BOOL isDecl(Parser *p);  // 判斷接下來是否為 DECL 宣告語句 
// push() : 功能:建立 tag 標記的非終端節點,並建立語意結構,然后推入堆棧中 
//          范例:Tree *node = push(p, IF, SemIF);
#define push(p, tag, SemType) sem=ObjNew(SemType, 1);Tree *node=push1(p, tag);node->sem=sem; 
Tree *push1(Parser *p, char* tag);  // 建立標記為 tag 的新子樹。 
Tree *pop(Parser *p, char* tag);    // 從堆棧中取出剖析完成的子樹,並檢查標記是否為 tag。 
BOOL isNext(Parser *p, char *tags); // 檢查下一個 token 的 tag 是否屬於 tags 標記之一。 
Tree *next(Parser *p, char *tags);  // 取得下一個 token,並確認其 tag 為 tags 標記之一。 
char *token(Tree *node);            // 取得樹葉節點 node 的 token。 
 
void pushBlock(Parser *p, Symbol *sym); // 將區塊符號推入堆棧 
#define popBlock(p) ArrayPop(p->blockStack) // 從堆棧取出區塊符號 
#define peekBlock(p) ArrayPeek(p->blockStack) // 取得最上面的區塊符號 
 
// Token 的集合,用來檢查是關鍵詞,操作數,基本型態,或者只是變量 ID。 
#define SET_KEYWORDS "|if|else|for|while|return|def|int|byte|char|float|struct|"
#define SET_OP1 "|++|--|"
#define SET_OP2 "|+|-|*|/|%|^|&|<<|>>|==|!=|<=|>=|<|>|&&||||"
#define SET_BTYPE "|int|byte|char|float|"
 
#endif

檔案:Parser.c

#include "Parser.h"
 
// 功能:Parser 剖析階段的測試程序。
// 范例:ParserTest("test.c1");
void ParserTest(char *fileName) {
    debug("=======ParserTest()==========\n");
    SymTable *symTable = SymTableNew(); // 建立符號表 
    char *text = fileToStr(fileName);   // 讀入 C1 語言程序代碼,成為一字符串 
    Tree *tree = parse(text, symTable); // 剖析該程序代碼,建立剖析樹與符號表。 
    SymTableDebug(symTable);            // 印出符號表。 
    TreeFree(tree);                     // 釋放剖析樹。 
    strFree(text);                      // 釋放程序代碼字符串 
    SymTableFree(symTable);             // 釋放符號表 
    memCheck();                         // 檢查內存
}
 
// 功能:剖析階段的主程序 
// 范例:Tree *tree = parse(text, symTable); 
Tree *parse(char *text, SymTable *symTable) {  // 剖析器的主要函數
    Scanner *scanner = ScannerNew(text);       // 建立掃描儀 (詞匯分析用) 
    Parser *p=ParserNew(scanner, symTable);    // 建立剖析器 (語法剖析用)
    Tree *tree = ParserParse(p, text);         // 剖析程序為語法樹 
    ParserFree(p);                             // 釋放頗析樹 
    ScannerFree(scanner);                      // 釋放掃描儀 
    return tree;                               // 傳回剖析器
}
 
// 功能:建立新的剖析器 Parser 對象 
// 范例:Parser *p = ParserNew(scanner, symTable);
Parser *ParserNew(Scanner *scanner, SymTable *symTable) {
    Parser *p = ObjNew(Parser, 1); // 分配剖析器空間 
    p->nodeStack = ArrayNew(10); // 分配 nodeStack 堆棧空間 
    p->blockStack = ArrayNew(10); // 分配 blockStack 堆棧空間 
    p->scanner = scanner; // 設定掃瞄器 
    p->symTable = symTable; // 設定符號表 
    ScannerNext(scanner); // 本剖析器總是先取得下一個 token,以便 isNext() 進行判斷。
    return p;
}
 
// 功能:釋放剖析器對象的內存 
// 范例:ParserFree(p); 
void ParserFree(Parser *p) {
    ArrayFree(p->blockStack, (FuncPtr1) BlockFree);  // 釋放 blockStack 堆棧空間 
    ArrayFree(p->nodeStack, NULL); // 釋放 nodeStack 堆棧空間 
    ObjFree(p); // 釋放剖析器空間
}
 
// 功能:剖析整個程序代碼 (text)。 
// 范例:ParserParse(p, text); 
Tree *ParserParse(Parser *p, char *text) { // 剖析對象的主函數
    debug("======= parsing ========\n");
    Tree *tree = parseProg(p); // 開始剖析整個程序 (PROG),並建立語法樹 p->tree
    if (p->nodeStack->count != 0) { // 如果剖析完成后堆棧是空的,那就是剖析成功
        ERROR();// 否則就是剖析失敗,有語法錯誤
    }
    return tree;
}
 
// 語法:PROG = (STRUCT | METHOD | DECL ; )* 
// 功能:剖析 PROG 並建立語法樹 
// 范例:Tree *prog = parseProg(p); 
Tree *parseProg(Parser *p) { // 剖析 PROG 規則
    SemProg *sem=push(p, PROG, SemProg); // 建立 PROG 的語法樹及語意結構
    pushBlock(p, Global);  // 推入全局區塊 
    while (!isNext(p, kEND)) {     //  剖析 BASE,直到程序結束或碰到 } 為止
        if (isNext(p, "struct"))
           parseStruct(p);
        else { // 由於 METHOD 與 DECL 的開頭都是 TYPE **ID ...,因此必須判斷是哪一種情況。
           if (isMethod(p)) { // 向前偷看后發現是 TYPE **ID(,所以是 Method 
                parseMethod(p);
           } else { // 否則就必須是 DECL ; 
                parseDecl(p);
                next(p, ";");
          }
        }
    }
    popBlock(p); // 取出全局區塊
    return pop(p, PROG); // 取出 PROG 的整棵語法樹 
}
 
// 語法:METHOD = TYPE **ID (PARAM_LIST?) BLOCK
// 功能:判斷到底接下來是否為 METHOD,是的話傳回 TRUE,否則傳回 FALSE 
//       由於 METHOD 與 DECL 的開頭都是 TYPE **ID ...,因此必須判斷是哪一種情況。
//       本函數會向前偷看,如果發現是 TYPE **ID(,那就應該是 Method。 
// 范例:if (isMethod(p)) parseMethod(p);
BOOL isMethod(Parser *p) {
    BOOL rzFlag = TRUE;
    Scanner *s = p->scanner; // s=掃描儀 
    ScannerStore(s); // 儲存掃描儀狀態 
    if (isNext(p, "int|byte|char|float|ID")) // 偷看 TYPE
        ScannerNext(s); // 略過 TYPE
    else 
        rzFlag=FALSE;
    while (isNext(p, "*")) ScannerNext(s); // 偷看並略過星號 
    if (isNext(p, ID))  // 偷看 ID
        ScannerNext(s); // 略過 ID 
    else 
        rzFlag=FALSE;
    if (!isNext(p, "(")) rzFlag=FALSE; // 如果接下來是 (,那么就應該是 Method。
    ScannerRestore(s); // 恢復掃描儀狀態。 
    return rzFlag;
}
 
// 語法:METHOD = TYPE **ID (PARAM_LIST?) BLOCK
// 功能:剖析 METHOD 並建立語法樹
// 范例:Tree *method = parseMethod(p); 
Tree* parseMethod(Parser *p) {
    SemMethod *sem=push(p, METHOD, SemMethod); // 建立 METHOD 的語法樹及語意結構
    sem->type=parseType(p); // 剖析 TYPE
 
    // 剖析 ** (n 個星號, n>=0)
    int starCount = 0; // 星號數量的初始值 
    while (isNext(p, "*")) { // 如果下一個是星號 
        next(p, "*"); // 取得該星號 
        starCount ++; // 將星號數加一 
    }
    sem->id = next(p, ID); // 剖析 ID
 
    // 建立 ID 的符號記錄 Symbol(id, METHOD) 
    char *id = token(sem->id);    // 取得符號名稱。 
    Symbol *sym = SymNew(Global, id, SymMethod); // 建立符號記錄 
    Method *method = sym->typePtr; // 設定 method 結構。 
    method->ret.typeSym = p->decl.typeSym; // 設定傳回符號 
    method->ret.starCount = p->decl.starCount; // 設定傳回符號的星號個數。 
    SymTablePut(p->symTable, sym); // 將符號記錄放入符號表中 
 
    pushBlock(p, sym); // 將 Method 符號推入區塊堆棧
 
    sem->symMethod = sym; // 設定語意結構 sem 的 symMethod 字段 
 
    // 剖析參數部分 (PARAM_LIST?) 
    next(p, "(");
    if (!isNext(p, ")")) // 如果接下來不是 ),那就是有 PARAM_LIST 
        sem->paramList = parseParamList(p); // 剖析 PARAM_LIST
    next(p, ")");
 
    sem->block = parseBlock(p); // 剖析 BLOCK
 
    popBlock(p);
    return pop(p, METHOD); // 取出 METHOD 的語法樹。
}
 
// 語法:STRUCT = struct ID { (DECL ;)* }
// 功能:剖析 STRUCT 並建立語法樹
// 范例:Tree *s = parseStruct(p);
Tree* parseStruct(Parser *p) {
    SemStruct *sem=push(p, STRUCT, SemStruct); // 建立 STRUCT 語法樹 
 
    next(p, "struct"); // 剖析 struct 
    sem->id = next(p, ID); // 剖析 ID
 
    // 建立 ID 的符號記錄 Symbol(id, METHOD) 
    char *id = token(sem->id);    // 取得符號名稱。 
    Symbol *sym = SymNew(Global, id, SymStruct); // 建立符號 -- 結構。 
    SymTablePut(p->symTable, sym); // 放入符號表。 
 
    sem->symStruct = sym;  // 設定語意結構 sem 的 symMethod 字段 
 
    pushBlock(p, sym); // 將 Struct 區塊推入堆棧
 
    // 剖析 { (DECL ;)* }
    next(p, "{"); 
    while (!isNext(p, "}")) {
        parseDecl(p);
        next(p, ";");
    }
    next(p, "}");
 
    popBlock(p); // 從區塊堆棧中取出 Struct 區塊 
 
    return pop(p, STRUCT); // 取出 STRUCT 的語法樹。
}
 
// 語法:BASE = IF | FOR | WHILE | BLOCK | STMT ;
// 功能:剖析 BASE 並建立 BASE 的語法樹 
// 范例:Tree *base = parseBase(p); 
Tree* parseBase(Parser *p) { // 剖析 BASE 規則
    SemBase *sem=push(p, BASE, SemBase); // 建立 BASE 的語法樹及語意結構
    if (isNext(p, "if")) // 如果下一個詞匯是 if
        parseIf(p); // 剖析 IF 程序段 
    else if (isNext(p, "for")) // 如果下一個詞匯是 for
        parseFor(p); // 剖析 FOR 程序段
    else if (isNext(p, "while")) // 如果下一個詞匯是 for
        parseWhile(p); // 剖析 WHILE 程序段
    else if (isNext(p, "{")) // 如果下一個詞匯是 {
        parseBlock(p); // 剖析 BLOCK 程序段
    else { // 否則應該是 STMT ; 
        parseStmt(p); // 剖析 STMT 程序段
        next(p, ";"); // 取得分號 ;  
    }
    return pop(p, BASE); // 取出 BASE 的剖析樹
}
 
// 語法:BLOCK = { BASE* }
// 功能:剖析 BLOCK 並建立語法樹 
// 范例:Tree *block = parseBlock(p); 
Tree* parseBlock(Parser *p) {
    SemBlock *sem=push(p, BLOCK, SemBlock); // 建立 BLOCK 的語法樹及語意結構 
 
    Symbol *pblock = peekBlock(p); // 取得父區塊 
    Symbol *sym = SymNew(pblock, "", SymBlock); // 建立區塊符號 
    Block *block = sym->typePtr; // 設定 block 結構。 
    SymTablePut(p->symTable, sym); // 將本區塊加入到符號表中 
 
    sem->symBlock = sym; // 設定本節點的語意結構 symBlock 為本區塊
 
    pushBlock(p, sym); // 將符號推入區塊堆棧
 
    next(p, "{"); // 剖析 { BASE* } 
    while (!isNext(p, "}")) 
        parseBase(p);
    next(p, "}");
 
    popBlock(p);  // 從區塊堆棧中取出 Block 區塊
 
    return pop(p, BLOCK); // 取出 BLOCK 的語法樹。
}
 
// 語法:FOR = for (STMT ; EXP ; STMT) BASE
// 功能:剖析 FOR 並建立語法樹
// 范例:Tree *f = parseFor(p); 
Tree* parseFor(Parser *p) {                  
    SemFor *sem=push(p, FOR, SemFor); // 建立 FOR 的語法樹及語意結構
    next(p, "for");                   // 取得 for
    next(p, "(");                     // 取得 (
    sem->stmt1 = parseStmt(p);        // 剖析 STMT
    next(p, ";");                     // 取得 ;
    sem->exp = parseExp(p);           // 剖析 EXP
    next(p, ";");                     // 取得 ;
    sem->stmt2 = parseStmt(p);        // 剖析 STMT
    next(p, ")");                     // 取得 )
    parseBase(p);                     // 剖析 BASE
    return pop(p, FOR);               // 取出 FOR 的語法樹。
}
 
// 語法:IF = if (EXP) BASE (else BASE)?
// 功能:剖析 IF 並建立語法樹
// 范例:Tree *f = parseIf(p);
Tree* parseIf(Parser *p) {
    SemIf *sem=push(p, IF, SemIf);     // 建立 IF 的語法樹及語意結構
    next(p, "if");                     // 取得 if
    next(p, "(");                      // 取得 (
    sem->exp = parseExp(p);            // 剖析 EXP 
    next(p, ")");                      // 取得 )
    sem->base1 = parseBase(p);         // 剖析 BASE
    if (isNext(p, "else")) {           // 如果下一個是 else 
        next(p, "else");               // 取得 else
        sem->base2 = parseBase(p);     // 剖析下一個 BASE
    }
    return pop(p, IF);                 // 取出 IF 的語法樹。
}
 
// 語法:WHILE = while (EXP) BASE
// 功能:剖析 WHILE 並建立語法樹
// 范例:Tree *w = parseWhile(p);
Tree* parseWhile(Parser *p) {
    SemWhile *sem=push(p, WHILE, SemWhile);// 建立 WHILE 的語法樹及語意結構
    next(p, "while");                      // 取得 while
    next(p, "(");                          // 取得 (
    sem->exp = parseExp(p);                // 剖析 EXP
    next(p, ")");                          // 取得 )
    sem->base = parseBase(p);              // 剖析 BASE
    return pop(p, WHILE);                  // 取出 WHILE 的語法樹。
}
 
// 語法:STMT = return EXP | DECL | PATH (EXP_LIST) | PATH = EXP | PATH OP1
// 功能:剖析 STMT 並建立語法樹
// 范例:Tree *stmt = parseStmt(p); 
Tree* parseStmt(Parser *p) {
    SemStmt *sem=push(p, STMT, SemStmt);// 建立 STMT 的語法樹及語意結構
    if (isNext(p, "return")) { // 如果下一個是 return,就剖析 return EXP 
        next(p, "return");
        sem->exp = parseExp(p);
    } else if (isDecl(p)) { // 如果是 DECL 
        sem->decl = parseDecl(p); // 剖析 DECL 
    } else { // 否則下一個必須是 PATH 
        sem->path = parsePath(p); // 剖析 PATH 
        if (isNext(p, "(")) { // 下一個是 (,代表是 PATH (EXP_LIST) 的情況 
            next(p, "(");
            sem->expList = parseExpList(p);
            next(p, ")");
        } else if (isNext(p, "=")) { // 下一個是 =,代表是 PATH = EXP 的情況 
            next(p, "=");
            sem->exp = parseExp(p);
        } else if (isNext(p, SET_OP1)) { // 下一個是OP1,代表是 PATH OP1 的情況 
            next(p, SET_OP1);
        } else
            ERROR();
    }
    return pop(p, STMT); // 取出 STMT 的語法樹。
}
 
// 語法:PATH = ATOM ((.|->) ATOM)*
// 功能:剖析 PATH 並建立語法樹 
// 范例:Tree *path = parsePath(p);
Tree* parsePath(Parser *p) {
    SemPath *sem=push(p, PATH, SemPath);// 建立 PATH 的語法樹及語意結構
    parseAtom(p);                       // 剖析 DECL 
    while (isNext(p, ".|->")) {         // 不斷取得 (.|->) ATOM
        next(p, ".|->");
        parseAtom(p);
    }
    return pop(p, PATH);                // 取出 PATH 的語法樹。
}
 
// 語法:ATOM = ID (([ EXP ])* |( EXP_LIST? ))
// 功能:剖析 ATOM 並建立語法樹
// 范例:Tree *atom = parseAtom(p); 
Tree* parseAtom(Parser *p) {
    SemAtom *sem=push(p, ATOM, SemAtom); // 建立 ATOM 的語法樹及語意結構
    sem->id = next(p, ID); // 取得 ID
    sem->subTag = ID; // 設定子標簽 (ID, CALL 或 ARRAY_MEMBER),讓語義分析與程序產生時使用
    if (isNext(p, "(")) { // 如果接下來是 (,則應該是函數呼叫 ID ( EXP_LIST? )
       next(p, "(");
       if (!isNext(p, ")"))
           sem->expList = parseExpList(p);
       next(p, ")");
       sem->subTag = CALL;
    } else if (isNext(p, "[")) { // 如果接下來是 [,則應該是數組宣告 ID ([ EXP ])*
        sem->subTag = ARRAY_MEMBER;
        while (isNext(p, "[")) {
            next(p, "[");
            Tree *exp = parseExp(p);
            next(p, "]");
        }
    }
    return pop(p, ATOM); // 取出 ATOM 的語法樹。
}
 
// 語法:PARAM = TYPE VAR
// 功能:剖析 PARAM 並建立語法樹
// 范例:Tree *param = parseParam(p); 
Tree* parseParam(Parser *p) {
    SemParam *sem = push(p, PARAM, SemParam);// 建立 PARAM 的語法樹及語意結構
    sem->type = parseType(p); // 剖析 TYPE
    sem->var = parseVar(p); // 剖析 VAR
    return pop(p, PARAM); // 取出 PARAM 的語法樹。
}
 
// 語法:DECL = TYPE VAR_LIST
// 功能:判斷到底接下來是否為 DECL,是的話傳回 TRUE,否則傳回 FALSE 
//       本函數會向前偷看,如果發現是 (int|byte|char|float|ID)** ID,那就是 DECL
// 范例:if (isDecl(p)) parseDecl(p);
BOOL isDecl(Parser *p) {
    BOOL rzFlag = TRUE;
    Scanner *s = p->scanner;                // s=掃描儀 
    ScannerStore(s);                        // 儲存掃描儀狀態 
    if (isNext(p, "int|byte|char|float|ID"))// 偷看 TYPE
        ScannerNext(s);                     // 略過 TYPE
    else 
        rzFlag=FALSE;
    while (isNext(p, "*")) ScannerNext(s);  // 偷看並略過星號 
    if (!isNext(p, ID)) rzFlag=FALSE;       // 偷看 ID
    ScannerRestore(s);                      // 恢復掃描儀狀態。 
    return rzFlag;
}
 
// 語法:DECL = TYPE VAR_LIST
// 功能:剖析 PROG 並建立語法樹 
// 范例:Tree *decl = parseDecl(p);
Tree* parseDecl(Parser *p) {
    SemDecl *sem = push(p, DECL, SemDecl);// 建立 DECL 的語法樹及語意結構
    sem->type = parseType(p); // 剖析 TYPE
    sem->varList = parseVarList(p); // 剖析 VAR_LIST
    return pop(p, DECL); // 取出 DECL 的語法樹。
}
 
// 語法:TYPE = (int | byte | char | float | ID) // ID is STRUCT_TYPE
// 功能:剖析 TYPE 並建立語法樹
// 范例:Tree *type = parseType(p); 
Tree* parseType(Parser *p) {
    SemType *sem=push(p, TYPE, SemType);// 建立 TYPE 的語法樹及語意結構
    Tree *type = next(p, "int|byte|char|float|ID");  // 取得 (int | byte | char | float | ID)
    char *typeName = token(type); // 取得型態名稱 
       p->decl.typeSym = SymTableGet(p->symTable, Global, typeName); // 從符號表中查出該型態的符號
       ASSERT(p->decl.typeSym != NULL);
    return pop(p, TYPE); // 取出 TYPE 的語法樹。
}
 
// 語法:VAR = ** ID ([ CINT ])* (= EXP)?
// 功能:剖析 VAR 並建立語法樹
// 范例:Tree *var = parseVar(p); 
Tree* parseVar(Parser *p) {
    SemVar *sem = push(p, VAR, SemVar);    // 建立 VAR 的語法樹及語意結構
    int starCount = 0; // 星號數量初始值為 0 
    while (isNext(p, "*")) { // 剖析 ** 
        next(p, "*"); // 取得星號 
        starCount ++; // 計算星號數量 
    }
    sem->id = next(p, ID); // 剖析 ID
 
    // 建立 ID 的符號記錄 Symbol(id, SymVar)
    Symbol *pblock = peekBlock(p); // 取得父區塊符號 
    char *id = token(sem->id); // 取得變量名稱 
    Symbol *sym = SymNew(pblock, id, SymVar); // 建立變量符號 
    Var *var = sym->typePtr; // 取出變量結構 
    var->starCount = starCount; // 設定變量結構中的星號數量 
    var->typeSym = p->decl.typeSym; // 設定變量結構中的符號 
    var->dimCount = 0;  // 設定變量結構中的數組維度 
    SymTablePut(p->symTable, sym); // 將變量加入符號表中 
 
    while (isNext(p, "[")) { // 剖析 ([ CINT ])*
        next(p, "[");
        Tree *cint = next(p, "CINT");
        ASSERT(var->dimCount<DIM_MAX);
        var->dim[var->dimCount++] = atoi(token(cint));
        next(p, "]");
    }
 
    if (pblock->symType == SymStruct) { // 如果父區塊是 Struct,那此 VAR 就是字段宣告。 
        Struct *stru = pblock->typePtr;
        ArrayAdd(stru->fields, sym); // 將變量加入 Struct 的字段 fields 中。 
    } else if (pblock->symType == SymMethod) { // 如果父區塊是 Method,那此 VAR 就是參數宣告。 
        Method *method = pblock->typePtr;
        ArrayAdd(method->params, sym); // 將變數加入 Method 的參數 params 中。 
    } else if (pblock->symType == SymBlock) { // 如果父區塊是 Block,那此 VAR 就是局部變量。
        Block *block = pblock->typePtr;
        ArrayAdd(block->vars, sym);// 將變數加入 Block 的局部變量 vars 中。 
    }
 
    if (isNext(p, "=")) { // 剖析 (= EXP)?
        next(p, "=");
        sem->exp = parseExp(p);
    }
    return pop(p, VAR);  // 取出 VAR 的語法樹。
}
 
// 語法:EXP = TERM (OP2 TERM)?
// 功能:剖析 EXP 並建立語法樹
// 范例:Tree *exp = parseExp(p); 
Tree* parseExp(Parser *p) {
    SemExp *sem = push(p, EXP, SemExp);// 建立 EXP 的語法樹及語意結構
    sem->term1 = parseTerm(p); // 剖析 TERM 
    if (isNext(p, SET_OP2)) { // 如果接下來是 OP2 ,則剖析 (OP2 TERM)?
        sem->op = next(p, SET_OP2);
        sem->term2 = parseTerm(p);
    }
    return pop(p, EXP); // 取出 EXP 的語法樹。
}
 
// 語法:TERM = ( EXP (OP2 EXP)? ) | CINT | CFLOAT | CSTR | PATH
// 功能:剖析 TERM 並建立語法樹
// 范例:Tree *term = parseTerm(p); 
Tree* parseTerm(Parser *p) {
    SemTerm *sem = push(p, TERM, SemTerm);// 建立 TERM 的語法樹及語意結構
    if (isNext(p, "(")) { // 如果下一個是 (,那就是 ( EXP (OP2 EXP)? ) 的情況 
        next(p, "(");
        sem->exp1 = parseExp(p);
        if (!isNext(p, ")")) { // 看看是否有 (OP2 EXP)
            next(p, SET_OP2);
            sem->exp2 = parseExp(p);
        }
        next(p, ")");
    } else if (isNext(p, "CINT|CFLOAT|CSTR")) { // 如果是 CINT, CFLOAT 或 CSTR 
        next(p, "CINT|CFLOAT|CSTR"); // 取得 CINT, CFLOAT 或 CSTR 
    } else
        parsePath(p); // 否則應該是 PATH,剖析之 
    return pop(p, TERM); // 取出 TERM 的語法樹。
}
 
// 語法:VAR_LIST = VAR (, VAR)*
// 功能:剖析 VarList 並建立語法樹
// 范例:Tree *varList = parseVarList(p); 
Tree* parseVarList(Parser *p) {
    SemVarList *sem = push(p, VAR_LIST, SemVarList);// 建立 VAR_LIST 的語法樹及語意結構
    parseVar(p); // 剖析 VAR 
    while (isNext(p, ",")) { // 剖析 (,VAR)* 
        next(p, ",");
        parseVar(p);
    }
    return pop(p, VAR_LIST); // 取出 VAR_LIST 的語法樹。
}
 
// 語法:EXP_LIST = EXP (, EXP)*
// 功能:剖析 EXP_LIST 並建立語法樹
// 范例:Tree *expList = parseExpList(p); 
Tree* parseExpList(Parser *p) {
    SemExpList *sem = push(p, EXP_LIST, SemExpList);// 建立 EXP_LIST 的語法樹及語意結構
    parseExp(p); // 剖析 EXP
    while (isNext(p, ",")) { // 剖析 (, EXP)*
        next(p, ",");
        parseExp(p);
    }
    return pop(p, EXP_LIST); // 取出 EXP_LIST 的語法樹。
}
 
// 語法:DECL_LIST = DECL (; DECL)*
// 功能:剖析 DECL_LIST 並建立語法樹
// 范例:Tree *declList = parseDeclList(p); 
Tree* parseDeclList(Parser *p) {
    SemDeclList *sem=push(p, DECL_LIST, SemDeclList);// 建立 DECL_LIST 的語法樹及語意結構
    parseDecl(p); // 剖析 DECL
    while (isNext(p, ";")) { // 剖析 (; DECL)*
        next(p, ";");
           parseDecl(p);
    }
    return pop(p, DECL_LIST); // 取出 DECL_LIST 的語法樹。
}
 
// 語法:PARAM_LIST = PARAM (, PARAM)*
// 功能:剖析 PARAM_LIST 並建立語法樹
// 范例:Tree *paramList = parseParamList(p); 
Tree* parseParamList(Parser *p) {
    SemParamList *sem=push(p, PARAM_LIST, SemParamList);// 建立 PARAM_LIST 的語法樹及語意結構
    parseParam(p); // 剖析 PARAM
    while (isNext(p, ";")) { // 剖析 (, PARAM)*
        next(p, ";");
           parseParam(p);
    }
    return pop(p, PARAM_LIST); // 取出 PARAM_LIST 的語法樹。
}
 
// ========================== 基本函數 ====================================
// 功能:取得 p->nodeStack->count 個空白, 以便印出剖析樹時能有階層式的排版。 
// 范例:debug("%s KEY:%s\n", level(p), s->tag);
char* level(Parser *p) {
    return strFill(p->spaces, ' ', p->nodeStack->count);
}
 
// 功能:判斷下一個 token 的標記是否為 tags 其中之一
// 范例:if (isNext(p, "struct")) parseStruct(p);
BOOL isNext(Parser *p, char *tags) { // 檢查下一個詞匯的型態
    Scanner *s = p->scanner;
    char tTags[MAX_LEN+1];
    sprintf(tTags, "|%s|", tags);
    if (strPartOf(s->tag, tTags))
        return TRUE;
    else
        return FALSE;
}
 
// 功能:取得下一個 token (標記必須為 tag 其中之一),然后掛到剖析樹上 
// 范例:Tree *node = next(p, "CINT|CFLOAT|CSTR");
Tree *next(Parser *p, char *tags) { // 檢查下一個詞匯的型態
    Scanner *s = p->scanner;
    if (isNext(p, tags)) { // 如果是pTypes型態之一
        Tree *child = TreeNew(s->tag);
        child->sem = strNew(s->token);  // 建立詞匯節點(token,type)
        Tree *parentTree = ArrayPeek(p->nodeStack); // 取得父節點,
        TreeAddChild(parentTree, child); // 加入父節點成為子樹
        if (strEqual(s->tag, s->token))
           debug("%s KEY:%s\n", level(p), s->tag);
        else
           debug("%s %s:%s\n", level(p), s->tag, s->token);
        ScannerNext(s);
        return child; // 傳回該詞匯
    } else { // 否則(下一個節點型態錯誤)
        debug("next():token=%s, tag=%s is not in tag(%s)\n", s->token, s->tag, tags); // 印出錯誤訊息
        ERROR();
        return NULL;
    }
}
 
// 功能:建立 tag 標記的非終端節點,並且推入堆棧中 
// 范例:Tree *node = push1(p, IF);
Tree* push1(Parser *p, char* tag) { // 建立 pType 型態的子樹,推入堆棧中
    debug("%s+%s\n", level(p), tag);
    Tree *node = TreeNew(tag);
    ArrayPush(p->nodeStack, node);
    return node;
}
 
// 功能:取出 tag 標記的非終端節點,然后掛到剖析樹上 
// 范例:Tree *node = pop(p, IF);
Tree* pop(Parser *p, char* tag) { // 取出 pTag 標記的子樹
    Tree *tree = ArrayPop(p->nodeStack); // 取得堆棧最上層的子樹
    debug("%s-%s\n", level(p), tree->tag); // 印出以便觀察
    if (strcmp(tree->tag, tag)!=0) {  // 如果型態不符合
        debug("pop(%s):should be %s\n",tree->tag, tag); //  印出錯誤訊息
        ERROR();
    }
    if (p->nodeStack->count > 0) { // 如果堆棧不是空的
      Tree *parentTree = ArrayPeek(p->nodeStack); //  取出上一層剖析樹
      TreeAddChild(parentTree, tree);  //  將建構完成的剖析樹掛到樹上,成為子樹
    }
    return tree;
}
 
// 功能:取得樹葉節點中的詞匯 (token) 
// 范例:char *token = token(node);
char *token(Tree *node) {
    return (char*) node->sem;
}
 
// 功能:將區塊符號 sym 推入區塊堆棧中 
// 范例:pushBlock(p, sym);
void pushBlock(Parser *p, Symbol *sym) {
    ArrayPush(p->blockStack, sym);
}

測試輸入程序:被剖析者 — test.c1

int x=1, y=2;
 
struct Date {
    int year, month, day;
}
 
struct Person {
    char *name;
    Date birthday;
}
 
int total(int* a) {
    int s = 0;
    for (int i=0; i<10; i++)
        s = s+a[i];
    return s;
}
 
char* getName(Person *p) {
    return p->name;
}
 
int main() {
    int b[10], a=3;
    int t = total(b);
    Person p;
    p.birthday.year = 1990;
    t = 3 + (5 * a);
    return t;
}

剖析器的輸出結果

======= parsing ========
+PROG
 +DECL
  +TYPE
    KEY:int
  -TYPE
  +VAR_LIST
   +VAR
     ID:x
V    x        0    003E2558 0040A340 int:*0:[0]      
     KEY:=
    +EXP
     +TERM
       CINT:1
     -TERM
    -EXP
   -VAR
    KEY:,
   +VAR
     ID:y
V    y        0    003E6590 0040A340 int:*0:[0]      
     KEY:=
    +EXP
     +TERM
       CINT:2
     -TERM
    -EXP
   -VAR
  -VAR_LIST
 -DECL
  KEY:;
 +STRUCT
   KEY:struct
   ID:Date
S    Date     0    003E6830 0040A340 
   KEY:{
  +DECL
   +TYPE
     KEY:int
   -TYPE
   +VAR_LIST
    +VAR
      ID:year
V    year     0    003E6A80 003E6830 int:*0:[0]      
    -VAR
     KEY:,
    +VAR
      ID:month
V    month    0    003E6BD8 003E6830 int:*0:[0]      
    -VAR
     KEY:,
    +VAR
      ID:day
V    day      0    003E6D18 003E6830 int:*0:[0]      
    -VAR
   -VAR_LIST
  -DECL
   KEY:;
   KEY:}
 -STRUCT
 +STRUCT
   KEY:struct
   ID:Person
S    Person   0    003E6EB8 0040A340 
   KEY:{
  +DECL
   +TYPE
     KEY:char
   -TYPE
   +VAR_LIST
    +VAR
      KEY:*
      ID:name
V    name     0    003E7148 003E6EB8 char:*1:[0]     
    -VAR
   -VAR_LIST
  -DECL
   KEY:;
  +DECL
   +TYPE
     ID:Date
   -TYPE
   +VAR_LIST
    +VAR
      ID:birthday
V    birthday 0    003E7398 003E6EB8 Date:*0:[0]     
    -VAR
   -VAR_LIST
  -DECL
   KEY:;
   KEY:}
 -STRUCT
 +METHOD
  +TYPE
    KEY:int
  -TYPE
   ID:total
M    total    0    003E75F8 0040A340 
   KEY:(
  +PARAM_LIST
   +PARAM
    +TYPE
      KEY:int
    -TYPE
    +VAR
      KEY:*
      ID:a
V    a        0    003E78A8 003E75F8 int:*1:[0]      
    -VAR
   -PARAM
  -PARAM_LIST
   KEY:)
  +BLOCK
B             0    003E79B0 003E75F8 
    KEY:{
   +BASE
    +STMT
     +DECL
      +TYPE
        KEY:int
      -TYPE
      +VAR_LIST
       +VAR
         ID:s
V    s        0    003E7C60 003E79B0 int:*0:[0]      
         KEY:=
        +EXP
         +TERM
           CINT:0
         -TERM
        -EXP
       -VAR
      -VAR_LIST
     -DECL
    -STMT
     KEY:;
   -BASE
   +BASE
    +FOR
      KEY:for
      KEY:(
     +STMT
      +DECL
       +TYPE
         KEY:int
       -TYPE
       +VAR_LIST
        +VAR
          ID:i
V    i        0    003E8128 003E79B0 int:*0:[0]      
          KEY:=
         +EXP
          +TERM
            CINT:0
          -TERM
         -EXP
        -VAR
       -VAR_LIST
      -DECL
     -STMT
      KEY:;
     +EXP
      +TERM
       +PATH
        +ATOM
          ID:i
        -ATOM
       -PATH
      -TERM
       KEY:<
      +TERM
        CINT:10
      -TERM
     -EXP
      KEY:;
     +STMT
      +PATH
       +ATOM
         ID:i
       -ATOM
      -PATH
       KEY:++
     -STMT
      KEY:)
     +BASE
      +STMT
       +PATH
        +ATOM
          ID:s
        -ATOM
       -PATH
        KEY:=
       +EXP
        +TERM
         +PATH
          +ATOM
            ID:s
          -ATOM
         -PATH
        -TERM
         KEY:+
        +TERM
         +PATH
          +ATOM
            ID:a
            KEY:[
           +EXP
            +TERM
             +PATH
              +ATOM
                ID:i
              -ATOM
             -PATH
            -TERM
           -EXP
            KEY:]
          -ATOM
         -PATH
        -TERM
       -EXP
      -STMT
       KEY:;
     -BASE
    -FOR
   -BASE
   +BASE
    +STMT
      KEY:return
     +EXP
      +TERM
       +PATH
        +ATOM
          ID:s
        -ATOM
       -PATH
      -TERM
     -EXP
    -STMT
     KEY:;
   -BASE
    KEY:}
  -BLOCK
 -METHOD
 +METHOD
  +TYPE
    KEY:char
  -TYPE
   KEY:*
   ID:getName
M    getName  0    003E9270 0040A340 
   KEY:(
  +PARAM_LIST
   +PARAM
    +TYPE
      ID:Person
    -TYPE
    +VAR
      KEY:*
      ID:p
V    p        0    003E9518 003E9270 Person:*1:[0]   
    -VAR
   -PARAM
  -PARAM_LIST
   KEY:)
  +BLOCK
B             0    003E9608 003E9270 
    KEY:{
   +BASE
    +STMT
      KEY:return
     +EXP
      +TERM
       +PATH
        +ATOM
          ID:p
        -ATOM
         KEY:->
        +ATOM
          ID:name
        -ATOM
       -PATH
      -TERM
     -EXP
    -STMT
     KEY:;
   -BASE
    KEY:}
  -BLOCK
 -METHOD
 +METHOD
  +TYPE
    KEY:int
  -TYPE
   ID:main
M    main     0    003E9B70 0040A340 
   KEY:(
   KEY:)
  +BLOCK
B             0    003E9CD8 003E9B70 
    KEY:{
   +BASE
    +STMT
     +DECL
      +TYPE
        KEY:int
      -TYPE
      +VAR_LIST
       +VAR
         ID:b
V    b        0    003E9F88 003E9CD8 int:*0:[0]      
         KEY:[
         CINT:10
         KEY:]
       -VAR
        KEY:,
       +VAR
         ID:a
V    a        0    003EA170 003E9CD8 int:*0:[0]      
         KEY:=
        +EXP
         +TERM
           CINT:3
         -TERM
        -EXP
       -VAR
      -VAR_LIST
     -DECL
    -STMT
     KEY:;
   -BASE
   +BASE
    +STMT
     +DECL
      +TYPE
        KEY:int
      -TYPE
      +VAR_LIST
       +VAR
         ID:t
V    t        0    003EA560 003E9CD8 int:*0:[0]      
         KEY:=
        +EXP
         +TERM
          +PATH
           +ATOM
             ID:total
             KEY:(
            +EXP_LIST
             +EXP
              +TERM
               +PATH
                +ATOM
                  ID:b
                -ATOM
               -PATH
              -TERM
             -EXP
            -EXP_LIST
             KEY:)
           -ATOM
          -PATH
         -TERM
        -EXP
       -VAR
      -VAR_LIST
     -DECL
    -STMT
     KEY:;
   -BASE
   +BASE
    +STMT
     +DECL
      +TYPE
        ID:Person
      -TYPE
      +VAR_LIST
       +VAR
         ID:p
V    p        0    003EAC48 003E9CD8 Person:*0:[0]   
       -VAR
      -VAR_LIST
     -DECL
    -STMT
     KEY:;
   -BASE
   +BASE
    +STMT
     +PATH
      +ATOM
        ID:p
      -ATOM
       KEY:.
      +ATOM
        ID:birthday
      -ATOM
       KEY:.
      +ATOM
        ID:year
      -ATOM
     -PATH
      KEY:=
     +EXP
      +TERM
        CINT:1990
      -TERM
     -EXP
    -STMT
     KEY:;
   -BASE
   +BASE
    +STMT
     +PATH
      +ATOM
        ID:t
      -ATOM
     -PATH
      KEY:=
     +EXP
      +TERM
        CINT:3
      -TERM
       KEY:+
      +TERM
        KEY:(
       +EXP
        +TERM
          CINT:5
        -TERM
         KEY:*
        +TERM
         +PATH
          +ATOM
            ID:a
          -ATOM
         -PATH
        -TERM
       -EXP
        KEY:)
      -TERM
     -EXP
    -STMT
     KEY:;
   -BASE
   +BASE
    +STMT
      KEY:return
     +EXP
      +TERM
       +PATH
        +ATOM
          ID:t
        -ATOM
       -PATH
      -TERM
     -EXP
    -STMT
     KEY:;
   -BASE
    KEY:}
  -BLOCK
 -METHOD
-PROG
type name     size this     scope    varType
V    i        0    003E8128 003E79B0 int:*0:[0]      
M    main     0    003E9B70 0040A340 
V    month    0    003E6BD8 003E6830 int:*0:[0]      
V    name     0    003E7148 003E6EB8 char:*1:[0]     
M    total    0    003E75F8 0040A340 
V    x        0    003E2558 0040A340 int:*0:[0]      
V    s        0    003E7C60 003E79B0 int:*0:[0]      
V    y        0    003E6590 0040A340 int:*0:[0]      
B             0    003E9CD8 003E9B70 
V    a        0    003EA170 003E9CD8 int:*0:[0]      
V    b        0    003E9F88 003E9CD8 int:*0:[1]      
S    Person   0    003E6EB8 0040A340 
T    int      4    003E5EB0 0040A340 
V    a        0    003E78A8 003E75F8 int:*1:[0]      
V    p        0    003EAC48 003E9CD8 Person:*0:[0]   
T    char     1    003E57E0 0040A340 
V    t        0    003EA560 003E9CD8 int:*0:[0]      
T    float    4    003E5778 0040A340 
B             0    003E79B0 003E75F8 
V    day      0    003E6D18 003E6830 int:*0:[0]      
M    getName  0    003E9270 0040A340 
V    birthday 0    003E7398 003E6EB8 Date:*0:[0]     
V    p        0    003E9518 003E9270 Person:*1:[0]   
V    year     0    003E6A80 003E6830 int:*0:[0]      
S    Date     0    003E6830 0040A340 
B             0    003E9608 003E9270 
Memory:newCount=2090 freeCount=2090

C1 程序語言

范例一

int x=1, y=2;
 
struct Date {
    int year, month, day;
}
 
struct Person {
    char *name;
    Date birthday;
}
 
int total(int* a) {
    int s = 0;
    for (int i=0; i<10; i++)
        s = s+a[i];
    return s;
}
 
char* getName(Person *p) {
    return p->name;
}
 
int main() {
    int b[10], a=3;
    int t = total(b);
    Person p;
    p.birthday.year = 1990;
    t = 3 + (5 * a);
    return t;
}

 

 

參考鏈接:

http://ccckmit.wikidot.com/ocs:compiler

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM