From 974f5db6bc1b7fe31b0e43608fafa86f033749e6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Thu, 5 Sep 2019 16:34:35 +0300 Subject: [PATCH] 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). --- pkg/vm/stack.go | 12 ++---------- pkg/vm/vm.go | 10 ++++++++++ pkg/vm/vm_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index ba425439b..a08882d8d 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -132,23 +132,15 @@ func (s *Stack) insert(e, at *Element) *Element { 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. // Be very careful using it and _always_ check both e and n before invocation // as it will silently do wrong things otherwise. func (s *Stack) InsertAt(e *Element, n int) *Element { - before := s.Peek(n) + before := s.Peek(n - 1) if before == nil { return nil } - return s.InsertBefore(e, before) + return s.insert(e, before) } // Push pushes the given element on the stack. diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 0ab9cbebd..237a77d0e 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -290,6 +290,16 @@ func (v *VM) execute(ctx *Context, op Instruction) { v.estack.Push(a) 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: n := int(v.estack.Pop().BigInt().Int64()) if n < 0 { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 635a8feea..781df0f8f 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -339,6 +339,47 @@ func TestXTUCKgood(t *testing.T) { 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) { prog := makeProgram(OVER) vm := load(prog)