Files
elm/core/elm.go
2024-01-25 10:57:23 +07:00

312 lines
5.9 KiB
Go

package elm
import (
"fmt"
"errors"
)
const STACK_SIZE = 1024
type Operation int32
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 int32
}
type Frame struct {
Locals map[int]int32
ReturnAddress int
}
func CreateFrame(returnAddress int32) *Frame {
f := Frame{}
f.Locals = make(map[int]int32)
f.ReturnAddress = int(returnAddress)
return &f
}
func (f *Frame) StoreVariable(address int, value int32) {
f.Locals[address] = value
}
func (f *Frame) GetVariable(address int) int32 {
return f.Locals[address]
}
type Machine struct {
program []Inst
stack []int32
stackFrame []*Frame
ip int32
sp int32
isHalted bool;
}
func Constructor() *Machine {
m := Machine{}
m.program = make([]Inst, 0)
m.stack = make([]int32, 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 (m* Machine) Execute() error {
instr := m.program[m.ip]
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] = instr.Operand
m.sp++
m.ip++
case POP:
if (m.sp <= 0) {
return errors.New("Empty Stack")
}
m.stack[m.sp - 1] = 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")
}
m.stack[m.sp - 2] += m.stack[m.sp - 1];
m.sp--
m.ip++
case SUB:
if (m.sp < 2) {
return errors.New("Stack size is less than required to execute binary operation")
}
m.stack[m.sp - 2] -= m.stack[m.sp - 1];
m.sp--
m.ip++
case MUL:
if (m.sp < 2) {
return errors.New("Stack size is less than required to execute binary operation")
}
m.stack[m.sp - 2] *= m.stack[m.sp - 1];
m.sp--
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] == 0 || m.stack[m.sp - 1] == 0) {
return errors.New("Divide by zero exception")
}
m.stack[m.sp - 2] /= m.stack[m.sp - 1];
m.sp--
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] = 1
} else {
m.stack[m.sp - 2] = 0
}
m.sp--
m.ip++
case JMP:
if (int32(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 (int32(len(m.program) - 1) < instr.Operand || instr.Operand < 0) {
return errors.New("Illegal access")
}
if (m.stack[m.sp - 1] >= 1) {
m.sp--
m.ip = instr.Operand
} else {
m.ip++
}
case CALL:
if (int32(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 = int32(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; int32(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()
}
*/