diff --git a/Makefile b/Makefile
index 2d1e390..4a8f0ca 100644
--- a/Makefile
+++ b/Makefile
@@ -25,20 +25,20 @@ UID = root
GID = 0
HAVE_POD := $(shell pod2text -h 2>/dev/null)
-all: $(tool).1 $(tool).go buildlocal
+all: $(tool).1 cmd/$(tool).go buildlocal
%.1: %.pod
ifdef HAVE_POD
pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1
endif
-%.go: %.pod
+cmd/%.go: %.pod
ifdef HAVE_POD
- echo "package main" > $*.go
- echo >> $*.go
- echo "var manpage = \`" >> $*.go
- pod2text $*.pod >> $*.go
- echo "\`" >> $*.go
+ echo "package main" > cmd/$*.go
+ echo >> cmd/$*.go
+ echo "var manpage = \`" >> cmd/$*.go
+ pod2text cmd/$*.pod >> cmd/$*.go
+ echo "\`" >> cmd/$*.go
endif
buildlocal:
diff --git a/calc.go b/cmd/calc.go
similarity index 99%
rename from calc.go
rename to cmd/calc.go
index bd1a205..e5ab3b7 100644
--- a/calc.go
+++ b/cmd/calc.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"errors"
diff --git a/calc_test.go b/cmd/calc_test.go
similarity index 99%
rename from calc_test.go
rename to cmd/calc_test.go
index 56401c8..10f079d 100644
--- a/calc_test.go
+++ b/cmd/calc_test.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"fmt"
@@ -327,7 +327,7 @@ func TestCalcLua(t *testing.T) {
LuaInterpreter = lua.NewState(lua.Options{SkipOpenLibs: true})
defer LuaInterpreter.Close()
- luarunner := NewInterpreter("example.lua", false)
+ luarunner := NewInterpreter("../example.lua", false)
luarunner.InitLua()
calc.SetInt(luarunner)
diff --git a/command.go b/cmd/command.go
similarity index 99%
rename from command.go
rename to cmd/command.go
index e33d59d..dbf2bc6 100644
--- a/command.go
+++ b/cmd/command.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"bufio"
diff --git a/funcs.go b/cmd/funcs.go
similarity index 99%
rename from funcs.go
rename to cmd/funcs.go
index 09d5e96..1856e13 100644
--- a/funcs.go
+++ b/cmd/funcs.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"errors"
diff --git a/interpreter.go b/cmd/interpreter.go
similarity index 99%
rename from interpreter.go
rename to cmd/interpreter.go
index 8fb3bb3..6f64150 100644
--- a/interpreter.go
+++ b/cmd/interpreter.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"errors"
diff --git a/pager.go b/cmd/pager.go
similarity index 99%
rename from pager.go
rename to cmd/pager.go
index e9e1ed0..5185d23 100644
--- a/pager.go
+++ b/cmd/pager.go
@@ -1,4 +1,4 @@
-package main
+package cmd
// pager setup using bubbletea
// file shamlelessly copied from:
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 0000000..c85ea15
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,195 @@
+/*
+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.6"
+
+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/rpn.go b/cmd/rpn.go
similarity index 99%
rename from rpn.go
rename to cmd/rpn.go
index 667134c..d0cb434 100644
--- a/rpn.go
+++ b/cmd/rpn.go
@@ -1,4 +1,4 @@
-package main
+package cmd
var manpage = `
NAME
diff --git a/stack.go b/cmd/stack.go
similarity index 99%
rename from stack.go
rename to cmd/stack.go
index 35baf16..83ec620 100644
--- a/stack.go
+++ b/cmd/stack.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"container/list"
diff --git a/stack_test.go b/cmd/stack_test.go
similarity index 99%
rename from stack_test.go
rename to cmd/stack_test.go
index b6ea807..f31e418 100644
--- a/stack_test.go
+++ b/cmd/stack_test.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"testing"
diff --git a/util.go b/cmd/util.go
similarity index 99%
rename from util.go
rename to cmd/util.go
index 43134d5..600598a 100644
--- a/util.go
+++ b/cmd/util.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"fmt"
diff --git a/util_test.go b/cmd/util_test.go
similarity index 98%
rename from util_test.go
rename to cmd/util_test.go
index 7c37735..ad0c3f2 100644
--- a/util_test.go
+++ b/cmd/util_test.go
@@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-package main
+package cmd
import (
"testing"
diff --git a/main.go b/main.go
index ac8285f..72c7ae7 100644
--- a/main.go
+++ b/main.go
@@ -18,182 +18,10 @@ along with this program. If not, see .
package main
import (
- "fmt"
- "log"
"os"
- "strings"
-
- "github.com/chzyer/readline"
- flag "github.com/spf13/pflag"
- lua "github.com/yuin/gopher-lua"
+ "rpn/cmd"
)
-const VERSION string = "2.1.5"
-
-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() {
- os.Exit(Main())
-}
-
-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)
+ os.Exit(cmd.Main())
}