394 lines
8.4 KiB
Go
394 lines
8.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type NodeType int
|
|
|
|
const (
|
|
ndIdent NodeType = iota
|
|
ndString
|
|
ndInteger
|
|
ndSequence
|
|
ndIf
|
|
ndPrtc
|
|
ndPrts
|
|
ndPrti
|
|
ndWhile
|
|
ndAssign
|
|
ndNegate
|
|
ndNot
|
|
ndMul
|
|
ndDiv
|
|
ndMod
|
|
ndAdd
|
|
ndSub
|
|
ndLss
|
|
ndLeq
|
|
ndGtr
|
|
ndGeq
|
|
ndEql
|
|
ndNeq
|
|
ndAnd
|
|
ndOr
|
|
)
|
|
|
|
type code = byte
|
|
|
|
const (
|
|
fetch code = iota
|
|
store
|
|
push
|
|
add
|
|
sub
|
|
mul
|
|
div
|
|
mod
|
|
lt
|
|
gt
|
|
le
|
|
ge
|
|
eq
|
|
ne
|
|
and
|
|
or
|
|
neg
|
|
not
|
|
jmp
|
|
jz
|
|
prtc
|
|
prts
|
|
prti
|
|
halt
|
|
)
|
|
|
|
type Tree struct {
|
|
nodeType NodeType
|
|
left *Tree
|
|
right *Tree
|
|
value string
|
|
}
|
|
|
|
// dependency: Ordered by NodeType, must remain in same order as NodeType enum
|
|
type atr struct {
|
|
enumText string
|
|
nodeType NodeType
|
|
opcode code
|
|
}
|
|
|
|
var atrs = []atr{
|
|
{"Identifier", ndIdent, 255},
|
|
{"String", ndString, 255},
|
|
{"Integer", ndInteger, 255},
|
|
{"Sequence", ndSequence, 255},
|
|
{"If", ndIf, 255},
|
|
{"Prtc", ndPrtc, 255},
|
|
{"Prts", ndPrts, 255},
|
|
{"Prti", ndPrti, 255},
|
|
{"While", ndWhile, 255},
|
|
{"Assign", ndAssign, 255},
|
|
{"Negate", ndNegate, neg},
|
|
{"Not", ndNot, not},
|
|
{"Multiply", ndMul, mul},
|
|
{"Divide", ndDiv, div},
|
|
{"Mod", ndMod, mod},
|
|
{"Add", ndAdd, add},
|
|
{"Subtract", ndSub, sub},
|
|
{"Less", ndLss, lt},
|
|
{"LessEqual", ndLeq, le},
|
|
{"Greater", ndGtr, gt},
|
|
{"GreaterEqual", ndGeq, ge},
|
|
{"Equal", ndEql, eq},
|
|
{"NotEqual", ndNeq, ne},
|
|
{"And", ndAnd, and},
|
|
{"Or", ndOr, or},
|
|
}
|
|
|
|
var (
|
|
stringPool []string
|
|
globals []string
|
|
object []code
|
|
)
|
|
|
|
var (
|
|
err error
|
|
scanner *bufio.Scanner
|
|
)
|
|
|
|
func reportError(msg string) {
|
|
log.Fatalf("error : %s\n", msg)
|
|
}
|
|
|
|
func check(err error) {
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func nodeType2Op(nodeType NodeType) code {
|
|
return atrs[nodeType].opcode
|
|
}
|
|
|
|
func makeNode(nodeType NodeType, left *Tree, right *Tree) *Tree {
|
|
return &Tree{nodeType, left, right, ""}
|
|
}
|
|
|
|
func makeLeaf(nodeType NodeType, value string) *Tree {
|
|
return &Tree{nodeType, nil, nil, value}
|
|
}
|
|
|
|
/*** Code generator ***/
|
|
|
|
func emitByte(c code) {
|
|
object = append(object, c)
|
|
}
|
|
|
|
func emitWord(n int) {
|
|
bs := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(bs, uint32(n))
|
|
for _, b := range bs {
|
|
emitByte(code(b))
|
|
}
|
|
}
|
|
|
|
func emitWordAt(at, n int) {
|
|
bs := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(bs, uint32(n))
|
|
for i := at; i < at+4; i++ {
|
|
object[i] = code(bs[i-at])
|
|
}
|
|
}
|
|
|
|
func hole() int {
|
|
t := len(object)
|
|
emitWord(0)
|
|
return t
|
|
}
|
|
|
|
func fetchVarOffset(id string) int {
|
|
for i := 0; i < len(globals); i++ {
|
|
if globals[i] == id {
|
|
return i
|
|
}
|
|
}
|
|
globals = append(globals, id)
|
|
return len(globals) - 1
|
|
}
|
|
|
|
func fetchStringOffset(st string) int {
|
|
for i := 0; i < len(stringPool); i++ {
|
|
if stringPool[i] == st {
|
|
return i
|
|
}
|
|
}
|
|
stringPool = append(stringPool, st)
|
|
return len(stringPool) - 1
|
|
}
|
|
|
|
func codeGen(x *Tree) {
|
|
if x == nil {
|
|
return
|
|
}
|
|
var n, p1, p2 int
|
|
switch x.nodeType {
|
|
case ndIdent:
|
|
emitByte(fetch)
|
|
n = fetchVarOffset(x.value)
|
|
emitWord(n)
|
|
case ndInteger:
|
|
emitByte(push)
|
|
n, err = strconv.Atoi(x.value)
|
|
check(err)
|
|
emitWord(n)
|
|
case ndString:
|
|
emitByte(push)
|
|
n = fetchStringOffset(x.value)
|
|
emitWord(n)
|
|
case ndAssign:
|
|
n = fetchVarOffset(x.left.value)
|
|
codeGen(x.right)
|
|
emitByte(store)
|
|
emitWord(n)
|
|
case ndIf:
|
|
codeGen(x.left) // if expr
|
|
emitByte(jz) // if false, jump
|
|
p1 = hole() // make room forjump dest
|
|
codeGen(x.right.left) // if true statements
|
|
if x.right.right != nil {
|
|
emitByte(jmp)
|
|
p2 = hole()
|
|
}
|
|
emitWordAt(p1, len(object)-p1)
|
|
if x.right.right != nil {
|
|
codeGen(x.right.right)
|
|
emitWordAt(p2, len(object)-p2)
|
|
}
|
|
case ndWhile:
|
|
p1 = len(object)
|
|
codeGen(x.left) // while expr
|
|
emitByte(jz) // if false, jump
|
|
p2 = hole() // make room for jump dest
|
|
codeGen(x.right) // statements
|
|
emitByte(jmp) // back to the top
|
|
emitWord(p1 - len(object)) // plug the top
|
|
emitWordAt(p2, len(object)-p2) // plug the 'if false, jump'
|
|
case ndSequence:
|
|
codeGen(x.left)
|
|
codeGen(x.right)
|
|
case ndPrtc:
|
|
codeGen(x.left)
|
|
emitByte(prtc)
|
|
case ndPrti:
|
|
codeGen(x.left)
|
|
emitByte(prti)
|
|
case ndPrts:
|
|
codeGen(x.left)
|
|
emitByte(prts)
|
|
case ndLss, ndGtr, ndLeq, ndGeq, ndEql, ndNeq,
|
|
ndAnd, ndOr, ndSub, ndAdd, ndDiv, ndMul, ndMod:
|
|
codeGen(x.left)
|
|
codeGen(x.right)
|
|
emitByte(nodeType2Op(x.nodeType))
|
|
case ndNegate, ndNot:
|
|
codeGen(x.left)
|
|
emitByte(nodeType2Op(x.nodeType))
|
|
default:
|
|
msg := fmt.Sprintf("error in code generator - found %d, expecting operator\n", x.nodeType)
|
|
reportError(msg)
|
|
}
|
|
}
|
|
|
|
func codeFinish() {
|
|
emitByte(halt)
|
|
}
|
|
|
|
func listCode() {
|
|
fmt.Printf("Datasize: %d Strings: %d\n", len(globals), len(stringPool))
|
|
for _, s := range stringPool {
|
|
fmt.Println(s)
|
|
}
|
|
pc := 0
|
|
for pc < len(object) {
|
|
fmt.Printf("%5d ", pc)
|
|
op := object[pc]
|
|
pc++
|
|
switch op {
|
|
case fetch:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
fmt.Printf("fetch [%d]\n", x)
|
|
pc += 4
|
|
case store:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
fmt.Printf("store [%d]\n", x)
|
|
pc += 4
|
|
case push:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
fmt.Printf("push %d\n", x)
|
|
pc += 4
|
|
case add:
|
|
fmt.Println("add")
|
|
case sub:
|
|
fmt.Println("sub")
|
|
case mul:
|
|
fmt.Println("mul")
|
|
case div:
|
|
fmt.Println("div")
|
|
case mod:
|
|
fmt.Println("mod")
|
|
case lt:
|
|
fmt.Println("lt")
|
|
case gt:
|
|
fmt.Println("gt")
|
|
case le:
|
|
fmt.Println("le")
|
|
case ge:
|
|
fmt.Println("ge")
|
|
case eq:
|
|
fmt.Println("eq")
|
|
case ne:
|
|
fmt.Println("ne")
|
|
case and:
|
|
fmt.Println("and")
|
|
case or:
|
|
fmt.Println("or")
|
|
case neg:
|
|
fmt.Println("neg")
|
|
case not:
|
|
fmt.Println("not")
|
|
case jmp:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
fmt.Printf("jmp (%d) %d\n", x, int32(pc)+x)
|
|
pc += 4
|
|
case jz:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
fmt.Printf("jz (%d) %d\n", x, int32(pc)+x)
|
|
pc += 4
|
|
case prtc:
|
|
fmt.Println("prtc")
|
|
case prti:
|
|
fmt.Println("prti")
|
|
case prts:
|
|
fmt.Println("prts")
|
|
case halt:
|
|
fmt.Println("halt")
|
|
default:
|
|
reportError(fmt.Sprintf("listCode: Unknown opcode %d", op))
|
|
}
|
|
}
|
|
}
|
|
|
|
func getEnumValue(name string) NodeType {
|
|
for _, atr := range atrs {
|
|
if atr.enumText == name {
|
|
return atr.nodeType
|
|
}
|
|
}
|
|
reportError(fmt.Sprintf("Unknown token %s\n", name))
|
|
return -1
|
|
}
|
|
|
|
func loadAst() *Tree {
|
|
var nodeType NodeType
|
|
var s string
|
|
if scanner.Scan() {
|
|
line := strings.TrimRight(scanner.Text(), " \t")
|
|
tokens := strings.Fields(line)
|
|
first := tokens[0]
|
|
if first[0] == ';' {
|
|
return nil
|
|
}
|
|
nodeType = getEnumValue(first)
|
|
le := len(tokens)
|
|
if le == 2 {
|
|
s = tokens[1]
|
|
} else if le > 2 {
|
|
idx := strings.Index(line, `"`)
|
|
s = line[idx:]
|
|
}
|
|
}
|
|
check(scanner.Err())
|
|
if s != "" {
|
|
return makeLeaf(nodeType, s)
|
|
}
|
|
left := loadAst()
|
|
right := loadAst()
|
|
return makeNode(nodeType, left, right)
|
|
}
|
|
|
|
func main() {
|
|
ast, err := os.Open("ast.txt")
|
|
check(err)
|
|
defer ast.Close()
|
|
scanner = bufio.NewScanner(ast)
|
|
codeGen(loadAst())
|
|
codeFinish()
|
|
listCode()
|
|
}
|