Files
rpnc/stack.go

206 lines
4.2 KiB
Go
Raw Normal View History

/*
Copyright © 2023 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2023-10-30 14:22:43 +01:00
package main
import (
"container/list"
"fmt"
"sync"
)
// The stack uses a linked list provided by container/list as storage
// and works after the LIFO principle (last in first out). Most of the
// work is being done in the linked list, but we add a couple of
// cenvenient functions, so that the user doesn't have to cope with
// list directly.
2023-10-30 14:22:43 +01:00
type Stack struct {
linklist list.List
2023-10-30 14:22:43 +01:00
backup list.List
debug bool
rev int
backuprev int
mutex sync.Mutex
}
// FIXME: maybe use a separate stack object for backup so that it has
// its own revision etc
2023-10-30 14:22:43 +01:00
func NewStack() *Stack {
return &Stack{
linklist: list.List{},
backup: list.List{},
rev: 0,
backuprev: 0,
}
2023-10-30 14:22:43 +01:00
}
func (s *Stack) Debug(msg string) {
if s.debug {
fmt.Printf("DEBUG(%03d): %s\n", s.rev, msg)
}
}
func (s *Stack) ToggleDebug() {
s.debug = !s.debug
}
func (s *Stack) Bump() {
s.rev++
}
// append an item to the stack
2023-10-30 14:22:43 +01:00
func (s *Stack) Push(x float64) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.Debug(fmt.Sprintf(" push to stack: %.2f", x))
s.Bump()
s.linklist.PushBack(x)
2023-10-30 14:22:43 +01:00
}
// remove and return an item from the stack
2023-10-30 14:22:43 +01:00
func (s *Stack) Pop() float64 {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.linklist.Len() == 0 {
2023-10-30 14:22:43 +01:00
return 0
}
tail := s.linklist.Back()
2023-10-30 14:22:43 +01:00
val := tail.Value
s.linklist.Remove(tail)
2023-10-30 14:22:43 +01:00
s.Debug(fmt.Sprintf(" remove from stack: %.2f", val))
2023-10-30 14:22:43 +01:00
s.Bump()
return val.(float64)
}
// just remove the last item, do not return it
func (s *Stack) Shift(num ...int) {
2023-10-30 14:22:43 +01:00
s.mutex.Lock()
defer s.mutex.Unlock()
count := 1
if len(num) > 0 {
count = num[0]
}
if s.linklist.Len() == 0 {
2023-10-30 14:22:43 +01:00
return
}
for i := 0; i < count; i++ {
tail := s.linklist.Back()
s.linklist.Remove(tail)
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
}
2023-10-30 14:22:43 +01:00
}
// just return the last item, do not remove it
func (s *Stack) Last(num ...int) []float64 {
count := 1
var items []float64
if len(num) > 0 {
count = num[0]
2023-10-30 14:22:43 +01:00
}
if s.linklist.Back() == nil {
return nil
}
for i := 0; i < count; i++ {
items = append(items, s.linklist.Back().Value.(float64))
}
return items
}
// Return all elements of the stack without modifying it.
func (s *Stack) All() []float64 {
items := []float64{}
for e := s.linklist.Front(); e != nil; e = e.Next() {
items = append(items, e.Value.(float64))
}
return items
2023-10-30 14:22:43 +01:00
}
// dump the stack to stdout, including backup if debug is enabled
2023-10-30 14:22:43 +01:00
func (s *Stack) Dump() {
fmt.Printf("Stack revision %d (%p):\n", s.rev, &s.linklist)
for e := s.linklist.Front(); e != nil; e = e.Next() {
2023-10-30 14:22:43 +01:00
fmt.Println(e.Value)
}
if s.debug {
fmt.Printf("Backup stack revision %d (%p):\n", s.backuprev, &s.backup)
for e := s.backup.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
}
}
func (s *Stack) Clear() {
s.Debug("DEBUG: clearing stack")
s.linklist = list.List{}
2023-10-30 14:22:43 +01:00
}
func (s *Stack) Len() int {
return s.linklist.Len()
2023-10-30 14:22:43 +01:00
}
func (s *Stack) Backup() {
// we need clean the list and restore it from scratch each time we
// make a backup, because the elements in list.List{} are pointers
// and lead to unexpected results. The methid here works reliably
// at least.
s.backup = list.List{}
for e := s.linklist.Front(); e != nil; e = e.Next() {
2023-10-30 14:22:43 +01:00
s.backup.PushBack(e.Value)
}
s.backuprev = s.rev
}
func (s *Stack) Restore() {
if s.rev == 0 {
fmt.Println("error: stack is empty.")
return
}
s.Debug(fmt.Sprintf("restoring stack to revision %d", s.backuprev))
s.rev = s.backuprev
s.linklist = s.backup
2023-10-30 14:22:43 +01:00
}
func (s *Stack) Reverse() {
newstack := list.List{}
for e := s.linklist.Front(); e != nil; e = e.Next() {
newstack.PushFront(e.Value)
}
s.linklist = newstack
}