{{ $root.errMsg }}

【深入淺出教你寫編譯器(Compiler)】五、虛擬機(Virtual Machine)(上)

(已刪除)

(已關閉)

(已標記為濫發)

(已保護)


為什麼我們需要虛擬機呢?因為我們要運行我們編譯好的程式。那為什麼我們不直接編譯到 native binary code 呢?這是因為真正的電腦資源十分有限,我們編寫起上來會比用虛擬機的做法難很多,那就嚴重超出了本教程的範圍了(其實是因為西杰還不太認識這個課題,要偷懶一下 )。當然,虛擬機的做法和 native code 的也有幾分相似,這裏就給讀者們一個初步的概念,大家真的想再接觸多一點底層的東西就要自己摸索一下了……

接著下來,我們就稱我們的虛擬機為 Wemachine 吧。

 

遊戲開始之前,我們當然要先定義一下遊戲規則﹣ Instruction set architecture(ISA),Wemachine 會支持以下的 Instruction set:


而且西杰將會假設我們的虛擬機有無限個 register,以便我們開發。

開始寫程式了,第一步我們要做的是讓 Wemachine 學會讀那堆指令,為了方便讀取,我們要限定指令的格式,格式如下:

opcode1 operand1, operand2, operand3;

opcode2 operand1, operand2;

每個指令由分號分隔開,在第一個空格號前的為指令的 opcode,接下來是一至三個 operand,以逗號分隔開,就是這麼簡單了。現在先開始寫一個 Parser 來分析以此格式寫的程式,西杰相信這一步應該很簡單,現在看看運行結果。

		var a:int = 3 / 5;
		a= a++ + a-- - ++a + --a;
		
		var b:bool = ! true && ! false || (1 + 3 == -3);
		b+=3;
		
		while (1 == 3){
		}
		
		if (b + 1){
		}
		

 

lwi $1,100;
print $1;
		

現在要開始實現功能了,首先要有 register 以記下數據,這個西杰會用一個 array 來做,$0 指向 array 第 0 個元素,$1 指向第 1 個元素,如此類推。然後我們要寫兩個 method 來 get set register 的數據,如果那個 register 未被使用過的話,它的數據就會是 0。於是我們就有了以下的程式:

 

Wemachine.prototype.resolveRegister = function (operand){
    if (typeof operand == "string" && operand.length > 0){
        if (operand[0] == "$"){
            return parseInt(operand.substr(1));
        }
    }
 
    Errors.push({
        type: Errors.RUNTIME_ERROR,
        msg: "Fail to resolve register",
        line: 0
    });
 
    return -1;
}
 
Wemachine.prototype.getRegisterContent = function (operand){
    operand = this.resolveRegister(operand);
    if (operand != -1){
        if (this.registers.length > operand){
            return this.registers[operand];
        }
    }
    return 0;
}
 
Wemachine.prototype.setRegisterContent = function (operand, value){
    operand = this.resolveRegister(operand);
    if (operand != -1){
        this.registers[operand] = value;
    }
}

<再續>...

compiler  


{{ ctrl.votes | shortNumber: 0 }}
寫於 {{ '2017-08-14T07:01:59.183Z' | calendarTime }}

{{ $root.errMsg }}