From f86c4c99511c5aecb2b6e47e93808d5391a7ca74 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 2 Nov 2023 10:11:39 +0100 Subject: [PATCH] added manpage and manpage display commands --- .gitignore | 1 + Makefile | 21 ++++- README.md | 49 ++++++++++++ calc.go | 6 +- main.go | 28 +++++++ rpn.go | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ rpn.pod | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 528 insertions(+), 3 deletions(-) create mode 100644 rpn.go create mode 100644 rpn.pod diff --git a/.gitignore b/.gitignore index 68c5392..ed13d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ releases rpn +rpn.1 diff --git a/Makefile b/Makefile index 91375f0..69027a4 100644 --- a/Makefile +++ b/Makefile @@ -23,8 +23,27 @@ archs = darwin freebsd linux windows PREFIX = /usr/local UID = root GID = 0 +HAVE_POD := $(shell pod2text -h 2>/dev/null) -all: buildlocal +all: $(tool).1 $(tool).go buildlocal + +%.1: %.pod +ifdef HAVE_POD + pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1 +endif + +%.go: %.pod +ifdef HAVE_POD + echo "package main" > $*.go + echo >> $*.go + echo "var manpage = \`" >> $*.go + pod2text $*.pod >> $*.go + echo "\`" >> $*.go + + echo "var usage = \`" >> $*.go + awk '/SYNOPS/{f=1;next} /DESCR/{f=0} f' $*.pod | sed 's/^ //' >> $*.go + echo "\`" >> $*.go +endif buildlocal: CGO_LDFLAGS='-static' go build -tags osusergo,netgo -ldflags "-extldflags=-static" -o $(tool) diff --git a/README.md b/README.md index 9d24bc7..d3a2d05 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,55 @@ Features: - can calculate data in batch mode (also from STDIN) - extensible with custom LUA functions +## 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. This is how it looks with debugging enabled in `rpn`: + +``` +rpn->debug [0/rev0]ยป 80 20 + 2 / 4 x +DEBUG(000): push to stack: 80.00 +DEBUG(001): push to stack: 20.00 +DEBUG(002): remove from stack: 20.00 +DEBUG(003): remove from stack: 80.00 +DEBUG(calc): evaluating: 80.00 + 20.00 +DEBUG(004): push to stack: 100.00 += 100 +DEBUG(005): push to stack: 2.00 +DEBUG(006): remove from stack: 2.00 +DEBUG(007): remove from stack: 100.00 +DEBUG(calc): evaluating: 100.00 / 2.00 +DEBUG(008): push to stack: 50.00 += 50 +DEBUG(009): push to stack: 4.00 +DEBUG(010): remove from stack: 4.00 +DEBUG(011): remove from stack: 50.00 +DEBUG(calc): evaluating: 50.00 x 4.00 +DEBUG(012): push to stack: 200.00 += 200 +``` + ## Usage Basically you enter numbers followed by an operator or a diff --git a/calc.go b/calc.go index 6517d08..6e7a445 100644 --- a/calc.go +++ b/calc.go @@ -76,9 +76,9 @@ Math operators: // commands, constants and operators, defined here to feed completion // and our mode switch in Eval() dynamically const ( - Commands string = `dump reverse debug undebug clear batch shift undo help history exit quit` + Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit` Operators string = `+ - * x / ^ % %- %+` - MathFunctions string = `sqrt remainder avg mean min max median` + MathFunctions string = `sqrt remainder` Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` BatchFunctions string = `median avg mean max min` ) @@ -255,6 +255,8 @@ func (c *Calc) Eval(line string) { fallthrough case "quit": os.Exit(0) + case "manual": + man() default: fmt.Println("unknown command or operator!") } diff --git a/main.go b/main.go index 144c8df..c4da892 100644 --- a/main.go +++ b/main.go @@ -18,8 +18,11 @@ along with this program. If not, see . package main import ( + "bytes" "fmt" + "log" "os" + "os/exec" "strings" "github.com/chzyer/readline" @@ -36,6 +39,7 @@ Usage: rpn [-bdvh] [] Options: -b, --batchmode enable batch mode -d, --debug enable debug mode + -m, --manual show manual -v, --version show version -h, --help show help @@ -49,6 +53,7 @@ func main() { showversion := false showhelp := false + showmanual := false enabledebug := false configfile := "" @@ -56,6 +61,7 @@ func main() { 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)") @@ -75,6 +81,11 @@ func main() { calc.ToggleDebug() } + if showmanual { + man() + os.Exit(0) + } + // the lua state object is global, instanciate it early L = lua.NewState(lua.Options{SkipOpenLibs: true}) defer L.Close() @@ -141,3 +152,20 @@ func inputIsStdin() bool { stat, _ := os.Stdin.Stat() return (stat.Mode() & os.ModeCharDevice) == 0 } + +func man() { + man := exec.Command("less", "-") + + var b bytes.Buffer + b.Write([]byte(manpage)) + + man.Stdout = os.Stdout + man.Stdin = &b + man.Stderr = os.Stderr + + err := man.Run() + + if err != nil { + log.Fatal(err) + } +} diff --git a/rpn.go b/rpn.go new file mode 100644 index 0000000..164db2a --- /dev/null +++ b/rpn.go @@ -0,0 +1,206 @@ +package main + +var manpage = ` +NAME + rpn - Reverse Polish Notation Calculator for the commandline + +SYNOPSIS + Usage: rpn [-bdvh] [] + + Options: + -b, --batchmode enable batch mode + -d, --debug enable debug mode + -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. + + 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. + + You can use the shift command to remove the last number from the stack. + +EXTENDING RPN USING LUA + You can use a lua script with lua functions to extend the calculator. By + default the tool looks for "~/.rpn.lua". You can also specify a script + 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 "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 (1,2 or -1 allowed), -1 means batch + mode. + + * 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! + +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 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 + +` +var usage = ` + +Usage: rpn [-bdvh] [] + +Options: + -b, --batchmode enable batch mode + -d, --debug enable debug mode + -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 + + +` diff --git a/rpn.pod b/rpn.pod new file mode 100644 index 0000000..1dea393 --- /dev/null +++ b/rpn.pod @@ -0,0 +1,220 @@ +=head1 NAME + +rpn - Reverse Polish Notation Calculator for the commandline + +=head1 SYNOPSIS + + Usage: rpn [-bdvh] [] + + Options: + -b, --batchmode enable batch mode + -d, --debug enable debug mode + -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. + +=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. + +You can use the B command to remove the last number from the +stack. + + +=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 (1,2 or -1 allowed), -1 means batch +mode. + +=item * + +help text + +=back + +Please refer to the lua language reference: +L for more details about LUA. + +B + +=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 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