diff --git a/go/calc.go b/go/calc.go
index 0805ccb..047b2d3 100644
--- a/go/calc.go
+++ b/go/calc.go
@@ -1,42 +1,73 @@
+/*
+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 (
+ "errors"
"fmt"
"math"
+ "os"
"regexp"
"strconv"
"strings"
"github.com/chzyer/readline"
- lua "github.com/yuin/gopher-lua"
)
type Calc struct {
- debug bool
- batch bool
- stdin bool
- stack *Stack
- history []string
- completer readline.AutoCompleter
- L *lua.LState
+ debug bool
+ batch bool
+ stdin bool
+ stack *Stack
+ history []string
+ completer readline.AutoCompleter
+ interpreter *Interpreter
}
+// help for lua functions will be added dynamically
const Help string = `Available commands:
-batch enable batch mode
-debug enable debug output
-dump display the stack contents
-clear clear the whole stack
-shift remove the last element of the stack
-history display calculation history
-help show this message
+batch toggle batch mode
+debug toggle debug output
+dump display the stack contents
+clear clear the whole stack
+shift remove the last element of the stack
+history display calculation history
+help|? show this message
+quit|exit|c-d|c-c exit program
Available operators:
-basic operators: + - * /
+basic operators: + - x /
+
+Available math functions:
+sqrt square root
+mod remainder of division
+max batch mode only: max of all values
+min batch mode only: min of all values
+mean batch mode only: mean of all values (alias: avg)
+median batch mode only: median of all values
+% percent
+%- substract percent
+%+ add percent
Math operators:
-^ power` // FIXME: add help strings from lua functions
+^ power`
-// That way I can add custom functions to completion
+// That way we can add custom functions to completion
func GetCompleteCustomFunctions() func(string) []string {
return func(line string) []string {
funcs := []string{}
@@ -58,12 +89,15 @@ func NewCalc() *Calc {
readline.PcItem("dump"),
readline.PcItem("reverse"),
readline.PcItem("debug"),
+ readline.PcItem("undebug"),
readline.PcItem("clear"),
readline.PcItem("batch"),
readline.PcItem("shift"),
readline.PcItem("undo"),
readline.PcItem("help"),
readline.PcItem("history"),
+ readline.PcItem("exit"),
+ readline.PcItem("quit"),
// ops
readline.PcItem("+"),
@@ -91,41 +125,69 @@ func NewCalc() *Calc {
readline.PcItem("sqrt"),
readline.PcItem("remainder"),
readline.PcItem("avg"),
+ readline.PcItem("mean"), // alias for avg
+ readline.PcItem("min"),
+ readline.PcItem("max"),
readline.PcItem("median"),
)
return &c
}
+// setup the interpreter, called from main()
+func (c *Calc) SetInt(I *Interpreter) {
+ c.interpreter = I
+}
+
func (c *Calc) ToggleDebug() {
c.debug = !c.debug
c.stack.ToggleDebug()
+ fmt.Printf("debugging set to %t\n", c.debug)
}
func (c *Calc) ToggleBatch() {
c.batch = !c.batch
+ fmt.Printf("batchmode set to %t\n", c.batch)
}
func (c *Calc) ToggleStdin() {
c.stdin = !c.stdin
}
+func (c *Calc) Prompt() string {
+ p := "\033[31m»\033[0m "
+ b := ""
+ if c.batch {
+ b = "->batch"
+ }
+ d := ""
+ v := ""
+ if c.debug {
+ d = "->debug"
+ v = fmt.Sprintf("/rev%d", c.stack.rev)
+ }
+
+ return fmt.Sprintf("rpn%s%s [%d%s]%s", b, d, c.stack.Len(), v, p)
+}
+
+// the actual work horse, evaluate a line of calc command[s]
func (c *Calc) Eval(line string) {
line = strings.TrimSpace(line)
- space := regexp.MustCompile(`\s+`)
- simple := regexp.MustCompile(`^[\+\-\*\/]$`)
- constants := []string{"E", "Pi", "Phi", "Sqrt2", "SqrtE", "SqrtPi",
- "SqrtPhi", "Ln2", "Log2E", "Ln10", "Log10E"}
- functions := []string{"sqrt", "remainder", "%", "%-", "%+"}
- batch := []string{"median", "avg"}
- luafuncs := []string{}
if line == "" {
return
}
- for luafunc := range LuaFuncs {
- luafuncs = append(luafuncs, luafunc)
+ space := regexp.MustCompile(`\s+`)
+ simple := regexp.MustCompile(`^[\+\-\*\/x\^]$`)
+ constants := []string{"E", "Pi", "Phi", "Sqrt2", "SqrtE", "SqrtPi",
+ "SqrtPhi", "Ln2", "Log2E", "Ln10", "Log10E"}
+ functions := []string{"sqrt", "remainder", "mod", "%", "%-", "%+"}
+ batch := []string{"median", "avg", "mean", "max", "min"}
+
+ luafuncs := []string{}
+ for name := range LuaFuncs {
+ luafuncs = append(luafuncs, name)
}
for _, item := range space.Split(line, -1) {
@@ -135,39 +197,53 @@ func (c *Calc) Eval(line string) {
c.stack.Backup()
c.stack.Push(num)
} else {
- if simple.MatchString(line) {
+ if simple.MatchString(item) {
+ // simple ops like + or x
c.simple(item[0])
continue
}
if contains(constants, item) {
+ // put the constant onto the stack
c.stack.Backup()
c.stack.Push(const2num(item))
continue
}
if contains(functions, item) {
+ // go builtin math function, if implemented
c.mathfunc(item)
continue
}
if contains(batch, item) {
+ // math functions only supported in batch mode like max or mean
c.batchfunc(item)
continue
}
if contains(luafuncs, item) {
+ // user provided custom lua functions
c.luafunc(item)
continue
}
+ // management commands
switch item {
+ case "?":
+ fallthrough
case "help":
fmt.Println(Help)
+ 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 "undebug":
+ c.debug = false
case "batch":
c.ToggleBatch()
case "clear":
@@ -185,8 +261,10 @@ func (c *Calc) Eval(line string) {
for _, entry := range c.history {
fmt.Println(entry)
}
- case "^":
- c.exp()
+ case "exit":
+ fallthrough
+ case "quit":
+ os.Exit(0)
default:
fmt.Println("unknown command or operator!")
}
@@ -194,10 +272,13 @@ func (c *Calc) Eval(line string) {
}
}
+// just a textual representation of math operations, viewable with the
+// history command
func (c *Calc) History(format string, args ...any) {
c.history = append(c.history, fmt.Sprintf(format, args...))
}
+// print the result
func (c *Calc) Result() float64 {
if !c.stdin {
fmt.Print("= ")
@@ -208,6 +289,13 @@ func (c *Calc) Result() float64 {
return c.stack.Last()
}
+func (c *Calc) Debug(msg string) {
+ if c.debug {
+ fmt.Printf("DEBUG(calc): %s\n", msg)
+ }
+}
+
+// do simple calculations
func (c *Calc) simple(op byte) {
c.stack.Backup()
@@ -216,15 +304,15 @@ func (c *Calc) simple(op byte) {
a := c.stack.Pop()
var x float64
- if c.debug {
- fmt.Printf("DEBUG: evaluating: %.2f %c %.2f\n", a, op, b)
- }
+ c.Debug(fmt.Sprintf("evaluating: %.2f %c %.2f", a, op, b))
switch op {
case '+':
x = a + b
case '-':
x = a - b
+ case 'x':
+ fallthrough // alias for *
case '*':
x = a * b
case '/':
@@ -233,6 +321,8 @@ func (c *Calc) simple(op byte) {
return
}
x = a / b
+ case '^':
+ x = math.Pow(a, b)
default:
panic("invalid operator!")
}
@@ -246,32 +336,11 @@ func (c *Calc) simple(op byte) {
}
}
- _ = c.Result()
-}
-
-func (c *Calc) exp() {
- c.stack.Backup()
-
- for c.stack.Len() > 1 {
- b := c.stack.Pop()
- a := c.stack.Pop()
- x := math.Pow(a, b)
-
- c.stack.Push(x)
-
- c.History("%f ^ %f = %f", a, b, x)
-
- if !c.batch {
- break
- }
- }
-
- _ = c.Result()
+ c.Result()
}
+// calc using go math lib functions
func (c *Calc) mathfunc(funcname string) {
- // FIXME: split into 2 funcs, one working with 1 the other with 2
- // args, saving Pop calls
c.stack.Backup()
for c.stack.Len() > 0 {
@@ -283,6 +352,8 @@ func (c *Calc) mathfunc(funcname string) {
x = math.Sqrt(a)
c.History("sqrt(%f) = %f", a, x)
+ case "mod":
+ fallthrough // alias
case "remainder":
b := c.stack.Pop()
a := c.stack.Pop()
@@ -319,9 +390,10 @@ func (c *Calc) mathfunc(funcname string) {
}
}
- _ = c.Result()
+ c.Result()
}
+// execute pure batch functions, operating on the whole stack
func (c *Calc) batchfunc(funcname string) {
if !c.batch {
fmt.Println("error: only available in batch mode")
@@ -344,6 +416,8 @@ func (c *Calc) batchfunc(funcname string) {
x = all[middle]
c.History("median(all)")
+ case "mean":
+ fallthrough // alias
case "avg":
var sum float64
@@ -353,6 +427,28 @@ func (c *Calc) batchfunc(funcname string) {
x = sum / float64(count)
c.History("avg(all)")
+ case "min":
+ x = c.stack.Pop() // initialize with the last one
+
+ for c.stack.Len() > 0 {
+ val := c.stack.Pop()
+ if val < x {
+ x = val
+ }
+ }
+
+ c.History("min(all)")
+ case "max":
+ x = c.stack.Pop() // initialize with the last one
+
+ for c.stack.Len() > 0 {
+ val := c.stack.Pop()
+ if val > x {
+ x = val
+ }
+ }
+
+ c.History("max(all)")
}
c.stack.Push(x)
@@ -360,20 +456,41 @@ func (c *Calc) batchfunc(funcname string) {
}
func (c *Calc) luafunc(funcname string) {
- // we may need to put them onto the stack afterwards!
- c.stack.Backup()
- b := c.stack.Pop()
- a := c.stack.Pop()
+ // called from calc loop
+ var x float64
+ var err error
+
+ switch c.interpreter.FuncNumArgs(funcname) {
+ case 1:
+ x, err = c.interpreter.CallLuaFunc(funcname, []float64{c.stack.Last()})
+ case 2:
+ x, err = c.interpreter.CallLuaFunc(funcname, c.stack.LastTwo())
+ case -1:
+ x, err = c.interpreter.CallLuaFunc(funcname, c.stack.All())
+ default:
+ x, err = 0, errors.New("invalid number of argument requested")
+ }
- x, err := CallLuaFunc(c.L, funcname, a, b)
if err != nil {
fmt.Println(err)
- c.stack.Push(a)
- c.stack.Push(b)
return
}
- c.History("%s(%f,%f) = %f", funcname, a, b, x)
+ c.stack.Backup()
+
+ switch c.interpreter.FuncNumArgs(funcname) {
+ case 1:
+ a := c.stack.Pop()
+ c.History("%s(%f) = %f", funcname, a, x)
+ case 2:
+ a := c.stack.Pop()
+ b := c.stack.Pop()
+ c.History("%s(%f,%f) = %f", funcname, a, b, x)
+ case -1:
+ c.stack.Clear()
+ c.History("%s(*) = %f", funcname, x)
+ }
+
c.stack.Push(x)
c.Result()
diff --git a/go/interpreter.go b/go/interpreter.go
index 87ed682..39c0ed8 100644
--- a/go/interpreter.go
+++ b/go/interpreter.go
@@ -1,3 +1,20 @@
+/*
+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 (
@@ -7,31 +24,59 @@ import (
lua "github.com/yuin/gopher-lua"
)
+type Interpreter struct {
+ debug bool
+}
+
// LUA interpreter, instanciated in main()
var L *lua.LState
-var LuaFuncs map[string]int
-
-// FIXME: add 2nd var with help string
-// called from lua to register a 1 arg math function
-func RegisterFuncOneArg(L *lua.LState) int {
- function := L.ToString(1)
- LuaFuncs[function] = 1
- return 1
+// holds a user provided lua function
+type LuaFunction struct {
+ name string
+ help string
+ numargs int
}
-// called from lua to register a 1 arg math function
-func RegisterFuncTwoArg(L *lua.LState) int {
- function := L.ToString(1)
- LuaFuncs[function] = 2
- return 1
-}
+// 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 InitLua(L *lua.LState) {
- LuaFuncs = map[string]int{}
- L.SetGlobal("RegisterFuncOneArg", L.NewFunction(RegisterFuncOneArg))
- L.SetGlobal("RegisterFuncTwoArg", L.NewFunction(RegisterFuncTwoArg))
+// initialize the lua environment properly
+func InitLua(config string, debug bool) *Interpreter {
+ // 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)
+ }
+ }
+ // load the lua config (which we expect to contain init() and math functions)
+ if err := L.DoFile(config); err != nil {
+ panic(err)
+ }
+
+ // instanciate
+ LuaFuncs = map[string]LuaFunction{}
+
+ // that way the user can call register(...) from lua inside init()
+ L.SetGlobal("register", L.NewFunction(register))
+
+ // actually call init()
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal("init"),
NRet: 0,
@@ -39,26 +84,68 @@ func InitLua(L *lua.LState) {
}); err != nil {
panic(err)
}
+
+ return &Interpreter{debug: debug}
}
-func CallLuaFunc(L *lua.LState, funcname string, a float64, b float64) (float64, error) {
- if LuaFuncs[funcname] == 1 {
+func (i *Interpreter) Debug(msg string) {
+ if i.debug {
+ fmt.Printf("DEBUG(lua): %s\n", msg)
+ }
+}
+
+func (i *Interpreter) FuncNumArgs(name string) int {
+ return LuaFuncs[name].numargs
+}
+
+// Call a user provided math function registered with register().
+//
+// Each function has to tell us how many args it expects, the actual
+// function call from here is different depending on the number of
+// 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
+// non-popped stack items. So the items will only removed from the
+// stack when the lua function execution is successfull.
+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))
+
+ switch LuaFuncs[funcname].numargs {
+ case 1:
// 1 arg variant
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal(funcname),
NRet: 1,
Protect: true,
- }, lua.LNumber(a)); err != nil {
+ }, lua.LNumber(items[0])); err != nil {
fmt.Println(err)
return 0, err
}
- } else {
+ case 2:
// 2 arg variant
if err := L.CallByParam(lua.P{
Fn: L.GetGlobal(funcname),
NRet: 1,
Protect: true,
- }, lua.LNumber(a), lua.LNumber(b)); err != nil {
+ }, lua.LNumber(items[0]), lua.LNumber(items[1])); err != nil {
+ return 0, err
+ }
+ case -1:
+ // batch variant, use lua table as array
+ tb := L.NewTable()
+
+ // put the whole stack into it
+ for _, item := range items {
+ tb.Append(lua.LNumber(item))
+ }
+
+ if err := L.CallByParam(lua.P{
+ Fn: L.GetGlobal(funcname),
+ NRet: 1,
+ Protect: true,
+ }, tb); err != nil {
return 0, err
}
}
@@ -69,5 +156,22 @@ func CallLuaFunc(L *lua.LState, funcname string, a float64, b float64) (float64,
return float64(res), nil
}
- return 0, errors.New("function did not return a float64!")
+ return 0, errors.New("function did not return a float64")
+}
+
+// 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)
+
+ LuaFuncs[function] = LuaFunction{
+ name: function,
+ numargs: numargs,
+ help: help,
+ }
+
+ return 1
}
diff --git a/go/main.go b/go/main.go
index 935d19b..4f97fed 100644
--- a/go/main.go
+++ b/go/main.go
@@ -1,8 +1,26 @@
+/*
+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"
+ "strings"
"github.com/chzyer/readline"
flag "github.com/spf13/pflag"
@@ -38,7 +56,8 @@ func main() {
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.StringVarP(&configfile, "config", "c", os.Getenv("HOME")+"/.rpn.lua", "config file (lua format)")
+ flag.StringVarP(&configfile, "config", "c",
+ os.Getenv("HOME")+"/.rpn.lua", "config file (lua format)")
flag.Parse()
@@ -56,43 +75,28 @@ func main() {
calc.ToggleDebug()
}
+ // the lua state object is global, instanciate it early
+ L = lua.NewState(lua.Options{SkipOpenLibs: true})
+ defer L.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 {
- // FIXME: put into interpreter.go, probably with its own obj
- // then just Interpreter.Init(configfile) should suffice
- L = lua.NewState(lua.Options{SkipOpenLibs: true})
- defer L.Close()
-
- // 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)
- }
- }
-
- if err := L.DoFile(configfile); err != nil {
- panic(err)
- }
-
- InitLua(L)
- calc.L = L
+ I := InitLua(configfile, enabledebug)
+ calc.SetInt(I)
}
+ if len(flag.Args()) > 1 {
+ // commandline calc operation, no readline etc needed
+ // called like rpn 2 2 +
+ calc.stdin = true
+ calc.Eval(strings.Join(flag.Args(), " "))
+ return
+ }
+
+ // interactive mode, need readline
rl, err := readline.NewEx(&readline.Config{
- Prompt: "\033[31m»\033[0m ",
+ Prompt: calc.Prompt(),
HistoryFile: os.Getenv("HOME") + "/.rpn-history",
HistoryLimit: 500,
AutoComplete: calc.completer,
@@ -108,19 +112,27 @@ func main() {
rl.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 := rl.Readline()
if err != nil {
break
}
calc.Eval(line)
+ rl.SetPrompt(calc.Prompt())
}
if len(flag.Args()) > 0 {
+ // called like this:
+ // echo 1 2 3 4 | rpn +
+ // batch mode enabled automatically
+ calc.batch = true
calc.Eval(flag.Args()[0])
}
}
diff --git a/go/stack.go b/go/stack.go
index 8ed84d8..c34ca07 100644
--- a/go/stack.go
+++ b/go/stack.go
@@ -1,3 +1,20 @@
+/*
+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 (
@@ -6,8 +23,14 @@ import (
"sync"
)
+// The stack uses a linked list provided by container/list as storage
+// and works after the LIFO principle (last in first out). Most of the
+// work is being done in the linked list, but we add a couple of
+// cenvenient functions, so that the user doesn't have to cope with
+// list directly.
+
type Stack struct {
- dll list.List
+ linklist list.List
backup list.List
debug bool
rev int
@@ -15,8 +38,15 @@ type Stack struct {
mutex sync.Mutex
}
+// FIXME: maybe use a separate stack object for backup so that it has
+// its own revision etc
func NewStack() *Stack {
- return &Stack{dll: list.List{}, backup: list.List{}, rev: 0, backuprev: 0}
+ return &Stack{
+ linklist: list.List{},
+ backup: list.List{},
+ rev: 0,
+ backuprev: 0,
+ }
}
func (s *Stack) Debug(msg string) {
@@ -33,6 +63,7 @@ func (s *Stack) Bump() {
s.rev++
}
+// append an item to the stack
func (s *Stack) Push(x float64) {
s.mutex.Lock()
defer s.mutex.Unlock()
@@ -40,52 +71,87 @@ func (s *Stack) Push(x float64) {
s.Debug(fmt.Sprintf(" push to stack: %.2f", x))
s.Bump()
- s.dll.PushBack(x)
+ s.linklist.PushBack(x)
}
+// remove and return an item from the stack
func (s *Stack) Pop() float64 {
s.mutex.Lock()
defer s.mutex.Unlock()
- if s.dll.Len() == 0 {
+ if s.linklist.Len() == 0 {
return 0
}
- tail := s.dll.Back()
+ tail := s.linklist.Back()
val := tail.Value
- s.dll.Remove(tail)
+ s.linklist.Remove(tail)
- s.Debug(fmt.Sprintf("remove from stack: %.2f", val))
+ s.Debug(fmt.Sprintf(" remove from stack: %.2f", val))
s.Bump()
return val.(float64)
}
+// just remove the last item, do not return it
func (s *Stack) Shift() {
s.mutex.Lock()
defer s.mutex.Unlock()
- if s.dll.Len() == 0 {
+ if s.linklist.Len() == 0 {
return
}
- tail := s.dll.Back()
- s.dll.Remove(tail)
+ tail := s.linklist.Back()
+ s.linklist.Remove(tail)
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
}
+// just return the last item, do not remove it
func (s *Stack) Last() float64 {
- if s.dll.Back() == nil {
+ if s.linklist.Back() == nil {
return 0
}
- return s.dll.Back().Value.(float64)
+ return s.linklist.Back().Value.(float64)
}
+// Return the last 2 elements of the stack without modifying it.
+//
+// We need to return the last 2 elements of the stack, however
+// container/list only supports access to 1 last element. So, we
+// pop the last, retrieve the second last and push the popped one
+// back.
+func (s *Stack) LastTwo() []float64 {
+ items := []float64{}
+ if s.linklist.Back() == nil {
+ return items
+ }
+
+ last := s.Pop()
+ items = append(items, last)
+ items = append(items, s.linklist.Back().Value.(float64))
+
+ s.Push(last)
+ return items
+}
+
+// Return all elements of the stack without modifying it.
+func (s *Stack) All() []float64 {
+ items := []float64{}
+
+ for e := s.linklist.Front(); e != nil; e = e.Next() {
+ items = append(items, e.Value.(float64))
+ }
+
+ return items
+}
+
+// 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.dll)
- for e := s.dll.Front(); e != nil; e = e.Next() {
+ 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)
}
@@ -100,11 +166,11 @@ func (s *Stack) Dump() {
func (s *Stack) Clear() {
s.Debug("DEBUG: clearing stack")
- s.dll = list.List{}
+ s.linklist = list.List{}
}
func (s *Stack) Len() int {
- return s.dll.Len()
+ return s.linklist.Len()
}
func (s *Stack) Backup() {
@@ -113,7 +179,7 @@ func (s *Stack) Backup() {
// and lead to unexpected results. The methid here works reliably
// at least.
s.backup = list.List{}
- for e := s.dll.Front(); e != nil; e = e.Next() {
+ for e := s.linklist.Front(); e != nil; e = e.Next() {
s.backup.PushBack(e.Value)
}
s.backuprev = s.rev
@@ -128,15 +194,15 @@ func (s *Stack) Restore() {
s.Debug(fmt.Sprintf("restoring stack to revision %d", s.backuprev))
s.rev = s.backuprev
- s.dll = s.backup
+ s.linklist = s.backup
}
func (s *Stack) Reverse() {
newstack := list.List{}
- for e := s.dll.Front(); e != nil; e = e.Next() {
+ for e := s.linklist.Front(); e != nil; e = e.Next() {
newstack.PushFront(e.Value)
}
- s.dll = newstack
+ s.linklist = newstack
}
diff --git a/go/test.lua b/go/test.lua
index 066f69f..f71649e 100644
--- a/go/test.lua
+++ b/go/test.lua
@@ -2,11 +2,16 @@ function add(a,b)
return a + b
end
+function test(a)
+ return a
+end
+
function parallelresistance(a,b)
return 1.0 / (a * b)
end
function init()
- RegisterFuncTwoArg("add")
- RegisterFuncTwoArg("parallelresistance")
+ register("add", 2, "addition")
+ register("test", 1, "test")
+ register("parallelresistance", 2, "parallel resistance")
end
diff --git a/go/util.go b/go/util.go
index 0962bb0..36af8c5 100644
--- a/go/util.go
+++ b/go/util.go
@@ -1,7 +1,25 @@
+/*
+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 "math"
+// find an item in a list
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {