322 lines
6.2 KiB
Go
322 lines
6.2 KiB
Go
package elm
|
|
import (
|
|
"fmt"
|
|
"errors"
|
|
)
|
|
|
|
const STACK_SIZE = 1024
|
|
|
|
type Operation int64
|
|
|
|
const (
|
|
HALT Operation = 0
|
|
PUSH Operation = 1
|
|
POP Operation = 2
|
|
DUP Operation = 3
|
|
ADD Operation = 4
|
|
SUB Operation = 5
|
|
MUL Operation = 6
|
|
DIV Operation = 7
|
|
EQ Operation = 8
|
|
JMP Operation = 9
|
|
JMPIF Operation = 10
|
|
CALL Operation = 11
|
|
RET Operation = 12
|
|
STORE Operation = 13
|
|
GET Operation = 14
|
|
)
|
|
|
|
type Inst struct {
|
|
Operation Operation
|
|
Operand int64
|
|
}
|
|
|
|
type Frame struct {
|
|
Locals map[int]ElmValue
|
|
ReturnAddress int
|
|
}
|
|
|
|
func CreateFrame(returnAddress int64) *Frame {
|
|
f := Frame{}
|
|
f.Locals = make(map[int]ElmValue)
|
|
f.ReturnAddress = int(returnAddress)
|
|
|
|
return &f
|
|
}
|
|
|
|
func (f *Frame) StoreVariable(address int, value ElmValue) {
|
|
f.Locals[address] = value
|
|
}
|
|
|
|
func (f *Frame) GetVariable(address int) ElmValue {
|
|
return f.Locals[address]
|
|
}
|
|
|
|
type Machine struct {
|
|
program []Inst
|
|
stack []ElmValue
|
|
stackFrame []*Frame
|
|
ip int64
|
|
sp int64
|
|
isHalted bool;
|
|
}
|
|
|
|
func Constructor() *Machine {
|
|
m := Machine{}
|
|
m.program = make([]Inst, 0)
|
|
m.stack = make([]ElmValue, STACK_SIZE)
|
|
m.stackFrame = make([]*Frame, 0)
|
|
m.stackFrame = append(m.stackFrame, CreateFrame(0))
|
|
m.ip = 0
|
|
m.sp = 0
|
|
m.isHalted = false
|
|
|
|
return &m
|
|
}
|
|
|
|
func (m* Machine) Push(i Inst) error {
|
|
m.program = append(m.program, i)
|
|
return nil
|
|
}
|
|
|
|
func add[T INumber] (a T, b T) T {return a + b}
|
|
func sub[T INumber] (a T, b T) T {return b - a}
|
|
func div[T INumber] (a T, b T) T {return b / a}
|
|
func mul[T INumber] (a T, b T) T {return a * b}
|
|
|
|
|
|
func (m* Machine) Execute() error {
|
|
instr := m.program[m.ip]
|
|
|
|
binaryOpNumber := func(fn func (int64, int64) int64) {
|
|
op1 := m.stack[m.sp - 1].Number
|
|
op2 := m.stack[m.sp - 2].Number
|
|
m.stack[m.sp - 2] = Number(fn(op1, op2))
|
|
m.sp--
|
|
}
|
|
|
|
switch op := instr.Operation; op {
|
|
case HALT:
|
|
m.isHalted = true
|
|
case PUSH:
|
|
if (m.sp >= STACK_SIZE) {
|
|
return errors.New("Stack Overflow");
|
|
}
|
|
|
|
m.stack[m.sp] = Number(instr.Operand)
|
|
m.sp++
|
|
m.ip++
|
|
case POP:
|
|
if (m.sp <= 0) {
|
|
return errors.New("Empty Stack")
|
|
}
|
|
|
|
m.stack[m.sp - 1] = Number(0)
|
|
m.sp--
|
|
m.ip++
|
|
case DUP:
|
|
if (m.sp - instr.Operand <= 0) {
|
|
return errors.New("Stack Underflow")
|
|
}
|
|
|
|
if (m.sp >= STACK_SIZE) {
|
|
return errors.New("Stack Overflow");
|
|
}
|
|
|
|
if (instr.Operand < 0) {
|
|
return errors.New("Illegal access")
|
|
}
|
|
|
|
m.stack[m.sp] = m.stack[m.sp - 1 - instr.Operand]
|
|
m.sp++
|
|
m.ip++
|
|
case ADD:
|
|
if (m.sp < 2) {
|
|
return errors.New("Stack size is less than required to execute binary operation")
|
|
}
|
|
|
|
binaryOpNumber(add[int64])
|
|
m.ip++
|
|
case SUB:
|
|
if (m.sp < 2) {
|
|
return errors.New("Stack size is less than required to execute binary operation")
|
|
}
|
|
|
|
binaryOpNumber(sub[int64])
|
|
m.ip++
|
|
case MUL:
|
|
if (m.sp < 2) {
|
|
return errors.New("Stack size is less than required to execute binary operation")
|
|
}
|
|
|
|
binaryOpNumber(mul[int64])
|
|
m.ip++
|
|
case DIV:
|
|
if (m.sp < 2) {
|
|
return errors.New("Stack size is less than required to execute binary operation")
|
|
}
|
|
|
|
if (m.stack[m.sp - 2].Number == 0 || m.stack[m.sp - 1].Number == 0) {
|
|
return errors.New("Divide by zero exception")
|
|
}
|
|
|
|
binaryOpNumber(div[int64])
|
|
m.ip++
|
|
case EQ:
|
|
if (m.sp < 2) {
|
|
return errors.New("Stack size is less than required to execute binary operation")
|
|
}
|
|
|
|
eq := m.stack[m.sp - 1] == m.stack[m.sp - 2]
|
|
if (eq) {
|
|
m.stack[m.sp - 2] = Number(1)
|
|
} else {
|
|
m.stack[m.sp - 2] = Number(0)
|
|
}
|
|
|
|
m.sp--
|
|
m.ip++
|
|
case JMP:
|
|
if (int64(len(m.program) - 1) < instr.Operand || instr.Operand < 0) {
|
|
return errors.New("Illegal access")
|
|
}
|
|
|
|
m.ip = instr.Operand
|
|
case JMPIF:
|
|
if (m.sp < 1) {
|
|
return errors.New("Stack Underflow")
|
|
}
|
|
|
|
if (int64(len(m.program) - 1) < instr.Operand || instr.Operand < 0) {
|
|
return errors.New("Illegal access")
|
|
}
|
|
|
|
if (m.stack[m.sp - 1].Number >= 1) {
|
|
m.sp--
|
|
m.ip = instr.Operand
|
|
} else {
|
|
m.ip++
|
|
}
|
|
case CALL:
|
|
if (int64(len(m.program) - 1) < instr.Operand || instr.Operand < 0) {
|
|
return errors.New("Illegal access")
|
|
}
|
|
|
|
m.stackFrame = append(m.stackFrame, CreateFrame(m.ip + 1))
|
|
m.ip = instr.Operand
|
|
case RET:
|
|
if len(m.stackFrame) < 2 {
|
|
return errors.New("Stackframe underflow")
|
|
}
|
|
|
|
currentFrame := m.stackFrame[len(m.stackFrame) - 1]
|
|
m.stackFrame = m.stackFrame[:len(m.stackFrame) - 1]
|
|
m.ip = int64(currentFrame.ReturnAddress)
|
|
case STORE:
|
|
if (m.sp < 1) {
|
|
return errors.New("Nothing to store on the stack")
|
|
}
|
|
|
|
if (instr.Operand < 0) {
|
|
return errors.New("Incorrect variable address")
|
|
}
|
|
|
|
currentFrame := m.stackFrame[len(m.stackFrame) - 1]
|
|
currentFrame.StoreVariable(int(instr.Operand), m.stack[m.sp - 1])
|
|
m.sp--
|
|
m.ip++
|
|
case GET:
|
|
if (instr.Operand < 0) {
|
|
return errors.New("Incorrect variable address")
|
|
}
|
|
|
|
currentFrame := m.stackFrame[len(m.stackFrame) - 1]
|
|
m.stack[m.sp] = currentFrame.GetVariable(int(instr.Operand))
|
|
m.sp++
|
|
m.ip++
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m* Machine) Run() error {
|
|
for !m.isHalted {
|
|
err := m.Execute()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m* Machine) Print() {
|
|
fmt.Println("Stack:");
|
|
for i := 0; int64(i) < m.sp; i++ {
|
|
fmt.Println(m.stack[i])
|
|
}
|
|
}
|
|
|
|
/*
|
|
func (m* Machine) DumpProgramBinary(filename string) error {
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return errors.New("Couldn't open file")
|
|
}
|
|
defer f.Close()
|
|
|
|
err = binary.Write(f, binary.LittleEndian, m.program)
|
|
if err != nil {
|
|
return err
|
|
//return errors.New("Couldn't write to file")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m* Machine) LoadProgramFromBinary(filename string) error {
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return errors.New("Couldn't open file")
|
|
}
|
|
defer f.Close()
|
|
|
|
program := make([]Inst, STACK_SIZE)
|
|
err = binary.Read(f, binary.LittleEndian, &program)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.program = program
|
|
return nil
|
|
}
|
|
*/
|
|
/*
|
|
func main() {
|
|
m := Constructor()
|
|
|
|
// err := m.InterpretProgramFromFile("program.lil")
|
|
// if err != nil {
|
|
// fmt.Fprintf(os.Stderr, "[ERR] %s\n", err);
|
|
// os.Exit(1)
|
|
// }
|
|
|
|
stackCounter := 0
|
|
jumpCounter := 0
|
|
for !m.isHalted && stackCounter < 100 {
|
|
|
|
if (m.program[m.ip].Operation == JMPIF) {
|
|
jumpCounter++
|
|
fmt.Printf("Jump Counter: %d \n", jumpCounter)
|
|
}
|
|
|
|
err = m.Execute()
|
|
//m.Print()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "[ERR] %s\n", err);
|
|
os.Exit(1)
|
|
}
|
|
|
|
stackCounter++
|
|
}
|
|
m.Print()
|
|
}
|
|
*/ |