-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
233 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
最後只剩下若語句還無法編譯了!在討論若語句之前,先來看看零.二版加入的運算子會如何編譯。 | ||
|
||
## 比較運算(及餘運算) | ||
|
||
精五僅提供 slt (及其立即數版本 slit),也就是小於真言,其餘運算都是透過調整暫存器順序與組合 xor 等等運算來完成的,下方源碼是其中一種作法。 | ||
|
||
```rust | ||
match 運算子 { | ||
// 加減乘除... | ||
O運算子::餘 => { | ||
// rem 指令是有號整數取餘 | ||
// 尚有 urem 指令,乃無號整數取餘,但音界咒並不支援 | ||
writeln!(真言檔, "\trem t0, t0, t1")?; | ||
} | ||
O運算子::等於 => { | ||
writeln!(真言檔, "\txor t2, t0, t1")?; // t2 = t0 ^ t1 | ||
writeln!(真言檔, "\tseqz t0, t2")?; // t0 = (t2 == 0) ? 1 : 0 | ||
} | ||
O運算子::異於 => { | ||
writeln!(真言檔, "\txor t2, t0, t1")?; // t2 = t0 ^ t1 | ||
writeln!(真言檔, "\tsnez t0, t2")?; // t0 = (t2 != 0) ? 1 : 0 | ||
} | ||
// 以下比較運算僅 slt 為精五真言 | ||
O運算子::小於 => { | ||
writeln!(真言檔, "\tslt t0, t0, t1")?; // t0 = (t0 < t1) | ||
} | ||
O運算子::大於 => { | ||
// 組譯為 slt t0, t1, t0 | ||
writeln!(真言檔, "\tsgt t0, t0, t1")?; // t0 = (t0 > t1) | ||
} | ||
O運算子::小於等於 => { | ||
// 甲<=乙,即 !(甲>乙) | ||
writeln!(真言檔, "\tsgt t0, t0, t1")?; // t0 = (t0 > t0) | ||
writeln!(真言檔, "\txori t0, t0, 1")?; // t0 = t0 ^ 1 | ||
} | ||
O運算子::大於等於 => { | ||
// 甲>=乙,即 !(甲<乙) | ||
writeln!(真言檔, "\tslt t0, t0, t1")?; // t0 = (t0 < t1) | ||
writeln!(真言檔, "\txori t0, t0, 1")?; // t0 = t0 ^ 1 | ||
} | ||
} | ||
``` | ||
|
||
## 精五決策 | ||
|
||
精五的決策分為兩種,一是條件決策,一是無條件跳躍。 | ||
|
||
### 條件決策 | ||
精五提供了 `beq`(相等則跳), `bne`(相異則跳), `bge`(大於等於則跳), `blt`(小於則跳)等多個條件決策真言。貧道實在不曉得為何比較運算只提供了一個 `slt` ,其他比較都要組合兩個真言才能做,而決策就如此大方。 | ||
|
||
條件決策真言的用法與形式皆雷同,僅語義不同。由於音界咒尚不注重優化,所以接下來僅使用與介紹 `beq`。 | ||
|
||
`beq` 接受三個參數 | ||
|
||
``` | ||
beq 暫存器甲, 暫存器乙, 立即數 | ||
``` | ||
|
||
當 `暫存器甲` 與 `暫存器乙`相等時,將 `立即數 * 2` 加到咒指針(program counter)上,這個立即數是一個 12 位元的有號整數,所以總共可以移動正負 4KB 的距離,在一個術之內跳躍綽綽有餘了。 | ||
|
||
補充:每個精五真言都佔 4 個位元組,那為和立即數是乘 2 不是乘 4 ?因為精五還支援壓縮指令,指令可以被壓縮到只佔用 2 個位元組。 | ||
|
||
|
||
### 無條件跳躍 | ||
若需要更遠的跳躍,則需要 `j` 系列的決策真言,例如偽真言 `call` 會呼叫另一個術,而術與術之間的距離可能很遠,所以 `call` 通常會被組譯成 `jal`,`jal` 在跳躍的同時還能將當下咒指針存入指定暫存器,以待未來返回原執行位置。 | ||
|
||
有時候吾人僅想要跳躍,但不在意當下咒指針,那可以直接用 `j` 偽真言。 | ||
|
||
```assembly | ||
j 立即數 | ||
``` | ||
|
||
會被組譯為 | ||
|
||
``` | ||
jal x0, 立即數 | ||
``` | ||
|
||
`x0` 是一個永遠為 0 的暫存器,咒指針就像丟垃圾一樣被丟到 `x0` 這個垃圾桶去。 | ||
|
||
在組合語言中,其實不用知道這些細節,決策真言可以直接搭配標籤來使用,組譯器會自動算好立即數填上去: | ||
|
||
```assembly | ||
beq t0, x0, 標籤1 | ||
# ... | ||
# t0 != 0 時該做的事 | ||
# ... | ||
標籤1: | ||
# ... | ||
# 繼續做事 | ||
# ... | ||
``` | ||
|
||
## 若語句真言生成 | ||
|
||
以上組語就類似於 | ||
|
||
```音界 | ||
若(算式)【 | ||
// 算式 != 0 時該做的事 | ||
】 | ||
// 繼續做事 | ||
``` | ||
的編譯結果。 | ||
|
||
若要支援`或若`、`不然`,就多加幾個標籤: | ||
|
||
```音界 | ||
若(算式1)【 | ||
... | ||
】或若(算式2)【 | ||
... | ||
】不然【 | ||
... | ||
】 | ||
繼續做事 | ||
``` | ||
|
||
可編譯為 | ||
|
||
``` | ||
# 計算算式1 | ||
beq t0, x0, 標籤1 | ||
... | ||
...若區塊 | ||
... | ||
j 標籤3 | ||
標籤1: | ||
# 計算算式2 | ||
beq t0, x0, 標籤2 | ||
... | ||
...或若區塊 | ||
... | ||
j 標籤3 | ||
標籤2: | ||
... | ||
...不然區塊 | ||
... | ||
j 標籤3 | ||
區塊群結尾標籤: | ||
... | ||
繼續做事 | ||
... | ||
``` | ||
|
||
由於`若`、`或若`、`不然`區塊僅有其一會執行,在區塊結尾都要跳躍到`區塊群結尾標籤`,以避免執行到其他區塊。 | ||
|
||
## 實作 | ||
|
||
標籤在同一個真言檔裡不可同名,需要謹慎管理標籤: | ||
|
||
```rust | ||
fn 新分支標籤名(&mut self) -> String { | ||
self.分支標籤計數 += 1; | ||
format!("分支標籤——{}", self.分支標籤計數) | ||
} | ||
fn 上標籤(&mut self, 標籤名: &String) -> io::Result<()> { | ||
writeln!(self.真言檔, "{}:", 標籤名) | ||
} | ||
fn t0為0則跳至標籤(&mut self, 標籤名: &String) -> io::Result<()> { | ||
writeln!(self.真言檔, "\tbeq t0, x0, {}", 標籤名) | ||
} | ||
fn 新區塊群結尾標籤名(&mut self) -> String { | ||
self.區塊群結尾標籤計數 += 1; | ||
format!("區塊群結尾標籤——{}", self.區塊群結尾標籤計數) | ||
} | ||
fn 跳至標籤(&mut self, 標籤名: &String) -> io::Result<()> { | ||
writeln!(self.真言檔, "j {}", 標籤名) | ||
} | ||
fn 生成若(&mut self, 若: &O若, 符號表: &O符號表) -> io::Result<()> { | ||
// 若 | ||
self.計算(&若.條件, 符號表)?; | ||
self.彈出()?; | ||
let mut 分支標籤名 = self.新分支標籤名(); | ||
self.t0為0則跳至標籤(&分支標籤名)?; | ||
// 區塊內的新增區域變數,在區塊外不應被擷取 | ||
// 故需複製符號表,以免原符號表被影響 | ||
self.生成區塊(&若.區塊, 符號表.clone())?; | ||
|
||
if 若.或若列表.len() == 0 && 若.不然.is_none() { | ||
// 僅有若,無或若、不然。 | ||
self.上標籤(&分支標籤名)?; | ||
return Ok(()); | ||
} | ||
|
||
let 區塊群結尾標籤名 = self.新區塊群結尾標籤名(); | ||
self.跳至標籤(&區塊群結尾標籤名)?; | ||
|
||
// 或若 | ||
for 或若 in &若.或若列表 { | ||
self.上標籤(&分支標籤名)?; | ||
self.計算(&或若.條件, 符號表)?; | ||
self.彈出()?; | ||
分支標籤名 = self.新分支標籤名(); | ||
self.t0為0則跳至標籤(&分支標籤名)?; | ||
self.生成區塊(&或若.區塊, 符號表.clone())?; | ||
self.跳至標籤(&區塊群結尾標籤名)?; | ||
} | ||
|
||
// 不然 | ||
self.上標籤(&分支標籤名)?; | ||
if let Some(不然) = &若.不然 { | ||
self.生成區塊(&不然.區塊, 符號表.clone())?; | ||
} | ||
|
||
self.上標籤(&區塊群結尾標籤名) | ||
} | ||
fn 生成區塊( | ||
&mut self, 區塊: &Vec<O句>, mut 符號表: O符號表 | ||
) -> io::Result<()> { | ||
for 句 in 區塊 { | ||
self.生成句(句, &mut 符號表)?; | ||
} | ||
Ok(()) | ||
} | ||
``` | ||
|
||
完整程式碼可見[音界咒源碼](https://github.com/MROS/yinjie-lang/blob/8aab11a3a6640fbe7691dbbe25cb48f8c4f69532/%E9%9B%B6%E8%99%9F%E7%B7%A8%E8%AD%AF%E5%99%A8/src/%E7%9C%9F%E8%A8%80%E7%94%9F%E6%88%90/%E7%9C%9F%E8%A8%80%E7%94%9F%E6%88%90%E5%99%A8.rs)。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters