vm: add a TUCK implementation with tests

Also fix stack's InsertAt for the edge case of inserting into a position that
currently is empty (the first good TUCK test illustrates this).
This commit is contained in:
Roman Khimov 2019-09-05 16:34:35 +03:00
parent eb224aeebe
commit 974f5db6bc
3 changed files with 53 additions and 10 deletions

View file

@ -132,23 +132,15 @@ func (s *Stack) insert(e, at *Element) *Element {
return e return e
} }
// InsertBefore will insert the element before the mark on the stack.
func (s *Stack) InsertBefore(e, mark *Element) *Element {
if mark == nil {
return nil
}
return s.insert(e, mark.prev)
}
// InsertAt will insert the given item (n) deep on the stack. // InsertAt will insert the given item (n) deep on the stack.
// Be very careful using it and _always_ check both e and n before invocation // Be very careful using it and _always_ check both e and n before invocation
// as it will silently do wrong things otherwise. // as it will silently do wrong things otherwise.
func (s *Stack) InsertAt(e *Element, n int) *Element { func (s *Stack) InsertAt(e *Element, n int) *Element {
before := s.Peek(n) before := s.Peek(n - 1)
if before == nil { if before == nil {
return nil return nil
} }
return s.InsertBefore(e, before) return s.insert(e, before)
} }
// Push pushes the given element on the stack. // Push pushes the given element on the stack.

View file

@ -290,6 +290,16 @@ func (v *VM) execute(ctx *Context, op Instruction) {
v.estack.Push(a) v.estack.Push(a)
v.estack.Push(b) v.estack.Push(b)
case TUCK:
a := v.estack.Dup(0)
if a == nil {
panic("no top-level element found")
}
if v.estack.Len() < 2 {
panic("can't TUCK with a one-element stack")
}
v.estack.InsertAt(a, 2)
case XSWAP: case XSWAP:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n < 0 { if n < 0 {

View file

@ -339,6 +339,47 @@ func TestXTUCKgood(t *testing.T) {
assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64()) assert.Equal(t, int64(topelement), vm.estack.Peek(xtuckdepth).BigInt().Int64())
} }
func TestTUCKbadNoitems(t *testing.T) {
prog := makeProgram(TUCK)
vm := load(prog)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestTUCKbadNoitem(t *testing.T) {
prog := makeProgram(TUCK)
vm := load(prog)
vm.estack.PushVal(1)
vm.Run()
assert.Equal(t, true, vm.state.HasFlag(faultState))
}
func TestTUCKgood(t *testing.T) {
prog := makeProgram(TUCK)
vm := load(prog)
vm.estack.PushVal(42)
vm.estack.PushVal(34)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
}
func TestTUCKgood2(t *testing.T) {
prog := makeProgram(TUCK)
vm := load(prog)
vm.estack.PushVal(11)
vm.estack.PushVal(42)
vm.estack.PushVal(34)
vm.Run()
assert.Equal(t, false, vm.state.HasFlag(faultState))
assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64())
assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64())
assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64())
assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64())
}
func TestOVERbadNoitem(t *testing.T) { func TestOVERbadNoitem(t *testing.T) {
prog := makeProgram(OVER) prog := makeProgram(OVER)
vm := load(prog) vm := load(prog)