From 2f56761bf1411c5d0b95f828f398aa326ce5400d Mon Sep 17 00:00:00 2001 From: "T.v.Dein" Date: Sun, 12 Nov 2023 20:29:10 +0100 Subject: [PATCH] Feature/commands (#18) * re-organized command structure * added 'dup' command --- calc.go | 178 ++++++++++++++++++++++++++--------------------- command.go | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 2 +- rpn.go | 1 + rpn.pod | 1 + util.go | 9 +++ 6 files changed, 310 insertions(+), 82 deletions(-) create mode 100644 command.go diff --git a/calc.go b/calc.go index 26de071..b32cc9f 100644 --- a/calc.go +++ b/calc.go @@ -20,8 +20,8 @@ package main import ( "errors" "fmt" - "os" "regexp" + "sort" "strconv" "strings" @@ -48,24 +48,17 @@ type Calc struct { 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 = `Available commands: -batch toggle batch mode (nobatch turns it off) -debug toggle debug output (nodebug turns it off) -showstack toggle show last 5 items of the stack (noshowtack turns it off) -dump display the stack contents -clear clear the whole stack -shift remove the last element of the stack -reverse reverse the stack elements -swap exchange the last two elements -vars show list of variables -history display calculation history -help|? show this message -quit|exit|c-d|c-c exit program - +const Help string = ` Operators: basic operators: + - x * / ^ (* is an alias of x) @@ -94,7 +87,7 @@ Register variables: // commands, constants and operators, defined here to feed completion // and our mode switch in Eval() dynamically const ( - Commands string = `dump reverse clear shift undo help history manual exit quit swap debug undebug nodebug batch nobatch showstack noshowstack vars` + //Commands string = `dump reverse clear shift undo help history manual exit quit swap debug undebug nodebug batch nobatch showstack noshowstack vars` Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` ) @@ -107,7 +100,6 @@ func GetCompleteCustomFunctions() func(string) []string { completions = append(completions, luafunc) } - completions = append(completions, strings.Split(Commands, " ")...) completions = append(completions, strings.Split(Constants, " ")...) return completions @@ -126,6 +118,22 @@ func (c *Calc) GetCompleteCustomFuncalls() func(string) []string { completions = append(completions, function) } + for command := range c.SettingsCommands { + completions = append(completions, command) + } + + for command := range c.ShowCommands { + completions = append(completions, command) + } + + for command := range c.StackCommands { + completions = append(completions, command) + } + + for command := range c.Commands { + completions = append(completions, command) + } + return completions } @@ -151,6 +159,8 @@ func NewCalc() *Calc { // pre-calculate mode switching arrays c.Constants = strings.Split(Constants, " ") + c.SetCommands() + return &c } @@ -274,74 +284,32 @@ func (c *Calc) Eval(line string) { continue } - // management commands + // internal commands + if _, ok := c.Commands[item]; ok { + c.Commands[item].Func(c) + continue + } + + if _, ok := c.ShowCommands[item]; ok { + c.ShowCommands[item].Func(c) + continue + } + + if _, ok := c.StackCommands[item]; ok { + c.StackCommands[item].Func(c) + continue + } + + if _, ok := c.SettingsCommands[item]; ok { + c.SettingsCommands[item].Func(c) + continue + } + switch item { case "?": fallthrough case "help": - fmt.Println(Help) - if len(LuaFuncs) > 0 { - fmt.Println("Lua functions:") - for name, function := range LuaFuncs { - fmt.Printf("%-20s %s\n", name, function.help) - } - } - case "dump": - c.stack.Dump() - case "debug": - c.ToggleDebug() - case "nodebug": - fallthrough - case "undebug": - c.debug = false - c.stack.debug = false - case "batch": - c.ToggleBatch() - case "nobatch": - c.batch = false - case "clear": - c.stack.Backup() - c.stack.Clear() - case "shift": - c.stack.Backup() - c.stack.Shift() - case "reverse": - c.stack.Backup() - c.stack.Reverse() - case "swap": - if c.stack.Len() < 2 { - fmt.Println("stack too small, can't swap") - } else { - c.stack.Backup() - c.stack.Swap() - } - case "undo": - c.stack.Restore() - case "history": - for _, entry := range c.history { - fmt.Println(entry) - } - case "showstack": - fallthrough - case "show": - c.ToggleShow() - case "noshowstack": - c.showstack = false - case "exit": - fallthrough - case "quit": - os.Exit(0) - case "manual": - 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") - } + c.PrintHelp() default: fmt.Println("unknown command or operator!") @@ -528,3 +496,51 @@ func (c *Calc) GetVar(name string) { fmt.Println("variable doesn't exist") } } + +func sortcommands(hash Commands) []string { + keys := make([]string, 0, len(hash)) + + for key := range hash { + keys = append(keys, key) + } + + sort.Strings(keys) + + return keys +} + +func (c *Calc) PrintHelp() { + fmt.Println("Available configuration commands:") + for _, name := range sortcommands(c.SettingsCommands) { + fmt.Printf("%-20s %s\n", name, c.SettingsCommands[name].Help) + } + fmt.Println() + + fmt.Println("Available show commands:") + for _, name := range sortcommands(c.ShowCommands) { + fmt.Printf("%-20s %s\n", name, c.ShowCommands[name].Help) + } + fmt.Println() + + fmt.Println("Available stack manipulation commands:") + for _, name := range sortcommands(c.StackCommands) { + fmt.Printf("%-20s %s\n", name, c.StackCommands[name].Help) + } + fmt.Println() + + fmt.Println("Other commands:") + for _, name := range sortcommands(c.Commands) { + fmt.Printf("%-20s %s\n", name, c.Commands[name].Help) + } + fmt.Println() + + fmt.Println(Help) + + // append lua functions, if any + if len(LuaFuncs) > 0 { + fmt.Println("Lua functions:") + for name, function := range LuaFuncs { + fmt.Printf("%-20s %s\n", name, function.help) + } + } +} diff --git a/command.go b/command.go new file mode 100644 index 0000000..69328c5 --- /dev/null +++ b/command.go @@ -0,0 +1,201 @@ +/* +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 main + +import ( + "fmt" + "os" +) + +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, + } +} + +// define all management (that is: non calculation) commands +func (c *Calc) SetCommands() { + c.SettingsCommands = 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 + }, + ), + } + + c.ShowCommands = 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") + } + }, + ), + } + + c.StackCommands = 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", + func(c *Calc) { + if c.stack.Len() < 2 { + fmt.Println("stack too small, can't swap") + } else { + c.stack.Backup() + c.stack.Swap() + } + }, + ), + + "undo": NewCommand( + "undo last operation", + func(c *Calc) { + c.stack.Restore() + }, + ), + + "dup": NewCommand( + "duplicate last stack item", + func(c *Calc) { + item := c.stack.Last() + if len(item) == 1 { + c.stack.Backup() + c.stack.Push(item[0]) + } else { + fmt.Println("stack empty") + } + }, + ), + } + + // 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["undebug"] = c.SettingsCommands["nodebug"] + c.SettingsCommands["show"] = c.SettingsCommands["showstack"] +} diff --git a/main.go b/main.go index 89a2aef..1f5c28a 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ import ( lua "github.com/yuin/gopher-lua" ) -const VERSION string = "2.0.8" +const VERSION string = "2.0.9" const Usage string = `This is rpn, a reverse polish notation calculator cli. diff --git a/rpn.go b/rpn.go index 15b5656..937f0f9 100644 --- a/rpn.go +++ b/rpn.go @@ -160,6 +160,7 @@ DESCRIPTION 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 history display calculation history help|? show this message quit|exit|c-d|c-c exit program diff --git a/rpn.pod b/rpn.pod index e36e8c8..79a5ffd 100644 --- a/rpn.pod +++ b/rpn.pod @@ -167,6 +167,7 @@ Commands: 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 history display calculation history help|? show this message quit|exit|c-d|c-c exit program diff --git a/util.go b/util.go index 1cdd587..6a50ffa 100644 --- a/util.go +++ b/util.go @@ -33,6 +33,15 @@ func contains(s []string, e string) bool { return false } +func exists(m map[string]interface{}, item string) bool { + // FIXME: try to use this for all cases + if _, ok := m[item]; ok { + return true + } + + return false +} + func const2num(name string) float64 { switch name { case "Pi":