本項(xiàng)目只是一個(gè)實(shí)驗(yàn)品,這點(diǎn)不必再?gòu)?qiáng)調(diào)。仁者見(jiàn)仁,智者見(jiàn)智。
本程序的最終目標(biāo)是虛擬出一套GUI(不是現(xiàn)在的黑框框),在這之上要做一些游戲、界面都是可以的(代碼可以跨平臺(tái),暫時(shí)沒(méi)在linux上試過(guò))。在這個(gè)過(guò)程中的一切操作都是實(shí)驗(yàn)性的。
命令行管道
【更新】19/01/21
由于在起步階段,讀文件的接口暫時(shí)還沒(méi)做(因此不能命令行直接解釋C文件了),打算先搞多任務(wù)系統(tǒng)以及類似動(dòng)態(tài)鏈接那樣的東西,步驟是將C文件編譯成兩個(gè)段——代碼段和數(shù)據(jù)段,然后加載到內(nèi)存空間中,那么不同的源代碼加載到不同的空間中即可,要鏈接的話,保存下函數(shù)簽名和地址就行。讀取二進(jìn)制文件的步驟——利用虛擬文件系統(tǒng)加載文件內(nèi)容到內(nèi)存空間中然后執(zhí)行。
任務(wù)比較多,因此可能需一段時(shí)間才能顯露雛形。
回答幾個(gè)問(wèn)題——
的控制流——自己水平不高,因此對(duì)call/cc要有研究才可實(shí)現(xiàn)(當(dāng)然靠copy的沒(méi)意思了)。網(wǎng)上都是教你如何用call/cc,很少教你去實(shí)現(xiàn)它。我不是用lisp去做東西,而是做lisp本身。干嘛用的?——可以將其看作qemu,但不完全是。我是想仿個(gè)操作系統(tǒng)帶界面罷了,做語(yǔ)法分析屬于邊角料內(nèi)容。不是會(huì)用工具就可以了,要去了解工具本身是如何運(yùn)作的,也就是俗話說(shuō)的造輪子。純屬toys(實(shí)驗(yàn)性項(xiàng)目),有興趣就去了解,沒(méi)興趣就算了。
【更新】19/01/20
/
由于有項(xiàng)目的代碼,因此直接移植UI代碼,瞬間搞定GUI。
示例代碼(已經(jīng)支持了超多語(yǔ)法!):
enum INTR_TABLE {
INTR_PUT_CHAR = 0,
INTR_PUT_NUMBER = 1,
INTR_SLEEP_RECORD = 100,
INTR_SLEEP_REACH = 101,
};
int sleep(int ms) {
ms;
interrupt 100;
interrupt 101;
}
int put_char(char c) {
c;
interrupt 0;
}
int put_string(char *text) {
while (put_char(*text++));
}
int put_int(int number) {
number;
interrupt 1;
}
int fib(int i) {
if (i > 2)
return fib(i - 1) + fib(i - 2);
else
return 1;
}
int fib2(int i) {
return i > 2 ? fib(i - 1) + fib(i - 2) : 1;
}
int sum(int i) {
int s = 0;
while (i > 0) {
s += i--;
}

return s;
}
int sum2(int n) {
int i, s;
for (i = 1, s = 0; i <= n ; ++i) {
s += i;
}
return s;
}
int sum3(int i) {
int s = 0;
do {
s += i--;
} while (i > 0);
return s;
}
int welcome() {
put_string(" ________ ________ ___ ________ ________ ________ \n");
put_string("|\\ __ \\|\\ __ \\ |\\ \\|\\ ___ \\|\\ ____\\|\\ ____\\ \n");
put_string("\\ \\ \\|\\ /\\ \\ \\|\\ \\ \\ \\ \\ \\ \\_|\\ \\ \\ \\___|\\ \\ \\___| \n");
put_string(" \\ \\ __ \\ \\ __ \\ ___\\ \\ \\ \\ \\ \\\\ \\ \\ \\ \\ \\ \\ \n");
put_string(" \\ \\ \\|\\ \\ \\ \\ \\ \\|\\ \\\\_\\ \\ \\ \\_\\\\ \\ \\ \\____\\ \\ \\_____\n");
put_string(" \\ \\_______\\ \\__\\ \\__\\ \\________\\ \\_______\\ \\_______\\ \\_______\\\n");
put_string(" \\|_______|\\|__|\\|__|\\|________|\\|_______|\\|_______|\\|_______|\n");
put_string("\n\n");
put_string("Welcome to @clibos system by bajdcc!");
put_string("\n\n");
}
enum TEST {
TEST_IF,
TEST_TRIOP,
TEST_WHILE,
TEST_FOR,
TEST_DO,
};
int test(int i) {
switch (i) {
case TEST_IF:
put_string("fib(10): "); put_int(fib2(10));
break;
case TEST_TRIOP:
put_string("fib2(10): "); put_int(fib(10));
break;
case TEST_WHILE:
put_string("sum(100): "); put_int(sum(100));
break;
case TEST_FOR:
put_string("sum2(100): "); put_int(sum2(100));

break;
case TEST_DO:
put_string("sum3(100): "); put_int(sum3(100));
break;
default:
put_string("undefined task");
break;
}
put_string("\n"); sleep(1000);
}
int main(int argc, char **argv) {
welcome();
int i;
for (i = TEST_IF; i <= TEST_DO; ++i)
test(i);
return 0;
}
上述代碼沒(méi)有使用標(biāo)準(zhǔn)庫(kù),與UI的交互通過(guò)實(shí)現(xiàn)(看作中斷)。
參數(shù):FPS=30,每幀虛擬機(jī)指令執(zhí)行1000次。下1秒跑完。(其實(shí)并行也可以,但會(huì)有UI刷新的問(wèn)題。)
效果:
UI抄了/。
效果圖
【本系列原本為系列,因語(yǔ)法分析部分已基本完成,因此后續(xù)的重點(diǎn)會(huì)放在虛擬機(jī)設(shè)計(jì)上。故此更名為虛擬機(jī)系列~】
經(jīng)過(guò)一番番的努力c++判斷是否在虛擬機(jī)中,制作的第一個(gè)C語(yǔ)言虛擬機(jī)誕生了!
目前支持語(yǔ)法如下:
支持的語(yǔ)法
綜觀整個(gè),那么其中我嚴(yán)重參考并感謝的有:
LR分析核心代碼及思路——輪子哥的vczh-/VlppC語(yǔ)言寄存器式簡(jiǎn)易虛擬機(jī)及前期指令設(shè)計(jì)——/write-a-C-
自己的微小貢獻(xiàn)之處有:
內(nèi)存池:負(fù)責(zé)語(yǔ)法樹(shù)的內(nèi)存管理虛頁(yè)管理:負(fù)責(zé)虛擬機(jī)中的地址轉(zhuǎn)換對(duì)LR分析的優(yōu)化,解決其中的二義性問(wèn)題其余的在詞法分析、語(yǔ)法分析、語(yǔ)義分析、AST生成、指令生成中涉及到的代碼
當(dāng)然,我的目標(biāo)并不是做一個(gè)C語(yǔ)言虛擬機(jī)就好了,這個(gè)虛擬機(jī)可是拿來(lái)用的!
先前的陳安:【Lisp系列】啟用LR文法!中嘗試了用lisp語(yǔ)言去做虛擬機(jī),但會(huì)有一個(gè)問(wèn)題:lisp中的循環(huán)難以實(shí)現(xiàn),而循環(huán)作為控制流語(yǔ)句最關(guān)鍵的就是含有跳轉(zhuǎn)指令,而虛擬機(jī)是以函數(shù)指針為參數(shù),進(jìn)行式回調(diào)的,因此無(wú)法實(shí)現(xiàn)跳轉(zhuǎn),所以我就棄了。。嘗試失敗。
目前可以編譯并運(yùn)行:
int fib(int i) {
if (i > 2)
return fib(i - 1) + fib(i - 2);
else
return 1;
}
int sum(int i) {
int s = 0;
while (i > 0) {
s += i--;
}
return s;

}
int sum2(int n) {
int i, s;
for (i = 1; i <= n; ++i) {
s += i;
}
return s;
}
int sum3(int i) {
int s = 0;
do {
s += i--;
} while (i > 0);
return s;
}
int main(int argc, char **argv){
fib(10);
sum(100);
sum2(100);
sum3(100);
}
遞歸及基本的控制流語(yǔ)句毫無(wú)壓力c++判斷是否在虛擬機(jī)中,雖然指令生成部分代碼比較不美觀,但真的比我之前寫的任何一款指令生成器都好看。
后續(xù)支持指針和結(jié)構(gòu)體之后 ,會(huì)更加強(qiáng)大。
調(diào)用方式
#include
#include "cexception.h"
#include "cparser.h"
#include "cgen.h"
extern int g_argc;
extern char **g_argv;
int main(int argc, char **argv) {
g_argc = argc;
g_argv = argv;
using namespace clib;
try {
cparser p;
cgen gen;
auto root = p.parse(R"(
int fib(int i) {
if (i > 2)
return fib(i - 1) + fib(i - 2);
else
return 1;
}
int sum(int i) {

int s = 0;
while (i > 0) {
s += i--;
}
return s;
}
int sum2(int n) {
int i, s;
for (i = 1; i <= n; ++i) {
s += i;
}
return s;
}
int sum3(int i) {
int s = 0;
do {
s += i--;
} while (i > 0);
return s;
}
int main(int argc, char **argv){
fib(10);
sum(100);
sum2(100);
sum3(100);
}
)", &gen);
cast::print(root, 0, std::cout);
gen.gen(root);
gen.eval();
} catch (const cexception &e) {
std::cout << "RUNTIME ERROR: " << e.msg << std::endl;
}
return 0;
}
要支持從文件讀取并運(yùn)行的話很簡(jiǎn)單,修改root的來(lái)源即可。
虛擬機(jī)運(yùn)行實(shí)況
截取一段:
0001> [C000009C] 08 ENT 0000001C(28)
---------------- STACK BEGIN <<<<
AX: 00000000 BP: E0000FF0 SP: E0000FD4 PC: C00000A0
[E0000FD4]> 00000000
[E0000FD8]> 00000000
[E0000FDC]> 00000000
[E0000FE0]> 00000000
[E0000FE4]> 00000000

[E0000FE8]> 00000000
[E0000FEC]> 00000000
[E0000FF0]> 00000000
[E0000FF4]> E0000FF8
[E0000FF8]> 0000000D
[E0000FFC]> 00000028
---------------- STACK END >>>>
0002> [C00000A4] 01 LEA FFFFFFFC(-4)
---------------- STACK BEGIN <<<<
AX: E0000FEC BP: E0000FF0 SP: E0000FD4 PC: C00000A8
[E0000FD4]> 00000000
[E0000FD8]> 00000000
[E0000FDC]> 00000000
[E0000FE0]> 00000000
[E0000FE4]> 00000000
[E0000FE8]> 00000000
[E0000FEC]> 00000000
[E0000FF0]> 00000000
[E0000FF4]> E0000FF8
[E0000FF8]> 0000000D
[E0000FFC]> 00000028
---------------- STACK END >>>>
0003> [C00000AC] 13 PUSH E0000FEC
---------------- STACK BEGIN <<<<
AX: E0000FEC BP: E0000FF0 SP: E0000FD0 PC: C00000AC
[E0000FD0]> E0000FEC
[E0000FD4]> 00000000
[E0000FD8]> 00000000
[E0000FDC]> 00000000
[E0000FE0]> 00000000
[E0000FE4]> 00000000
[E0000FE8]> 00000000
[E0000FEC]> 00000000
[E0000FF0]> 00000000
[E0000FF4]> E0000FF8
[E0000FF8]> 0000000D
[E0000FFC]> 00000028
---------------- STACK END >>>>
這里就要感謝VMM虛頁(yè)機(jī)制了,定義的四個(gè)段為:
定義的段基址
虛頁(yè)機(jī)制支持權(quán)限控制,目前還未使用起來(lái)。
全局?jǐn)?shù)據(jù)、字符串保存在數(shù)據(jù)段中,生成的指令保存在代碼段中,程序運(yùn)行使用棧,堆的話在后面支持語(yǔ)法會(huì)用到(會(huì)以系統(tǒng)調(diào)用實(shí)現(xiàn))。
后續(xù)計(jì)劃
在題圖上已有涉及:走模擬操作系統(tǒng)的老路,希望這次可以比/做得更好。