Skip to content

Commit ac20c48

Browse files
committed
ch2.1 done
1 parent 3043a8d commit ac20c48

File tree

5 files changed

+106
-66
lines changed

5 files changed

+106
-66
lines changed

SUMMARY.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [最小编译器](./ch1-basic/ch1-02-mini-compiler.md)
99
- [LLVM汇编简介](./ch1-basic/ch1-03-llvm-ir.md)
1010
- [表达式](./ch2-expr/readme.md)
11+
- [加减法表达式](./ch2-expr/ch2-01-add-sub.md)
1112
- [最小µGo程序](./ch3-hello-ugo/readme.md)
1213
- [变量和作用域](./ch4-block-and-var/readme.md)
1314
- [if分支和for循环](./ch5-if-for/readme.md)
@@ -17,7 +18,6 @@
1718

1819
<!--
1920
- [表达式](./ch2-expr/readme.md)
20-
- [加减法表达式](./ch2-expr/ch2-01-add-sub.md)
2121
- [乘除法表达式](./ch2-expr/ch2-02-mul-div.md)
2222
- [解析表达式语法树](./ch2-expr/ch2-03-parser.md)
2323
- [重构解析器](./ch2-expr/ch2-04-parser-v2.md)

ch2-expr/ch2-01-add-sub.md

