code re-organization, added more commands and more flexible lua api

This commit is contained in:
2023-11-01 13:07:08 +01:00
parent 1a89d0ab85
commit 19b8aa7883
6 changed files with 467 additions and 145 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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()

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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])
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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
}

View File

@@ -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

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package main
import "math"
// find an item in a list
func contains(s []string, e string) bool {
for _, a := range s {
if a == e {