diff --git a/.gh-dash.yml b/.gh-dash.yml
deleted file mode 100644
index abdce81..0000000
--- a/.gh-dash.yml
+++ /dev/null
@@ -1,96 +0,0 @@
-prSections:
- - title: Responsible PRs
- filters: repo:tlinden/rpnc is:open NOT dependabot
- layout:
- repoName:
- hidden: true
-
- - title: Responsible Dependabot PRs
- filters: repo:tlinden/rpnc is:open dependabot
- layout:
- repoName:
- hidden: true
-
-issuesSections:
- - title: Responsible Issues
- filters: is:open repo:tlinden/rpnc -author:@me
- layout:
- repoName:
- hidden: true
-
- - title: Note-to-Self Issues
- filters: is:open repo:tlinden/rpnc author:@me
- layout:
- creator:
- hidden: true
- repoName:
- hidden: true
-
-defaults:
- preview:
- open: false
- width: 100
-
-keybindings:
- universal:
- - key: "shift+down"
- builtin: pageDown
- - key: "shift+up"
- builtin: pageUp
- prs:
- - key: g
- name: gitu
- command: >
- cd {{.RepoPath}} && /home/scip/bin/gitu
- - key: M
- name: squash-merge
- command: gh pr merge --rebase --squash --admin --repo {{.RepoName}} {{.PrNumber}}
- - key: i
- name: show ci checks
- command: gh pr checks --repo {{.RepoName}} {{.PrNumber}} | glow -p
- - key: e
- name: edit pr
- command: ~/.config/gh-dash/edit-gh-pr {{.RepoName}} {{.PrNumber}}
- - key: E
- name: open repo in emacs
- command: emacsclient {{.RepoPath}} &
- issues:
- - key: v
- name: view
- command: gh issue view --repo {{.RepoName}} {{.IssueNumber}} | glow -p
- - key: l
- name: add label
- command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --add-label $(gum choose bug enhancement question dependencies wontfix)
- - key: L
- name: remove label
- command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --remove-label $(gum choose bug enhancement question dependencies wontfix)
- - key: E
- name: open repo in emacs
- command: emacsclient {{.RepoPath}} &
-
-theme:
- ui:
- sectionsShowCount: true
- table:
- compact: false
- showSeparator: true
- colors:
- text:
- primary: "#E2E1ED"
- secondary: "#6770cb"
- inverted: "#242347"
- faint: "#b0793b"
- warning: "#E0AF68"
- success: "#3DF294"
- background:
- selected: "#1B1B33"
- border:
- primary: "#383B5B"
- secondary: "#39386B"
- faint: "#8d3e0b"
-
-repoPaths:
- :owner/:repo: ~/dev/:repo
-
-pager:
- diff: delta
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
deleted file mode 100644
index 00917fe..0000000
--- a/.goreleaser.yaml
+++ /dev/null
@@ -1,69 +0,0 @@
-# vim: set ts=2 sw=2 tw=0 fo=cnqoj
-
-version: 2
-
-before:
- hooks:
- - go mod tidy
-
-gitea_urls:
- api: https://codeberg.org/api/v1
- download: https://codeberg.org
-
-builds:
- - env:
- - CGO_ENABLED=0
- goos:
- - linux
- - windows
- - darwin
- - freebsd
-
-archives:
- - formats: [tar.gz]
- # this name template makes the OS and Arch compatible with the results of `uname`.
- name_template: >-
- {{ .ProjectName }}_
- {{- title .Os }}_
- {{- if eq .Arch "amd64" }}x86_64
- {{- else if eq .Arch "386" }}i386
- {{- else }}{{ .Arch }}{{ end }}
- {{- if .Arm }}v{{ .Arm }}{{ end }}_{{ .Tag }}
- # use zip for windows archives
- format_overrides:
- - goos: windows
- formats: [zip]
- - goos: linux
- formats: [tar.gz,binary]
- files:
- - src: "*.md"
- strip_parent: true
- - src: "docs/*"
- strip_parent: true
- - src: Makefile.dist
- dst: Makefile
- wrap_in_directory: true
-
-changelog:
- sort: asc
- filters:
- exclude:
- - "^docs:"
- - "^test:"
- groups:
- - title: Improved
- regexp: '^.*?(feat|add|new)(\([[:word:]]+\))??!?:.+$'
- order: 0
- - title: Fixed
- regexp: '^.*?(bug|fix)(\([[:word:]]+\))??!?:.+$'
- order: 1
- - title: Changed
- order: 999
-
-release:
- header: "# Release Notes"
- footer: >-
-
- ---
-
- Full Changelog: [{{ .PreviousTag }}...{{ .Tag }}](https://codeberg.org/scip/rpnc/compare/{{ .PreviousTag }}...{{ .Tag }})
diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml
deleted file mode 100644
index d370fc9..0000000
--- a/.woodpecker/build.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-matrix:
- platform:
- - linux/amd64
- goversion:
- - 1.24
-
-labels:
- platform: ${platform}
-
-steps:
- build:
- when:
- event: [push]
- image: golang:${goversion}
- commands:
- - go get
- - go build
- - go test
-
- linter:
- when:
- event: [push]
- image: golang:${goversion}
- commands:
- - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0
- - golangci-lint --version
- - golangci-lint run ./...
diff --git a/.woodpecker/release.yaml b/.woodpecker/release.yaml
deleted file mode 100644
index 916c008..0000000
--- a/.woodpecker/release.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-# build release
-
-labels:
- platform: linux/amd64
-
-steps:
- goreleaser:
- image: goreleaser/goreleaser
- when:
- event: [tag]
- environment:
- GITEA_TOKEN:
- from_secret: DEPLOY_TOKEN
- commands:
- - goreleaser release --clean --verbose
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 4a8f0ca..0000000
--- a/Makefile
+++ /dev/null
@@ -1,97 +0,0 @@
-
-# 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 .
-
-
-#
-# 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
-HAVE_POD := $(shell pod2text -h 2>/dev/null)
-
-all: $(tool).1 cmd/$(tool).go buildlocal
-
-%.1: %.pod
-ifdef HAVE_POD
- pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1
-endif
-
-cmd/%.go: %.pod
-ifdef HAVE_POD
- echo "package main" > cmd/$*.go
- echo >> cmd/$*.go
- echo "var manpage = \`" >> cmd/$*.go
- pod2text cmd/$*.pod >> cmd/$*.go
- echo "\`" >> cmd/$*.go
-endif
-
-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 testdata
-
-test: clean
- go test ./... $(ARGS)
-
-testfuzzy: clean
- go test -fuzz ./... $(ARGS)
-
-testlint: test lint
-
-lint:
- golangci-lint run
-
-lint-full:
- golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest,forbidigo,godox,dupword,forcetypeassert,goerr113,gomnd
-
-singletest:
- @echo "Call like this: make singletest TEST=TestPrepareColumns ARGS=-v"
- go test -run $(TEST) $(ARGS)
-
-cover-report:
- go test ./... -cover -coverprofile=coverage.out
- go tool cover -html=coverage.out
-
-goupdate:
- go get -t -u=patch ./...
-
-buildall:
- ./mkrel.sh $(tool) $(VERSION)
-
-release:
- gh release create v$(VERSION) --generate-notes
-
-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
diff --git a/Makefile.dist b/Makefile.dist
deleted file mode 100644
index cb76bca..0000000
--- a/Makefile.dist
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*-make-*-
-
-.PHONY: install all
-
-tool = rpn
-PREFIX = /usr/local
-UID = root
-GID = 0
-
-all:
- @echo "Type 'sudo make install' to install the tool."
- @echo "To change prefix, type 'sudo make install PREFIX=/opt'"
-
-install:
- install -d -o $(UID) -g $(GID) $(PREFIX)/bin
- install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
- install -d -o $(UID) -g $(GID) $(PREFIX)/share/doc
- install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
- install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/
- install -o $(UID) -g $(GID) -m 444 *.md $(PREFIX)/share/doc/
diff --git a/README.md b/README.md
index d0bc5ef..8acfe1b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+[](https://ci.codeberg.org/repos/15511)
[](https://codeberg.org/scip/rpnc/raw/branch/master/LICENSE)
[](https://goreportcard.com/report/codeberg.org/scip/rpnc)
diff --git a/archived/README.md b/archived/README.md
deleted file mode 100644
index 920a961..0000000
--- a/archived/README.md
+++ /dev/null
@@ -1,264 +0,0 @@
-## Reverse Polish Notation Calculator for the commandline
-
-This is a small commandline calculator which takes its input in
-[reverse polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)
-form.
-
-It has an unlimited stack, supports various stack manipulation
-commands, can be used interactively or via a pipe and has a collector
-mode. It doesn't have any other dependencies than Perl.
-
-## Usage
-
-Calculate the summary resistance of parallel resistors with 220, 330
-and 440 Ohm using the following formula:
-
- 1 / (1/R1 + 1/R2 + 1/R3)
-
-Here's the sample session:
-
- 0 % 1
- stack 1: 1
-
- 1 % 1
- stack 2: 1
- stack 1: 1
-
- 2 % 220
- stack 3: 1
- stack 2: 1
- stack 1: 220
-
- 3 % /
- stack 2: 1
- stack 1: 0.00454545454545455
-
- => 0.00454545454545455
-
- 2 % 1
- stack 3: 1
- stack 2: 0.00454545454545455
- stack 1: 1
-
- 3 % 330
- stack 4: 1
- stack 3: 0.00454545454545455
- stack 2: 1
- stack 1: 330
-
- 4 % /
- stack 3: 1
- stack 2: 0.00454545454545455
- stack 1: 0.00303030303030303
-
- => 0.00303030303030303
-
- 3 % 1
- stack 4: 1
- stack 3: 0.00454545454545455
- stack 2: 0.00303030303030303
- stack 1: 1
-
- 4 % 440
- stack 5: 1
- stack 4: 0.00454545454545455
- stack 3: 0.00303030303030303
- stack 2: 1
- stack 1: 440
-
- 5 % /
- stack 4: 1
- stack 3: 0.00454545454545455
- stack 2: 0.00303030303030303
- stack 1: 0.00227272727272727
-
- => 0.00227272727272727
-
- 4 % +
- stack 3: 1
- stack 2: 0.00454545454545455
- stack 1: 0.0053030303030303
-
- => 0.0053030303030303
-
- 3 % +
- stack 2: 1
- stack 1: 0.00984848484848485
-
- => 0.00984848484848485
-
- 2 % /
- stack 1: 101.538461538462
-
- => 101.538461538462
-
-The *%* character denotes the interactive prompt. What we basically entered was:
-
- 1 1 220 / 1 330 / 1 440 / + + /
-
-Which translates to:
-
- 1 ((1 / 220) + (1 / 330) + (1 / 440))
-
-So, you're entering the numbers and operators as you would do on
-paper. To learn more, refer to the Wikipedia page linked above.
-
-## Collector mode
-
-Beside traditional RPN you can also enter a special mode, called
-*collector mode* by entering the ( command. The collector
-mode has its own stack (a sub stack) which is independed of the
-primary stack. Inside this mode you can use all operators, however
-they work on *ALL* items on the sub stack.
-
-So, let's compare. If you had in normal RPN mode the following stack:
-
- 3
- 5
- 6
-
-and then entering the + operator, the calculator would pop
-5 and 6 from the stack, add them and push the result 11 back to the
-stack.
-
-However, if you are in collector mode with this stack, then all the
-items would be added, the sub stack would be cleared and the result 14
-would be added to the primary stack.
-
-You will leave the collector mode after an operator has been
-executed. But you can also just leave the collector mode with the
-command ) leaving the sub stack intact. That is, upon
-re-entering collector mode at a later time, you'll find the unaltered
-sub stack of before.
-
-## Undo
-
-Every operation which modifies the stack can be reversed by entering
-the u command. There's only one level of undo and no redo.
-
-## Functions
-
-You can define functions anytime directly on the cli or in a file called
-`~/.rpnc`. A function has a name (which must not collide with existing
-functions and commands) and a body of commands.
-
-Example:
-
- f res2vcc 1.22 R1 R2 + R2 / 1 + *
-
-Which calculates:
-
- (((R1 + R2) / R2) + 1) * 1.22 = ??
-
-To use it later, just enter the variables into the stack followed by the
-function name:
-
- 470
- 220
- res2vcc
- => 2.79
-
-You can also put the function definition in the config file
-`~/.rpnc`. Empty lines and lines beginning with `#` will be ignored.
-
-Another way to define a function is to use perl code directly. The
-perl code must be a closure string and surrounded by braces. You can
-access the stack via `@_`. Here's an example:
-
- f pr { return "1.0 / (" . join(' + ', map { "1.0 / $_"} @_) . ")" }
-
-This function calculates the parallel resistance of a number of
-resistors. It adds up all values from the stack. Usage:
-
- 22
- 47
- 330
- pr
- => 41.14
-
-
-## Using STDIN via a PIPE
-
-If the commandline includes any operator, commands will be read from
-STDIN, the result will be printed to STDOUT wihout any decoration and
-the program will exit. Commands can be separated by whitespace or
-newline.
-
-Examples:
-
- echo "2 2" | rpnc +
- (echo 2; echo 2) | rpnc +
-
-Both commands will print 4 to STDOUT.
-
-
-## Complete list of all supported commands:
-
-### Stack Management
-
-* s show the stack
-* ss show the whole stack
-* sc clear stack
-* scx clear last stack element
-* sr reverse the stack
-* srt rotate the stack
-
-## Configuration
-
-* td toggle debugging (-d)
-* ts toggle display of the stack (-n)
-
-## Supported mathematical operators:
-
-* + add
-* - substract
-* / divide
-* * multiply
-* ^ expotentiate
-* % percent
-* %+ add percent
-* %- substract percent
-* %d percentual difference
-* & bitwise AND
-* | bitwise OR
-* x bitwise XOR
-* m median
-* a average
-* v pull root (2nd if stack==1)
-* ( enter collect mode
-* ) leave collect mode
-
-## Register Commands
-
-* r put element into register
-* rc clear register
-* rcx clear last register element
-
-## Various Commands
-
-* u undo last operation
-* q finish (C-d works as well)
-* h show history of past operations
-* ? print help
-
-## Converters
-
-* tl gallons to liters
-* tk miles to kilometers
-* tm yards to meters
-* tc inches to centimeters
-* tkb bytes to kilobytes
-* tmb bytes to megabytes
-* tgb bytes to gigabytes
-* ttb bytes to terabytes
-
-## Function Comands
-
-* f NAME CODE define a functions (see ab above)
-* fs show list of defined functions
-
-
-## Copyleft
-
-Copyleft (L) 2019 - Thomas von Dein.
-Licensed under the terms of the GPL 3.0.
diff --git a/archived/rpnc b/archived/rpnc
deleted file mode 100755
index 23bde99..0000000
--- a/archived/rpnc
+++ /dev/null
@@ -1,679 +0,0 @@
-#!/usr/bin/perl
-
-use Term::ReadLine;
-use Data::Dumper;
-use Getopt::Long;
-use Data::Dumper;
-
-use strict;
-use warnings;
-
-my (@stack, @substack, @backup, @subbackup, @hist, @register);
-my $term = Term::ReadLine->new('rpn calc');
-my $debug = 0;
-my $showstack = 1;
-my $mgt = 0;
-my $tty = 1;
-my $VERSION = '1.10';
-my $sub = 0;
-my $maxstack = 10;
-my $maxreg = 5;
-my $silent = 1;
-my $op;
-
-# management commands, always lower case letters or words
-my %commands = (
- # stack commands
- s => sub { $mgt = 1; dumpstack(); $mgt = 0; },
- sa => sub { $mgt = 1; dumpstack(1); $mgt = 0;},
- sc => sub { clearstack(); },
- scx => sub { clearstack(1); dumpstack(); },
- sr => sub { reversestack(); },
- srt => sub { rotatestack(); },
- # collector
- '(' => sub { $sub = 1 },
- ')' => sub { stack2sub(); },
- # register stuff
- r => sub { last_to_reg(); dumpstack(); },
- rcx => sub { clearreg(1); dumpstack(); },
- rc => sub { clearreg(); },
- # main
- '?' => sub { help(); },
- u => sub { undo(); dumpstack(); },
- h => sub { showhist(); },
- q => sub { exit; },
- # toggles
- td => sub { $debug ^= 1; },
- ts => sub { $showstack ^= 1; },
- # functions
- fs => sub { showfuncs(); },
- );
-
-# executed 1:1, or aliased
-my %alias = qw(^ ** x ^ < << > >> + + - - / / * * & & | |);
-
-# holds user functions
-my %custom;
-
-# hand coded functions
-my %func = (
- '%' => sub {
- # X % of Y
- my ($a, $b) = getlast(2);
- if (defined $b) {
- return "($a / 100) * $b";
- }
- },
-
- '%d' => sub {
- # percentual difference
- my ($a, $b) = getlast(2);
- if (defined $b) {
- return "(($a - $b) / $b) * 100"
- }
- },
-
- '%+' => sub {
- # Y + (X $ of Y)
- my ($a, $b) = getlast(2);
- if (defined $b) {
- return "$a + (($a / 100) * $b)";
- }
- },
-
- '%-' => sub {
- # Y - (X $ of Y)
- my ($a, $b) = getlast(2);
- if (defined $b) {
- return "$a - (($a / 100) * $b)";
- }
- },
-
- 'v' => sub {
- # square root
- my ($a) = getlast(1);
- if (defined $a) {
- return "$a ** (1 / 2)";
- }
- },
-
- 'm' => sub {
- # median
- my @values = getlast(2); # we need 2 or all in sub mode
- if (scalar @values >= 2) {
- my $c = $#values;
- if (scalar @values % 2 == 0) {
- # even
- return "((sort qw(@values))[$c / 2] + (sort qw(@values))[($c / 2) + 1]) / 2";
- }
- else {
- # uneven
- return "(sort qw(@values))[$c / 2]";
- }
- }
- else {
- print "median only possible with 2 or more values\n";
- undo();
- return 0;
- }
- },
-
- 'a' => sub {
- # average
- my @values = getlast(2); # we need 2 or all in sub mode
- if (scalar @values > 1) {
- return "(" . join(' + ', @values) . ") / " . scalar @values;
- }
- else {
- print "average only possible with 2 or more values\n";
- undo();
- return 0;
- }
- },
-
- # converters:
- # gallons to liters
- 'tl' => sub { return convert("* 3.785") },
- # yards to meters
- 'tm' => sub { return convert("* 91.44") },
- # miles to kilometers
- 'tk' => sub { return convert("* 1.609") },
- # inches to cm
- 'tc' => sub { return convert("* 2.54") },
- # to 'bytes
- 'tkb' => sub { return convert("/ 1000") },
- 'tmb' => sub { return convert("/ 1000 / 1000") },
- 'tgb' => sub { return convert("/ 1000 / 1000 / 1000") },
- 'ttb' => sub { return convert("/ 1000 / 1000 / 1000 / 1000") },
- );
-
-# math constants, always upper case letters, usable via eval{}
-use constant PI => 3.141592653589793;
-use constant V2 => 1.414213562373095;
-use constant V3 => 1.732050807568877;
-
-# handle command line
-my ($o_h, $o_v, $o_s);
-Getopt::Long::Configure( qw(no_ignore_case));
-if (! GetOptions (
- "version|v" => \$o_v,
- "help|h" => \$o_h,
- "debug|d" => \$debug,
- "nostack|n" => \$o_s
- ) ) {
- help();
- exit;
-}
-
-if ($o_v) {
- print "$0 version $VERSION\n";
- exit;
-}
-
-if ($o_h) {
- help();
- exit;
-}
-
-if ($o_s) {
- $showstack = 0;
-}
-
-# load config, if any
-if (-s "$ENV{HOME}/.rpnc") {
- if (open RC, "< $ENV{HOME}/.rpnc") {
- while () {
- chomp();
- next if (/^\s*#/ || /^\s*$/);
- looptokenize($_);
- }
- close RC;
- $silent = 0;
- }
-}
-
-
-# run in commandline mode?
-$op = shift;
-if ($op) {
- $tty = 0;
- while () {
- chomp;
- push @stack, split /\s\s*/;
- }
- print calc($op);
- exit;
-}
-
-# else: run interactively
-# main
-my $OUT = $term->OUT || \*STDOUT;
-while ( defined ($_ = $term->readline(prompt())) ) {
- looptokenize($_);
-}
-
-
-1;
-
-
-# converter helper
-sub convert {
- my $code = shift;
- my ($a) = getlast(1);
- if (defined $a) {
- return "$a $code";
- }
-}
-
-
-sub looptokenize {
- # disassemble user input into tokens
- my $tokens = shift;
-
- if ($tokens =~ /^f\s/) {
- # function definition
- defun($tokens);
- }
- else {
- foreach my $tok (split /\s+/, $tokens) {
- if ($tok =~ /^-?[A-Z\.\d]+?$/) {
- # number or register fetch
- if ($tok =~ /^R(\d+?)/) {
- # fetch number from register $1 and put it to stack
- my $r = getreg($1);
- if ($r) {
- pushstack($r);
- }
- else {
- print "invalid register index!\n";
- next;
- }
- }
- else {
- # put number to stsack
- pushstack($tok);
- }
- dumpstack();
- }
- else {
- # operator or command, execute
- if (exists $commands{$tok}) {
- cmd($tok);
- }
- else {
- print calc($tok);
- }
- }
- }
- }
-}
-
-sub cmd {
- my $c = shift;
-
- if (exists $commands{$c}) {
- my $sub = $commands{$c};
- &$sub;
- }
- else {
- print "unknown command '$c'!\n";
- }
-}
-
-sub showhist {
- foreach my $entry (@hist) {
- printf "History: %10s = %s\n", $entry->[0], $entry->[1];
- }
-}
-
-sub clearstack {
- my $one = shift;
-
- backup();
-
- if ($sub) {
- if ($one) {
- pop @substack;
- }
- else {
- @substack = ();
- }
- }
- else {
- if ($one) {
- pop @stack;
- }
- else {
- @stack = ();
- }
- }
-}
-
-sub reversestack {
- backup();
- if ($sub) {
- @substack = reverse @substack;
- }
- else {
- @stack = reverse @stack;
- }
- dumpstack();
-}
-
-sub rotatestack {
- backup();
- if ($sub) {
- my $f = shift @substack;
- @substack = (@substack, $f);
- }
- else {
- my $f = shift @stack;
- @stack = (@stack, $f);
- }
- dumpstack();
-}
-
-sub pushstack {
- my $num = shift;
- if ($num) {
- if ($num =~ /^\./) {
- $num = '0' . $num;
- }
- if ($sub) {
- push @substack, $num;
- }
- else {
- push @stack, $num;
- }
- }
-}
-
-sub dumpstack {
- if (! $showstack && !$mgt) {
- return;
- }
-
- my $max = shift;
- my $x = ' ';
- my $prefix = 'stack';
- my @all;
-
- if ($sub) {
- @all = @substack;
- $prefix = 'collectorstack';
- }
- else {
- @all = @stack;
- }
-
- my $abs = scalar @all;
- if (! $max && $abs > $maxstack) {
- my $min = $max - ($max * 2);
- @all = @all[$min .. -1];
- printf "%s [..]\n", $prefix;
- }
-
- if (@register) {
- my $p = 1;
- foreach my $n (@register) {
- printf "register R%d: %s\n", $p++, $n;
- }
- }
- print "\n";
-
- my $p = scalar @all;
- foreach my $n (@all) {
- $x = 'X' if($p == 1);
- printf "%s %s %4d: %s\n", $prefix, $x, $p--, $n;
- }
- print "\n";
-}
-
-sub undo {
- if ($sub) {
- @substack = @subbackup;
- }
- else {
- @stack = @backup;
- }
-}
-
-sub backup {
- if ($sub) {
- @subbackup = @substack;
- }
- else {
- @backup = @stack;
- }
-}
-
-
-sub getlast {
- # return and remove last 1, 2 or all elements of current stack
- my $request = shift;
-
- my @all = ();
-
- backup();
-
- if ($sub) {
- # ignore request count
- @all = @substack;
- @substack = ();
- }
- else {
- if (@stack) {
- if (scalar @stack == 1) {
- if ($request > 1) {
- print "At least $request variables must be on the stack!\n";
- }
- else {
- @all = pop @stack;
- }
- }
- elsif (scalar @stack >= 2) {
- @all = splice(@stack, -1 * $request, $request);
- }
- }
- else {
- print "Please enter one or more numbers to operate on!\n";
- }
- }
-
- return @all;
-}
-
-sub getreg {
- # fetch $n'th element from register
- my $n = shift;
- if ($n <= scalar @register) {
- return $register[$n-1];
- }
- else {
- return 0;
- }
-}
-
-sub last_to_reg {
- # put last stack element to register
- my $n;
- if ($sub) {
- if (@substack) {
- $n = $substack[-1];
- }
- }
- else {
- if (@stack) {
- $n = $stack[-1];
- }
- }
-
- if ($n) {
- if (scalar @register == $maxreg) {
- shift @register;
- }
- push @register, $n;
- }
-}
-
-sub clearreg {
- my $one = shift;
-
- if ($one) {
- pop @register;
- }
- else {
- @register = ();
- }
-}
-
-sub stack2sub {
- if (! $sub && scalar @substack == 0 && scalar @stack > 1) {
- # not in collector mode, empty substack, move stack to substack, enter collect
- backup();
- @substack = @stack;
- @stack = ();
- $sub = 1;
- }
- else {
- # leave collector mode
- $sub = 0;
- }
-}
-
-sub prompt {
- my $count;
- my $prompt;
-
- if ($sub) {
- $count = scalar @substack;
- $prompt = '%--(';
- }
- else {
- $count = scalar @stack;
- $prompt = '%';
- }
-
- return sprintf "%3d %s ", $count, $prompt;
-}
-
-sub calc {
- my $op = shift;
- my $res;
- my $code;
-
- if (exists $alias{$op}) {
- my @last = getlast(2);
- $op = $alias{$op};
- $code = join(" $op ", @last);
- }
- elsif (exists $func{$op}) {
- my $sub = $func{$op};
- $code = &$sub();
- return unless $code;
- }
- else {
- print "syntax error or unknown command ($op)!\n";
- undo();
- return;
- }
-
- # execute
- eval "\$res = $code";
-
- if ($@) {
- # error, reset stack
- print "Syntax error: $@, resetting stack\n";
- undo();
- }
- else {
- push @stack, $res;
- $sub = 0;
-
- if ($debug) {
- print "DEBUG: $code = $res\n";
- }
-
- if ($tty) {
- dumpstack();
- push @hist, [$res, $code];
- return "=> $res\n\n";
- }
- else {
- return "$res\n";
- }
- }
-}
-
-
-sub defun {
- # define a function, use N1 .. NN as function arguments
- my $code = shift;
- my ($op, $name, @tokens) = split /\s\s*/, $code;
-
- if ($name !~ /^[a-zA-Z0-9_]+$/) {
- print "invalid function name (a-z0-9_)!\n";
- return;
- }
-
- if (! exists $custom{$name}) {
- # no need to check twice and overwriting of custom function must be legal
- if (grep {$name eq $_} keys %commands) {
- print "reserved function name (command)!\n";
- return;
- }
-
- if (grep {$name eq $_} keys %func) {
- print "reserved function name (function)!\n";
- return;
- }
- }
-
- $custom{$name} = "@tokens";
-
- if ($custom{$name} =~ /^\{.*\}$/) {
- # perl code
- $func{$name} = sub { return eval "@tokens" };
- }
- else {
- # rpnc code
- $func{$name} = sub {
- my $max = scalar @_;
- my @args = reverse(@_);
-
- # replace N1..NN with actual stack items
- my @body;
- foreach my $item (@tokens) {
- if ($item =~ /^([A-Z])(\d+)$/) {
- my $letter = $1;
- my $i = $2;
- if ($i <= $max) {
- push @body, $args[$i-1];
- }
- else {
- print "undefined variable ${letter}${i}!\n";
- push @body, 0;
- }
- }
- else {
- push @body, $item;
- }
- }
-
- # execute @body
- looptokenize("@body");
- };
- }
-
- print "function $name() defined.\n" unless $silent;
-}
-
-sub showfuncs {
- foreach my $f (sort keys %custom) {
- print "Function $f():\n $custom{$f}\n\n";
- }
-}
-
-sub help {
- print qq~
-Reverse Polish Notation Calculator, version $VERSION.
-Copyleft (L) 2019-2020 - Thomas von Dein.
-Licensed under the terms of the GPL 3.0.
-
-Commandline: rpn [-d] []
-
-If is provided, read numbers from STDIN,
-otherwise runs interactively.
-
-Configure: Available math operators:
- td toggle debugging (-d) ( enter collect mode
- ts toggle display of stack (-n) ) leave collect || stack => collect
- + add
-Stack Management: - substract
- s show the stack / divide
- sa show the whole stack * multiply
- scx clear X (last stack element) ^ expotentiate
- sc clear stack % percent (%+ add %- substract)
- sr reverse the stack %d percentual difference
- srt rotate the stack & bitwise AND
- | bitwise OR
-Register Management: x bitwise XOR
- r put X to register < > bitwise shift left or right
- R1-9 push value of register to stack v square root
- rcx clear X (last register element) m median
- rc clear register a average
-
-Converters:
- tl gallons => liters tkb bytes => kb
- tk miles => kilometers tmb bytes => mb
- tm yards => meters tgb bytes => gb
- tc inches => centimeters ttb bytes => tb
-
-Various Commands: Functions:
- u undo last operation f op op... (use N1..NN for stack)
- h show history of past operations fs show list of defined functions
- q finish (C-d works as well) Using register: enter R + index, e.g. R1
- ? print help Constants: PI V2 V3
-
-~;
-}
diff --git a/cmd/calc.go b/cmd/calc.go
deleted file mode 100644
index e5ab3b7..0000000
--- a/cmd/calc.go
+++ /dev/null
@@ -1,624 +0,0 @@
-/*
-Copyright © 2023-2024 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 .
-*/
-
-package cmd
-
-import (
- "errors"
- "fmt"
- "math"
- "regexp"
- "sort"
- "strconv"
- "strings"
-
- "github.com/chzyer/readline"
-)
-
-type Calc struct {
- debug bool
- batch bool
- stdin bool
- showstack bool
- intermediate bool
- notdone bool // set to true as long as there are items left in the eval loop
- precision int
-
- stack *Stack
- history []string
- completer readline.AutoCompleter
- interpreter *Interpreter
- Space *regexp.Regexp
- Comment *regexp.Regexp
- Register *regexp.Regexp
- Constants []string
- LuaFunctions []string
-
- Funcalls Funcalls
- BatchFuncalls Funcalls
-
- // different kinds of commands, displays nicer in help output
- StackCommands Commands
- SettingsCommands Commands
- ShowCommands Commands
- Commands Commands
-
- Vars map[string]float64
-}
-
-// help for lua functions will be added dynamically
-const Help string = `
-Operators:
-basic operators: + - x * / ^ (* is an alias of x)
-
-Bitwise operators: and or xor < (left shift) > (right shift)
-
-Percent functions:
-% percent
-%- subtract percent
-%+ add percent
-
-Math functions (see https://pkg.go.dev/math):
-mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
-erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
-log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
-y1 copysign dim hypot
-
-Converter functions:
-cm-to-inch yards-to-meters bytes-to-kilobytes
-inch-to-cm meters-to-yards bytes-to-megabytes
-gallons-to-liters miles-to-kilometers bytes-to-gigabytes
-liters-to-gallons kilometers-to-miles bytes-to-terabytes
-
-Batch functions:
-sum sum of all values (alias: +)
-max max of all values
-min min of all values
-mean mean of all values (alias: avg)
-median median of all values
-
-Register variables:
->NAME Put last stack element into variable NAME
- 1 {
- completions = append(completions, command)
- }
- }
-
- for command := range c.ShowCommands {
- if len(command) > 1 {
- completions = append(completions, command)
- }
- }
-
- for command := range c.StackCommands {
- if len(command) > 1 {
- completions = append(completions, command)
- }
- }
-
- for command := range c.Commands {
- if len(command) > 1 {
- completions = append(completions, command)
- }
- }
-
- return completions
- }
-}
-
-func NewCalc() *Calc {
- calc := Calc{stack: NewStack(), debug: false, precision: Precision}
-
- calc.Funcalls = DefineFunctions()
- calc.BatchFuncalls = DefineBatchFunctions()
- calc.Vars = map[string]float64{}
-
- calc.completer = readline.NewPrefixCompleter(
- // custom lua functions
- readline.PcItemDynamic(GetCompleteCustomFunctions()),
- readline.PcItemDynamic(calc.GetCompleteCustomFuncalls()),
- )
-
- calc.Space = regexp.MustCompile(`\s+`)
- calc.Comment = regexp.MustCompile(`#.*`) // ignore everything after #
- calc.Register = regexp.MustCompile(`^([<>])([A-Z][A-Z0-9]*)`)
-
- // pre-calculate mode switching arrays
- calc.Constants = strings.Split(Constants, " ")
-
- calc.SetCommands()
-
- return &calc
-}
-
-// setup the interpreter, called from main(), import lua functions
-func (c *Calc) SetInt(interpreter *Interpreter) {
- c.interpreter = interpreter
-
- for name := range LuaFuncs {
- c.LuaFunctions = append(c.LuaFunctions, name)
- }
-}
-
-func (c *Calc) ToggleDebug() {
- c.debug = !c.debug
- c.stack.ToggleDebug()
- fmt.Printf("debugging set to %t\n", c.debug)
-}
-
-func (c *Calc) ToggleBatch() {
- c.batch = !c.batch
- fmt.Printf("batchmode set to %t\n", c.batch)
-}
-
-func (c *Calc) ToggleStdin() {
- c.stdin = !c.stdin
-}
-
-func (c *Calc) ToggleShow() {
- c.showstack = !c.showstack
-}
-
-func (c *Calc) Prompt() string {
- prompt := "\033[31m»\033[0m "
- batch := ""
-
- if c.batch {
- batch = "->batch"
- }
-
- debug := ""
- revision := ""
-
- if c.debug {
- debug = "->debug"
- revision = fmt.Sprintf("/rev%d", c.stack.rev)
- }
-
- return fmt.Sprintf("rpn%s%s [%d%s]%s", batch, debug, c.stack.Len(), revision, prompt)
-}
-
-// the actual work horse, evaluate a line of calc command[s]
-func (c *Calc) Eval(line string) error {
- // remove surrounding whitespace and comments, if any
- line = strings.TrimSpace(c.Comment.ReplaceAllString(line, ""))
-
- if line == "" {
- return nil
- }
-
- items := c.Space.Split(line, -1)
-
- for pos, item := range items {
- if pos+1 < len(items) {
- c.notdone = true
- } else {
- c.notdone = false
- }
-
- if err := c.EvalItem(item); err != nil {
- return err
- }
- }
-
- if c.showstack && !c.stdin {
- dots := ""
-
- if c.stack.Len() > ShowStackLen {
- dots = "... "
- }
-
- last := c.stack.Last(ShowStackLen)
-
- fmt.Printf("stack: %s%s\n", dots, list2str(last))
- }
-
- return nil
-}
-
-func (c *Calc) EvalItem(item string) error {
- num, err := strconv.ParseFloat(item, 64)
-
- if err == nil {
- c.stack.Backup()
- c.stack.Push(num)
-
- return nil
- }
-
- // try time
- var hour, min int
- _, err = fmt.Sscanf(item, "%d:%d", &hour, &min)
- if err == nil {
- c.stack.Backup()
- c.stack.Push(float64(hour) + float64(min)/60)
-
- return nil
- }
-
- // try hex
- var i int
- _, err = fmt.Sscanf(item, "0x%x", &i)
- if err == nil {
- c.stack.Backup()
- c.stack.Push(float64(i))
-
- return nil
- }
-
- if contains(c.Constants, item) {
- // put the constant onto the stack
- c.stack.Backup()
- c.stack.Push(const2num(item))
-
- return nil
- }
-
- if exists(c.Funcalls, item) {
- if err := c.DoFuncall(item); err != nil {
- return Error(err.Error())
- }
-
- c.Result()
-
- return nil
- }
-
- if exists(c.BatchFuncalls, item) {
- if !c.batch {
- return Error("only supported in batch mode")
- }
-
- if err := c.DoFuncall(item); err != nil {
- return Error(err.Error())
- }
-
- c.Result()
-
- return nil
- }
-
- if contains(c.LuaFunctions, item) {
- // user provided custom lua functions
- c.EvalLuaFunction(item)
-
- return nil
- }
-
- regmatches := c.Register.FindStringSubmatch(item)
- if len(regmatches) == 3 {
- switch regmatches[1] {
- case ">":
- c.PutVar(regmatches[2])
- case "<":
- c.GetVar(regmatches[2])
- }
-
- return nil
- }
-
- // internal commands
- // FIXME: propagate errors
- if exists(c.Commands, item) {
- c.Commands[item].Func(c)
-
- return nil
- }
-
- if exists(c.ShowCommands, item) {
- c.ShowCommands[item].Func(c)
-
- return nil
- }
-
- if exists(c.StackCommands, item) {
- c.StackCommands[item].Func(c)
-
- return nil
- }
-
- if exists(c.SettingsCommands, item) {
- c.SettingsCommands[item].Func(c)
-
- return nil
- }
-
- switch item {
- case "?", "help":
- c.PrintHelp()
-
- default:
- return Error("unknown command or operator")
- }
-
- return nil
-}
-
-// Execute a math function, check if it is defined just in case
-func (c *Calc) DoFuncall(funcname string) error {
- var function *Funcall
- if c.batch {
- function = c.BatchFuncalls[funcname]
- } else {
- function = c.Funcalls[funcname]
- }
-
- if function == nil {
- return Error("function not defined but in completion list")
- }
-
- var args Numbers
-
- batch := false
-
- if function.Expectargs == -1 {
- // batch mode, but always < stack len, so check first
- args = c.stack.All()
- batch = true
- } else {
- // this is way better behavior than just using 0 in place of
- // non-existing stack items
- if c.stack.Len() < function.Expectargs {
- return errors.New("stack doesn't provide enough arguments")
- }
-
- args = c.stack.Last(function.Expectargs)
- }
-
- c.Debug(fmt.Sprintf("calling %s with args: %v", funcname, args))
-
- // the actual lambda call, so to say. We provide a slice of
- // the requested size, fetched from the stack (but not popped
- // yet!)
- funcresult := function.Func(args)
-
- if funcresult.Err != nil {
- // leave the stack untouched in case of any error
- return funcresult.Err
- }
-
- // don't forget to backup!
- c.stack.Backup()
-
- // "pop"
- if batch {
- // get rid of stack
- c.stack.Clear()
- } else {
- // remove operands
- c.stack.Shift(function.Expectargs)
- }
-
- // save result
- c.stack.Push(funcresult.Res)
-
- // thanks a lot
- c.SetHistory(funcname, args, funcresult.Res)
-
- return nil
-}
-
-// we need to add a history entry for each operation
-func (c *Calc) SetHistory(op string, args Numbers, res float64) {
- c.History("%s %s -> %f", list2str(args), op, res)
-}
-
-// just a textual representation of math operations, viewable with the
-// history command
-func (c *Calc) History(format string, args ...any) {
- c.history = append(c.history, fmt.Sprintf(format, args...))
-}
-
-// print the result
-func (c *Calc) Result() float64 {
- // we only print the result if it's either a final result or
- // (if it is intermediate) if -i has been given
- if c.intermediate || !c.notdone {
- // only needed in repl
- if !c.stdin {
- fmt.Print("= ")
- }
-
- result := c.stack.Last()[0]
- truncated := math.Trunc(result)
- precision := c.precision
-
- if result == truncated {
- precision = 0
- }
-
- format := fmt.Sprintf("%%.%df\n", precision)
- fmt.Printf(format, result)
- }
-
- return c.stack.Last()[0]
-}
-
-func (c *Calc) Debug(msg string) {
- if c.debug {
- fmt.Printf("DEBUG(calc): %s\n", msg)
- }
-}
-
-func (c *Calc) EvalLuaFunction(funcname string) {
- // called from calc loop
- var luaresult float64
-
- var err error
-
- switch c.interpreter.FuncNumArgs(funcname) {
- case 0:
- fallthrough
- case 1:
- luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last())
- case 2:
- luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last(2))
- case -1:
- luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.All())
- default:
- luaresult, err = 0, errors.New("invalid number of argument requested")
- }
-
- if err != nil {
- fmt.Println(err)
-
- return
- }
-
- c.stack.Backup()
-
- dopush := true
-
- switch c.interpreter.FuncNumArgs(funcname) {
- case 0:
- a := c.stack.Last()
-
- if len(a) == 1 {
- c.History("%s(%f) = %f", funcname, a, luaresult)
- }
-
- dopush = false
- case 1:
- a := c.stack.Pop()
- c.History("%s(%f) = %f", funcname, a, luaresult)
- case 2:
- a := c.stack.Pop()
- b := c.stack.Pop()
- c.History("%s(%f,%f) = %f", funcname, a, b, luaresult)
- case -1:
- c.stack.Clear()
- c.History("%s(*) = %f", funcname, luaresult)
- }
-
- if dopush {
- c.stack.Push(luaresult)
- }
-
- c.Result()
-}
-
-func (c *Calc) PutVar(name string) {
- last := c.stack.Last()
-
- if len(last) == 1 {
- c.Debug(fmt.Sprintf("register %.2f in %s", last[0], name))
- c.Vars[name] = last[0]
- } else {
- fmt.Println("empty stack")
- }
-}
-
-func (c *Calc) GetVar(name string) {
- if exists(c.Vars, name) {
- c.Debug(fmt.Sprintf("retrieve %.2f from %s", c.Vars[name], name))
- c.stack.Backup()
- c.stack.Push(c.Vars[name])
- } else {
- fmt.Println("variable doesn't exist")
- }
-}
-
-func sortcommands(hash Commands) []string {
- keys := make([]string, 0, len(hash))
-
- for key := range hash {
- if len(key) > 1 {
- keys = append(keys, key)
- }
- }
-
- sort.Strings(keys)
-
- return keys
-}
-
-func (c *Calc) PrintHelp() {
- output := "Available configuration commands:\n"
-
- for _, name := range sortcommands(c.SettingsCommands) {
- output += fmt.Sprintf("%-20s %s\n", name, c.SettingsCommands[name].Help)
- }
-
- output += "\nAvailable show commands:\n"
-
- for _, name := range sortcommands(c.ShowCommands) {
- output += fmt.Sprintf("%-20s %s\n", name, c.ShowCommands[name].Help)
- }
-
- output += "\nAvailable stack manipulation commands:\n"
-
- for _, name := range sortcommands(c.StackCommands) {
- output += fmt.Sprintf("%-20s %s\n", name, c.StackCommands[name].Help)
- }
-
- output += "\nOther commands:\n"
-
- for _, name := range sortcommands(c.Commands) {
- output += fmt.Sprintf("%-20s %s\n", name, c.Commands[name].Help)
- }
-
- output += "\n" + Help
-
- // append lua functions, if any
- if len(LuaFuncs) > 0 {
- output += "\nLua functions:\n"
-
- for name, function := range LuaFuncs {
- output += fmt.Sprintf("%-20s %s\n", name, function.help)
- }
- }
-
- Pager("rpn help overview", output)
-}
diff --git a/cmd/calc_test.go b/cmd/calc_test.go
deleted file mode 100644
index 10f079d..0000000
--- a/cmd/calc_test.go
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-import (
- "fmt"
- "strconv"
- "strings"
- "testing"
-
- lua "github.com/yuin/gopher-lua"
-)
-
-func TestCommentsAndWhitespace(t *testing.T) {
- calc := NewCalc()
-
- var tests = []struct {
- name string
- cmd []string
- exp float64 // last element of the stack
- }{
- {
- name: "whitespace prefix",
- cmd: []string{" 5"},
- exp: 5.0,
- },
- {
- name: "whitespace postfix",
- cmd: []string{"5 "},
- exp: 5.0,
- },
- {
- name: "whitespace both",
- cmd: []string{" 5 "},
- exp: 5.0,
- },
- {
- name: "comment line w/ spaces",
- cmd: []string{"5", " # 19"},
- exp: 5.0,
- },
- {
- name: "comment line w/o spaces",
- cmd: []string{"5", `#19`},
- exp: 5.0,
- },
- {
- name: "inline comment w/ spaces",
- cmd: []string{"5 # 19"},
- exp: 5.0,
- },
- {
- name: "inline comment w/o spaces",
- cmd: []string{"5#19"},
- exp: 5.0,
- },
- }
-
- for _, test := range tests {
- testname := fmt.Sprintf("%s .(expect %.2f)",
- test.name, test.exp)
-
- t.Run(testname, func(t *testing.T) {
- for _, line := range test.cmd {
- if err := calc.Eval(line); err != nil {
- t.Error(err.Error())
- }
- }
- got := calc.stack.Last()
-
- if len(got) > 0 {
- if got[0] != test.exp {
- t.Errorf("parsing failed:\n+++ got: %f\n--- want: %f",
- got, test.exp)
- }
- }
-
- if calc.stack.Len() != 1 {
- t.Errorf("invalid stack size:\n+++ got: %d\n--- want: 1",
- calc.stack.Len())
- }
- })
-
- calc.stack.Clear()
- }
-}
-
-func TestCalc(t *testing.T) {
- calc := NewCalc()
-
- var tests = []struct {
- name string
- cmd string
- exp float64
- batch bool
- }{
- // ops
- {
- 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,
- },
-
- // math tests
- {
- 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,
- },
-
- // constants tests
- {
- name: "pitimes2",
- cmd: `Pi 2 *`,
- exp: 6.283185307179586,
- },
- {
- name: "pi+sqrt2",
- cmd: `Pi Sqrt2 +`,
- exp: 4.555806215962888,
- },
-
- // batch tests
- {
- 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,
- },
-
- // stack tests
- {
- name: "use-vars",
- cmd: `10 >TEN clear 5 \n", calc.stack.All(), line)
- switch line {
- case "help", "?":
- return
- }
- if err := calc.EvalItem(line); err == nil {
- t.Logf("given: <%s>", line)
- // not corpus and empty?
- if !contains(legal, line) && len(line) > 0 {
- item := strings.TrimSpace(calc.Comment.ReplaceAllString(line, ""))
- _, hexerr := fmt.Sscanf(item, "0x%x", &hexnum)
- _, timeerr := fmt.Sscanf(item, "%d:%d", &hour, &min)
- // no comment?
- if len(item) > 0 {
- // no known command or function?
- if _, err := strconv.ParseFloat(item, 64); err != nil {
- if !contains(calc.Constants, item) &&
- !exists(calc.Funcalls, item) &&
- !exists(calc.BatchFuncalls, item) &&
- !contains(calc.LuaFunctions, item) &&
- !exists(calc.Commands, item) &&
- !exists(calc.ShowCommands, item) &&
- !exists(calc.SettingsCommands, item) &&
- !exists(calc.StackCommands, item) &&
- !calc.Register.MatchString(item) &&
- item != "?" && item != "help" &&
- hexerr != nil &&
- timeerr != nil {
- t.Errorf("Fuzzy input accepted: <%s>", line)
- }
- }
- }
- }
- }
- })
-}
diff --git a/cmd/command.go b/cmd/command.go
deleted file mode 100644
index dbf2bc6..0000000
--- a/cmd/command.go
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-import (
- "bufio"
- "fmt"
- "log"
- "os"
- "os/exec"
- "strconv"
- "strings"
-)
-
-type CommandFunction func(*Calc)
-
-type Command struct {
- Help string
- Func CommandFunction
-}
-
-type Commands map[string]*Command
-
-func NewCommand(help string, function CommandFunction) *Command {
- return &Command{
- Help: help,
- Func: function,
- }
-}
-
-func (c *Calc) SetSettingsCommands() Commands {
- return Commands{
- // Toggles
- "debug": NewCommand(
- "toggle debugging",
- func(c *Calc) {
- c.ToggleDebug()
- },
- ),
-
- "nodebug": NewCommand(
- "disable debugging",
- func(c *Calc) {
- c.debug = false
- c.stack.debug = false
- },
- ),
-
- "batch": NewCommand(
- "toggle batch mode",
- func(c *Calc) {
- c.ToggleBatch()
- },
- ),
-
- "nobatch": NewCommand(
- "disable batch mode",
- func(c *Calc) {
- c.batch = false
- },
- ),
-
- "showstack": NewCommand(
- "toggle show last 5 items of the stack",
- func(c *Calc) {
- c.ToggleShow()
- },
- ),
-
- "noshowstack": NewCommand(
- "disable display of the stack",
- func(c *Calc) {
- c.showstack = false
- },
- ),
- }
-}
-
-func (c *Calc) SetShowCommands() Commands {
- return Commands{
- // Display commands
- "dump": NewCommand(
- "display the stack contents",
- func(c *Calc) {
- c.stack.Dump()
- },
- ),
-
- "history": NewCommand(
- "display calculation history",
- func(c *Calc) {
- for _, entry := range c.history {
- fmt.Println(entry)
- }
- },
- ),
-
- "vars": NewCommand(
- "show list of variables",
- func(c *Calc) {
- if len(c.Vars) > 0 {
- fmt.Printf("%-20s %s\n", "VARIABLE", "VALUE")
- for k, v := range c.Vars {
- fmt.Printf("%-20s -> %.2f\n", k, v)
- }
- } else {
- fmt.Println("no vars registered")
- }
- },
- ),
-
- "hex": NewCommand(
- "show last stack item in hex form (converted to int)",
- func(c *Calc) {
- if c.stack.Len() > 0 {
- fmt.Printf("0x%x\n", int(c.stack.Last()[0]))
- }
- },
- ),
- }
-}
-
-func (c *Calc) SetStackCommands() Commands {
- return Commands{
- "clear": NewCommand(
- "clear the whole stack",
- func(c *Calc) {
- c.stack.Backup()
- c.stack.Clear()
- },
- ),
-
- "shift": NewCommand(
- "remove the last element of the stack",
- func(c *Calc) {
- c.stack.Backup()
- c.stack.Shift()
- },
- ),
-
- "reverse": NewCommand(
- "reverse the stack elements",
- func(c *Calc) {
- c.stack.Backup()
- c.stack.Reverse()
- },
- ),
-
- "swap": NewCommand(
- "exchange the last two elements",
- CommandSwap,
- ),
-
- "undo": NewCommand(
- "undo last operation",
- func(c *Calc) {
- c.stack.Restore()
- },
- ),
-
- "dup": NewCommand(
- "duplicate last stack item",
- CommandDup,
- ),
-
- "edit": NewCommand(
- "edit the stack interactively",
- CommandEdit,
- ),
- }
-}
-
-// define all management (that is: non calculation) commands
-func (c *Calc) SetCommands() {
- c.SettingsCommands = c.SetSettingsCommands()
- c.ShowCommands = c.SetShowCommands()
- c.StackCommands = c.SetStackCommands()
-
- // general commands
- c.Commands = Commands{
- "exit": NewCommand(
- "exit program",
- func(c *Calc) {
- os.Exit(0)
- },
- ),
-
- "manual": NewCommand(
- "show manual",
- func(c *Calc) {
- man()
- },
- ),
- }
-
- // aliases
- c.Commands["quit"] = c.Commands["exit"]
-
- c.SettingsCommands["d"] = c.SettingsCommands["debug"]
- c.SettingsCommands["b"] = c.SettingsCommands["batch"]
- c.SettingsCommands["s"] = c.SettingsCommands["showstack"]
-
- c.SettingsCommands["togglebatch"] = c.SettingsCommands["batch"]
- c.SettingsCommands["toggledebug"] = c.SettingsCommands["debug"]
- c.SettingsCommands["toggleshowstack"] = c.SettingsCommands["showstack"]
-
- c.ShowCommands["h"] = c.ShowCommands["history"]
- c.ShowCommands["p"] = c.ShowCommands["dump"]
- c.ShowCommands["v"] = c.ShowCommands["vars"]
-
- c.StackCommands["c"] = c.StackCommands["clear"]
- c.StackCommands["u"] = c.StackCommands["undo"]
-}
-
-// added to the command map:
-func CommandSwap(c *Calc) {
- if c.stack.Len() < 2 {
- fmt.Println("stack too small, can't swap")
- } else {
- c.stack.Backup()
- c.stack.Swap()
- }
-}
-
-func CommandDup(c *Calc) {
- item := c.stack.Last()
- if len(item) == 1 {
- c.stack.Backup()
- c.stack.Push(item[0])
- } else {
- fmt.Println("stack empty")
- }
-}
-
-func CommandEdit(calc *Calc) {
- if calc.stack.Len() == 0 {
- fmt.Println("empty stack")
-
- return
- }
-
- calc.stack.Backup()
-
- // put the stack contents into a tmp file
- tmp, err := os.CreateTemp("", "stack")
- if err != nil {
- fmt.Println(err)
-
- return
- }
-
- defer func() {
- if err := os.Remove(tmp.Name()); err != nil {
- log.Fatal(err)
- }
- }()
-
- comment := `# add or remove numbers as you wish.
-# each number must be on its own line.
-# numbers must be floating point formatted.
-`
- _, err = tmp.WriteString(comment)
-
- if err != nil {
- fmt.Println(err)
-
- return
- }
-
- for _, item := range calc.stack.All() {
- _, err = fmt.Fprintf(tmp, "%f\n", item)
- if err != nil {
- fmt.Println(err)
-
- return
- }
- }
-
- if err := tmp.Close(); err != nil {
- log.Fatal(err)
- }
-
- // determine which editor to use
- editor := "vi"
-
- enveditor, present := os.LookupEnv("EDITOR")
- if present {
- if editor != "" {
- if _, err := os.Stat(editor); err == nil {
- editor = enveditor
- }
- }
- }
-
- // execute editor with our tmp file containing current stack
- cmd := exec.Command(editor, tmp.Name())
-
- cmd.Stdin = os.Stdin
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
-
- err = cmd.Run()
- if err != nil {
- fmt.Println("could not run editor command: ", err)
-
- return
- }
-
- // read the file back in
- modified, err := os.Open(tmp.Name())
- if err != nil {
- fmt.Println("Error opening file:", err)
-
- return
- }
- defer func() {
- if err := modified.Close(); err != nil {
- log.Fatal(err)
- }
- }()
-
- // reset the stack
- calc.stack.Clear()
-
- // and put the new contents (if legit) back onto the stack
- scanner := bufio.NewScanner(modified)
- for scanner.Scan() {
- line := strings.TrimSpace(calc.Comment.ReplaceAllString(scanner.Text(), ""))
- if line == "" {
- continue
- }
-
- num, err := strconv.ParseFloat(line, 64)
- if err != nil {
- fmt.Printf("%s is not a floating point number!\n", line)
-
- continue
- }
-
- calc.stack.Push(num)
- }
-
- if err := scanner.Err(); err != nil {
- fmt.Println("Error reading from file:", err)
- }
-}
diff --git a/cmd/funcs.go b/cmd/funcs.go
deleted file mode 100644
index 1856e13..0000000
--- a/cmd/funcs.go
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-import (
- "errors"
- "math"
-)
-
-type Result struct {
- Res float64
- Err error
-}
-
-type Numbers []float64
-
-type Function func(Numbers) Result
-
-// every function we are able to call must be of type Funcall, which
-// needs to specify how many numbers it expects and the actual go
-// function to be executed.
-//
-// The function has to take a float slice as argument and return a
-// float and an error object. The float slice is guaranteed to have
-// the expected number of arguments.
-//
-// However, Lua functions are handled differently, see interpreter.go.
-type Funcall struct {
- Expectargs int // -1 means batch only mode, you'll get the whole stack as arg
- Func Function
-}
-
-// will hold all hard coded functions and operators
-type Funcalls map[string]*Funcall
-
-// convenience function, create a new Funcall object, if expectargs
-// was not specified, 2 is assumed.
-func NewFuncall(function Function, expectargs ...int) *Funcall {
- expect := 2
-
- if len(expectargs) > 0 {
- expect = expectargs[0]
- }
-
- return &Funcall{
- Expectargs: expect,
- Func: function,
- }
-}
-
-// Convenience function, create new result
-func NewResult(n float64, e error) Result {
- return Result{Res: n, Err: e}
-}
-
-// the actual functions, called once during initialization.
-func DefineFunctions() Funcalls {
- funcmap := map[string]*Funcall{
- // simple operators, they all expect 2 args
- "+": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]+arg[1], nil)
- },
- ),
-
- "-": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]-arg[1], nil)
- },
- ),
-
- "x": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]*arg[1], nil)
- },
- ),
-
- "/": NewFuncall(
- func(arg Numbers) Result {
- if arg[1] == 0 {
- return NewResult(0, errors.New("division by null"))
- }
-
- return NewResult(arg[0]/arg[1], nil)
- },
- ),
-
- "^": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Pow(arg[0], arg[1]), nil)
- },
- ),
-
- "%": NewFuncall(
- func(arg Numbers) Result {
- return NewResult((arg[0]/100)*arg[1], nil)
- },
- ),
-
- "%-": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]-((arg[0]/100)*arg[1]), nil)
- },
- ),
-
- "%+": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]+((arg[0]/100)*arg[1]), nil)
- },
- ),
-
- "mod": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Remainder(arg[0], arg[1]), nil)
- },
- ),
-
- "sqrt": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Sqrt(arg[0]), nil)
- },
- 1),
-
- "abs": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Abs(arg[0]), nil)
- },
- 1),
-
- "acos": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Acos(arg[0]), nil)
- },
- 1),
-
- "acosh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Acosh(arg[0]), nil)
- },
- 1),
-
- "asin": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Asin(arg[0]), nil)
- },
- 1),
-
- "asinh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Asinh(arg[0]), nil)
- },
- 1),
-
- "atan": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Atan(arg[0]), nil)
- },
- 1),
-
- "atan2": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Atan2(arg[0], arg[1]), nil)
- },
- 2),
-
- "atanh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Atanh(arg[0]), nil)
- },
- 1),
-
- "cbrt": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Cbrt(arg[0]), nil)
- },
- 1),
-
- "ceil": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Ceil(arg[0]), nil)
- },
- 1),
-
- "cos": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Cos(arg[0]), nil)
- },
- 1),
-
- "cosh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Cosh(arg[0]), nil)
- },
- 1),
-
- "erf": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Erf(arg[0]), nil)
- },
- 1),
-
- "erfc": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Erfc(arg[0]), nil)
- },
- 1),
-
- "erfcinv": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Erfcinv(arg[0]), nil)
- },
- 1),
-
- "erfinv": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Erfinv(arg[0]), nil)
- },
- 1),
-
- "exp": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Exp(arg[0]), nil)
- },
- 1),
-
- "exp2": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Exp2(arg[0]), nil)
- },
- 1),
-
- "expm1": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Expm1(arg[0]), nil)
- },
- 1),
-
- "floor": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Floor(arg[0]), nil)
- },
- 1),
-
- "gamma": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Gamma(arg[0]), nil)
- },
- 1),
-
- "ilogb": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(float64(math.Ilogb(arg[0])), nil)
- },
- 1),
-
- "j0": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.J0(arg[0]), nil)
- },
- 1),
-
- "j1": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.J1(arg[0]), nil)
- },
- 1),
-
- "log": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Log(arg[0]), nil)
- },
- 1),
-
- "log10": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Log10(arg[0]), nil)
- },
- 1),
-
- "log1p": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Log1p(arg[0]), nil)
- },
- 1),
-
- "log2": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Log2(arg[0]), nil)
- },
- 1),
-
- "logb": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Logb(arg[0]), nil)
- },
- 1),
-
- "pow": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Pow(arg[0], arg[1]), nil)
- },
- 2),
-
- "round": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Round(arg[0]), nil)
- },
- 1),
-
- "roundtoeven": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.RoundToEven(arg[0]), nil)
- },
- 1),
-
- "sin": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Sin(arg[0]), nil)
- },
- 1),
-
- "sinh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Sinh(arg[0]), nil)
- },
- 1),
-
- "tan": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Tan(arg[0]), nil)
- },
- 1),
-
- "tanh": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Tanh(arg[0]), nil)
- },
- 1),
-
- "trunc": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Trunc(arg[0]), nil)
- },
- 1),
-
- "y0": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Y0(arg[0]), nil)
- },
- 1),
-
- "y1": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Y1(arg[0]), nil)
- },
- 1),
-
- "copysign": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Copysign(arg[0], arg[1]), nil)
- },
- 2),
-
- "dim": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Dim(arg[0], arg[1]), nil)
- },
- 2),
-
- "hypot": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(math.Hypot(arg[0], arg[1]), nil)
- },
- 2),
-
- // converters of all kinds
- "cm-to-inch": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/2.54, nil)
- },
- 1),
-
- "inch-to-cm": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]*2.54, nil)
- },
- 1),
-
- "gallons-to-liters": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]*3.785, nil)
- },
- 1),
-
- "liters-to-gallons": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/3.785, nil)
- },
- 1),
-
- "yards-to-meters": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]*91.44, nil)
- },
- 1),
-
- "meters-to-yards": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/91.44, nil)
- },
- 1),
-
- "miles-to-kilometers": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]*1.609, nil)
- },
- 1),
-
- "kilometers-to-miles": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/1.609, nil)
- },
- 1),
-
- "bytes-to-kilobytes": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/1024, nil)
- },
- 1),
-
- "bytes-to-megabytes": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/1024/1024, nil)
- },
- 1),
-
- "bytes-to-gigabytes": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/1024/1024/1024, nil)
- },
- 1),
-
- "bytes-to-terabytes": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(arg[0]/1024/1024/1024/1024, nil)
- },
- 1),
-
- "or": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(float64(int(arg[0])|int(arg[1])), nil)
- },
- 2),
-
- "and": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(float64(int(arg[0])&int(arg[1])), nil)
- },
- 2),
-
- "xor": NewFuncall(
- func(arg Numbers) Result {
- return NewResult(float64(int(arg[0])^int(arg[1])), nil)
- },
- 2),
-
- "<": NewFuncall(
- func(arg Numbers) Result {
- // Shift by negative number provibited, so check it.
- // Note that we check against uint64 overflow as well here
- if arg[1] < 0 || uint64(arg[1]) > math.MaxInt64 {
- return NewResult(0, errors.New("negative shift amount"))
- }
-
- return NewResult(float64(int(arg[0])<": NewFuncall(
- func(arg Numbers) Result {
- if arg[1] < 0 || uint64(arg[1]) > math.MaxInt64 {
- return NewResult(0, errors.New("negative shift amount"))
- }
-
- return NewResult(float64(int(arg[0])>>int(arg[1])), nil)
- },
- 2),
- }
-
- // aliases
- funcmap["*"] = funcmap["x"]
- funcmap["remainder"] = funcmap["mod"]
-
- return funcmap
-}
-
-func DefineBatchFunctions() Funcalls {
- funcmap := map[string]*Funcall{
- "median": NewFuncall(
- func(args Numbers) Result {
- middle := len(args) / 2
-
- return NewResult(args[middle], nil)
- },
- -1),
-
- "mean": NewFuncall(
- func(args Numbers) Result {
- var sum float64
- for _, item := range args {
- sum += item
- }
-
- return NewResult(sum/float64(len(args)), nil)
- },
- -1),
-
- "min": NewFuncall(
- func(args Numbers) Result {
- var min float64
- min, args = args[0], args[1:]
- for _, item := range args {
- if item < min {
- min = item
- }
- }
-
- return NewResult(min, nil)
- },
- -1),
-
- "max": NewFuncall(
- func(args Numbers) Result {
- var max float64
- max, args = args[0], args[1:]
- for _, item := range args {
- if item > max {
- max = item
- }
- }
-
- return NewResult(max, nil)
- },
- -1),
-
- "sum": NewFuncall(
- func(args Numbers) Result {
- var sum float64
- for _, item := range args {
- sum += item
- }
-
- return NewResult(sum, nil)
- },
- -1),
- }
-
- // aliases
- funcmap["+"] = funcmap["sum"]
- funcmap["avg"] = funcmap["mean"]
-
- return funcmap
-}
diff --git a/cmd/interpreter.go b/cmd/interpreter.go
deleted file mode 100644
index 6f64150..0000000
--- a/cmd/interpreter.go
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-import (
- "errors"
- "fmt"
-
- lua "github.com/yuin/gopher-lua"
-)
-
-type Interpreter struct {
- debug bool
- script string
-}
-
-// LuaInterpreter is the lua interpreter, instantiated in main()
-var LuaInterpreter *lua.LState
-
-// holds a user provided lua function
-type LuaFunction struct {
- name string
- help string
- numargs int
-}
-
-// LuaFuncs must be global since init() is being called from lua which
-// doesn't have access to the interpreter instance
-var LuaFuncs map[string]LuaFunction
-
-func NewInterpreter(script string, debug bool) *Interpreter {
- return &Interpreter{debug: debug, script: script}
-}
-
-// initialize the lua environment properly
-func (i *Interpreter) InitLua() {
- // we only load a subset of lua Open modules and don't allow
- // net, system or io stuff
- for _, pair := range []struct {
- n string
- f lua.LGFunction
- }{
- {lua.LoadLibName, lua.OpenPackage},
- {lua.BaseLibName, lua.OpenBase},
- {lua.TabLibName, lua.OpenTable},
- {lua.DebugLibName, lua.OpenDebug},
- {lua.MathLibName, lua.OpenMath},
- } {
- if err := LuaInterpreter.CallByParam(lua.P{
- Fn: LuaInterpreter.NewFunction(pair.f),
- NRet: 0,
- Protect: true,
- }, lua.LString(pair.n)); err != nil {
- panic(err)
- }
- }
-
- // load the lua config (which we expect to contain init() and math functions)
- if err := LuaInterpreter.DoFile(i.script); err != nil {
- panic(err)
- }
-
- // instantiate
- LuaFuncs = map[string]LuaFunction{}
-
- // that way the user can call register(...) from lua inside init()
- LuaInterpreter.SetGlobal("register", LuaInterpreter.NewFunction(register))
-
- // actually call init()
- if err := LuaInterpreter.CallByParam(lua.P{
- Fn: LuaInterpreter.GetGlobal("init"),
- NRet: 0,
- Protect: true,
- }); err != nil {
- panic(err)
- }
-}
-
-func (i *Interpreter) Debug(msg string) {
- if i.debug {
- fmt.Printf("DEBUG(lua): %s\n", msg)
- }
-}
-
-func (i *Interpreter) FuncNumArgs(name string) int {
- return LuaFuncs[name].numargs
-}
-
-// Call a user provided math function registered with register().
-//
-// Each function has to tell us how many args it expects, the actual
-// function call from here is different depending on the number of
-// arguments. 1 uses the last item of the stack, 2 the last two and -1
-// all items (which translates to batch mode)
-//
-// The items array will be provided by calc.Eval(), these are
-// non-popped stack items. So the items will only removed from the
-// stack when the lua function execution is successful.
-func (i *Interpreter) CallLuaFunc(funcname string, items []float64) (float64, error) {
- i.Debug(fmt.Sprintf("calling lua func %s() with %d args",
- funcname, LuaFuncs[funcname].numargs))
-
- switch LuaFuncs[funcname].numargs {
- case 0, 1:
- // 1 arg variant
- if err := LuaInterpreter.CallByParam(lua.P{
- Fn: LuaInterpreter.GetGlobal(funcname),
- NRet: 1,
- Protect: true,
- }, lua.LNumber(items[0])); err != nil {
- return 0, fmt.Errorf("failed to exec lua func %s: %w", funcname, err)
- }
- case 2:
- // 2 arg variant
- if err := LuaInterpreter.CallByParam(lua.P{
- Fn: LuaInterpreter.GetGlobal(funcname),
- NRet: 1,
- Protect: true,
- }, lua.LNumber(items[0]), lua.LNumber(items[1])); err != nil {
- return 0, fmt.Errorf("failed to exec lua func %s: %w", funcname, err)
- }
- case -1:
- // batch variant, use lua table as array
- table := LuaInterpreter.NewTable()
-
- // put the whole stack into it
- for _, item := range items {
- table.Append(lua.LNumber(item))
- }
-
- if err := LuaInterpreter.CallByParam(lua.P{
- Fn: LuaInterpreter.GetGlobal(funcname),
- NRet: 1,
- Protect: true,
- }, table); err != nil {
- return 0, fmt.Errorf("failed to exec lua func %s: %w", funcname, err)
- }
- }
-
- // get result and cast to float64
- if res, ok := LuaInterpreter.Get(-1).(lua.LNumber); ok {
- LuaInterpreter.Pop(1)
-
- return float64(res), nil
- }
-
- return 0, errors.New("function did not return a float64")
-}
-
-// called from lua to register a math function numargs may be 1, 2 or
-// -1, it denotes the number of items from the stack requested by the
-// lua function. -1 means batch mode, that is all items
-func register(lstate *lua.LState) int {
- function := lstate.ToString(1)
- numargs := lstate.ToInt(2)
- help := lstate.ToString(3)
-
- LuaFuncs[function] = LuaFunction{
- name: function,
- numargs: numargs,
- help: help,
- }
-
- return 1
-}
diff --git a/cmd/pager.go b/cmd/pager.go
deleted file mode 100644
index 5185d23..0000000
--- a/cmd/pager.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package cmd
-
-// pager setup using bubbletea
-// file shamlelessly copied from:
-// https://github.com/charmbracelet/bubbletea/tree/main/examples/pager
-
-import (
- "fmt"
- "os"
- "strings"
-
- "github.com/charmbracelet/bubbles/viewport"
- tea "github.com/charmbracelet/bubbletea"
- "github.com/charmbracelet/lipgloss"
-)
-
-var (
- titleStyle = func() lipgloss.Style {
- b := lipgloss.RoundedBorder()
- b.Right = "├"
- return lipgloss.NewStyle().BorderStyle(b).Padding(0, 1)
- }()
-
- infoStyle = func() lipgloss.Style {
- b := lipgloss.RoundedBorder()
- b.Left = "┤"
- return titleStyle.BorderStyle(b)
- }()
-)
-
-type model struct {
- content string
- title string
- ready bool
- viewport viewport.Model
-}
-
-func (m model) Init() tea.Cmd {
- return nil
-}
-
-func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
- var (
- cmd tea.Cmd
- cmds []tea.Cmd
- )
-
- switch msg := msg.(type) {
- case tea.KeyMsg:
- if k := msg.String(); k == "ctrl+c" || k == "q" || k == "esc" {
- return m, tea.Quit
- }
-
- case tea.WindowSizeMsg:
- headerHeight := lipgloss.Height(m.headerView())
- footerHeight := lipgloss.Height(m.footerView())
- verticalMarginHeight := headerHeight + footerHeight
-
- if !m.ready {
- // Since this program is using the full size of the viewport we
- // need to wait until we've received the window dimensions before
- // we can initialize the viewport. The initial dimensions come in
- // quickly, though asynchronously, which is why we wait for them
- // here.
- m.viewport = viewport.New(msg.Width, msg.Height-verticalMarginHeight)
- m.viewport.YPosition = headerHeight
- m.viewport.SetContent(m.content)
- m.ready = true
- } else {
- m.viewport.Width = msg.Width
- m.viewport.Height = msg.Height - verticalMarginHeight
- }
- }
-
- // Handle keyboard and mouse events in the viewport
- m.viewport, cmd = m.viewport.Update(msg)
- cmds = append(cmds, cmd)
-
- return m, tea.Batch(cmds...)
-}
-
-func (m model) View() string {
- if !m.ready {
- return "\n Initializing..."
- }
- return fmt.Sprintf("%s\n%s\n%s", m.headerView(), m.viewport.View(), m.footerView())
-}
-
-func (m model) headerView() string {
- // title := titleStyle.Render("RPN Help Overview")
- title := titleStyle.Render(m.title)
- line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(title)))
- return lipgloss.JoinHorizontal(lipgloss.Center, title, line)
-}
-
-func (m model) footerView() string {
- info := infoStyle.Render(fmt.Sprintf("%3.f%%", m.viewport.ScrollPercent()*100))
- line := strings.Repeat("─", max(0, m.viewport.Width-lipgloss.Width(info)))
- return lipgloss.JoinHorizontal(lipgloss.Center, line, info)
-}
-
-func max(a, b int) int {
- if a > b {
- return a
- }
- return b
-}
-
-func Pager(title, message string) {
- p := tea.NewProgram(
- model{content: message, title: title},
- tea.WithAltScreen(), // use the full size of the terminal in its "alternate screen buffer"
- tea.WithMouseCellMotion(), // turn on mouse support so we can track the mouse wheel
- )
-
- if _, err := p.Run(); err != nil {
- fmt.Println("could not run pager:", err)
- os.Exit(1)
- }
-}
diff --git a/cmd/root.go b/cmd/root.go
deleted file mode 100644
index 66286dd..0000000
--- a/cmd/root.go
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
-Copyright © 2023-2024 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 .
-*/
-
-package cmd
-
-import (
- "fmt"
- "log"
- "os"
- "strings"
-
- "github.com/chzyer/readline"
- flag "github.com/spf13/pflag"
- lua "github.com/yuin/gopher-lua"
-)
-
-const VERSION string = "2.1.7"
-
-const Usage string = `This is rpn, a reverse polish notation calculator cli.
-
-Usage: rpn [-bdvh] []
-
-Options:
- -b, --batchmode enable batch mode
- -d, --debug enable debug mode
- -s, --stack show last 5 items of the stack (off by default)
- -i --intermediate print intermediate results
- -m, --manual show manual
- -c, --config load containing LUA code
- -p, --precision floating point number precision (default 2)
- -v, --version show version
- -h, --help show help
-
-When is given, batch mode ist automatically enabled. Use
-this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
-
-Copyright (c) 2023-2025 T.v.Dein`
-
-func Main() int {
- calc := NewCalc()
-
- showversion := false
- showhelp := false
- showmanual := false
- enabledebug := false
- configfile := ""
-
- flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
- flag.BoolVarP(&calc.showstack, "show-stack", "s", false, "show stack")
- flag.BoolVarP(&calc.intermediate, "showin-termediate", "i", false,
- "show intermediate results")
- flag.BoolVarP(&enabledebug, "debug", "d", false, "debug mode")
- flag.BoolVarP(&showversion, "version", "v", false, "show version")
- flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
- flag.BoolVarP(&showmanual, "manual", "m", false, "show manual")
- flag.StringVarP(&configfile, "config", "c",
- os.Getenv("HOME")+"/.rpn.lua", "config file (lua format)")
- flag.IntVarP(&calc.precision, "precision", "p", Precision, "floating point precision")
-
- flag.Parse()
-
- if showversion {
- fmt.Printf("This is rpn version %s\n", VERSION)
-
- return 0
- }
-
- if showhelp {
- fmt.Println(Usage)
-
- return 0
- }
-
- if enabledebug {
- calc.ToggleDebug()
- }
-
- if showmanual {
- man()
-
- return 0
- }
-
- // the lua state object is global, instantiate it early
- LuaInterpreter = lua.NewState(lua.Options{SkipOpenLibs: true})
- defer LuaInterpreter.Close()
-
- // our config file is interpreted as lua code, only functions can
- // be defined, init() will be called by InitLua().
- if _, err := os.Stat(configfile); err == nil {
- luarunner := NewInterpreter(configfile, enabledebug)
- luarunner.InitLua()
- calc.SetInt(luarunner)
-
- if calc.debug {
- fmt.Println("loaded config")
- }
- } else if calc.debug {
- fmt.Println(err)
- }
-
- if len(flag.Args()) > 1 {
- // commandline calc operation, no readline etc needed
- // called like rpn 2 2 +
- calc.stdin = true
- if err := calc.Eval(strings.Join(flag.Args(), " ")); err != nil {
- fmt.Println(err)
-
- return 1
- }
-
- return 0
- }
-
- // interactive mode, need readline
- reader, err := readline.NewEx(&readline.Config{
- Prompt: calc.Prompt(),
- HistoryFile: os.Getenv("HOME") + "/.rpn-history",
- HistoryLimit: 500,
- AutoComplete: calc.completer,
- InterruptPrompt: "^C",
- EOFPrompt: "exit",
- HistorySearchFold: true,
- })
-
- if err != nil {
- panic(err)
- }
- defer func() {
- if err := reader.Close(); err != nil {
- log.Fatal(err)
- }
- }()
-
- reader.CaptureExitSignal()
-
- if inputIsStdin() {
- // commands are coming on stdin, however we will still enter
- // the same loop since readline just reads fine from stdin
- calc.ToggleStdin()
- }
-
- for {
- // primary program repl
- line, err := reader.Readline()
- if err != nil {
- break
- }
-
- err = calc.Eval(line)
- if err != nil {
- fmt.Println(err)
- }
-
- reader.SetPrompt(calc.Prompt())
- }
-
- if len(flag.Args()) > 0 {
- // called like this:
- // echo 1 2 3 4 | rpn +
- // batch mode enabled automatically
- calc.batch = true
- if err = calc.Eval(flag.Args()[0]); err != nil {
- fmt.Println(err)
-
- return 1
- }
- }
-
- return 0
-}
-
-func inputIsStdin() bool {
- stat, _ := os.Stdin.Stat()
-
- return (stat.Mode() & os.ModeCharDevice) == 0
-}
-
-func man() {
- Pager("rpn manual page", manpage)
-}
diff --git a/cmd/rpn.go b/cmd/rpn.go
deleted file mode 100644
index f4ab240..0000000
--- a/cmd/rpn.go
+++ /dev/null
@@ -1,356 +0,0 @@
-package cmd
-
-var manpage = `
-NAME
- rpn - Programmable command-line calculator using reverse polish notation
-
-SYNOPSIS
- Usage: rpn [-bdvh] []
-
- Options:
- -b, --batchmode enable batch mode
- -d, --debug enable debug mode
- -s, --stack show last 5 items of the stack (off by default)
- -i --intermediate print intermediate results
- -m, --manual show manual
- -c, --config load containing LUA code
- -p, --precision floating point number precision (default 2)
- -v, --version show version
- -h, --help show help
-
- When is given, batch mode ist automatically enabled. Use
- this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
-
-DESCRIPTION
- rpn is a command line calculator using reverse polish notation.
-
- Working principle
- Reverse Polish Notation (short: RPN) requires to have a stack where
- numbers and results are being put. So, you put numbers onto the stack
- and each math operation uses these for calculation, removes them and
- puts the result back.
-
- To visualize it, let's look at a calculation:
-
- ((80 + 20) / 2) * 4
-
- This is how you enter the formula int an RPN calculator and how the
- stack evolves during the operation:
-
- | rpn commands | stack contents | calculation |
- |--------------|----------------|---------------|
- | 80 | 80 | |
- | 20 | 80 20 | |
- | + | 100 | 80 + 20 = 100 |
- | 2 | 100 2 | |
- | / | 50 | 100 / 2 = 50 |
- | 4 | 50 4 | |
- | x | 200 | 50 * 4 = 200 |
-
- The last stack element 200 is the calculation result.
-
- USAGE
- The default mode of operation is the interactive mode. You'll get a
- prompt which shows you the current size of the stack. At the prompt you
- enter numbers followed by operators or mathematical functions. You can
- use completion for the functions. You can either enter each number or
- operator on its own line or separated by whitespace, that doesn't
- matter. After a calculation the result will be immediately displayed
- (and added to the stack). You can quit interactive mode using the
- commands quit or exit or hit one of the "ctrl-d" or "ctrl-c" key
- combinations.
-
- If you feed data to standard input (STDIN), rpn just does the
- calculation denoted in the contet fed in via stdin, prints the result
- and exits. You can also specify a calculation on the commandline.
-
- Here are the three variants ($ is the shell prompt):
-
- $ rpn
- rpn> 2
- rpn> 2
- rpn> +
- = 4
-
- $ rpn
- rpn> 2 2 +
- = 4
-
- $ echo 2 2 + | rpn
- 4
-
- $ rpn 2 2 +
- 4
-
- The rpn calculator provides a batch mode which you can use to do math
- operations on many numbers. Batch mode can be enabled using the
- commandline option "-b" or toggled using the interactive command batch.
- Not all math operations and functions work in batch mode though.
-
- Example of batch mode usage:
-
- $ rpn -b
- rpn->batch > 2 2 2 2 +
- = 8
-
- $ rpn
- rpn> batch
- rpn->batch> 2 2 2 2 +
- 8
-
- $ echo 2 2 2 2 + | rpn -b
- 8
-
- $ echo 2 2 2 2 | rpn +
- 8
-
- If the first parameter to rpn is a math operator or function, batch mode
- is enabled automatically, see last example.
-
- You can enter integers, floating point numbers (positive or negative) or
- hex numbers (prefixed with 0x). Time values in hh::mm format are
- possible as well.
-
- STACK MANIPULATION
- There are lots of stack manipulation commands provided. The most
- important one is undo which goes back to the stack before the last math
- operation.
-
- You can use dump to display the stack. If debugging is enabled ("-d"
- switch or debug toggle command), then the backup stack is also being
- displayed.
-
- 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.
-
- BUILTIN OPERATORS AND FUNCTIONS
- Basic operators:
-
- + add
- - subtract
- / divide
- x multiply (alias: *)
- ^ power
-
- Bitwise operators:
-
- and bitwise and
- or bitwise or
- xor bitwise xor
- < left shift
- > right shift
-
- Percent functions:
-
- % percent
- %- subtract percent
- %+ add percent
-
- Batch functions:
-
- sum sum of all values (alias: +)
- max max of all values
- min min of all values
- mean mean of all values (alias: avg)
- median median of all values
-
- Math functions:
-
- mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
- erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
- log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
- y1 copysign dim hypot
-
- Conversion functions:
-
- cm-to-inch yards-to-meters bytes-to-kilobytes
- inch-to-cm meters-to-yards bytes-to-megabytes
- gallons-to-liters miles-to-kilometers bytes-to-gigabytes
- liters-to-gallons kilometers-to-miles bytes-to-terabytes
-
- Configuration Commands:
-
- [no]batch toggle batch mode (nobatch turns it off)
- [no]debug toggle debug output (nodebug turns it off)
- [no]showstack show the last 5 items of the stack (noshowtack turns it off)
-
- Show commands:
-
- dump display the stack contents
- hex show last stack item in hex form (converted to int)
- history display calculation history
- vars show list of variables
-
- Stack manipulation commands:
-
- 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
- dup duplicate last stack item
- undo undo last operation
- edit edit the stack interactively using vi or $EDITOR
-
- Other commands:
-
- help|? show this message
- manual show manual
- quit|exit|c-d|c-c exit program
-
- Register variables:
-
- >NAME Put last stack element into variable NAME
- NAME" command to put a value into
- variable "NAME". Use "-c flag.
-
- Here's an example of such a script:
-
- function add(a,b)
- return a + b
- end
-
- function init()
- register("add", 2, "addition")
- end
-
- Here we created a function "add()" which adds two parameters. All
- parameters are "FLOAT64" numbers. You don't have to worry about stack
- management, this is taken care of automatically.
-
- The function "init()" MUST be defined, it will be called on startup. You
- can do anything you like in there, but you need to call the "register()"
- function to register your functions to the calculator. This function
- takes these parameters:
-
- * function name
-
- * number of arguments expected (see below)
-
- Number of expected arguments can be:
-
- - 0: expect 1 argument but do NOT modify the stack
- - 1-n: do a singular calculation
- - -1: batch mode work with all numbers on the stack
-
- * help text
-
- Please refer to the lua language reference:
- for more details about LUA.
-
- Please note, that io, networking and system stuff is not allowed though.
- So you can't open files, execute other programs or open a connection to
- the outside!
-
-CONFIGURATION
- rpn can be configured via command line flags (see usage above). Most of
- the flags are also available as interactive commands, such as "--batch"
- has the same effect as the batch command.
-
- The floating point number precision option "-p, --precision" however is
- not available as interactive command, it MUST be configured on the
- command line, if needed. The default precision is 2.
-
-GETTING HELP
- In interactive mode you can enter the help command (or ?) to get a short
- help along with a list of all supported operators and functions.
-
- To read the manual you can use the manual command in interactive mode.
- The commandline option "-m" does the same thing.
-
- If you have installed rpn as a package or using the distributed tarball,
- there will also be a manual page you can read using "man rpn".
-
-BUGS
- In order to report a bug, unexpected behavior, feature requests or to
- submit a patch, please open an issue on github:
- .
-
-LICENSE
- This software is licensed under the GNU GENERAL PUBLIC LICENSE version
- 3.
-
- Copyright (c) 2023-2024 by Thomas von Dein
-
- This software uses the following GO modules:
-
- readline (github.com/chzyer/readline)
- Released under the MIT License, Copyright (c) 2016-2023 ChenYe
-
- pflag (https://github.com/spf13/pflag)
- Released under the BSD 3 license, Copyright 2013-2023 Steve Francia
-
- gopher-lua (github.com/yuin/gopher-lua)
- Released under the MIT License, Copyright (c) 2015-2023 Yusuke
- Inuzuka
-
-AUTHORS
- Thomas von Dein tom AT vondein DOT org
-
-`
diff --git a/cmd/stack.go b/cmd/stack.go
deleted file mode 100644
index 83ec620..0000000
--- a/cmd/stack.go
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-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.
-
-type Stack struct {
- linklist list.List
- 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
-func NewStack() *Stack {
- return &Stack{
- linklist: list.List{},
- backup: list.List{},
- rev: 0,
- backuprev: 0,
- }
-}
-
-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
-func (s *Stack) Push(item float64) {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- s.Debug(fmt.Sprintf(" push to stack: %.2f", item))
-
- s.Bump()
- s.linklist.PushBack(item)
-}
-
-// remove and return an item from the stack
-func (s *Stack) Pop() float64 {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- if s.linklist.Len() == 0 {
- return 0
- }
-
- tail := s.linklist.Back()
- val := tail.Value
- s.linklist.Remove(tail)
-
- s.Debug(fmt.Sprintf(" remove from stack: %.2f", val))
-
- s.Bump()
-
- return val.(float64)
-}
-
-// just remove the last item, do not return it
-func (s *Stack) Shift(num ...int) {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- count := 1
-
- if len(num) > 0 {
- count = num[0]
- }
-
- if s.linklist.Len() == 0 {
- 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))
- }
-}
-
-func (s *Stack) Swap() {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- if s.linklist.Len() < 2 {
- return
- }
-
- prevA := s.linklist.Back()
- s.linklist.Remove(prevA)
-
- prevB := s.linklist.Back()
- s.linklist.Remove(prevB)
-
- s.Debug(fmt.Sprintf("swapping %.2f with %.2f", prevB.Value, prevA.Value))
-
- s.linklist.PushBack(prevA.Value)
- s.linklist.PushBack(prevB.Value)
-}
-
-// Return the last num items from the stack w/o modifying it.
-func (s *Stack) Last(num ...int) []float64 {
- items := []float64{}
- stacklen := s.Len()
- count := 1
-
- if len(num) > 0 {
- count = num[0]
- }
-
- for e := s.linklist.Front(); e != nil; e = e.Next() {
- if stacklen <= count {
- items = append(items, e.Value.(float64))
- }
- stacklen--
- }
-
- 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
-}
-
-// dump the stack to stdout, including backup if debug is enabled
-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() {
- 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("clearing stack")
-
- s.linklist = list.List{}
-}
-
-func (s *Stack) Len() int {
- return s.linklist.Len()
-}
-
-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.mutex.Lock()
- defer s.mutex.Unlock()
-
- s.Debug(fmt.Sprintf("backing up %d items from rev %d",
- s.linklist.Len(), s.rev))
-
- s.backup = list.List{}
- for e := s.linklist.Front(); e != nil; e = e.Next() {
- s.backup.PushBack(e.Value.(float64))
- }
-
- s.backuprev = s.rev
-}
-
-func (s *Stack) Restore() {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- 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 = list.List{}
- for e := s.backup.Front(); e != nil; e = e.Next() {
- s.linklist.PushBack(e.Value.(float64))
- }
-}
-
-func (s *Stack) Reverse() {
- s.mutex.Lock()
- defer s.mutex.Unlock()
-
- items := []float64{}
-
- for e := s.linklist.Front(); e != nil; e = e.Next() {
- tail := s.linklist.Back()
- items = append(items, tail.Value.(float64))
- s.linklist.Remove(tail)
- }
-
- for i := len(items) - 1; i >= 0; i-- {
- s.linklist.PushFront(items[i])
- }
-}
diff --git a/cmd/stack_test.go b/cmd/stack_test.go
deleted file mode 100644
index f31e418..0000000
--- a/cmd/stack_test.go
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-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) {
- stack := NewStack()
- stack.Push(5)
- got := stack.Pop()
-
- if got != 5.0 {
- t.Errorf("pop failed:\n+++ got: %f\n--- want: %f",
- got, 5.0)
- }
-
- if stack.Len() != 0 {
- t.Errorf("stack not empty after pop()")
- }
- })
-}
-
-func TestPops(t *testing.T) {
- t.Run("pops", func(t *testing.T) {
- stack := NewStack()
- stack.Push(5)
- stack.Push(5)
- stack.Push(5)
- stack.Pop()
-
- if stack.Len() != 2 {
- t.Errorf("stack len not correct after pop:\n+++ got: %d\n--- want: %d",
- stack.Len(), 2)
- }
- })
-}
-
-func TestShift(t *testing.T) {
- t.Run("shift", func(t *testing.T) {
- stack := NewStack()
- stack.Shift()
-
- if stack.Len() != 0 {
- t.Errorf("stack not empty after shift()")
- }
- })
-}
-
-func TestClear(t *testing.T) {
- t.Run("clear", func(t *testing.T) {
- stack := NewStack()
- stack.Push(5)
- stack.Push(5)
- stack.Push(5)
- stack.Clear()
-
- if stack.Len() != 0 {
- t.Errorf("stack not empty after clear()")
- }
- })
-}
-
-func TestLast(t *testing.T) {
- t.Run("last", func(t *testing.T) {
- stack := NewStack()
- stack.Push(5)
- got := stack.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 stack.Len() != 1 {
- t.Errorf("stack modified after last()")
- }
- })
-}
-
-func TestAll(t *testing.T) {
- t.Run("all", func(t *testing.T) {
- stack := NewStack()
- list := []float64{2, 4, 6, 8}
-
- for _, item := range list {
- stack.Push(item)
- }
-
- got := stack.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 stack.Len() != len(list) {
- t.Errorf("stack modified after last()")
- }
- })
-}
-
-func TestBackupRestore(t *testing.T) {
- t.Run("shift", func(t *testing.T) {
- stack := NewStack()
- stack.Push(5)
- stack.Backup()
- stack.Clear()
- stack.Restore()
-
- if stack.Len() != 1 {
- t.Errorf("stack not correctly restored()")
- }
-
- value := stack.Pop()
- if value != 5.0 {
- t.Errorf("stack not identical to old revision:\n+++ got: %f\n--- want: %f",
- value, 5.0)
- }
- })
-}
-
-func TestReverse(t *testing.T) {
- t.Run("reverse", func(t *testing.T) {
- stack := NewStack()
- list := []float64{2, 4, 6}
- reverse := []float64{6, 4, 2}
-
- for _, item := range list {
- stack.Push(item)
- }
-
- stack.Reverse()
-
- got := stack.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])
- }
- }
- })
-}
diff --git a/cmd/util.go b/cmd/util.go
deleted file mode 100644
index 600598a..0000000
--- a/cmd/util.go
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-import (
- "fmt"
- "math"
- "strings"
-)
-
-// find an item in a list, generic variant
-func contains[E comparable](s []E, v E) bool {
- for _, vs := range s {
- if v == vs {
- return true
- }
- }
-
- return false
-}
-
-// look if a key in a map exists, generic variant
-func exists[K comparable, V any](m map[K]V, v K) bool {
- if _, ok := m[v]; ok {
- 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
- }
-}
-
-func list2str(list Numbers) string {
- return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(list)), " "), "[]")
-}
-
-func Error(m string) error {
- return fmt.Errorf("Error: %s", m)
-}
diff --git a/cmd/util_test.go b/cmd/util_test.go
deleted file mode 100644
index ad0c3f2..0000000
--- a/cmd/util_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-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 .
-*/
-
-package cmd
-
-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")
- }
- })
-}
diff --git a/example.lua b/example.lua
deleted file mode 100644
index fa38af7..0000000
--- a/example.lua
+++ /dev/null
@@ -1,38 +0,0 @@
--- simple function, return the lower number of the two operands
-function lower(a,b)
- if a < b then
- return a
- else
- return b
- end
-end
-
--- calculate parallel resistance. Batch function (registered with -1,
--- see below). Takes a table as parameter.
---
--- Formula: 1/( (1/R1) + (1/R2) + ...)
-function parallelresistance(list)
- sumres = 0
-
- for i, value in ipairs(list) do
- sumres = sumres + 1 / value
- end
-
- return 1 / sumres
-end
-
--- converter example
-function inch2centimeter(inches)
- return inches * 2.54
-end
-
-function init()
- -- expects 2 args
- register("lower", 2, "lower")
-
- -- expects a list of all numbers on the stack, batch mode
- register("parallelresistance", -1, "parallel resistance")
-
- -- expects 1 arg, but doesn't pop()
- register("inch2centimeter", 0)
-end
diff --git a/go.mod b/go.mod
deleted file mode 100644
index e85dea5..0000000
--- a/go.mod
+++ /dev/null
@@ -1,34 +0,0 @@
-module rpn
-
-go 1.24.5
-
-require (
- github.com/charmbracelet/bubbles v0.21.0
- github.com/charmbracelet/bubbletea v1.3.10
- github.com/charmbracelet/lipgloss v1.1.0
- github.com/chzyer/readline v1.5.1
- github.com/rogpeppe/go-internal v1.14.1
- github.com/spf13/pflag v1.0.10
- github.com/yuin/gopher-lua v1.1.1
-)
-
-require (
- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
- github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
- github.com/charmbracelet/x/ansi v0.10.1 // indirect
- github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
- github.com/charmbracelet/x/term v0.2.1 // indirect
- github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
- github.com/muesli/cancelreader v0.2.2 // indirect
- github.com/muesli/termenv v0.16.0 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/text v0.3.8 // indirect
- golang.org/x/tools v0.26.0 // indirect
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 870ea95..0000000
--- a/go.sum
+++ /dev/null
@@ -1,60 +0,0 @@
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
-github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
-github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
-github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
-github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
-github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
-github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
-github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
-github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
-github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
-github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
-github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
-github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
-github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
-github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
-github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
-github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
-github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
-github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
-github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
-github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
-github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
-github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
-github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
diff --git a/main.go b/main.go
deleted file mode 100644
index 72c7ae7..0000000
--- a/main.go
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-Copyright © 2023-2024 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 .
-*/
-
-package main
-
-import (
- "os"
- "rpn/cmd"
-)
-
-func main() {
- os.Exit(cmd.Main())
-}
diff --git a/main_test.go b/main_test.go
deleted file mode 100644
index 377b2c2..0000000
--- a/main_test.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package main
-
-import (
- "testing"
-
- "github.com/rogpeppe/go-internal/testscript"
-)
-
-func TestMain(m *testing.M) {
- testscript.Main(m, map[string]func(){
- "rpn": main,
- })
-}
-
-func TestRpn(t *testing.T) {
- testscript.Run(t, testscript.Params{
- Dir: "t",
- })
-}
diff --git a/rpn.pod b/rpn.pod
deleted file mode 100644
index 0bbff7c..0000000
--- a/rpn.pod
+++ /dev/null
@@ -1,400 +0,0 @@
-=head1 NAME
-
-rpn - Programmable command-line calculator using reverse polish notation
-
-=head1 SYNOPSIS
-
- Usage: rpn [-bdvh] []
-
- Options:
- -b, --batchmode enable batch mode
- -d, --debug enable debug mode
- -s, --stack show last 5 items of the stack (off by default)
- -i --intermediate print intermediate results
- -m, --manual show manual
- -c, --config load containing LUA code
- -p, --precision floating point number precision (default 2)
- -v, --version show version
- -h, --help show help
-
- When is given, batch mode ist automatically enabled. Use
- this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
-
-=head1 DESCRIPTION
-
-rpn is a command line calculator using reverse polish notation.
-
-=head2 Working principle
-
-Reverse Polish Notation (short: RPN) requires to have a stack where
-numbers and results are being put. So, you put numbers onto the stack
-and each math operation uses these for calculation, removes them and
-puts the result back.
-
-To visualize it, let's look at a calculation:
-
- ((80 + 20) / 2) * 4
-
-This is how you enter the formula int an RPN calculator and how the
-stack evolves during the operation:
-
- | rpn commands | stack contents | calculation |
- |--------------|----------------|---------------|
- | 80 | 80 | |
- | 20 | 80 20 | |
- | + | 100 | 80 + 20 = 100 |
- | 2 | 100 2 | |
- | / | 50 | 100 / 2 = 50 |
- | 4 | 50 4 | |
- | x | 200 | 50 * 4 = 200 |
-
-The last stack element 200 is the calculation result.
-
-=head2 USAGE
-
-The default mode of operation is the interactive mode. You'll get a
-prompt which shows you the current size of the stack. At the prompt
-you enter numbers followed by operators or mathematical functions. You
-can use completion for the functions. You can either enter each number
-or operator on its own line or separated by whitespace, that doesn't
-matter. After a calculation the result will be immediately displayed
-(and added to the stack). You can quit interactive mode using the
-commands B or B or hit one of the C or C
-key combinations.
-
-If you feed data to standard input (STDIN), rpn just does the
-calculation denoted in the contet fed in via stdin, prints the result
-and exits. You can also specify a calculation on the commandline.
-
-Here are the three variants ($ is the shell prompt):
-
- $ rpn
- rpn> 2
- rpn> 2
- rpn> +
- = 4
-
- $ rpn
- rpn> 2 2 +
- = 4
-
- $ echo 2 2 + | rpn
- 4
-
- $ rpn 2 2 +
- 4
-
-The rpn calculator provides a batch mode which you can use to do math
-operations on many numbers. Batch mode can be enabled using the
-commandline option C<-b> or toggled using the interactive command
-B. Not all math operations and functions work in batch mode
-though.
-
-Example of batch mode usage:
-
- $ rpn -b
- rpn->batch > 2 2 2 2 +
- = 8
-
- $ rpn
- rpn> batch
- rpn->batch> 2 2 2 2 +
- 8
-
- $ echo 2 2 2 2 + | rpn -b
- 8
-
- $ echo 2 2 2 2 | rpn +
- 8
-
-
-If the first parameter to rpn is a math operator or function, batch
-mode is enabled automatically, see last example.
-
-You can enter integers, floating point numbers (positive or negative)
-or hex numbers (prefixed with 0x). Time values in hh::mm format are
-possible as well.
-
-=head2 STACK MANIPULATION
-
-There are lots of stack manipulation commands provided. The most
-important one is B which goes back to the stack before the last
-math operation.
-
-You can use B to display the stack. If debugging
-is enabled (C<-d> switch or B toggle command), then the backup
-stack is also being displayed.
-
-The stack can be reversed using the B command. However,
-sometimes only the last two values are in the wrong order. Use the
-B command to exchange them.
-
-You can use the B command to remove the last number from the
-stack.
-
-=head2 BUILTIN OPERATORS AND FUNCTIONS
-
-Basic operators:
-
- + add
- - subtract
- / divide
- x multiply (alias: *)
- ^ power
-
-Bitwise operators:
-
- and bitwise and
- or bitwise or
- xor bitwise xor
- < left shift
- > right shift
-
-Percent functions:
-
- % percent
- %- subtract percent
- %+ add percent
-
-Batch functions:
-
- sum sum of all values (alias: +)
- max max of all values
- min min of all values
- mean mean of all values (alias: avg)
- median median of all values
-
-Math functions:
-
- mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
- erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
- log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
- y1 copysign dim hypot
-
-Conversion functions:
-
- cm-to-inch yards-to-meters bytes-to-kilobytes
- inch-to-cm meters-to-yards bytes-to-megabytes
- gallons-to-liters miles-to-kilometers bytes-to-gigabytes
- liters-to-gallons kilometers-to-miles bytes-to-terabytes
-
-Configuration Commands:
-
- [no]batch toggle batch mode (nobatch turns it off)
- [no]debug toggle debug output (nodebug turns it off)
- [no]showstack show the last 5 items of the stack (noshowtack turns it off)
-
-Show commands:
-
- dump display the stack contents
- hex show last stack item in hex form (converted to int)
- history display calculation history
- vars show list of variables
-
-Stack manipulation commands:
-
- 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
- dup duplicate last stack item
- undo undo last operation
- edit edit the stack interactively using vi or $EDITOR
-
-Other commands:
-
- help|? show this message
- manual show manual
- quit|exit|c-d|c-c exit program
-
-
-Register variables:
-
- >NAME Put last stack element into variable NAME
- 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 COMMENTS
-
-Lines starting with C<#> are being ignored as comments. You can also
-append comments to rpn input, e.g.:
-
- # a comment
- 123 # another comment
-
-In this case only 123 will be added to the stack.
-
-=head1 VARIABLES
-
-You can register the last item of the stack into a variable. Variable
-names must be all caps. Use the ">NAME" command to put a value into
-variable "NAME". Use " can be used to get a list of all variables.
-
-=head1 EXTENDING RPN USING LUA
-
-You can use a lua script with lua functions to extend the
-calculator. By default the tool looks for C<~/.rpn.lua>. You can also
-specify a script using the -c flag.
-
-Here's an example of such a script:
-
- function add(a,b)
- return a + b
- end
-
- function init()
- register("add", 2, "addition")
- end
-
-Here we created a function C which adds two parameters. All
-parameters are C numbers. You don't have to worry about stack
-management, this is taken care of automatically.
-
-The function C B be defined, it will be called on
-startup. You can do anything you like in there, but you need to call
-the C function to register your functions to the
-calculator. This function takes these parameters:
-
-=over
-
-=item *
-
-function name
-
-=item *
-
-number of arguments expected (see below)
-
-Number of expected arguments can be:
-
- - 0: expect 1 argument but do NOT modify the stack
- - 1-n: do a singular calculation
- - -1: batch mode work with all numbers on the stack
-
-=item *
-
-help text
-
-=back
-
-Please refer to the lua language reference:
-L for more details about LUA.
-
-B
-
-=head1 CONFIGURATION
-
-B can be configured via command line flags (see usage
-above). Most of the flags are also available as interactive commands,
-such as C<--batch> has the same effect as the B command.
-
-The floating point number precision option C<-p, --precision> however
-is not available as interactive command, it MUST be configured on the
-command line, if needed. The default precision is 2.
-
-=head1 GETTING HELP
-
-In interactive mode you can enter the B command (or B>) to get
-a short help along with a list of all supported operators and
-functions.
-
-To read the manual you can use the B command in interactive
-mode. The commandline option C<-m> does the same thing.
-
-If you have installed rpn as a package or using the distributed
-tarball, there will also be a manual page you can read using C.
-
-=head1 BUGS
-
-In order to report a bug, unexpected behavior, feature requests
-or to submit a patch, please open an issue on github:
-L.
-
-=head1 LICENSE
-
-This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3.
-
-Copyright (c) 2023-2024 by Thomas von Dein
-
-This software uses the following GO modules:
-
-=over 4
-
-=item readline (github.com/chzyer/readline)
-
-Released under the MIT License, Copyright (c) 2016-2023 ChenYe
-
-=item pflag (https://github.com/spf13/pflag)
-
-Released under the BSD 3 license, Copyright 2013-2023 Steve Francia
-
-=item gopher-lua (github.com/yuin/gopher-lua)
-
-Released under the MIT License, Copyright (c) 2015-2023 Yusuke Inuzuka
-
-=back
-
-=head1 AUTHORS
-
-Thomas von Dein B
-
-=cut
diff --git a/rpnc.mp4 b/rpnc.mp4
deleted file mode 100644
index 63c88c8..0000000
Binary files a/rpnc.mp4 and /dev/null differ
diff --git a/t/cmdline-command.txtar b/t/cmdline-command.txtar
deleted file mode 100644
index 3c12966..0000000
--- a/t/cmdline-command.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn 1 2 dump
-stdout 'Stack revision 2 .0x'
diff --git a/t/cmdline-invalidcommand.txtar b/t/cmdline-invalidcommand.txtar
deleted file mode 100644
index 826370d..0000000
--- a/t/cmdline-invalidcommand.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-! exec rpn 1 2 dumb
-stdout 'unknown command or operator'
diff --git a/t/cmdline-precision.txtar b/t/cmdline-precision.txtar
deleted file mode 100644
index df88c4d..0000000
--- a/t/cmdline-precision.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn -p 4 2 3 /
-stdout '0.6667\n'
diff --git a/t/cmdline-short-stack.txtar b/t/cmdline-short-stack.txtar
deleted file mode 100644
index 193ac8e..0000000
--- a/t/cmdline-short-stack.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-! exec rpn 4 +
-stdout 'stack doesn''t provide enough arguments'
diff --git a/t/cmdlinecalc-debug.txtar b/t/cmdlinecalc-debug.txtar
deleted file mode 100644
index 681cde8..0000000
--- a/t/cmdlinecalc-debug.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn -d 44 55 *
-stdout 'push to stack: 2420.00\n'
diff --git a/t/cmdlinecalc-divzero.txtar b/t/cmdlinecalc-divzero.txtar
deleted file mode 100644
index 061b3b1..0000000
--- a/t/cmdlinecalc-divzero.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-! exec rpn 100 50 50 - /
-stdout 'division by null'
diff --git a/t/cmdlinecalc-lua.txtar b/t/cmdlinecalc-lua.txtar
deleted file mode 100644
index 229454a..0000000
--- a/t/cmdlinecalc-lua.txtar
+++ /dev/null
@@ -1,16 +0,0 @@
-exec rpn -d -c test.lua 3 5 lower
-stdout '3\n'
-
--- test.lua --
-function lower(a,b)
- if a < b then
- return a
- else
- return b
- end
-end
-
-function init()
- -- expects 2 args
- register("lower", 2, "lower")
-end
diff --git a/t/cmdlinecalc-time.txtar b/t/cmdlinecalc-time.txtar
deleted file mode 100644
index 2660705..0000000
--- a/t/cmdlinecalc-time.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn 09:55 4:15 -
-stdout '5.67\n'
diff --git a/t/cmdlinecalc.txtar b/t/cmdlinecalc.txtar
deleted file mode 100644
index 5b41f71..0000000
--- a/t/cmdlinecalc.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn 44 55 *
-stdout '2420\n'
diff --git a/t/getusage.txtar b/t/getusage.txtar
deleted file mode 100644
index 1ec1b55..0000000
--- a/t/getusage.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn -h
-stdout 'This is rpn'
diff --git a/t/getversion.txtar b/t/getversion.txtar
deleted file mode 100644
index 516b157..0000000
--- a/t/getversion.txtar
+++ /dev/null
@@ -1,2 +0,0 @@
-exec rpn -v
-stdout 'This is rpn version'
diff --git a/t/stdin-batch-cmd.txtar b/t/stdin-batch-cmd.txtar
deleted file mode 100644
index 115eaa6..0000000
--- a/t/stdin-batch-cmd.txtar
+++ /dev/null
@@ -1,4 +0,0 @@
-exec echo 1 2 3 4 5 batch median
-stdin stdout
-exec rpn
-[unix] stdout '3\n'
diff --git a/t/stdin-batch.txtar b/t/stdin-batch.txtar
deleted file mode 100644
index c4db8ee..0000000
--- a/t/stdin-batch.txtar
+++ /dev/null
@@ -1,4 +0,0 @@
-exec echo 1 2 3 4 5
-stdin stdout
-[unix] exec rpn median
-[unix] stdout '3\n'
diff --git a/t/stdin-calc.txtar b/t/stdin-calc.txtar
deleted file mode 100644
index dd8df1a..0000000
--- a/t/stdin-calc.txtar
+++ /dev/null
@@ -1,4 +0,0 @@
-exec echo 10 10 +
-stdin stdout
-exec rpn
-[unix] stdout '20\n'
diff --git a/t/stdin-use-vars.txtar b/t/stdin-use-vars.txtar
deleted file mode 100644
index 758befe..0000000
--- a/t/stdin-use-vars.txtar
+++ /dev/null
@@ -1,13 +0,0 @@
-stdin input.txt
-exec rpn
-[unix] stdout '28\n'
-
--- input.txt --
-10
-10
-+
->SUM
-clear
-8
-