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、提取指令 :IR = [PC]
- 操作2、更新計數器 :PC = PC + 4
- 解碼階段
- 操作3、解碼 :控制單元對IR進行譯碼后,設定數據流向開關與 ALU 的運算模式
- 運行時間
- 操作4、執行 :數據流入 ALU,經過運算后,流回指定的緩存器
V-OS: 橫跨操作系統與硬件的虛擬機系統
- 設計一個虛擬機系統,可以將 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
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 | `#´
- Lua 5.1 Reference Manual — http://www.lua.org/manual/5.1/manual.html
- 最后有 Lua 的 BNF。
- Lua Interpreter in C — http://www.lua.org/source/5.1/lua.c.html
- Lua Compiler in Lua — http://lua-users.org/wiki/LuaCompilerInLua
- Lua Interpreter in Lua — http://lua-users.org/wiki/LuaInterpreterInLua
- 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
開放計算機計划 — 最新版本下載
- 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