+31-65
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,43 @@ define i32 @main() {
1616

1717
如果将输入的`1+3-2`转化为`[]string{"1", "+", "3", "-", "2"}` 形式,我们则可以通过以下代码输出对应的汇编程序:
1818

19-
```go
20-
func gen_asm(tokens []string) string {
21-
var buf bytes.Buffer
22-
fmt.Fprintln(&buf, `define i32 @main() {`)
19+
```wa
2320
24-
var idx int
21+
func genAsm(tokens: []string) => string {
22+
sb: strings.Builder
23+
sb.WriteString("define i32 @main() {\n")
24+
25+
idx: int
2526
for i, tok := range tokens {
2627
if i == 0 {
27-
fmt.Fprintf(&buf, "\t%%t%d = add i32 0, %v\n",
28-
idx, tokens[i],
29-
)
28+
t0 := "%t" + strconv.Itoa(idx)
29+
sb.WriteString("\t" + t0 + " = add i32 0, " + tokens[i] + "\n")
3030
continue
3131
}
3232
switch tok {
3333
case "+":
3434
idx++
35-
fmt.Fprintf(&buf, "\t%%t%d = add i32 %%t%d, %v\n",
36-
idx, idx-1, tokens[i+1],
37-
)
35+
t0 := "%t" + strconv.Itoa(idx)
36+
t1 := "%t" + strconv.Itoa(idx-1)
37+
sb.WriteString("\t" + t0 + " = add i32 " + t1 + ", " + tokens[i+1] + "\n")
3838
case "-":
3939
idx++
40-
fmt.Fprintf(&buf, "\t%%t%d = sub i32 %%t%d, %v\n",
41-
idx, idx-1, tokens[i+1],
42-
)
40+
t0 := "%t" + strconv.Itoa(idx)
41+
t1 := "%t" + strconv.Itoa(idx-1)
42+
sb.WriteString("\t" + t0 + " = sub i32 " + t1 + ", " + tokens[i+1] + "\n")
4343
}
4444
}
45-
fmt.Fprintf(&buf, "\tret i32 %%t%d\n", idx)
46-
fmt.Fprintln(&buf, `}`)
45+
sb.WriteString("\tret i32 %t" + strconv.Itoa(idx) + "\n")
46+
sb.WriteString(`}`)
4747
48-
return buf.String()
48+
return sb.String()
4949
}
5050
```
5151

5252
而如何将输入的字符串拆分为记号数组本质上属于词法分析的问题。我们先以最简单的方式实现:
5353

54-
```go
55-
func parse_tokens(code string) (tokens []string) {
54+
```wa
55+
func parseTokens(code: string) => (tokens: []string) {
5656
for code != "" {
5757
if idx := strings.IndexAny(code, "+-"); idx >= 0 {
5858
if idx > 0 {
@@ -74,62 +74,28 @@ func parse_tokens(code string) (tokens []string) {
7474

7575
然后对上个版本的compile函数稍加改造以支持加法和减法的运算表达式编译:
7676

77-
```go
78-
func compile(code string) {
79-
tokens := parse_tokens(code)
80-
output := gen_asm(tokens)
81-
82-
os.WriteFile("a.out.ll", []byte(output), 0666)
83-
exec.Command("clang", "-Wno-override-module", "-o", "a.out", "a.out.ll").Run()
84-
}
85-
```
86-
87-
为了便于测试,我们再包装一个run函数:
88-
89-
```go
90-
func run(code string) int {
91-
compile(code)
92-
if err := exec.Command("./a.out").Run(); err != nil {
93-
return err.(*exec.ExitError).ExitCode()
94-
}
95-
return 0
77+
```wa
78+
func compile(code: string) {
79+
tokens := parseTokens(code)
80+
output := genAsm(tokens)
81+
println(output)
9682
}
9783
```
9884

99-
run函数将输入的表达式程序编译并运行、最后返回状态码。然后构造单元测试:
100-
101-
```go
102-
func TestRun(t *testing.T) {
103-
for i, tt := range tests {
104-
if got := run(tt.code); got != tt.value {
105-
t.Fatalf("%d: expect = %v, got = %v", i, tt.value, got)
106-
}
107-
}
108-
}
109-
110-
var tests = []struct {
111-
code string
112-
value int
113-
}{
114-
{code: "1", value: 1},
115-
{code: "1+1", value: 2},
116-
{code: "1 + 3 - 2 ", value: 2},
117-
{code: "1+2+3+4", value: 10},
118-
}
119-
```
12085

121-
确认单元测试没有问题后,更新main函数:
86+
更新main函数:
12287

123-
```go
124-
func main() {
125-
code, _ := io.ReadAll(os.Stdin)
126-
fmt.Println(run(string(code)))
88+
```wa
89+
func main {
90+
compile(`1+2+3`)
12791
}
12892
```
12993

13094
通过以下命令执行:
13195

13296
```
133-
$ echo "1+2+3" | go run main.go
97+
$ wa run main.wa > _main.ll
98+
$ clang -Wno-override-module -o a.out _main.ll
99+
$ ./a.out || echo $?
134100
6
135101
```

ch2-expr/code-2.1/Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
default:
2+
wa run main.wa > _main.ll
3+
clang -Wno-override-module -o a.out _main.ll
4+
./a.out || echo $$?
5+
6+
-@rm a.out
7+
8+
clean:
9+
-rm a.out

ch2-expr/code-2.1/_main.ll

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
define i32 @main() {
2+
%t0 = add i32 0, 1
3+
%t1 = add i32 %t0, 2
4+
%t2 = add i32 %t1, 3
5+
ret i32 %t2
6+
}

ch2-expr/code-2.1/main.wa

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import "strings"
2+
import "strconv"
3+
4+
func main {
5+
compile(`1+2+3`)
6+
}
7+
8+
func compile(code: string) {
9+
tokens := parseTokens(code)
10+
output := genAsm(tokens)
11+
println(output)
12+
}
13+
14+
func parseTokens(code: string) => (tokens: []string) {
15+
for code != "" {
16+
if idx := strings.IndexAny(code, "+-"); idx >= 0 {
17+
if idx > 0 {
18+
tokens = append(tokens, strings.TrimSpace(code[:idx]))
19+
}
20+
tokens = append(tokens, code[idx:][:1])
21+
code = code[idx+1:]
22+
continue
23+
}
24+
25+
tokens = append(tokens, strings.TrimSpace(code))
26+
return
27+
}
28+
return
29+
}
30+
31+
func genAsm(tokens: []string) => string {
32+
sb: strings.Builder
33+
sb.WriteString("define i32 @main() {\n")
34+
35+
idx: int
36+
for i, tok := range tokens {
37+
if i == 0 {
38+
t0 := "%t" + strconv.Itoa(idx)
39+
sb.WriteString("\t" + t0 + " = add i32 0, " + tokens[i] + "\n")
40+
continue
41+
}
42+
switch tok {
43+
case "+":
44+
idx++
45+
t0 := "%t" + strconv.Itoa(idx)
46+
t1 := "%t" + strconv.Itoa(idx-1)
47+
sb.WriteString("\t" + t0 + " = add i32 " + t1 + ", " + tokens[i+1] + "\n")
48+
case "-":
49+
idx++
50+
t0 := "%t" + strconv.Itoa(idx)
51+
t1 := "%t" + strconv.Itoa(idx-1)
52+
sb.WriteString("\t" + t0 + " = sub i32 " + t1 + ", " + tokens[i+1] + "\n")
53+
}
54+
}
55+
sb.WriteString("\tret i32 %t" + strconv.Itoa(idx) + "\n")
56+
sb.WriteString(`}`)
57+
58+
return sb.String()
59+
}

0 commit comments

Comments
 (0)