From 62188dda0c9b9c7265eede2f2dbda6fe5c05cfa9 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Fri, 26 Jan 2024 13:08:15 +0100 Subject: [PATCH] fix linter errors --- Makefile | 8 ++ calc.go | 259 +++++++++++++++++++++------------------ calc_test.go | 45 +++---- command.go | 255 +++++++++++++++++++++------------------ funcs.go | 319 +++++++++++++++++++++++++------------------------ interpreter.go | 64 +++++----- main.go | 37 +++--- rpn.go | 4 +- rpn.pod | 4 +- stack.go | 31 +++-- stack_test.go | 82 ++++++------- util.go | 4 +- 12 files changed, 599 insertions(+), 513 deletions(-) diff --git a/Makefile b/Makefile index a449299..643b956 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,14 @@ test: clean 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) diff --git a/calc.go b/calc.go index 6c8a198..4d52c7a 100644 --- a/calc.go +++ b/calc.go @@ -69,7 +69,7 @@ Bitwise operators: and or xor < (left shift) > (right shift) Percent functions: % percent -%- substract percent +%- subtract percent %+ add percent Math functions (see https://pkg.go.dev/math): @@ -92,9 +92,9 @@ 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` - Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` - Precision int = 2 + Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` + Precision int = 2 + ShowStackLen int = 5 ) // That way we can add custom functions to completion @@ -150,37 +150,36 @@ func (c *Calc) GetCompleteCustomFuncalls() func(string) []string { return completions } - } func NewCalc() *Calc { - c := Calc{stack: NewStack(), debug: false, precision: Precision} + calc := Calc{stack: NewStack(), debug: false, precision: Precision} - c.Funcalls = DefineFunctions() - c.BatchFuncalls = DefineBatchFunctions() - c.Vars = map[string]float64{} + calc.Funcalls = DefineFunctions() + calc.BatchFuncalls = DefineBatchFunctions() + calc.Vars = map[string]float64{} - c.completer = readline.NewPrefixCompleter( + calc.completer = readline.NewPrefixCompleter( // custom lua functions readline.PcItemDynamic(GetCompleteCustomFunctions()), - readline.PcItemDynamic(c.GetCompleteCustomFuncalls()), + readline.PcItemDynamic(calc.GetCompleteCustomFuncalls()), ) - c.Space = regexp.MustCompile(`\s+`) - c.Comment = regexp.MustCompile(`#.*`) // ignore everything after # - c.Register = regexp.MustCompile(`^([<>])([A-Z][A-Z0-9]*)`) + 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 - c.Constants = strings.Split(Constants, " ") + calc.Constants = strings.Split(Constants, " ") - c.SetCommands() + calc.SetCommands() - return &c + return &calc } // setup the interpreter, called from main(), import lua functions -func (c *Calc) SetInt(I *Interpreter) { - c.interpreter = I +func (c *Calc) SetInt(interpreter *Interpreter) { + c.interpreter = interpreter for name := range LuaFuncs { c.LuaFunctions = append(c.LuaFunctions, name) @@ -207,22 +206,22 @@ func (c *Calc) ToggleShow() { } func (c *Calc) Prompt() string { - p := "\033[31m»\033[0m " - b := "" + prompt := "\033[31m»\033[0m " + batch := "" if c.batch { - b = "->batch" + batch = "->batch" } - d := "" - v := "" + debug := "" + revision := "" if c.debug { - d = "->debug" - v = fmt.Sprintf("/rev%d", c.stack.rev) + debug = "->debug" + revision = fmt.Sprintf("/rev%d", c.stack.rev) } - return fmt.Sprintf("rpn%s%s [%d%s]%s", b, d, c.stack.Len(), v, p) + 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] @@ -251,10 +250,12 @@ func (c *Calc) Eval(line string) error { if c.showstack && !c.stdin { dots := "" - if c.stack.Len() > 5 { + if c.stack.Len() > ShowStackLen { dots = "... " } - last := c.stack.Last(5) + + last := c.stack.Last(ShowStackLen) + fmt.Printf("stack: %s%s\n", dots, list2str(last)) } @@ -267,93 +268,106 @@ func (c *Calc) EvalItem(item string) error { if err == nil { c.stack.Backup() c.stack.Push(num) - } else { - // 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 + + 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()) } - if contains(c.Constants, item) { - // put the constant onto the stack - c.stack.Backup() - c.stack.Push(const2num(item)) - return nil + c.Result() + + return nil + } + + if exists(c.BatchFuncalls, item) { + if !c.batch { + return Error("only supported in batch mode") } - if exists(c.Funcalls, item) { - if err := c.DoFuncall(item); err != nil { - return Error(err.Error()) - } else { - c.Result() - } - return nil + if err := c.DoFuncall(item); err != nil { + return Error(err.Error()) } - if exists(c.BatchFuncalls, item) { - if !c.batch { - return Error("only supported in batch mode") - } + c.Result() - if err := c.DoFuncall(item); err != nil { - return Error(err.Error()) - } else { - c.Result() - } - return nil + 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]) } - if contains(c.LuaFunctions, item) { - // user provided custom lua functions - c.EvalLuaFunction(item) - return nil - } + 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) - // internal commands - // FIXME: propagate errors - if exists(c.Commands, item) { - c.Commands[item].Func(c) - return nil - } + return nil + } - if exists(c.ShowCommands, item) { - c.ShowCommands[item].Func(c) - return nil - } + if exists(c.ShowCommands, item) { + c.ShowCommands[item].Func(c) - if exists(c.StackCommands, item) { - c.StackCommands[item].Func(c) - return nil - } + return nil + } - if exists(c.SettingsCommands, item) { - c.SettingsCommands[item].Func(c) - return nil - } + if exists(c.StackCommands, item) { + c.StackCommands[item].Func(c) - switch item { - case "?": - fallthrough - case "help": - c.PrintHelp() + return nil + } - default: - return Error("unknown command or operator") - } + if exists(c.SettingsCommands, item) { + c.SettingsCommands[item].Func(c) + + return nil + } + + switch item { + case "?": + fallthrough + case "help": + c.PrintHelp() + + default: + return Error("unknown command or operator") } return nil @@ -373,6 +387,7 @@ func (c *Calc) DoFuncall(funcname string) error { } var args Numbers + batch := false if function.Expectargs == -1 { @@ -394,11 +409,11 @@ func (c *Calc) DoFuncall(funcname string) error { // the actual lambda call, so to say. We provide a slice of // the requested size, fetched from the stack (but not popped // yet!) - R := function.Func(args) + funcresult := function.Func(args) - if R.Err != nil { + if funcresult.Err != nil { // leave the stack untouched in case of any error - return R.Err + return funcresult.Err } // don't forget to backup! @@ -414,10 +429,11 @@ func (c *Calc) DoFuncall(funcname string) error { } // save result - c.stack.Push(R.Res) + c.stack.Push(funcresult.Res) // thanks a lot - c.SetHistory(funcname, args, R.Res) + c.SetHistory(funcname, args, funcresult.Res) + return nil } @@ -465,24 +481,26 @@ func (c *Calc) Debug(msg string) { func (c *Calc) EvalLuaFunction(funcname string) { // called from calc loop - var x float64 + var luaresult float64 + var err error switch c.interpreter.FuncNumArgs(funcname) { case 0: fallthrough case 1: - x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last()) + luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last()) case 2: - x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last(2)) + luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last(2)) case -1: - x, err = c.interpreter.CallLuaFunc(funcname, c.stack.All()) + luaresult, err = c.interpreter.CallLuaFunc(funcname, c.stack.All()) default: - x, err = 0, errors.New("invalid number of argument requested") + luaresult, err = 0, errors.New("invalid number of argument requested") } if err != nil { fmt.Println(err) + return } @@ -493,24 +511,26 @@ func (c *Calc) EvalLuaFunction(funcname string) { switch c.interpreter.FuncNumArgs(funcname) { case 0: a := c.stack.Last() + if len(a) == 1 { - c.History("%s(%f) = %f", funcname, a, x) + c.History("%s(%f) = %f", funcname, a, luaresult) } + dopush = false case 1: a := c.stack.Pop() - c.History("%s(%f) = %f", funcname, a, x) + 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, x) + c.History("%s(%f,%f) = %f", funcname, a, b, luaresult) case -1: c.stack.Clear() - c.History("%s(*) = %f", funcname, x) + c.History("%s(*) = %f", funcname, luaresult) } if dopush { - c.stack.Push(x) + c.stack.Push(luaresult) } c.Result() @@ -553,27 +573,35 @@ func sortcommands(hash Commands) []string { 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) @@ -581,6 +609,7 @@ func (c *Calc) PrintHelp() { // 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/calc_test.go b/calc_test.go index 87f0925..85d75a8 100644 --- a/calc_test.go +++ b/calc_test.go @@ -71,12 +71,12 @@ func TestCommentsAndWhitespace(t *testing.T) { }, } - for _, tt := range tests { + for _, test := range tests { testname := fmt.Sprintf("%s .(expect %.2f)", - tt.name, tt.exp) + test.name, test.exp) t.Run(testname, func(t *testing.T) { - for _, line := range tt.cmd { + for _, line := range test.cmd { if err := calc.Eval(line); err != nil { t.Errorf(err.Error()) } @@ -84,9 +84,9 @@ func TestCommentsAndWhitespace(t *testing.T) { got := calc.stack.Last() if len(got) > 0 { - if got[0] != tt.exp { + if got[0] != test.exp { t.Errorf("parsing failed:\n+++ got: %f\n--- want: %f", - got, tt.exp) + got, test.exp) } } @@ -94,7 +94,6 @@ func TestCommentsAndWhitespace(t *testing.T) { t.Errorf("invalid stack size:\n+++ got: %d\n--- want: 1", calc.stack.Len()) } - }) calc.stack.Clear() @@ -286,20 +285,20 @@ func TestCalc(t *testing.T) { }, } - for _, tt := range tests { + for _, test := range tests { testname := fmt.Sprintf("cmd-%s-expect-%.2f", - tt.name, tt.exp) + test.name, test.exp) t.Run(testname, func(t *testing.T) { - calc.batch = tt.batch - if err := calc.Eval(tt.cmd); err != nil { + calc.batch = test.batch + if err := calc.Eval(test.cmd); err != nil { t.Errorf(err.Error()) } got := calc.Result() calc.stack.Clear() - if got != tt.exp { + if got != test.exp { t.Errorf("calc failed:\n+++ got: %f\n--- want: %f", - got, tt.exp) + got, test.exp) } }) } @@ -324,23 +323,24 @@ func TestCalcLua(t *testing.T) { } calc := NewCalc() - L = lua.NewState(lua.Options{SkipOpenLibs: true}) - defer L.Close() + + LuaInterpreter = lua.NewState(lua.Options{SkipOpenLibs: true}) + defer LuaInterpreter.Close() luarunner := NewInterpreter("example.lua", false) luarunner.InitLua() calc.SetInt(luarunner) - for _, tt := range tests { - testname := fmt.Sprintf("lua-%s", tt.function) + for _, test := range tests { + testname := fmt.Sprintf("lua-%s", test.function) t.Run(testname, func(t *testing.T) { calc.stack.Clear() - for _, item := range tt.stack { + for _, item := range test.stack { calc.stack.Push(item) } - calc.EvalLuaFunction(tt.function) + calc.EvalLuaFunction(test.function) got := calc.stack.Last() @@ -349,9 +349,9 @@ func TestCalcLua(t *testing.T) { calc.stack.Len()) } - if got[0] != tt.exp { + if got[0] != test.exp { t.Errorf("lua function %s failed:\n+++ got: %f\n--- want: %f", - tt.function, got, tt.exp) + test.function, got, test.exp) } }) } @@ -380,7 +380,8 @@ func FuzzEval(f *testing.F) { } calc := NewCalc() - var i int + + var hexnum int f.Fuzz(func(t *testing.T, line string) { t.Logf("Stack:\n%v\n", calc.stack.All()) @@ -389,7 +390,7 @@ func FuzzEval(f *testing.F) { // not corpus and empty? if !contains(legal, line) && len(line) > 0 { item := strings.TrimSpace(calc.Comment.ReplaceAllString(line, "")) - _, hexerr := fmt.Sscanf(item, "0x%x", &i) + _, hexerr := fmt.Sscanf(item, "0x%x", &hexnum) // no comment? if len(item) > 0 { // no known command or function? diff --git a/command.go b/command.go index c460200..1daed2d 100644 --- a/command.go +++ b/command.go @@ -42,9 +42,8 @@ func NewCommand(help string, function CommandFunction) *Command { } } -// define all management (that is: non calculation) commands -func (c *Calc) SetCommands() { - c.SettingsCommands = Commands{ +func (c *Calc) SetSettingsCommands() Commands { + return Commands{ // Toggles "debug": NewCommand( "toggle debugging", @@ -89,8 +88,10 @@ func (c *Calc) SetCommands() { }, ), } +} - c.ShowCommands = Commands{ +func (c *Calc) SetShowCommands() Commands { + return Commands{ // Display commands "dump": NewCommand( "display the stack contents", @@ -131,8 +132,10 @@ func (c *Calc) SetCommands() { }, ), } +} - c.StackCommands = Commands{ +func (c *Calc) SetStackCommands() Commands { + return Commands{ "clear": NewCommand( "clear the whole stack", func(c *Calc) { @@ -159,14 +162,7 @@ func (c *Calc) SetCommands() { "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() - } - }, + CommandSwap, ), "undo": NewCommand( @@ -178,113 +174,21 @@ func (c *Calc) SetCommands() { "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") - } - }, + CommandDup, ), "edit": NewCommand( "edit the stack interactively", - func(c *Calc) { - if c.stack.Len() == 0 { - fmt.Println("empty stack") - return - } - - c.stack.Backup() - - // put the stack contents into a tmp file - tmp, err := os.CreateTemp("", "stack") - if err != nil { - fmt.Println(err) - return - } - defer os.Remove(tmp.Name()) - - 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 c.stack.All() { - _, err = fmt.Fprintf(tmp, "%f\n", item) - if err != nil { - fmt.Println(err) - return - } - } - - tmp.Close() - - // 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 modified.Close() - - // reset the stack - c.stack.Clear() - - // and put the new contents (if legit) back onto the stack - scanner := bufio.NewScanner(modified) - for scanner.Scan() { - line := strings.TrimSpace(c.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 - } - - c.stack.Push(num) - } - - if err := scanner.Err(); err != nil { - fmt.Println("Error reading from file:", err) - } - }, + 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{ @@ -317,3 +221,126 @@ func (c *Calc) SetCommands() { 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 os.Remove(tmp.Name()) + + 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 + } + } + + tmp.Close() + + // 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 modified.Close() + + // 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/funcs.go b/funcs.go index 4b116d4..2e3580a 100644 --- a/funcs.go +++ b/funcs.go @@ -22,14 +22,14 @@ import ( "math" ) -type R struct { +type Result struct { Res float64 Err error } type Numbers []float64 -type Function func(Numbers) R +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 @@ -64,446 +64,450 @@ func NewFuncall(function Function, expectargs ...int) *Funcall { } // Convenience function, create new result -func NewR(n float64, e error) R { - return R{Res: n, Err: e} +func NewResult(n float64, e error) Result { + return Result{Res: n, Err: e} } // the actual functions, called once during initialization. func DefineFunctions() Funcalls { - f := map[string]*Funcall{ + funcmap := map[string]*Funcall{ // simple operators, they all expect 2 args "+": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]+arg[1], nil) + func(arg Numbers) Result { + return NewResult(arg[0]+arg[1], nil) }, ), "-": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]-arg[1], nil) + func(arg Numbers) Result { + return NewResult(arg[0]-arg[1], nil) }, ), "x": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]*arg[1], nil) + func(arg Numbers) Result { + return NewResult(arg[0]*arg[1], nil) }, ), "/": NewFuncall( - func(arg Numbers) R { + func(arg Numbers) Result { if arg[1] == 0 { - return NewR(0, errors.New("division by null")) + return NewResult(0, errors.New("division by null")) } - return NewR(arg[0]/arg[1], nil) + return NewResult(arg[0]/arg[1], nil) }, ), "^": NewFuncall( - func(arg Numbers) R { - return NewR(math.Pow(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Pow(arg[0], arg[1]), nil) }, ), "%": NewFuncall( - func(arg Numbers) R { - return NewR((arg[0]/100)*arg[1], nil) + func(arg Numbers) Result { + return NewResult((arg[0]/100)*arg[1], nil) }, ), "%-": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]-((arg[0]/100)*arg[1]), nil) + func(arg Numbers) Result { + return NewResult(arg[0]-((arg[0]/100)*arg[1]), nil) }, ), "%+": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]+((arg[0]/100)*arg[1]), nil) + func(arg Numbers) Result { + return NewResult(arg[0]+((arg[0]/100)*arg[1]), nil) }, ), "mod": NewFuncall( - func(arg Numbers) R { - return NewR(math.Remainder(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Remainder(arg[0], arg[1]), nil) }, ), "sqrt": NewFuncall( - func(arg Numbers) R { - return NewR(math.Sqrt(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Sqrt(arg[0]), nil) }, 1), "abs": NewFuncall( - func(arg Numbers) R { - return NewR(math.Abs(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Abs(arg[0]), nil) }, 1), "acos": NewFuncall( - func(arg Numbers) R { - return NewR(math.Acos(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Acos(arg[0]), nil) }, 1), "acosh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Acosh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Acosh(arg[0]), nil) }, 1), "asin": NewFuncall( - func(arg Numbers) R { - return NewR(math.Asin(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Asin(arg[0]), nil) }, 1), "asinh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Asinh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Asinh(arg[0]), nil) }, 1), "atan": NewFuncall( - func(arg Numbers) R { - return NewR(math.Atan(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Atan(arg[0]), nil) }, 1), "atan2": NewFuncall( - func(arg Numbers) R { - return NewR(math.Atan2(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Atan2(arg[0], arg[1]), nil) }, 2), "atanh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Atanh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Atanh(arg[0]), nil) }, 1), "cbrt": NewFuncall( - func(arg Numbers) R { - return NewR(math.Cbrt(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Cbrt(arg[0]), nil) }, 1), "ceil": NewFuncall( - func(arg Numbers) R { - return NewR(math.Ceil(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Ceil(arg[0]), nil) }, 1), "cos": NewFuncall( - func(arg Numbers) R { - return NewR(math.Cos(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Cos(arg[0]), nil) }, 1), "cosh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Cosh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Cosh(arg[0]), nil) }, 1), "erf": NewFuncall( - func(arg Numbers) R { - return NewR(math.Erf(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Erf(arg[0]), nil) }, 1), "erfc": NewFuncall( - func(arg Numbers) R { - return NewR(math.Erfc(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Erfc(arg[0]), nil) }, 1), "erfcinv": NewFuncall( - func(arg Numbers) R { - return NewR(math.Erfcinv(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Erfcinv(arg[0]), nil) }, 1), "erfinv": NewFuncall( - func(arg Numbers) R { - return NewR(math.Erfinv(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Erfinv(arg[0]), nil) }, 1), "exp": NewFuncall( - func(arg Numbers) R { - return NewR(math.Exp(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Exp(arg[0]), nil) }, 1), "exp2": NewFuncall( - func(arg Numbers) R { - return NewR(math.Exp2(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Exp2(arg[0]), nil) }, 1), "expm1": NewFuncall( - func(arg Numbers) R { - return NewR(math.Expm1(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Expm1(arg[0]), nil) }, 1), "floor": NewFuncall( - func(arg Numbers) R { - return NewR(math.Floor(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Floor(arg[0]), nil) }, 1), "gamma": NewFuncall( - func(arg Numbers) R { - return NewR(math.Gamma(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Gamma(arg[0]), nil) }, 1), "ilogb": NewFuncall( - func(arg Numbers) R { - return NewR(float64(math.Ilogb(arg[0])), nil) + func(arg Numbers) Result { + return NewResult(float64(math.Ilogb(arg[0])), nil) }, 1), "j0": NewFuncall( - func(arg Numbers) R { - return NewR(math.J0(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.J0(arg[0]), nil) }, 1), "j1": NewFuncall( - func(arg Numbers) R { - return NewR(math.J1(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.J1(arg[0]), nil) }, 1), "log": NewFuncall( - func(arg Numbers) R { - return NewR(math.Log(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Log(arg[0]), nil) }, 1), "log10": NewFuncall( - func(arg Numbers) R { - return NewR(math.Log10(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Log10(arg[0]), nil) }, 1), "log1p": NewFuncall( - func(arg Numbers) R { - return NewR(math.Log1p(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Log1p(arg[0]), nil) }, 1), "log2": NewFuncall( - func(arg Numbers) R { - return NewR(math.Log2(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Log2(arg[0]), nil) }, 1), "logb": NewFuncall( - func(arg Numbers) R { - return NewR(math.Logb(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Logb(arg[0]), nil) }, 1), "pow": NewFuncall( - func(arg Numbers) R { - return NewR(math.Pow(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Pow(arg[0], arg[1]), nil) }, 2), "round": NewFuncall( - func(arg Numbers) R { - return NewR(math.Round(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Round(arg[0]), nil) }, 1), "roundtoeven": NewFuncall( - func(arg Numbers) R { - return NewR(math.RoundToEven(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.RoundToEven(arg[0]), nil) }, 1), "sin": NewFuncall( - func(arg Numbers) R { - return NewR(math.Sin(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Sin(arg[0]), nil) }, 1), "sinh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Sinh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Sinh(arg[0]), nil) }, 1), "tan": NewFuncall( - func(arg Numbers) R { - return NewR(math.Tan(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Tan(arg[0]), nil) }, 1), "tanh": NewFuncall( - func(arg Numbers) R { - return NewR(math.Tanh(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Tanh(arg[0]), nil) }, 1), "trunc": NewFuncall( - func(arg Numbers) R { - return NewR(math.Trunc(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Trunc(arg[0]), nil) }, 1), "y0": NewFuncall( - func(arg Numbers) R { - return NewR(math.Y0(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Y0(arg[0]), nil) }, 1), "y1": NewFuncall( - func(arg Numbers) R { - return NewR(math.Y1(arg[0]), nil) + func(arg Numbers) Result { + return NewResult(math.Y1(arg[0]), nil) }, 1), "copysign": NewFuncall( - func(arg Numbers) R { - return NewR(math.Copysign(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Copysign(arg[0], arg[1]), nil) }, 2), "dim": NewFuncall( - func(arg Numbers) R { - return NewR(math.Dim(arg[0], arg[1]), nil) + func(arg Numbers) Result { + return NewResult(math.Dim(arg[0], arg[1]), nil) }, 2), "hypot": NewFuncall( - func(arg Numbers) R { - return NewR(math.Hypot(arg[0], arg[1]), nil) + 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) R { - return NewR(arg[0]/2.54, nil) + func(arg Numbers) Result { + return NewResult(arg[0]/2.54, nil) }, 1), "inch-to-cm": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]*2.54, nil) + func(arg Numbers) Result { + return NewResult(arg[0]*2.54, nil) }, 1), "gallons-to-liters": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]*3.785, nil) + func(arg Numbers) Result { + return NewResult(arg[0]*3.785, nil) }, 1), "liters-to-gallons": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]/3.785, nil) + func(arg Numbers) Result { + return NewResult(arg[0]/3.785, nil) }, 1), "yards-to-meters": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]*91.44, nil) + func(arg Numbers) Result { + return NewResult(arg[0]*91.44, nil) }, 1), "meters-to-yards": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]/91.44, nil) + func(arg Numbers) Result { + return NewResult(arg[0]/91.44, nil) }, 1), "miles-to-kilometers": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]*1.609, nil) + func(arg Numbers) Result { + return NewResult(arg[0]*1.609, nil) }, 1), "kilometers-to-miles": NewFuncall( - func(arg Numbers) R { - return NewR(arg[0]/1.609, nil) + func(arg Numbers) Result { + return NewResult(arg[0]/1.609, nil) }, 1), "or": NewFuncall( - func(arg Numbers) R { - return NewR(float64(int(arg[0])|int(arg[1])), nil) + func(arg Numbers) Result { + return NewResult(float64(int(arg[0])|int(arg[1])), nil) }, 2), "and": NewFuncall( - func(arg Numbers) R { - return NewR(float64(int(arg[0])&int(arg[1])), nil) + func(arg Numbers) Result { + return NewResult(float64(int(arg[0])&int(arg[1])), nil) }, 2), "xor": NewFuncall( - func(arg Numbers) R { - return NewR(float64(int(arg[0])^int(arg[1])), nil) + func(arg Numbers) Result { + return NewResult(float64(int(arg[0])^int(arg[1])), nil) }, 2), "<": NewFuncall( - func(arg Numbers) R { + func(arg Numbers) Result { // Shift by negative number provibited, so check it. - // Note that we check agains uint64 overflow as well here + // Note that we check against uint64 overflow as well here if arg[1] < 0 || uint64(arg[1]) > math.MaxInt64 { - return NewR(0, errors.New("negative shift amount")) + return NewResult(0, errors.New("negative shift amount")) } - return NewR(float64(int(arg[0])<": NewFuncall( - func(arg Numbers) R { + func(arg Numbers) Result { if arg[1] < 0 || uint64(arg[1]) > math.MaxInt64 { - return NewR(0, errors.New("negative shift amount")) + return NewResult(0, errors.New("negative shift amount")) } - return NewR(float64(int(arg[0])>>int(arg[1])), nil) + + return NewResult(float64(int(arg[0])>>int(arg[1])), nil) }, 2), } // aliases - f["*"] = f["x"] - f["remainder"] = f["mod"] + funcmap["*"] = funcmap["x"] + funcmap["remainder"] = funcmap["mod"] - return f + return funcmap } func DefineBatchFunctions() Funcalls { - f := map[string]*Funcall{ + funcmap := map[string]*Funcall{ "median": NewFuncall( - func(args Numbers) R { + func(args Numbers) Result { middle := len(args) / 2 - return NewR(args[middle], nil) + + return NewResult(args[middle], nil) }, -1), "mean": NewFuncall( - func(args Numbers) R { + func(args Numbers) Result { var sum float64 for _, item := range args { sum += item } - return NewR(sum/float64(len(args)), nil) + + return NewResult(sum/float64(len(args)), nil) }, -1), "min": NewFuncall( - func(args Numbers) R { + func(args Numbers) Result { var min float64 min, args = args[0], args[1:] for _, item := range args { @@ -511,12 +515,13 @@ func DefineBatchFunctions() Funcalls { min = item } } - return NewR(min, nil) + + return NewResult(min, nil) }, -1), "max": NewFuncall( - func(args Numbers) R { + func(args Numbers) Result { var max float64 max, args = args[0], args[1:] for _, item := range args { @@ -524,24 +529,26 @@ func DefineBatchFunctions() Funcalls { max = item } } - return NewR(max, nil) + + return NewResult(max, nil) }, -1), "sum": NewFuncall( - func(args Numbers) R { + func(args Numbers) Result { var sum float64 for _, item := range args { sum += item } - return NewR(sum, nil) + + return NewResult(sum, nil) }, -1), } // aliases - f["+"] = f["sum"] - f["avg"] = f["mean"] + funcmap["+"] = funcmap["sum"] + funcmap["avg"] = funcmap["mean"] - return f + return funcmap } diff --git a/interpreter.go b/interpreter.go index 760a5af..87e948b 100644 --- a/interpreter.go +++ b/interpreter.go @@ -29,8 +29,8 @@ type Interpreter struct { script string } -// LUA interpreter, instanciated in main() -var L *lua.LState +// LuaInterpreter is the lua interpreter, instantiated in main() +var LuaInterpreter *lua.LState // holds a user provided lua function type LuaFunction struct { @@ -39,8 +39,8 @@ type LuaFunction struct { numargs int } -// must be global since init() is being called from lua which doesn't -// have access to the interpreter instance +// 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 { @@ -61,8 +61,8 @@ func (i *Interpreter) InitLua() { {lua.DebugLibName, lua.OpenDebug}, {lua.MathLibName, lua.OpenMath}, } { - if err := L.CallByParam(lua.P{ - Fn: L.NewFunction(pair.f), + if err := LuaInterpreter.CallByParam(lua.P{ + Fn: LuaInterpreter.NewFunction(pair.f), NRet: 0, Protect: true, }, lua.LString(pair.n)); err != nil { @@ -71,19 +71,19 @@ func (i *Interpreter) InitLua() { } // load the lua config (which we expect to contain init() and math functions) - if err := L.DoFile(i.script); err != nil { + if err := LuaInterpreter.DoFile(i.script); err != nil { panic(err) } - // instanciate + // instantiate LuaFuncs = map[string]LuaFunction{} // that way the user can call register(...) from lua inside init() - L.SetGlobal("register", L.NewFunction(register)) + LuaInterpreter.SetGlobal("register", LuaInterpreter.NewFunction(register)) // actually call init() - if err := L.CallByParam(lua.P{ - Fn: L.GetGlobal("init"), + if err := LuaInterpreter.CallByParam(lua.P{ + Fn: LuaInterpreter.GetGlobal("init"), NRet: 0, Protect: true, }); err != nil { @@ -108,9 +108,9 @@ func (i *Interpreter) FuncNumArgs(name string) int { // 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 provded by calc.Eval(), these are +// 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 successfull. +// 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)) @@ -120,44 +120,44 @@ func (i *Interpreter) CallLuaFunc(funcname string, items []float64) (float64, er fallthrough case 1: // 1 arg variant - if err := L.CallByParam(lua.P{ - Fn: L.GetGlobal(funcname), + if err := LuaInterpreter.CallByParam(lua.P{ + Fn: LuaInterpreter.GetGlobal(funcname), NRet: 1, Protect: true, }, lua.LNumber(items[0])); err != nil { - fmt.Println(err) - return 0, err + return 0, fmt.Errorf("failed to exec lua func %s: %w", funcname, err) } case 2: // 2 arg variant - if err := L.CallByParam(lua.P{ - Fn: L.GetGlobal(funcname), + 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, err + return 0, fmt.Errorf("failed to exec lua func %s: %w", funcname, err) } case -1: // batch variant, use lua table as array - tb := L.NewTable() + table := LuaInterpreter.NewTable() // put the whole stack into it for _, item := range items { - tb.Append(lua.LNumber(item)) + table.Append(lua.LNumber(item)) } - if err := L.CallByParam(lua.P{ - Fn: L.GetGlobal(funcname), + if err := LuaInterpreter.CallByParam(lua.P{ + Fn: LuaInterpreter.GetGlobal(funcname), NRet: 1, Protect: true, - }, tb); err != nil { - return 0, err + }, 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 := L.Get(-1).(lua.LNumber); ok { - L.Pop(1) + if res, ok := LuaInterpreter.Get(-1).(lua.LNumber); ok { + LuaInterpreter.Pop(1) + return float64(res), nil } @@ -167,10 +167,10 @@ func (i *Interpreter) CallLuaFunc(funcname string, items []float64) (float64, er // 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(L *lua.LState) int { - function := L.ToString(1) - numargs := L.ToInt(2) - help := L.ToString(3) +func register(lstate *lua.LState) int { + function := lstate.ToString(1) + numargs := lstate.ToInt(2) + help := lstate.ToString(3) LuaFuncs[function] = LuaFunction{ name: function, diff --git a/main.go b/main.go index 28cf9e9..81ef074 100644 --- a/main.go +++ b/main.go @@ -81,11 +81,13 @@ func Main() int { if showversion { fmt.Printf("This is rpn version %s\n", VERSION) + return 0 } if showhelp { fmt.Println(Usage) + return 0 } @@ -95,12 +97,13 @@ func Main() int { if showmanual { man() + return 0 } - // the lua state object is global, instanciate it early - L = lua.NewState(lua.Options{SkipOpenLibs: true}) - defer L.Close() + // 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(). @@ -108,13 +111,12 @@ func Main() int { luarunner := NewInterpreter(configfile, enabledebug) luarunner.InitLua() calc.SetInt(luarunner) + if calc.debug { fmt.Println("loaded config") } - } else { - if calc.debug { - fmt.Println(err) - } + } else if calc.debug { + fmt.Println(err) } if len(flag.Args()) > 1 { @@ -123,6 +125,7 @@ func Main() int { calc.stdin = true if err := calc.Eval(strings.Join(flag.Args(), " ")); err != nil { fmt.Println(err) + return 1 } @@ -130,7 +133,7 @@ func Main() int { } // interactive mode, need readline - rl, err := readline.NewEx(&readline.Config{ + reader, err := readline.NewEx(&readline.Config{ Prompt: calc.Prompt(), HistoryFile: os.Getenv("HOME") + "/.rpn-history", HistoryLimit: 500, @@ -143,8 +146,8 @@ func Main() int { if err != nil { panic(err) } - defer rl.Close() - rl.CaptureExitSignal() + defer reader.Close() + reader.CaptureExitSignal() if inputIsStdin() { // commands are coming on stdin, however we will still enter @@ -154,7 +157,7 @@ func Main() int { for { // primary program repl - line, err := rl.Readline() + line, err := reader.Readline() if err != nil { break } @@ -163,7 +166,8 @@ func Main() int { if err != nil { fmt.Println(err) } - rl.SetPrompt(calc.Prompt()) + + reader.SetPrompt(calc.Prompt()) } if len(flag.Args()) > 0 { @@ -173,6 +177,7 @@ func Main() int { calc.batch = true if err = calc.Eval(flag.Args()[0]); err != nil { fmt.Println(err) + return 1 } } @@ -182,17 +187,19 @@ func Main() int { func inputIsStdin() bool { stat, _ := os.Stdin.Stat() + return (stat.Mode() & os.ModeCharDevice) == 0 } func man() { + var buf bytes.Buffer + man := exec.Command("less", "-") - var b bytes.Buffer - b.Write([]byte(manpage)) + buf.WriteString(manpage) man.Stdout = os.Stdout - man.Stdin = &b + man.Stdin = &buf man.Stderr = os.Stderr err := man.Run() diff --git a/rpn.go b/rpn.go index e9ccd6b..59763ce 100644 --- a/rpn.go +++ b/rpn.go @@ -129,7 +129,7 @@ DESCRIPTION Basic operators: + add - - substract + - subtract / divide x multiply (alias: *) ^ power @@ -145,7 +145,7 @@ DESCRIPTION Percent functions: % percent - %- substract percent + %- subtract percent %+ add percent Batch functions: diff --git a/rpn.pod b/rpn.pod index f03c0cc..6e16dcd 100644 --- a/rpn.pod +++ b/rpn.pod @@ -136,7 +136,7 @@ stack. Basic operators: + add - - substract + - subtract / divide x multiply (alias: *) ^ power @@ -152,7 +152,7 @@ Bitwise operators: Percent functions: % percent - %- substract percent + %- subtract percent %+ add percent Batch functions: diff --git a/stack.go b/stack.go index c853db0..35baf16 100644 --- a/stack.go +++ b/stack.go @@ -64,14 +64,14 @@ func (s *Stack) Bump() { } // append an item to the stack -func (s *Stack) Push(x float64) { +func (s *Stack) Push(item float64) { s.mutex.Lock() defer s.mutex.Unlock() - s.Debug(fmt.Sprintf(" push to stack: %.2f", x)) + s.Debug(fmt.Sprintf(" push to stack: %.2f", item)) s.Bump() - s.linklist.PushBack(x) + s.linklist.PushBack(item) } // remove and return an item from the stack @@ -90,6 +90,7 @@ func (s *Stack) Pop() float64 { s.Debug(fmt.Sprintf(" remove from stack: %.2f", val)) s.Bump() + return val.(float64) } @@ -123,32 +124,33 @@ func (s *Stack) Swap() { return } - a := s.linklist.Back() - s.linklist.Remove(a) + prevA := s.linklist.Back() + s.linklist.Remove(prevA) - b := s.linklist.Back() - s.linklist.Remove(b) + prevB := s.linklist.Back() + s.linklist.Remove(prevB) - s.Debug(fmt.Sprintf("swapping %.2f with %.2f", b.Value, a.Value)) + s.Debug(fmt.Sprintf("swapping %.2f with %.2f", prevB.Value, prevA.Value)) - s.linklist.PushBack(a.Value) - s.linklist.PushBack(b.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{} - i := s.Len() + stacklen := s.Len() count := 1 + if len(num) > 0 { count = num[0] } for e := s.linklist.Front(); e != nil; e = e.Next() { - if i <= count { + if stacklen <= count { items = append(items, e.Value.(float64)) } - i-- + stacklen-- } return items @@ -168,12 +170,14 @@ func (s *Stack) All() []float64 { // 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) } @@ -215,6 +219,7 @@ func (s *Stack) Restore() { if s.rev == 0 { fmt.Println("error: stack is empty.") + return } diff --git a/stack_test.go b/stack_test.go index aa83e96..b6ea807 100644 --- a/stack_test.go +++ b/stack_test.go @@ -35,16 +35,16 @@ func TestPush(t *testing.T) { func TestPop(t *testing.T) { t.Run("pop", func(t *testing.T) { - s := NewStack() - s.Push(5) - got := s.Pop() + 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 s.Len() != 0 { + if stack.Len() != 0 { t.Errorf("stack not empty after pop()") } }) @@ -52,25 +52,25 @@ func TestPop(t *testing.T) { func TestPops(t *testing.T) { t.Run("pops", func(t *testing.T) { - s := NewStack() - s.Push(5) - s.Push(5) - s.Push(5) - s.Pop() + stack := NewStack() + stack.Push(5) + stack.Push(5) + stack.Push(5) + stack.Pop() - if s.Len() != 2 { + if stack.Len() != 2 { t.Errorf("stack len not correct after pop:\n+++ got: %d\n--- want: %d", - s.Len(), 2) + stack.Len(), 2) } }) } func TestShift(t *testing.T) { t.Run("shift", func(t *testing.T) { - s := NewStack() - s.Shift() + stack := NewStack() + stack.Shift() - if s.Len() != 0 { + if stack.Len() != 0 { t.Errorf("stack not empty after shift()") } }) @@ -78,13 +78,13 @@ func TestShift(t *testing.T) { func TestClear(t *testing.T) { t.Run("clear", func(t *testing.T) { - s := NewStack() - s.Push(5) - s.Push(5) - s.Push(5) - s.Clear() + stack := NewStack() + stack.Push(5) + stack.Push(5) + stack.Push(5) + stack.Clear() - if s.Len() != 0 { + if stack.Len() != 0 { t.Errorf("stack not empty after clear()") } }) @@ -92,9 +92,9 @@ func TestClear(t *testing.T) { func TestLast(t *testing.T) { t.Run("last", func(t *testing.T) { - s := NewStack() - s.Push(5) - got := s.Last() + stack := NewStack() + stack.Push(5) + got := stack.Last() if len(got) != 1 { t.Errorf("last failed:\n+++ got: %d elements\n--- want: %d elements", @@ -106,7 +106,7 @@ func TestLast(t *testing.T) { got, 5.0) } - if s.Len() != 1 { + if stack.Len() != 1 { t.Errorf("stack modified after last()") } }) @@ -114,14 +114,14 @@ func TestLast(t *testing.T) { func TestAll(t *testing.T) { t.Run("all", func(t *testing.T) { - s := NewStack() + stack := NewStack() list := []float64{2, 4, 6, 8} for _, item := range list { - s.Push(item) + stack.Push(item) } - got := s.All() + got := stack.All() if len(got) != len(list) { t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements", @@ -135,7 +135,7 @@ func TestAll(t *testing.T) { } } - if s.Len() != len(list) { + if stack.Len() != len(list) { t.Errorf("stack modified after last()") } }) @@ -143,37 +143,37 @@ func TestAll(t *testing.T) { func TestBackupRestore(t *testing.T) { t.Run("shift", func(t *testing.T) { - s := NewStack() - s.Push(5) - s.Backup() - s.Clear() - s.Restore() + stack := NewStack() + stack.Push(5) + stack.Backup() + stack.Clear() + stack.Restore() - if s.Len() != 1 { + if stack.Len() != 1 { t.Errorf("stack not correctly restored()") } - a := s.Pop() - if a != 5.0 { + value := stack.Pop() + if value != 5.0 { t.Errorf("stack not identical to old revision:\n+++ got: %f\n--- want: %f", - a, 5.0) + value, 5.0) } }) } func TestReverse(t *testing.T) { t.Run("reverse", func(t *testing.T) { - s := NewStack() + stack := NewStack() list := []float64{2, 4, 6} reverse := []float64{6, 4, 2} for _, item := range list { - s.Push(item) + stack.Push(item) } - s.Reverse() + stack.Reverse() - got := s.All() + got := stack.All() if len(got) != len(list) { t.Errorf("all failed:\n+++ got: %d elements\n--- want: %d elements", diff --git a/util.go b/util.go index 46593a5..43134d5 100644 --- a/util.go +++ b/util.go @@ -30,6 +30,7 @@ func contains[E comparable](s []E, v E) bool { return true } } + return false } @@ -38,6 +39,7 @@ func exists[K comparable, V any](m map[K]V, v K) bool { if _, ok := m[v]; ok { return true } + return false } @@ -73,5 +75,5 @@ func list2str(list Numbers) string { } func Error(m string) error { - return fmt.Errorf("Error: %s!", m) + return fmt.Errorf("Error: %s", m) }