2023-10-30 14:22:43 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
"github.com/chzyer/readline"
|
|
|
|
|
flag "github.com/spf13/pflag"
|
2023-10-31 19:02:40 +01:00
|
|
|
lua "github.com/yuin/gopher-lua"
|
2023-10-30 14:22:43 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const VERSION string = "0.0.1"
|
|
|
|
|
|
|
|
|
|
const Usage string = `This is rpn, a reverse polish notation calculator cli.
|
|
|
|
|
|
|
|
|
|
Usage: rpn [-bdvh] [<operator>]
|
|
|
|
|
|
|
|
|
|
Options:
|
|
|
|
|
-b, --batchmode enable batch mode
|
|
|
|
|
-d, --debug enable debug mode
|
|
|
|
|
-v, --version show version
|
|
|
|
|
-h, --help show help
|
|
|
|
|
|
|
|
|
|
When <operator> is given, batch mode ist automatically enabled. Use
|
|
|
|
|
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
|
|
|
|
|
|
2023-10-31 19:02:40 +01:00
|
|
|
Copyright (c) 2023 T.v.Dein`
|
2023-10-30 14:22:43 +01:00
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
calc := NewCalc()
|
|
|
|
|
|
|
|
|
|
showversion := false
|
|
|
|
|
showhelp := false
|
|
|
|
|
enabledebug := false
|
2023-10-31 19:02:40 +01:00
|
|
|
configfile := ""
|
2023-10-30 14:22:43 +01:00
|
|
|
|
|
|
|
|
flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
|
|
|
|
|
flag.BoolVarP(&enabledebug, "debug", "d", false, "debug mode")
|
|
|
|
|
flag.BoolVarP(&showversion, "version", "v", false, "show version")
|
|
|
|
|
flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
|
2023-10-31 19:02:40 +01:00
|
|
|
flag.StringVarP(&configfile, "config", "c", os.Getenv("HOME")+"/.rpn.lua", "config file (lua format)")
|
|
|
|
|
|
2023-10-30 14:22:43 +01:00
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
|
|
if showversion {
|
|
|
|
|
fmt.Printf("This is rpn version %s\n", VERSION)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if showhelp {
|
|
|
|
|
fmt.Println(Usage)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if enabledebug {
|
|
|
|
|
calc.ToggleDebug()
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-31 19:02:40 +01:00
|
|
|
if _, err := os.Stat(configfile); err == nil {
|
2023-10-31 19:12:09 +01:00
|
|
|
// FIXME: put into interpreter.go, probably with its own obj
|
|
|
|
|
// then just Interpreter.Init(configfile) should suffice
|
|
|
|
|
L = lua.NewState(lua.Options{SkipOpenLibs: true})
|
2023-10-31 19:02:40 +01:00
|
|
|
defer L.Close()
|
|
|
|
|
|
2023-10-31 19:12:09 +01:00
|
|
|
// we only load a subset of lua Open modules and don't allow
|
|
|
|
|
// net, system or io stuff
|
|
|
|
|
for _, pair := range []struct {
|
|
|
|
|
n string
|
|
|
|
|
f lua.LGFunction
|
|
|
|
|
}{
|
|
|
|
|
{lua.LoadLibName, lua.OpenPackage},
|
|
|
|
|
{lua.BaseLibName, lua.OpenBase},
|
|
|
|
|
{lua.TabLibName, lua.OpenTable},
|
|
|
|
|
{lua.DebugLibName, lua.OpenDebug},
|
|
|
|
|
{lua.MathLibName, lua.OpenMath},
|
|
|
|
|
} {
|
|
|
|
|
if err := L.CallByParam(lua.P{
|
|
|
|
|
Fn: L.NewFunction(pair.f),
|
|
|
|
|
NRet: 0,
|
|
|
|
|
Protect: true,
|
|
|
|
|
}, lua.LString(pair.n)); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-31 19:02:40 +01:00
|
|
|
if err := L.DoFile(configfile); err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InitLua(L)
|
|
|
|
|
calc.L = L
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-30 14:22:43 +01:00
|
|
|
rl, err := readline.NewEx(&readline.Config{
|
|
|
|
|
Prompt: "\033[31m»\033[0m ",
|
2023-10-30 19:13:24 +01:00
|
|
|
HistoryFile: os.Getenv("HOME") + "/.rpn-history",
|
2023-10-30 14:22:43 +01:00
|
|
|
HistoryLimit: 500,
|
|
|
|
|
AutoComplete: calc.completer,
|
|
|
|
|
InterruptPrompt: "^C",
|
|
|
|
|
EOFPrompt: "exit",
|
|
|
|
|
HistorySearchFold: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
defer rl.Close()
|
|
|
|
|
rl.CaptureExitSignal()
|
|
|
|
|
|
|
|
|
|
if inputIsStdin() {
|
|
|
|
|
calc.ToggleStdin()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
line, err := rl.Readline()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calc.Eval(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(flag.Args()) > 0 {
|
|
|
|
|
calc.Eval(flag.Args()[0])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func inputIsStdin() bool {
|
|
|
|
|
stat, _ := os.Stdin.Stat()
|
|
|
|
|
return (stat.Mode() & os.ModeCharDevice) == 0
|
|
|
|
|
}
|