2 Commits

Author SHA1 Message Date
T.v.Dein
31a0ddd547 add variable support, implements #10 (#14) 2023-11-08 14:47:15 +01:00
T.v.Dein
fa5f8dcb3b suppress intermediate results unless -i, addresses #11, fix man (#13) 2023-11-08 14:43:34 +01:00
5 changed files with 144 additions and 28 deletions

View File

@@ -22,6 +22,7 @@ Features:
- completion - completion
- history - history
- comments (comment character is `#`) - comments (comment character is `#`)
- variables
## Demo ## Demo

89
calc.go
View File

@@ -33,17 +33,22 @@ type Calc struct {
batch bool batch bool
stdin bool stdin bool
showstack bool showstack bool
intermediate bool
notdone bool // set to true as long as there are items left in the eval loop
stack *Stack stack *Stack
history []string history []string
completer readline.AutoCompleter completer readline.AutoCompleter
interpreter *Interpreter interpreter *Interpreter
Space *regexp.Regexp Space *regexp.Regexp
Comment *regexp.Regexp Comment *regexp.Regexp
Register *regexp.Regexp
Constants []string Constants []string
LuaFunctions []string LuaFunctions []string
Funcalls Funcalls Funcalls Funcalls
BatchFuncalls Funcalls BatchFuncalls Funcalls
Vars map[string]float64
} }
// help for lua functions will be added dynamically // help for lua functions will be added dynamically
@@ -56,6 +61,7 @@ clear clear the whole stack
shift remove the last element of the stack shift remove the last element of the stack
reverse reverse the stack elements reverse reverse the stack elements
swap exchange the last two elements swap exchange the last two elements
vars show list of variables
history display calculation history history display calculation history
help|? show this message help|? show this message
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
@@ -79,12 +85,16 @@ sum sum of all values (alias: +)
max max of all values max max of all values
min min of all values min min of all values
mean mean of all values (alias: avg) mean mean of all values (alias: avg)
median median of all values` median median of all values
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack`
// commands, constants and operators, defined here to feed completion // commands, constants and operators, defined here to feed completion
// and our mode switch in Eval() dynamically // and our mode switch in Eval() dynamically
const ( const (
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit swap show` Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit swap show vars`
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
) )
@@ -126,6 +136,7 @@ func NewCalc() *Calc {
c.Funcalls = DefineFunctions() c.Funcalls = DefineFunctions()
c.BatchFuncalls = DefineBatchFunctions() c.BatchFuncalls = DefineBatchFunctions()
c.Vars = map[string]float64{}
c.completer = readline.NewPrefixCompleter( c.completer = readline.NewPrefixCompleter(
// custom lua functions // custom lua functions
@@ -135,6 +146,7 @@ func NewCalc() *Calc {
c.Space = regexp.MustCompile(`\s+`) c.Space = regexp.MustCompile(`\s+`)
c.Comment = regexp.MustCompile(`#.*`) // ignore everything after # c.Comment = regexp.MustCompile(`#.*`) // ignore everything after #
c.Register = regexp.MustCompile(`^([<>])([A-Z][A-Z0-9]*)`)
// pre-calculate mode switching arrays // pre-calculate mode switching arrays
c.Constants = strings.Split(Constants, " ") c.Constants = strings.Split(Constants, " ")
@@ -198,7 +210,15 @@ func (c *Calc) Eval(line string) {
return return
} }
for _, item := range c.Space.Split(line, -1) { items := c.Space.Split(line, -1)
for pos, item := range items {
if pos+1 < len(items) {
c.notdone = true
} else {
c.notdone = false
}
num, err := strconv.ParseFloat(item, 64) num, err := strconv.ParseFloat(item, 64)
if err == nil { if err == nil {
@@ -243,15 +263,28 @@ func (c *Calc) Eval(line string) {
continue continue
} }
regmatches := c.Register.FindStringSubmatch(item)
if len(regmatches) == 3 {
switch regmatches[1] {
case ">":
c.PutVar(regmatches[2])
case "<":
c.GetVar(regmatches[2])
}
continue
}
// management commands // management commands
switch item { switch item {
case "?": case "?":
fallthrough fallthrough
case "help": case "help":
fmt.Println(Help) fmt.Println(Help)
fmt.Println("Lua functions:") if len(LuaFuncs) > 0 {
for name, function := range LuaFuncs { fmt.Println("Lua functions:")
fmt.Printf("%-20s %s\n", name, function.help) for name, function := range LuaFuncs {
fmt.Printf("%-20s %s\n", name, function.help)
}
} }
case "dump": case "dump":
c.stack.Dump() c.stack.Dump()
@@ -291,6 +324,16 @@ func (c *Calc) Eval(line string) {
os.Exit(0) os.Exit(0)
case "manual": case "manual":
man() man()
case "vars":
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")
}
default: default:
fmt.Println("unknown command or operator!") fmt.Println("unknown command or operator!")
} }
@@ -379,11 +422,16 @@ func (c *Calc) History(format string, args ...any) {
// print the result // print the result
func (c *Calc) Result() float64 { func (c *Calc) Result() float64 {
if !c.stdin { // we only print the result if it's either a final result or
fmt.Print("= ") // (if it is intermediate) if -i has been given
} if c.intermediate || !c.notdone {
// only needed in repl
if !c.stdin {
fmt.Print("= ")
}
fmt.Println(c.stack.Last()[0]) fmt.Println(c.stack.Last()[0])
}
return c.stack.Last()[0] return c.stack.Last()[0]
} }
@@ -434,3 +482,24 @@ func (c *Calc) luafunc(funcname string) {
c.Result() 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 _, ok := c.Vars[name]; ok {
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")
}
}

19
main.go
View File

@@ -30,19 +30,20 @@ import (
lua "github.com/yuin/gopher-lua" lua "github.com/yuin/gopher-lua"
) )
const VERSION string = "2.0.4" const VERSION string = "2.0.5"
const Usage string = `This is rpn, a reverse polish notation calculator cli. const Usage string = `This is rpn, a reverse polish notation calculator cli.
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-s, --stack show last 5 items of the stack (off by default) -s, --stack show last 5 items of the stack (off by default)
-m, --manual show manual -i --intermediate print intermediate results
-v, --version show version -m, --manual show manual
-h, --help show help -v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -59,7 +60,9 @@ func main() {
configfile := "" configfile := ""
flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode") flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
flag.BoolVarP(&calc.showstack, "showstack", "s", false, "show stack") flag.BoolVarP(&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(&enabledebug, "debug", "d", false, "debug mode")
flag.BoolVarP(&showversion, "version", "v", false, "show version") flag.BoolVarP(&showversion, "version", "v", false, "show version")
flag.BoolVarP(&showhelp, "help", "h", false, "show usage") flag.BoolVarP(&showhelp, "help", "h", false, "show usage")

36
rpn.go
View File

@@ -2,16 +2,19 @@ package main
var manpage = ` var manpage = `
NAME NAME
rpn - Reverse Polish Notation Calculator for the commandline rpn - Programmable command-line calculator using reverse polish notation
SYNOPSIS SYNOPSIS
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-v, --version show version -s, --stack show last 5 items of the stack (off by default)
-h, --help show help -i --intermediate print intermediate results
-m, --manual show manual
-v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -161,6 +164,11 @@ DESCRIPTION
help|? show this message help|? show this message
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack
Refer to https://pkg.go.dev/math for details about those functions. Refer to https://pkg.go.dev/math for details about those functions.
INTERACTIVE REPL INTERACTIVE REPL
@@ -193,6 +201,24 @@ INTERACTIVE REPL
ctrl-r ctrl-r
Search through history. Search through history.
COMMENTS
Lines starting with "#" 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.
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 "<NAME" to retrieve the value of variable "NAME"
and put it onto the stack.
The command vars can be used to get a list of all variables.
EXTENDING RPN USING LUA EXTENDING RPN USING LUA
You can use a lua script with lua functions to extend the calculator. By You can use a lua script with lua functions to extend the calculator. By
default the tool looks for "~/.rpn.lua". You can also specify a script default the tool looks for "~/.rpn.lua". You can also specify a script

27
rpn.pod
View File

@@ -1,16 +1,19 @@
=head1 NAME =head1 NAME
rpn - Reverse Polish Notation Calculator for the commandline rpn - Programmable command-line calculator using reverse polish notation
=head1 SYNOPSIS =head1 SYNOPSIS
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-v, --version show version -s, --stack show last 5 items of the stack (off by default)
-h, --help show help -i --intermediate print intermediate results
-m, --manual show manual
-v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -169,6 +172,11 @@ Commands:
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack
Refer to https://pkg.go.dev/math for details about those functions. Refer to https://pkg.go.dev/math for details about those functions.
=head1 INTERACTIVE REPL =head1 INTERACTIVE REPL
@@ -223,6 +231,15 @@ append comments to rpn input, e.g.:
In this case only 123 will be added to the stack. 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 "<NAME" to retrieve the value of variable "NAME"
and put it onto the stack.
The command B<vars> can be used to get a list of all variables.
=head1 EXTENDING RPN USING LUA =head1 EXTENDING RPN USING LUA
You can use a lua script with lua functions to extend the You can use a lua script with lua functions to extend the