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 {