304 lines
7.0 KiB
Go
304 lines
7.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"log"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
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
|
|
)
|
|
|
|
var codeMap = map[string]code{
|
|
"fetch": fetch,
|
|
"store": store,
|
|
"push": push,
|
|
"add": add,
|
|
"sub": sub,
|
|
"mul": mul,
|
|
"div": div,
|
|
"mod": mod,
|
|
"lt": lt,
|
|
"gt": gt,
|
|
"le": le,
|
|
"ge": ge,
|
|
"eq": eq,
|
|
"ne": ne,
|
|
"and": and,
|
|
"or": or,
|
|
"neg": neg,
|
|
"not": not,
|
|
"jmp": jmp,
|
|
"jz": jz,
|
|
"prtc": prtc,
|
|
"prts": prts,
|
|
"prti": prti,
|
|
"halt": halt,
|
|
}
|
|
|
|
var (
|
|
err error
|
|
scanner *bufio.Scanner
|
|
object []code
|
|
stringPool []string
|
|
)
|
|
|
|
func reportError(msg string) {
|
|
log.Fatalf("error : %s\n", msg)
|
|
}
|
|
|
|
func check(err error) {
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func btoi(b bool) int32 {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func itob(i int32) bool {
|
|
if i != 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
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))
|
|
}
|
|
}
|
|
|
|
/*** Virtual Machine interpreter ***/
|
|
func runVM(dataSize int) {
|
|
stack := make([]int32, dataSize+1)
|
|
pc := int32(0)
|
|
for {
|
|
op := object[pc]
|
|
pc++
|
|
switch op {
|
|
case fetch:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
stack = append(stack, stack[x])
|
|
pc += 4
|
|
case store:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
ln := len(stack)
|
|
stack[x] = stack[ln-1]
|
|
stack = stack[:ln-1]
|
|
pc += 4
|
|
case push:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
stack = append(stack, x)
|
|
pc += 4
|
|
case add:
|
|
ln := len(stack)
|
|
stack[ln-2] += stack[ln-1]
|
|
stack = stack[:ln-1]
|
|
case sub:
|
|
ln := len(stack)
|
|
stack[ln-2] -= stack[ln-1]
|
|
stack = stack[:ln-1]
|
|
case mul:
|
|
ln := len(stack)
|
|
stack[ln-2] *= stack[ln-1]
|
|
stack = stack[:ln-1]
|
|
case div:
|
|
ln := len(stack)
|
|
stack[ln-2] = int32(float64(stack[ln-2]) / float64(stack[ln-1]))
|
|
stack = stack[:ln-1]
|
|
case mod:
|
|
ln := len(stack)
|
|
stack[ln-2] = int32(math.Mod(float64(stack[ln-2]), float64(stack[ln-1])))
|
|
stack = stack[:ln-1]
|
|
case lt:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] < stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case gt:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] > stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case le:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] <= stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case ge:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] >= stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case eq:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] == stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case ne:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(stack[ln-2] != stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case and:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(itob(stack[ln-2]) && itob(stack[ln-1]))
|
|
stack = stack[:ln-1]
|
|
case or:
|
|
ln := len(stack)
|
|
stack[ln-2] = btoi(itob(stack[ln-2]) || itob(stack[ln-1]))
|
|
stack = stack[:ln-1]
|
|
case neg:
|
|
ln := len(stack)
|
|
stack[ln-1] = -stack[ln-1]
|
|
case not:
|
|
ln := len(stack)
|
|
stack[ln-1] = btoi(!itob(stack[ln-1]))
|
|
case jmp:
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
pc += x
|
|
case jz:
|
|
ln := len(stack)
|
|
v := stack[ln-1]
|
|
stack = stack[:ln-1]
|
|
if v != 0 {
|
|
pc += 4
|
|
} else {
|
|
x := int32(binary.LittleEndian.Uint32(object[pc : pc+4]))
|
|
pc += x
|
|
}
|
|
case prtc:
|
|
ln := len(stack)
|
|
fmt.Printf("%c", stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case prts:
|
|
ln := len(stack)
|
|
fmt.Printf("%s", stringPool[stack[ln-1]])
|
|
stack = stack[:ln-1]
|
|
case prti:
|
|
ln := len(stack)
|
|
fmt.Printf("%d", stack[ln-1])
|
|
stack = stack[:ln-1]
|
|
case halt:
|
|
return
|
|
default:
|
|
reportError(fmt.Sprintf("Unknown opcode %d\n", op))
|
|
}
|
|
}
|
|
}
|
|
|
|
func translate(s string) string {
|
|
var d strings.Builder
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == '\\' && (i+1) < len(s) {
|
|
if s[i+1] == 'n' {
|
|
d.WriteByte('\n')
|
|
i++
|
|
} else if s[i+1] == '\\' {
|
|
d.WriteByte('\\')
|
|
i++
|
|
}
|
|
} else {
|
|
d.WriteByte(s[i])
|
|
}
|
|
}
|
|
return d.String()
|
|
}
|
|
|
|
func loadCode() int {
|
|
var dataSize int
|
|
firstLine := true
|
|
for scanner.Scan() {
|
|
line := strings.TrimRight(scanner.Text(), " \t")
|
|
if len(line) == 0 {
|
|
if firstLine {
|
|
reportError("empty line")
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
lineList := strings.Fields(line)
|
|
if firstLine {
|
|
dataSize, err = strconv.Atoi(lineList[1])
|
|
check(err)
|
|
nStrings, err := strconv.Atoi(lineList[3])
|
|
check(err)
|
|
for i := 0; i < nStrings; i++ {
|
|
scanner.Scan()
|
|
s := strings.Trim(scanner.Text(), "\"\n")
|
|
stringPool = append(stringPool, translate(s))
|
|
}
|
|
firstLine = false
|
|
continue
|
|
}
|
|
offset, err := strconv.Atoi(lineList[0])
|
|
check(err)
|
|
instr := lineList[1]
|
|
opCode, ok := codeMap[instr]
|
|
if !ok {
|
|
reportError(fmt.Sprintf("Unknown instruction %s at %d", instr, opCode))
|
|
}
|
|
emitByte(opCode)
|
|
switch opCode {
|
|
case jmp, jz:
|
|
p, err := strconv.Atoi(lineList[3])
|
|
check(err)
|
|
emitWord(p - offset - 1)
|
|
case push:
|
|
value, err := strconv.Atoi(lineList[2])
|
|
check(err)
|
|
emitWord(value)
|
|
case fetch, store:
|
|
value, err := strconv.Atoi(strings.Trim(lineList[2], "[]"))
|
|
check(err)
|
|
emitWord(value)
|
|
}
|
|
}
|
|
check(scanner.Err())
|
|
return dataSize
|
|
}
|
|
|
|
func main() {
|
|
codeGen, err := os.Open("codegen.txt")
|
|
check(err)
|
|
defer codeGen.Close()
|
|
scanner = bufio.NewScanner(codeGen)
|
|
runVM(loadCode())
|
|
}
|