mirror of
https://codeberg.org/scip/rpnc.git
synced 2025-12-16 20:11:02 +01:00
lots of additions
- added Makefile for building - added math constant support - added many more calc operators and functions - added more stack manipulation functions
This commit is contained in:
54
go/Makefile
Normal file
54
go/Makefile
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
# 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/>.
|
||||
|
||||
|
||||
#
|
||||
# no need to modify anything below
|
||||
tool = rpn
|
||||
VERSION = $(shell grep VERSION main.go | head -1 | cut -d '"' -f2)
|
||||
archs = darwin freebsd linux windows
|
||||
PREFIX = /usr/local
|
||||
UID = root
|
||||
GID = 0
|
||||
|
||||
all: buildlocal
|
||||
|
||||
buildlocal:
|
||||
CGO_LDFLAGS='-static' go build -tags osusergo,netgo -ldflags "-extldflags=-static" -o $(tool)
|
||||
|
||||
install: buildlocal
|
||||
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
|
||||
install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
|
||||
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
|
||||
install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/
|
||||
|
||||
clean:
|
||||
rm -rf $(tool) coverage.out
|
||||
|
||||
test:
|
||||
go test -v ./...
|
||||
bash t/test.sh
|
||||
|
||||
singletest:
|
||||
@echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
|
||||
go test -run $(TEST) github.com/tlinden/rpn/$(MOD)
|
||||
|
||||
cover-report:
|
||||
go test ./... -cover -coverprofile=coverage.out
|
||||
go tool cover -html=coverage.out
|
||||
|
||||
goupdate:
|
||||
go get -t -u=patch ./...
|
||||
223
go/calc.go
223
go/calc.go
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -14,6 +15,7 @@ type Calc struct {
|
||||
batch bool
|
||||
stdin bool
|
||||
stack *Stack
|
||||
history []string
|
||||
completer readline.AutoCompleter
|
||||
}
|
||||
|
||||
@@ -23,27 +25,57 @@ debug enable debug output
|
||||
dump display the stack contents
|
||||
clear clear the whole stack
|
||||
shift remove the last element of the stack
|
||||
history display calculation history
|
||||
help show this message
|
||||
|
||||
Available operators:
|
||||
basic operators: + - * /
|
||||
`
|
||||
|
||||
Math operators:
|
||||
^ power`
|
||||
|
||||
func NewCalc() *Calc {
|
||||
c := Calc{stack: NewStack(), debug: false}
|
||||
|
||||
c.completer = readline.NewPrefixCompleter(
|
||||
// commands
|
||||
readline.PcItem("dump"),
|
||||
readline.PcItem("reverse"),
|
||||
readline.PcItem("debug"),
|
||||
readline.PcItem("clear"),
|
||||
readline.PcItem("batch"),
|
||||
readline.PcItem("shift"),
|
||||
readline.PcItem("undo"),
|
||||
readline.PcItem("help"),
|
||||
readline.PcItem("history"),
|
||||
|
||||
// ops
|
||||
readline.PcItem("+"),
|
||||
readline.PcItem("-"),
|
||||
readline.PcItem("*"),
|
||||
readline.PcItem("/"),
|
||||
readline.PcItem("^"),
|
||||
readline.PcItem("%"),
|
||||
readline.PcItem("%-"),
|
||||
readline.PcItem("%+"),
|
||||
|
||||
// constants
|
||||
readline.PcItem("Pi"),
|
||||
readline.PcItem("Phi"),
|
||||
readline.PcItem("Sqrt2"),
|
||||
readline.PcItem("SqrtE"),
|
||||
readline.PcItem("SqrtPi"),
|
||||
readline.PcItem("SqrtPhi"),
|
||||
readline.PcItem("Ln2"),
|
||||
readline.PcItem("Log2E"),
|
||||
readline.PcItem("Ln10"),
|
||||
readline.PcItem("Log10E"),
|
||||
|
||||
// math functions
|
||||
readline.PcItem("sqrt"),
|
||||
readline.PcItem("remainder"),
|
||||
readline.PcItem("avg"),
|
||||
readline.PcItem("median"),
|
||||
)
|
||||
|
||||
return &c
|
||||
@@ -65,7 +97,11 @@ func (c *Calc) ToggleStdin() {
|
||||
func (c *Calc) Eval(line string) {
|
||||
line = strings.TrimSpace(line)
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
simple := regexp.MustCompile(`[\+\-\*\/]`)
|
||||
simple := regexp.MustCompile(`^[\+\-\*\/]$`)
|
||||
constants := []string{"E", "Pi", "Phi", "Sqrt2", "SqrtE", "SqrtPi",
|
||||
"SqrtPhi", "Ln2", "Log2E", "Ln10", "Log10E"}
|
||||
functions := []string{"sqrt", "remainder", "%", "%-", "%+"}
|
||||
batch := []string{"median", "avg"}
|
||||
|
||||
if line == "" {
|
||||
return
|
||||
@@ -83,6 +119,22 @@ func (c *Calc) Eval(line string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if contains(constants, item) {
|
||||
c.stack.Backup()
|
||||
c.stack.Push(const2num(item))
|
||||
continue
|
||||
}
|
||||
|
||||
if contains(functions, item) {
|
||||
c.mathfunc(item)
|
||||
continue
|
||||
}
|
||||
|
||||
if contains(batch, item) {
|
||||
c.batchfunc(item)
|
||||
continue
|
||||
}
|
||||
|
||||
switch item {
|
||||
case "help":
|
||||
fmt.Println(Help)
|
||||
@@ -98,8 +150,17 @@ func (c *Calc) Eval(line string) {
|
||||
case "shift":
|
||||
c.stack.Backup()
|
||||
c.stack.Shift()
|
||||
case "reverse":
|
||||
c.stack.Backup()
|
||||
c.stack.Reverse()
|
||||
case "undo":
|
||||
c.stack.Restore()
|
||||
case "history":
|
||||
for _, entry := range c.history {
|
||||
fmt.Println(entry)
|
||||
}
|
||||
case "^":
|
||||
c.exp()
|
||||
default:
|
||||
fmt.Println("unknown command or operator!")
|
||||
}
|
||||
@@ -107,12 +168,18 @@ func (c *Calc) Eval(line string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Calc) Result() {
|
||||
func (c *Calc) History(format string, args ...any) {
|
||||
c.history = append(c.history, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (c *Calc) Result() float64 {
|
||||
if !c.stdin {
|
||||
fmt.Print("= ")
|
||||
}
|
||||
|
||||
fmt.Println(c.stack.Last())
|
||||
|
||||
return c.stack.Last()
|
||||
}
|
||||
|
||||
func (c *Calc) simple(op byte) {
|
||||
@@ -146,10 +213,158 @@ func (c *Calc) simple(op byte) {
|
||||
|
||||
c.stack.Push(x)
|
||||
|
||||
c.History("%f %c %f = %f", a, op, b, x)
|
||||
|
||||
if !c.batch {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.Result()
|
||||
_ = c.Result()
|
||||
}
|
||||
|
||||
func (c *Calc) exp() {
|
||||
c.stack.Backup()
|
||||
|
||||
for c.stack.Len() > 1 {
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
x := math.Pow(a, b)
|
||||
|
||||
c.stack.Push(x)
|
||||
|
||||
c.History("%f ^ %f = %f", a, b, x)
|
||||
|
||||
if !c.batch {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_ = c.Result()
|
||||
}
|
||||
|
||||
func (c *Calc) mathfunc(funcname string) {
|
||||
// FIXME: split into 2 funcs, one working with 1 the other with 2
|
||||
// args, saving Pop calls
|
||||
c.stack.Backup()
|
||||
|
||||
for c.stack.Len() > 0 {
|
||||
var x float64
|
||||
|
||||
switch funcname {
|
||||
case "sqrt":
|
||||
a := c.stack.Pop()
|
||||
x = math.Sqrt(a)
|
||||
c.History("sqrt(%f) = %f", a, x)
|
||||
|
||||
case "remainder":
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
x = math.Remainder(a, b)
|
||||
c.History("remainderf(%f / %f) = %f", a, b, x)
|
||||
|
||||
case "%":
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
|
||||
x = (a / 100) * b
|
||||
c.History("%f percent of %f = %f", b, a, x)
|
||||
|
||||
case "%-":
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
|
||||
x = a - ((a / 100) * b)
|
||||
c.History("%f minus %f percent of %f = %f", a, b, a, x)
|
||||
|
||||
case "%+":
|
||||
b := c.stack.Pop()
|
||||
a := c.stack.Pop()
|
||||
|
||||
x = a + ((a / 100) * b)
|
||||
c.History("%f plus %f percent of %f = %f", a, b, a, x)
|
||||
|
||||
}
|
||||
|
||||
c.stack.Push(x)
|
||||
|
||||
if !c.batch {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_ = c.Result()
|
||||
}
|
||||
|
||||
func (c *Calc) batchfunc(funcname string) {
|
||||
if !c.batch {
|
||||
fmt.Println("error: only available in batch mode")
|
||||
}
|
||||
|
||||
c.stack.Backup()
|
||||
var x float64
|
||||
count := c.stack.Len()
|
||||
|
||||
switch funcname {
|
||||
case "median":
|
||||
all := []float64{}
|
||||
|
||||
for c.stack.Len() > 0 {
|
||||
all = append(all, c.stack.Pop())
|
||||
}
|
||||
|
||||
middle := count / 2
|
||||
|
||||
x = all[middle]
|
||||
c.History("median(all)")
|
||||
|
||||
case "avg":
|
||||
var sum float64
|
||||
|
||||
for c.stack.Len() > 0 {
|
||||
sum += c.stack.Pop()
|
||||
}
|
||||
|
||||
x = sum / float64(count)
|
||||
c.History("avg(all)")
|
||||
}
|
||||
|
||||
c.stack.Push(x)
|
||||
_ = c.Result()
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func const2num(name string) float64 {
|
||||
switch name {
|
||||
case "Pi":
|
||||
return math.Pi
|
||||
case "Phi":
|
||||
return math.Phi
|
||||
case "Sqrt2":
|
||||
return math.Sqrt2
|
||||
case "SqrtE":
|
||||
return math.SqrtE
|
||||
case "SqrtPi":
|
||||
return math.SqrtPi
|
||||
case "SqrtPhi":
|
||||
return math.SqrtPhi
|
||||
case "Ln2":
|
||||
return math.Ln2
|
||||
case "Log2E":
|
||||
return math.Log2E
|
||||
case "Ln10":
|
||||
return math.Ln10
|
||||
case "Log10E":
|
||||
return math.Log10E
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func main() {
|
||||
|
||||
rl, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "\033[31m»\033[0m ",
|
||||
HistoryFile: os.Getenv("HOME") + ".rpn-history",
|
||||
HistoryFile: os.Getenv("HOME") + "/.rpn-history",
|
||||
HistoryLimit: 500,
|
||||
AutoComplete: calc.completer,
|
||||
InterruptPrompt: "^C",
|
||||
|
||||
14
go/stack.go
14
go/stack.go
@@ -55,7 +55,7 @@ func (s *Stack) Pop() float64 {
|
||||
val := tail.Value
|
||||
s.dll.Remove(tail)
|
||||
|
||||
s.Debug(fmt.Sprintf("DEBUG: remove from stack: %.2f", val))
|
||||
s.Debug(fmt.Sprintf("remove from stack: %.2f", val))
|
||||
|
||||
s.Bump()
|
||||
return val.(float64)
|
||||
@@ -72,7 +72,7 @@ func (s *Stack) Shift() {
|
||||
tail := s.dll.Back()
|
||||
s.dll.Remove(tail)
|
||||
|
||||
s.Debug(fmt.Sprintf("DEBUG: remove from stack: %.2f", tail.Value))
|
||||
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
|
||||
}
|
||||
|
||||
func (s *Stack) Last() float64 {
|
||||
@@ -130,3 +130,13 @@ func (s *Stack) Restore() {
|
||||
s.rev = s.backuprev
|
||||
s.dll = s.backup
|
||||
}
|
||||
|
||||
func (s *Stack) Reverse() {
|
||||
newstack := list.List{}
|
||||
|
||||
for e := s.dll.Front(); e != nil; e = e.Next() {
|
||||
newstack.PushFront(e.Value)
|
||||
}
|
||||
|
||||
s.dll = newstack
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user