|
| 1 | +最後只剩下若語句還無法編譯了!在討論若語句之前,先來看看零.二版加入的運算子會如何編譯。 |
| 2 | + |
| 3 | +## 比較運算(及餘運算) |
| 4 | + |
| 5 | +精五僅提供 slt (及其立即數版本 slit),也就是小於真言,其餘運算都是透過調整暫存器順序與組合 xor 等等運算來完成的,下方源碼是其中一種作法。 |
| 6 | + |
| 7 | +```rust |
| 8 | +match 運算子 { |
| 9 | + // 加減乘除... |
| 10 | + O運算子::餘 => { |
| 11 | + // rem 指令是有號整數取餘 |
| 12 | + // 尚有 urem 指令,乃無號整數取餘,但音界咒並不支援 |
| 13 | + writeln!(真言檔, "\trem t0, t0, t1")?; |
| 14 | + } |
| 15 | + O運算子::等於 => { |
| 16 | + writeln!(真言檔, "\txor t2, t0, t1")?; // t2 = t0 ^ t1 |
| 17 | + writeln!(真言檔, "\tseqz t0, t2")?; // t0 = (t2 == 0) ? 1 : 0 |
| 18 | + } |
| 19 | + O運算子::異於 => { |
| 20 | + writeln!(真言檔, "\txor t2, t0, t1")?; // t2 = t0 ^ t1 |
| 21 | + writeln!(真言檔, "\tsnez t0, t2")?; // t0 = (t2 != 0) ? 1 : 0 |
| 22 | + } |
| 23 | + // 以下比較運算僅 slt 為精五真言 |
| 24 | + O運算子::小於 => { |
| 25 | + writeln!(真言檔, "\tslt t0, t0, t1")?; // t0 = (t0 < t1) |
| 26 | + } |
| 27 | + O運算子::大於 => { |
| 28 | + // 組譯為 slt t0, t1, t0 |
| 29 | + writeln!(真言檔, "\tsgt t0, t0, t1")?; // t0 = (t0 > t1) |
| 30 | + } |
| 31 | + O運算子::小於等於 => { |
| 32 | + // 甲<=乙,即 !(甲>乙) |
| 33 | + writeln!(真言檔, "\tsgt t0, t0, t1")?; // t0 = (t0 > t0) |
| 34 | + writeln!(真言檔, "\txori t0, t0, 1")?; // t0 = t0 ^ 1 |
| 35 | + } |
| 36 | + O運算子::大於等於 => { |
| 37 | + // 甲>=乙,即 !(甲<乙) |
| 38 | + writeln!(真言檔, "\tslt t0, t0, t1")?; // t0 = (t0 < t1) |
| 39 | + writeln!(真言檔, "\txori t0, t0, 1")?; // t0 = t0 ^ 1 |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +## 精五決策 |
| 45 | + |
| 46 | +精五的決策分為兩種,一是條件決策,一是無條件跳躍。 |
| 47 | + |
| 48 | +### 條件決策 |
| 49 | +精五提供了 `beq`(相等則跳), `bne`(相異則跳), `bge`(大於等於則跳), `blt`(小於則跳)等多個條件決策真言。貧道實在不曉得為何比較運算只提供了一個 `slt` ,其他比較都要組合兩個真言才能做,而決策就如此大方。 |
| 50 | + |
| 51 | +條件決策真言的用法與形式皆雷同,僅語義不同。由於音界咒尚不注重優化,所以接下來僅使用與介紹 `beq`。 |
| 52 | + |
| 53 | +`beq` 接受三個參數 |
| 54 | + |
| 55 | +``` |
| 56 | +beq 暫存器甲, 暫存器乙, 立即數 |
| 57 | +``` |
| 58 | + |
| 59 | +當 `暫存器甲` 與 `暫存器乙`相等時,將 `立即數 * 2` 加到咒指針(program counter)上,這個立即數是一個 12 位元的有號整數,所以總共可以移動正負 4KB 的距離,在一個術之內跳躍綽綽有餘了。 |
| 60 | + |
| 61 | +補充:每個精五真言都佔 4 個位元組,那為和立即數是乘 2 不是乘 4 ?因為精五還支援壓縮指令,指令可以被壓縮到只佔用 2 個位元組。 |
| 62 | + |
| 63 | + |
| 64 | +### 無條件跳躍 |
| 65 | +若需要更遠的跳躍,則需要 `j` 系列的決策真言,例如偽真言 `call` 會呼叫另一個術,而術與術之間的距離可能很遠,所以 `call` 通常會被組譯成 `jal`,`jal` 在跳躍的同時還能將當下咒指針存入指定暫存器,以待未來返回原執行位置。 |
| 66 | + |
| 67 | +有時候吾人僅想要跳躍,但不在意當下咒指針,那可以直接用 `j` 偽真言。 |
| 68 | + |
| 69 | +```assembly |
| 70 | +j 立即數 |
| 71 | +``` |
| 72 | + |
| 73 | +會被組譯為 |
| 74 | + |
| 75 | +``` |
| 76 | +jal x0, 立即數 |
| 77 | +``` |
| 78 | + |
| 79 | +`x0` 是一個永遠為 0 的暫存器,咒指針就像丟垃圾一樣被丟到 `x0` 這個垃圾桶去。 |
| 80 | + |
| 81 | +在組合語言中,其實不用知道這些細節,決策真言可以直接搭配標籤來使用,組譯器會自動算好立即數填上去: |
| 82 | + |
| 83 | +```assembly |
| 84 | +beq t0, x0, 標籤1 |
| 85 | +# ... |
| 86 | +# t0 != 0 時該做的事 |
| 87 | +# ... |
| 88 | +
|
| 89 | +標籤1: |
| 90 | +# ... |
| 91 | +# 繼續做事 |
| 92 | +# ... |
| 93 | +
|
| 94 | +``` |
| 95 | + |
| 96 | +## 若語句真言生成 |
| 97 | + |
| 98 | +以上組語就類似於 |
| 99 | + |
| 100 | +```音界 |
| 101 | +若(算式)【 |
| 102 | + // 算式 != 0 時該做的事 |
| 103 | +】 |
| 104 | +// 繼續做事 |
| 105 | +``` |
| 106 | +的編譯結果。 |
| 107 | + |
| 108 | +若要支援`或若`、`不然`,就多加幾個標籤: |
| 109 | + |
| 110 | +```音界 |
| 111 | +若(算式1)【 |
| 112 | + ... |
| 113 | +】或若(算式2)【 |
| 114 | + ... |
| 115 | +】不然【 |
| 116 | + ... |
| 117 | +】 |
| 118 | +繼續做事 |
| 119 | +``` |
| 120 | + |
| 121 | +可編譯為 |
| 122 | + |
| 123 | +``` |
| 124 | +# 計算算式1 |
| 125 | +beq t0, x0, 標籤1 |
| 126 | +... |
| 127 | +...若區塊 |
| 128 | +... |
| 129 | +j 標籤3 |
| 130 | +
|
| 131 | +
|
| 132 | +標籤1: |
| 133 | +# 計算算式2 |
| 134 | +beq t0, x0, 標籤2 |
| 135 | +... |
| 136 | +...或若區塊 |
| 137 | +... |
| 138 | +j 標籤3 |
| 139 | +
|
| 140 | +
|
| 141 | +標籤2: |
| 142 | +... |
| 143 | +...不然區塊 |
| 144 | +... |
| 145 | +j 標籤3 |
| 146 | +
|
| 147 | +
|
| 148 | +
|
| 149 | +區塊群結尾標籤: |
| 150 | +... |
| 151 | +繼續做事 |
| 152 | +... |
| 153 | +``` |
| 154 | + |
| 155 | +由於`若`、`或若`、`不然`區塊僅有其一會執行,在區塊結尾都要跳躍到`區塊群結尾標籤`,以避免執行到其他區塊。 |
| 156 | + |
| 157 | +## 實作 |
| 158 | + |
| 159 | +標籤在同一個真言檔裡不可同名,需要謹慎管理標籤: |
| 160 | + |
| 161 | +```rust |
| 162 | +fn 新分支標籤名(&mut self) -> String { |
| 163 | + self.分支標籤計數 += 1; |
| 164 | + format!("分支標籤——{}", self.分支標籤計數) |
| 165 | +} |
| 166 | +fn 上標籤(&mut self, 標籤名: &String) -> io::Result<()> { |
| 167 | + writeln!(self.真言檔, "{}:", 標籤名) |
| 168 | +} |
| 169 | +fn t0為0則跳至標籤(&mut self, 標籤名: &String) -> io::Result<()> { |
| 170 | + writeln!(self.真言檔, "\tbeq t0, x0, {}", 標籤名) |
| 171 | +} |
| 172 | +fn 新區塊群結尾標籤名(&mut self) -> String { |
| 173 | + self.區塊群結尾標籤計數 += 1; |
| 174 | + format!("區塊群結尾標籤——{}", self.區塊群結尾標籤計數) |
| 175 | +} |
| 176 | +fn 跳至標籤(&mut self, 標籤名: &String) -> io::Result<()> { |
| 177 | + writeln!(self.真言檔, "j {}", 標籤名) |
| 178 | +} |
| 179 | +fn 生成若(&mut self, 若: &O若, 符號表: &O符號表) -> io::Result<()> { |
| 180 | + // 若 |
| 181 | + self.計算(&若.條件, 符號表)?; |
| 182 | + self.彈出()?; |
| 183 | + let mut 分支標籤名 = self.新分支標籤名(); |
| 184 | + self.t0為0則跳至標籤(&分支標籤名)?; |
| 185 | + // 區塊內的新增區域變數,在區塊外不應被擷取 |
| 186 | + // 故需複製符號表,以免原符號表被影響 |
| 187 | + self.生成區塊(&若.區塊, 符號表.clone())?; |
| 188 | + |
| 189 | + if 若.或若列表.len() == 0 && 若.不然.is_none() { |
| 190 | + // 僅有若,無或若、不然。 |
| 191 | + self.上標籤(&分支標籤名)?; |
| 192 | + return Ok(()); |
| 193 | + } |
| 194 | + |
| 195 | + let 區塊群結尾標籤名 = self.新區塊群結尾標籤名(); |
| 196 | + self.跳至標籤(&區塊群結尾標籤名)?; |
| 197 | + |
| 198 | + // 或若 |
| 199 | + for 或若 in &若.或若列表 { |
| 200 | + self.上標籤(&分支標籤名)?; |
| 201 | + self.計算(&或若.條件, 符號表)?; |
| 202 | + self.彈出()?; |
| 203 | + 分支標籤名 = self.新分支標籤名(); |
| 204 | + self.t0為0則跳至標籤(&分支標籤名)?; |
| 205 | + self.生成區塊(&或若.區塊, 符號表.clone())?; |
| 206 | + self.跳至標籤(&區塊群結尾標籤名)?; |
| 207 | + } |
| 208 | + |
| 209 | + // 不然 |
| 210 | + self.上標籤(&分支標籤名)?; |
| 211 | + if let Some(不然) = &若.不然 { |
| 212 | + self.生成區塊(&不然.區塊, 符號表.clone())?; |
| 213 | + } |
| 214 | + |
| 215 | + self.上標籤(&區塊群結尾標籤名) |
| 216 | +} |
| 217 | +fn 生成區塊( |
| 218 | + &mut self, 區塊: &Vec<O句>, mut 符號表: O符號表 |
| 219 | +) -> io::Result<()> { |
| 220 | + for 句 in 區塊 { |
| 221 | + self.生成句(句, &mut 符號表)?; |
| 222 | + } |
| 223 | + Ok(()) |
| 224 | +} |
| 225 | +``` |
| 226 | + |
| 227 | +完整程式碼可見[音界咒源碼](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)。 |
0 commit comments