Implement storing variables

This commit is contained in:
2024-01-23 22:11:19 +07:00
parent a5e5523244
commit ab0efa2c0c
2 changed files with 82 additions and 0 deletions

24
elm.go
View File

@@ -28,6 +28,8 @@ const (
JMPIF Operation = 10
CALL Operation = 11
RET Operation = 12
STORE Operation = 13
GET Operation = 14
)
type Inst struct {
@@ -206,6 +208,28 @@ func (m* Machine) Execute() error {
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
}

View File

@@ -442,6 +442,7 @@ func TestCallPass(t * testing.T) {
assert.True(t, m.isHalted, "Virtual Machine should be Halted");
assert.Equal(t, int32(1), m.stack[0], "Should skip the pushed value")
assert.Equal(t, int32(69), m.stack[1], "Should skip the pushed value")
assert.Equal(t, 2, len(m.stackFrame), "Should create the function stack frame")
assert.Equal(t, int32(0), m.stack[2], "Should be zero afterall")
}
@@ -464,4 +465,61 @@ func TestRetPass(t * testing.T) {
assert.Equal(t, int32(69), m.stack[1], "Should skip the pushed value")
assert.Equal(t, int32(6), m.stack[2], "Should skip the pushed value")
assert.Equal(t, int32(0), m.stack[3], "Should be zero afterall")
assert.Equal(t, 1, len(m.stackFrame), "Should pop the function stack frame")
}
func TestStoreWontPassIfStackEmpty(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: STORE, Operand: 1})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestStoreWontPassIfNegativeOperand(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: STORE, Operand: -1})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.NotNil(t, err, "Should be errored");
}
func TestStoreShouldStoreOnTheFrame(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: STORE, Operand: 0})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should not be errored");
assert.Equal(t, int32(0), m.sp, "Should decrement the stack pointer")
assert.Equal(t, int32(6), m.stackFrame[0].Locals[0], "Should contain the value")
assert.Equal(t, int32(2), m.ip, "Instruction pointer should be incremented");
}
func TestGetShouldGetVariableFromFrame(t *testing.T) {
m := Constructor()
m.Push(Inst{Operation: PUSH, Operand: 6})
m.Push(Inst{Operation: STORE, Operand: 0})
m.Push(Inst{Operation: PUSH, Operand: 1})
m.Push(Inst{Operation: GET, Operand: 0})
m.Push(Inst{Operation: HALT})
err := m.Run()
assert.Nil(t, err, "Should not be errored");
assert.Equal(t, int32(6), m.stack[1], "Should contain the value")
assert.Equal(t, int32(4), m.ip, "Instruction pointer should be incremented");
}