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() } */