mirror of
https://codeberg.org/scip/rpnc.git
synced 2025-12-17 20:41:01 +01:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e963a770a7 | ||
|
|
ad2d9d98d6 | ||
|
|
bb49cb7626 | ||
|
|
9441be35ef | ||
|
|
dac5c0967a | ||
|
|
b5430403fd |
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[bug-report]"
|
||||||
|
labels: bug
|
||||||
|
assignees: TLINDEN
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describtion**
|
||||||
|
<!-- Please provide a clear and concise description of the issue: -->
|
||||||
|
|
||||||
|
|
||||||
|
**Steps To Reproduce**
|
||||||
|
<!-- Please detail the steps to reproduce the behavior: -->
|
||||||
|
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
<!-- What do you expected to happen instead? -->
|
||||||
|
|
||||||
|
|
||||||
|
**Version information**
|
||||||
|
<!--
|
||||||
|
Please provide as much version information as possible:
|
||||||
|
- if you have just installed a binary, provide the output of: rpn -v
|
||||||
|
- if you installed from source, provide the output of: make show-version
|
||||||
|
- provide additional details: operating system and version and shell environment
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
**Additional informations**
|
||||||
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest a feature
|
||||||
|
title: "[feature-request]"
|
||||||
|
labels: feature-request
|
||||||
|
assignees: TLINDEN
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describtion**
|
||||||
|
<!-- Please provide a clear and concise description of the feature you desire: -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Version information**
|
||||||
|
<!--
|
||||||
|
Just in case the feature is already present, please provide as
|
||||||
|
much version information as possible:
|
||||||
|
- if you have just installed a binary, provide the output of: rpn -v
|
||||||
|
- if you installed from source, provide the output of: make show-version
|
||||||
|
- provide additional details: operating system and version and shell environment
|
||||||
|
-->
|
||||||
|
|
||||||
36
.github/workflows/ci.yaml
vendored
Normal file
36
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: build-and-test-rpn
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: [1.21]
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
name: Build
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.version }}
|
||||||
|
id: go
|
||||||
|
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
run: go build
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: make test
|
||||||
|
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.21
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
releases
|
releases
|
||||||
rpn
|
rpn
|
||||||
rpn.1
|
rpn.1
|
||||||
|
coverage.out
|
||||||
|
|||||||
17
Makefile
17
Makefile
@@ -39,10 +39,6 @@ ifdef HAVE_POD
|
|||||||
echo "var manpage = \`" >> $*.go
|
echo "var manpage = \`" >> $*.go
|
||||||
pod2text $*.pod >> $*.go
|
pod2text $*.pod >> $*.go
|
||||||
echo "\`" >> $*.go
|
echo "\`" >> $*.go
|
||||||
|
|
||||||
echo "var usage = \`" >> $*.go
|
|
||||||
awk '/SYNOPS/{f=1;next} /DESCR/{f=0} f' $*.pod | sed 's/^ //' >> $*.go
|
|
||||||
echo "\`" >> $*.go
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
buildlocal:
|
buildlocal:
|
||||||
@@ -59,7 +55,6 @@ clean:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
go test -v ./...
|
go test -v ./...
|
||||||
bash t/test.sh
|
|
||||||
|
|
||||||
singletest:
|
singletest:
|
||||||
@echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
|
@echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
|
||||||
@@ -77,3 +72,15 @@ buildall:
|
|||||||
|
|
||||||
release: buildall
|
release: buildall
|
||||||
gh release create v$(VERSION) --generate-notes releases/*
|
gh release create v$(VERSION) --generate-notes releases/*
|
||||||
|
|
||||||
|
show-versions: buildlocal
|
||||||
|
@echo "### rpn version:"
|
||||||
|
@./rpn -v
|
||||||
|
|
||||||
|
@echo
|
||||||
|
@echo "### go module versions:"
|
||||||
|
@go list -m all
|
||||||
|
|
||||||
|
@echo
|
||||||
|
@echo "### go version used for building:"
|
||||||
|
@grep -m 1 go go.mod
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -1,4 +1,8 @@
|
|||||||
## Reverse Polish Notation Calculator for the commandline
|
## Programmable command-line calculator using reverse polish notation
|
||||||
|
|
||||||
|
[](https://github.com/tlinden/rpnc/actions)
|
||||||
|
[](https://github.com/tlinden/rpnc/blob/master/LICENSE)
|
||||||
|
[](https://goreportcard.com/report/github.com/tlinden/rpnc)
|
||||||
|
|
||||||
This is a small commandline calculator which takes its input in
|
This is a small commandline calculator which takes its input in
|
||||||
[reverse polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)
|
[reverse polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)
|
||||||
@@ -11,10 +15,13 @@ Features:
|
|||||||
- various stack manipulation commands
|
- various stack manipulation commands
|
||||||
- basic math operators
|
- basic math operators
|
||||||
- advanced math functions (not yet complete)
|
- advanced math functions (not yet complete)
|
||||||
- provides interactive repl
|
|
||||||
- can be used on the commandline
|
- can be used on the commandline
|
||||||
- can calculate data in batch mode (also from STDIN)
|
- can calculate data in batch mode (also from STDIN)
|
||||||
- extensible with custom LUA functions
|
- extensible with custom LUA functions
|
||||||
|
- provides interactive repl
|
||||||
|
- completion
|
||||||
|
- history
|
||||||
|
|
||||||
|
|
||||||
## Working principle
|
## Working principle
|
||||||
|
|
||||||
@@ -65,6 +72,8 @@ DEBUG(012): push to stack: 200.00
|
|||||||
= 200
|
= 200
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For a very good explanation how reverse polish notation and the stack works [watch this video by Prof. Brailsford](https://youtu.be/7ha78yWRDlE?si=9MCp59jAAG8fXavP)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Basically you enter numbers followed by an operator or a
|
Basically you enter numbers followed by an operator or a
|
||||||
|
|||||||
91
calc.go
91
calc.go
@@ -20,7 +20,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -33,6 +32,7 @@ type Calc struct {
|
|||||||
debug bool
|
debug bool
|
||||||
batch bool
|
batch bool
|
||||||
stdin bool
|
stdin bool
|
||||||
|
showstack bool
|
||||||
stack *Stack
|
stack *Stack
|
||||||
history []string
|
history []string
|
||||||
completer readline.AutoCompleter
|
completer readline.AutoCompleter
|
||||||
@@ -49,9 +49,12 @@ type Calc struct {
|
|||||||
const Help string = `Available commands:
|
const Help string = `Available commands:
|
||||||
batch toggle batch mode
|
batch toggle batch mode
|
||||||
debug toggle debug output
|
debug toggle debug output
|
||||||
|
show show the last 5 items of the stack
|
||||||
dump display the stack contents
|
dump display the stack contents
|
||||||
clear clear the whole stack
|
clear clear the whole stack
|
||||||
shift remove the last element of the stack
|
shift remove the last element of the stack
|
||||||
|
reverse reverse the stack elements
|
||||||
|
swap exchange the last two elements
|
||||||
history display calculation history
|
history display calculation history
|
||||||
help|? show this message
|
help|? show this message
|
||||||
quit|exit|c-d|c-c exit program
|
quit|exit|c-d|c-c exit program
|
||||||
@@ -80,7 +83,7 @@ median median of all values`
|
|||||||
// commands, constants and operators, defined here to feed completion
|
// commands, constants and operators, defined here to feed completion
|
||||||
// and our mode switch in Eval() dynamically
|
// and our mode switch in Eval() dynamically
|
||||||
const (
|
const (
|
||||||
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit`
|
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit swap show`
|
||||||
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
|
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -161,14 +164,21 @@ func (c *Calc) ToggleStdin() {
|
|||||||
c.stdin = !c.stdin
|
c.stdin = !c.stdin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Calc) ToggleShow() {
|
||||||
|
c.showstack = !c.showstack
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Calc) Prompt() string {
|
func (c *Calc) Prompt() string {
|
||||||
p := "\033[31m»\033[0m "
|
p := "\033[31m»\033[0m "
|
||||||
b := ""
|
b := ""
|
||||||
|
|
||||||
if c.batch {
|
if c.batch {
|
||||||
b = "->batch"
|
b = "->batch"
|
||||||
}
|
}
|
||||||
|
|
||||||
d := ""
|
d := ""
|
||||||
v := ""
|
v := ""
|
||||||
|
|
||||||
if c.debug {
|
if c.debug {
|
||||||
d = "->debug"
|
d = "->debug"
|
||||||
v = fmt.Sprintf("/rev%d", c.stack.rev)
|
v = fmt.Sprintf("/rev%d", c.stack.rev)
|
||||||
@@ -192,20 +202,6 @@ func (c *Calc) Eval(line string) {
|
|||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
c.stack.Push(num)
|
c.stack.Push(num)
|
||||||
} else {
|
} else {
|
||||||
/*
|
|
||||||
if contains(c.MathFunctions, item) {
|
|
||||||
// go builtin math function, if implemented
|
|
||||||
c.mathfunc(item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains(c.BatchFunctions, item) {
|
|
||||||
// math functions only supported in batch mode like max or mean
|
|
||||||
c.batchfunc(item)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if contains(c.Constants, item) {
|
if contains(c.Constants, item) {
|
||||||
// put the constant onto the stack
|
// put the constant onto the stack
|
||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
@@ -271,12 +267,21 @@ func (c *Calc) Eval(line string) {
|
|||||||
case "reverse":
|
case "reverse":
|
||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
c.stack.Reverse()
|
c.stack.Reverse()
|
||||||
|
case "swap":
|
||||||
|
if c.stack.Len() < 2 {
|
||||||
|
fmt.Println("stack too small, can't swap")
|
||||||
|
} else {
|
||||||
|
c.stack.Backup()
|
||||||
|
c.stack.Swap()
|
||||||
|
}
|
||||||
case "undo":
|
case "undo":
|
||||||
c.stack.Restore()
|
c.stack.Restore()
|
||||||
case "history":
|
case "history":
|
||||||
for _, entry := range c.history {
|
for _, entry := range c.history {
|
||||||
fmt.Println(entry)
|
fmt.Println(entry)
|
||||||
}
|
}
|
||||||
|
case "show":
|
||||||
|
c.ToggleShow()
|
||||||
case "exit":
|
case "exit":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "quit":
|
case "quit":
|
||||||
@@ -288,6 +293,16 @@ func (c *Calc) Eval(line string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.showstack && !c.stdin {
|
||||||
|
dots := ""
|
||||||
|
|
||||||
|
if c.stack.Len() > 5 {
|
||||||
|
dots = "... "
|
||||||
|
}
|
||||||
|
last := c.stack.Last(5)
|
||||||
|
fmt.Printf("stack: %s%s\n", dots, list2str(last))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute a math function, check if it is defined just in case
|
// Execute a math function, check if it is defined just in case
|
||||||
@@ -376,50 +391,6 @@ func (c *Calc) Debug(msg string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do simple calculations
|
|
||||||
func (c *Calc) simple(op byte) {
|
|
||||||
c.stack.Backup()
|
|
||||||
|
|
||||||
for c.stack.Len() > 1 {
|
|
||||||
b := c.stack.Pop()
|
|
||||||
a := c.stack.Pop()
|
|
||||||
var x float64
|
|
||||||
|
|
||||||
c.Debug(fmt.Sprintf("evaluating: %.2f %c %.2f", a, op, b))
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case '+':
|
|
||||||
x = a + b
|
|
||||||
case '-':
|
|
||||||
x = a - b
|
|
||||||
case 'x':
|
|
||||||
fallthrough // alias for *
|
|
||||||
case '*':
|
|
||||||
x = a * b
|
|
||||||
case '/':
|
|
||||||
if b == 0 {
|
|
||||||
fmt.Println("error: division by null!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x = a / b
|
|
||||||
case '^':
|
|
||||||
x = math.Pow(a, b)
|
|
||||||
default:
|
|
||||||
panic("invalid operator!")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.stack.Push(x)
|
|
||||||
|
|
||||||
c.History("%f %c %f = %f", a, op, b, x)
|
|
||||||
|
|
||||||
if !c.batch {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Result()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Calc) luafunc(funcname string) {
|
func (c *Calc) luafunc(funcname string) {
|
||||||
// called from calc loop
|
// called from calc loop
|
||||||
var x float64
|
var x float64
|
||||||
|
|||||||
141
calc_test.go
Normal file
141
calc_test.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCalc(t *testing.T) {
|
||||||
|
calc := NewCalc()
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
cmd string
|
||||||
|
exp float64
|
||||||
|
batch bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plus",
|
||||||
|
cmd: `15 15 +`,
|
||||||
|
exp: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "power",
|
||||||
|
cmd: `4 2 ^`,
|
||||||
|
exp: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minus",
|
||||||
|
cmd: `100 50 -`,
|
||||||
|
exp: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multi",
|
||||||
|
cmd: `4 4 x`,
|
||||||
|
exp: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "divide",
|
||||||
|
cmd: `10 2 /`,
|
||||||
|
exp: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "percent",
|
||||||
|
cmd: `400 20 %`,
|
||||||
|
exp: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "percent-minus",
|
||||||
|
cmd: `400 20 %-`,
|
||||||
|
exp: 320,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "percent-plus",
|
||||||
|
cmd: `400 20 %+`,
|
||||||
|
exp: 480,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mod",
|
||||||
|
cmd: `9 2 mod`,
|
||||||
|
exp: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sqrt",
|
||||||
|
cmd: `16 sqrt`,
|
||||||
|
exp: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ceil",
|
||||||
|
cmd: `15.5 ceil`,
|
||||||
|
exp: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dim",
|
||||||
|
cmd: `6 4 dim`,
|
||||||
|
exp: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch-sum",
|
||||||
|
cmd: `2 2 2 2 sum`,
|
||||||
|
exp: 8,
|
||||||
|
batch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch-median",
|
||||||
|
cmd: `1 2 3 4 5 median`,
|
||||||
|
exp: 3,
|
||||||
|
batch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch-mean",
|
||||||
|
cmd: `2 2 8 2 2 mean`,
|
||||||
|
exp: 3.2,
|
||||||
|
batch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch-min",
|
||||||
|
cmd: `1 2 3 4 5 min`,
|
||||||
|
exp: 1,
|
||||||
|
batch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch-max",
|
||||||
|
cmd: `1 2 3 4 5 max`,
|
||||||
|
exp: 5,
|
||||||
|
batch: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
testname := fmt.Sprintf("cmd-%s-expect-%.2f",
|
||||||
|
tt.name, tt.exp)
|
||||||
|
|
||||||
|
t.Run(testname, func(t *testing.T) {
|
||||||
|
calc.batch = tt.batch
|
||||||
|
calc.Eval(tt.cmd)
|
||||||
|
got := calc.Result()
|
||||||
|
calc.stack.Clear()
|
||||||
|
if got != tt.exp {
|
||||||
|
t.Errorf("calc failed:\n+++ got: %f\n--- want: %f",
|
||||||
|
got, tt.exp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
4
main.go
4
main.go
@@ -30,7 +30,7 @@ import (
|
|||||||
lua "github.com/yuin/gopher-lua"
|
lua "github.com/yuin/gopher-lua"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION string = "2.0.1"
|
const VERSION string = "2.0.3"
|
||||||
|
|
||||||
const Usage string = `This is rpn, a reverse polish notation calculator cli.
|
const Usage string = `This is rpn, a reverse polish notation calculator cli.
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ Usage: rpn [-bdvh] [<operator>]
|
|||||||
Options:
|
Options:
|
||||||
-b, --batchmode enable batch mode
|
-b, --batchmode enable batch mode
|
||||||
-d, --debug enable debug mode
|
-d, --debug enable debug mode
|
||||||
|
-s, --stack show last 5 items of the stack (off by default)
|
||||||
-m, --manual show manual
|
-m, --manual show manual
|
||||||
-v, --version show version
|
-v, --version show version
|
||||||
-h, --help show help
|
-h, --help show help
|
||||||
@@ -58,6 +59,7 @@ func main() {
|
|||||||
configfile := ""
|
configfile := ""
|
||||||
|
|
||||||
flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
|
flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
|
||||||
|
flag.BoolVarP(&calc.showstack, "showstack", "s", false, "show stack")
|
||||||
flag.BoolVarP(&enabledebug, "debug", "d", false, "debug mode")
|
flag.BoolVarP(&enabledebug, "debug", "d", false, "debug mode")
|
||||||
flag.BoolVarP(&showversion, "version", "v", false, "show version")
|
flag.BoolVarP(&showversion, "version", "v", false, "show version")
|
||||||
flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
|
flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
|
||||||
|
|||||||
62
rpn.go
62
rpn.go
@@ -111,7 +111,9 @@ DESCRIPTION
|
|||||||
switch or debug toggle command), then the backup stack is also being
|
switch or debug toggle command), then the backup stack is also being
|
||||||
displayed.
|
displayed.
|
||||||
|
|
||||||
The stack can be reversed using the reverse command.
|
The stack can be reversed using the reverse command. However, sometimes
|
||||||
|
only the last two values are in the wrong order. Use the swap command to
|
||||||
|
exchange them.
|
||||||
|
|
||||||
You can use the shift command to remove the last number from the stack.
|
You can use the shift command to remove the last number from the stack.
|
||||||
|
|
||||||
@@ -145,8 +147,52 @@ DESCRIPTION
|
|||||||
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
|
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
|
||||||
y1 copysign dim hypot
|
y1 copysign dim hypot
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
batch toggle batch mode
|
||||||
|
debug toggle debug output
|
||||||
|
dump display the stack contents
|
||||||
|
clear clear the whole stack
|
||||||
|
shift remove the last element of the stack
|
||||||
|
reverse reverse the stack elements
|
||||||
|
swap exchange the last two stack elements
|
||||||
|
show show the last 5 items of the stack
|
||||||
|
history display calculation history
|
||||||
|
help|? show this message
|
||||||
|
quit|exit|c-d|c-c exit program
|
||||||
|
|
||||||
Refer to https://pkg.go.dev/math for details about those functions.
|
Refer to https://pkg.go.dev/math for details about those functions.
|
||||||
|
|
||||||
|
INTERACTIVE REPL
|
||||||
|
While you can use rpn in the command-line, the best experience you'll
|
||||||
|
have is the interactive repl (read eval print loop). Just execute "rpn"
|
||||||
|
and you'll be there.
|
||||||
|
|
||||||
|
In interactive mode you can use TAB completion to complete commands,
|
||||||
|
operators and functions. There's also a history, which allows you to
|
||||||
|
repeat complicated calculations (as long as you've entered them in one
|
||||||
|
line).
|
||||||
|
|
||||||
|
There are also a lot of key bindings, here are the most important ones:
|
||||||
|
|
||||||
|
ctrl-c + ctrl-d
|
||||||
|
Exit interactive rpn
|
||||||
|
|
||||||
|
ctrl-z
|
||||||
|
Send rpn to the backgound.
|
||||||
|
|
||||||
|
ctrl-a
|
||||||
|
Beginning of line.
|
||||||
|
|
||||||
|
ctrl-e
|
||||||
|
End of line.
|
||||||
|
|
||||||
|
ctrl-l
|
||||||
|
Clear the screen.
|
||||||
|
|
||||||
|
ctrl-r
|
||||||
|
Search through history.
|
||||||
|
|
||||||
EXTENDING RPN USING LUA
|
EXTENDING RPN USING LUA
|
||||||
You can use a lua script with lua functions to extend the calculator. By
|
You can use a lua script with lua functions to extend the calculator. By
|
||||||
default the tool looks for "~/.rpn.lua". You can also specify a script
|
default the tool looks for "~/.rpn.lua". You can also specify a script
|
||||||
@@ -222,17 +268,3 @@ AUTHORS
|
|||||||
Thomas von Dein tom AT vondein DOT org
|
Thomas von Dein tom AT vondein DOT org
|
||||||
|
|
||||||
`
|
`
|
||||||
var usage = `
|
|
||||||
|
|
||||||
Usage: rpn [-bdvh] [<operator>]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-b, --batchmode enable batch mode
|
|
||||||
-d, --debug enable debug mode
|
|
||||||
-v, --version show version
|
|
||||||
-h, --help show help
|
|
||||||
|
|
||||||
When <operator> is given, batch mode ist automatically enabled. Use
|
|
||||||
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|||||||
61
rpn.pod
61
rpn.pod
@@ -116,7 +116,9 @@ You can use B<dump> to display the stack. If debugging
|
|||||||
is enabled (C<-d> switch or B<debug> toggle command), then the backup
|
is enabled (C<-d> switch or B<debug> toggle command), then the backup
|
||||||
stack is also being displayed.
|
stack is also being displayed.
|
||||||
|
|
||||||
The stack can be reversed using the B<reverse> command.
|
The stack can be reversed using the B<reverse> command. However,
|
||||||
|
sometimes only the last two values are in the wrong order. Use the
|
||||||
|
B<swap> command to exchange them.
|
||||||
|
|
||||||
You can use the B<shift> command to remove the last number from the
|
You can use the B<shift> command to remove the last number from the
|
||||||
stack.
|
stack.
|
||||||
@@ -152,8 +154,65 @@ Math functions:
|
|||||||
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
|
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
|
||||||
y1 copysign dim hypot
|
y1 copysign dim hypot
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
batch toggle batch mode
|
||||||
|
debug toggle debug output
|
||||||
|
dump display the stack contents
|
||||||
|
clear clear the whole stack
|
||||||
|
shift remove the last element of the stack
|
||||||
|
reverse reverse the stack elements
|
||||||
|
swap exchange the last two stack elements
|
||||||
|
show show the last 5 items of the stack
|
||||||
|
history display calculation history
|
||||||
|
help|? show this message
|
||||||
|
quit|exit|c-d|c-c exit program
|
||||||
|
|
||||||
|
|
||||||
Refer to https://pkg.go.dev/math for details about those functions.
|
Refer to https://pkg.go.dev/math for details about those functions.
|
||||||
|
|
||||||
|
=head1 INTERACTIVE REPL
|
||||||
|
|
||||||
|
While you can use rpn in the command-line, the best experience you'll
|
||||||
|
have is the interactive repl (read eval print loop). Just execute
|
||||||
|
C<rpn> and you'll be there.
|
||||||
|
|
||||||
|
In interactive mode you can use TAB completion to complete commands,
|
||||||
|
operators and functions. There's also a history, which allows you to
|
||||||
|
repeat complicated calculations (as long as you've entered them in one
|
||||||
|
line).
|
||||||
|
|
||||||
|
There are also a lot of key bindings, here are the most important
|
||||||
|
ones:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item ctrl-c + ctrl-d
|
||||||
|
|
||||||
|
Exit interactive rpn
|
||||||
|
|
||||||
|
=item ctrl-z
|
||||||
|
|
||||||
|
Send rpn to the backgound.
|
||||||
|
|
||||||
|
=item ctrl-a
|
||||||
|
|
||||||
|
Beginning of line.
|
||||||
|
|
||||||
|
=item ctrl-e
|
||||||
|
|
||||||
|
End of line.
|
||||||
|
|
||||||
|
=item ctrl-l
|
||||||
|
|
||||||
|
Clear the screen.
|
||||||
|
|
||||||
|
=item ctrl-r
|
||||||
|
|
||||||
|
Search through history.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
=head1 EXTENDING RPN USING LUA
|
=head1 EXTENDING RPN USING LUA
|
||||||
|
|
||||||
You can use a lua script with lua functions to extend the
|
You can use a lua script with lua functions to extend the
|
||||||
|
|||||||
20
stack.go
20
stack.go
@@ -115,6 +115,26 @@ func (s *Stack) Shift(num ...int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stack) Swap() {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
if s.linklist.Len() < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a := s.linklist.Back()
|
||||||
|
s.linklist.Remove(a)
|
||||||
|
|
||||||
|
b := s.linklist.Back()
|
||||||
|
s.linklist.Remove(b)
|
||||||
|
|
||||||
|
s.Debug(fmt.Sprintf("swapping %.2f with %.2f", b.Value, a.Value))
|
||||||
|
|
||||||
|
s.linklist.PushBack(a.Value)
|
||||||
|
s.linklist.PushBack(b.Value)
|
||||||
|
}
|
||||||
|
|
||||||
// Return the last num items from the stack w/o modifying it.
|
// Return the last num items from the stack w/o modifying it.
|
||||||
func (s *Stack) Last(num ...int) []float64 {
|
func (s *Stack) Last(num ...int) []float64 {
|
||||||
items := []float64{}
|
items := []float64{}
|
||||||
|
|||||||
190
stack_test.go
Normal file
190
stack_test.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPush(t *testing.T) {
|
||||||
|
t.Run("push", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
|
||||||
|
if s.linklist.Back().Value != 5.0 {
|
||||||
|
t.Errorf("push failed:\n+++ got: %f\n--- want: %f",
|
||||||
|
s.linklist.Back().Value, 5.0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPop(t *testing.T) {
|
||||||
|
t.Run("pop", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
got := s.Pop()
|
||||||
|
|
||||||
|
if got != 5.0 {
|
||||||
|
t.Errorf("pop failed:\n+++ got: %f\n--- want: %f",
|
||||||
|
got, 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Errorf("stack not empty after pop()")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPops(t *testing.T) {
|
||||||
|
t.Run("pops", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
s.Push(5)
|
||||||
|
s.Push(5)
|
||||||
|
s.Pop()
|
||||||
|
|
||||||
|
if s.Len() != 2 {
|
||||||
|
t.Errorf("stack len not correct after pop:\n+++ got: %d\n--- want: %d",
|
||||||
|
s.Len(), 2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShift(t *testing.T) {
|
||||||
|
t.Run("shift", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Shift()
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Errorf("stack not empty after shift()")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClear(t *testing.T) {
|
||||||
|
t.Run("clear", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
s.Push(5)
|
||||||
|
s.Push(5)
|
||||||
|
s.Clear()
|
||||||
|
|
||||||
|
if s.Len() != 0 {
|
||||||
|
t.Errorf("stack not empty after clear()")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLast(t *testing.T) {
|
||||||
|
t.Run("last", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
got := s.Last()
|
||||||
|
|
||||||
|
if len(got) != 1 {
|
||||||
|
t.Errorf("last failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||||
|
len(got), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got[0] != 5.0 {
|
||||||
|
t.Errorf("last failed:\n+++ got: %f\n--- want: %f",
|
||||||
|
got, 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Len() != 1 {
|
||||||
|
t.Errorf("stack modified after last()")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAll(t *testing.T) {
|
||||||
|
t.Run("all", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
list := []float64{2, 4, 6, 8}
|
||||||
|
|
||||||
|
for _, item := range list {
|
||||||
|
s.Push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := s.All()
|
||||||
|
|
||||||
|
if len(got) != len(list) {
|
||||||
|
t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||||
|
len(got), len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(list); i++ {
|
||||||
|
if got[i] != list[i] {
|
||||||
|
t.Errorf("all failed (element %d):\n+++ got: %f\n--- want: %f",
|
||||||
|
i, got[i], list[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Len() != len(list) {
|
||||||
|
t.Errorf("stack modified after last()")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackupRestore(t *testing.T) {
|
||||||
|
t.Run("shift", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
s.Push(5)
|
||||||
|
s.Backup()
|
||||||
|
s.Clear()
|
||||||
|
s.Restore()
|
||||||
|
|
||||||
|
if s.Len() != 1 {
|
||||||
|
t.Errorf("stack not correctly restored()")
|
||||||
|
}
|
||||||
|
|
||||||
|
a := s.Pop()
|
||||||
|
if a != 5.0 {
|
||||||
|
t.Errorf("stack not identical to old revision:\n+++ got: %f\n--- want: %f",
|
||||||
|
a, 5.0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReverse(t *testing.T) {
|
||||||
|
t.Run("reverse", func(t *testing.T) {
|
||||||
|
s := NewStack()
|
||||||
|
list := []float64{2, 4, 6}
|
||||||
|
reverse := []float64{6, 4, 2}
|
||||||
|
|
||||||
|
for _, item := range list {
|
||||||
|
s.Push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Reverse()
|
||||||
|
|
||||||
|
got := s.All()
|
||||||
|
|
||||||
|
if len(got) != len(list) {
|
||||||
|
t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements",
|
||||||
|
len(got), len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(reverse); i++ {
|
||||||
|
if got[i] != reverse[i] {
|
||||||
|
t.Errorf("reverse failed (element %d):\n+++ got: %f\n--- want: %f",
|
||||||
|
i, got[i], list[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
32
util_test.go
Normal file
32
util_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContains(t *testing.T) {
|
||||||
|
list := []string{"a", "b", "c"}
|
||||||
|
|
||||||
|
t.Run("contains", func(t *testing.T) {
|
||||||
|
if !contains(list, "a") {
|
||||||
|
t.Errorf("a in [a,b,c] not found")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user