-
Notifications
You must be signed in to change notification settings - Fork 1
/
compile.go
122 lines (112 loc) · 2.97 KB
/
compile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"fmt"
"strings"
)
type Code struct {
labels int
Instructions []string
}
func (c *Code) DataLabel() string { return "Data" }
func (c *Code) DataMax() int { return 100000 }
func (c *Code) LoopLabels() (start, end string) {
c.labels++
return fmt.Sprintf("loop_%d", c.labels), fmt.Sprintf("end_loop_%d", c.labels)
}
func formatIns(ins, params string, args ...interface{}) string {
indent := strings.Repeat(" ", 8)
params = fmt.Sprintf(params, args...)
if params == "" {
return indent + ins
}
ins = fmt.Sprintf("%-10s", ins)
return indent + ins + params
}
func (c *Code) add(s string) { c.Instructions = append(c.Instructions, s) }
func (c *Code) addf(format string, args ...interface{}) { c.add(fmt.Sprintf(format, args...)) }
func (c *Code) Dir(format string, args ...interface{}) { c.addf(format, args...) }
func (c *Code) Label(name string) { c.addf("%s:", name) }
func (c *Code) Ins(ins, params string, args ...interface{}) { c.add(formatIns(ins, params, args...)) }
func CompileOp(op Op, code *Code) {
switch op.Token {
case GT:
if op.Num == 1 {
code.Ins("inc", "ebx")
} else {
code.Ins("add", "ebx, %d", op.Num)
}
case LT:
if op.Num == 1 {
code.Ins("dec", "ebx")
} else {
code.Ins("sub", "ebx, %d", op.Num)
}
case PLUS:
if op.Num == 1 {
code.Ins("inc", "byte [%s+ebx]", code.DataLabel())
} else {
code.Ins("add", "byte [%s+ebx], %d", code.DataLabel(), op.Num)
}
case SUB:
if op.Num == 1 {
code.Ins("dec", "byte [%s+ebx]", code.DataLabel())
} else {
code.Ins("sub", "byte [%s+ebx], %d", code.DataLabel(), op.Num)
}
case DOT:
code.Ins("push", "dword [%s+ebx]", code.DataLabel())
for i := 0; i < op.Num; i++ {
code.Ins("call", "putchar")
}
code.Ins("pop", "ecx")
case COMMA:
for i := 0; i < op.Num; i++ {
code.Ins("call", "getchar")
}
code.Ins("mov", "[%s+ebx], byte al", code.DataLabel())
default:
panic(fmt.Errorf("unsuported op: %s", op))
}
}
func CompileLoop(loop Loop, code *Code) {
start, end := code.LoopLabels()
code.Label(start)
code.Ins("mov", "al, [%s+ebx]", code.DataLabel())
code.Ins("cmp", "al, 0")
code.Ins("je", end)
for _, n := range loop {
CompileNode(n, code)
}
code.Ins("jmp", start)
code.Label(end)
}
func CompileNode(node Node, code *Code) {
switch node := node.(type) {
case Op:
CompileOp(node, code)
case Loop:
CompileLoop(node, code)
default:
panic(fmt.Errorf("unsuported node: %s", node))
}
}
func Compile(p Program) []string {
var code Code
code.Dir("extern putchar, getchar")
code.Dir("global main")
code.Dir("segment .data")
code.Dir("%s times %d db 0", code.DataLabel(), code.DataMax())
code.Dir("segment .text")
code.Label("main")
code.Ins("enter", "0, 0")
code.Ins("pusha", "")
code.Ins("mov", "ebx, 0")
for _, n := range p {
CompileNode(n, &code)
}
code.Ins("popa", "")
code.Ins("mov", "eax, 0")
code.Ins("leave", "")
code.Ins("ret", "")
return code.Instructions
}