Implement storing variables
This commit is contained in:
24
elm.go
24
elm.go
@@ -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
|
||||
}
|
||||
|
||||
58
elm_test.go
58
elm_test.go
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user