9 Commits

Author SHA1 Message Date
T.v.Dein
b13fbc63e3 Merge pull request #16 from TLINDEN/internal/add-lua-tests
Internal/add lua tests
2023-11-09 18:49:32 +01:00
bacbfcc517 added debug output to Backup() 2023-11-09 18:47:31 +01:00
b91e024569 added more tests 2023-11-09 18:34:38 +01:00
a6f8a0fdbe renamed luafunc() 2023-11-09 18:34:29 +01:00
7b656c492a fix reverse and backup&restore 2023-11-09 18:34:07 +01:00
7d0443ce4b bump version 2023-11-09 18:34:00 +01:00
T.v.Dein
a964a99f3d Fix/lua no funcs known (#15)
* lua fixes:

- fix lua function calling, didn't work in the last
releases (regression)
- add lua funcs which don't modify the stack (for converters etc)
- added better lua examples
2023-11-08 19:03:37 +01:00
T.v.Dein
31a0ddd547 add variable support, implements #10 (#14) 2023-11-08 14:47:15 +01:00
T.v.Dein
fa5f8dcb3b suppress intermediate results unless -i, addresses #11, fix man (#13) 2023-11-08 14:43:34 +01:00
10 changed files with 364 additions and 71 deletions

View File

@@ -22,6 +22,7 @@ Features:
- completion - completion
- history - history
- comments (comment character is `#`) - comments (comment character is `#`)
- variables
## Demo ## Demo
@@ -224,10 +225,15 @@ the `register()` function to register your functions to the
calculator. This function takes these parameters: calculator. This function takes these parameters:
- function name - function name
- number of arguments expected (1,2 or -1 allowed), -1 means batch - number of arguments expected (see below)
mode
- help text - help text
Number of expected arguments can be:
- 0: expect 1 argument but do NOT modify the stack
- 1-n: do a singular calculation
- -1: batch mode work with all numbers on the stack
Please [refer to the lua language Please [refer to the lua language
reference](https://www.lua.org/manual/5.4/) for more details about reference](https://www.lua.org/manual/5.4/) for more details about
LUA. LUA.

121
calc.go
View File

@@ -33,17 +33,22 @@ type Calc struct {
batch bool batch bool
stdin bool stdin bool
showstack bool showstack bool
intermediate bool
notdone bool // set to true as long as there are items left in the eval loop
stack *Stack stack *Stack
history []string history []string
completer readline.AutoCompleter completer readline.AutoCompleter
interpreter *Interpreter interpreter *Interpreter
Space *regexp.Regexp Space *regexp.Regexp
Comment *regexp.Regexp Comment *regexp.Regexp
Register *regexp.Regexp
Constants []string Constants []string
LuaFunctions []string LuaFunctions []string
Funcalls Funcalls Funcalls Funcalls
BatchFuncalls Funcalls BatchFuncalls Funcalls
Vars map[string]float64
} }
// help for lua functions will be added dynamically // help for lua functions will be added dynamically
@@ -56,6 +61,7 @@ clear clear the whole stack
shift remove the last element of the stack shift remove the last element of the stack
reverse reverse the stack elements reverse reverse the stack elements
swap exchange the last two elements swap exchange the last two elements
vars show list of variables
history display calculation history history display calculation history
help|? show this message help|? show this message
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
@@ -79,12 +85,16 @@ sum sum of all values (alias: +)
max max of all values max max of all values
min min of all values min min of all values
mean mean of all values (alias: avg) mean mean of all values (alias: avg)
median median of all values` median median of all values
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack`
// commands, constants and operators, defined here to feed completion // commands, constants and operators, defined here to feed completion
// and our mode switch in Eval() dynamically // and our mode switch in Eval() dynamically
const ( const (
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit swap show` Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit swap show vars`
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E` Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
) )
@@ -126,6 +136,7 @@ func NewCalc() *Calc {
c.Funcalls = DefineFunctions() c.Funcalls = DefineFunctions()
c.BatchFuncalls = DefineBatchFunctions() c.BatchFuncalls = DefineBatchFunctions()
c.Vars = map[string]float64{}
c.completer = readline.NewPrefixCompleter( c.completer = readline.NewPrefixCompleter(
// custom lua functions // custom lua functions
@@ -135,20 +146,21 @@ func NewCalc() *Calc {
c.Space = regexp.MustCompile(`\s+`) c.Space = regexp.MustCompile(`\s+`)
c.Comment = regexp.MustCompile(`#.*`) // ignore everything after # c.Comment = regexp.MustCompile(`#.*`) // ignore everything after #
c.Register = regexp.MustCompile(`^([<>])([A-Z][A-Z0-9]*)`)
// pre-calculate mode switching arrays // pre-calculate mode switching arrays
c.Constants = strings.Split(Constants, " ") c.Constants = strings.Split(Constants, " ")
for name := range LuaFuncs {
c.LuaFunctions = append(c.LuaFunctions, name)
}
return &c return &c
} }
// setup the interpreter, called from main() // setup the interpreter, called from main(), import lua functions
func (c *Calc) SetInt(I *Interpreter) { func (c *Calc) SetInt(I *Interpreter) {
c.interpreter = I c.interpreter = I
for name := range LuaFuncs {
c.LuaFunctions = append(c.LuaFunctions, name)
}
} }
func (c *Calc) ToggleDebug() { func (c *Calc) ToggleDebug() {
@@ -198,7 +210,15 @@ func (c *Calc) Eval(line string) {
return return
} }
for _, item := range c.Space.Split(line, -1) { items := c.Space.Split(line, -1)
for pos, item := range items {
if pos+1 < len(items) {
c.notdone = true
} else {
c.notdone = false
}
num, err := strconv.ParseFloat(item, 64) num, err := strconv.ParseFloat(item, 64)
if err == nil { if err == nil {
@@ -239,7 +259,18 @@ func (c *Calc) Eval(line string) {
if contains(c.LuaFunctions, item) { if contains(c.LuaFunctions, item) {
// user provided custom lua functions // user provided custom lua functions
c.luafunc(item) c.EvalLuaFunction(item)
continue
}
regmatches := c.Register.FindStringSubmatch(item)
if len(regmatches) == 3 {
switch regmatches[1] {
case ">":
c.PutVar(regmatches[2])
case "<":
c.GetVar(regmatches[2])
}
continue continue
} }
@@ -249,9 +280,11 @@ func (c *Calc) Eval(line string) {
fallthrough fallthrough
case "help": case "help":
fmt.Println(Help) fmt.Println(Help)
fmt.Println("Lua functions:") if len(LuaFuncs) > 0 {
for name, function := range LuaFuncs { fmt.Println("Lua functions:")
fmt.Printf("%-20s %s\n", name, function.help) for name, function := range LuaFuncs {
fmt.Printf("%-20s %s\n", name, function.help)
}
} }
case "dump": case "dump":
c.stack.Dump() c.stack.Dump()
@@ -291,6 +324,16 @@ func (c *Calc) Eval(line string) {
os.Exit(0) os.Exit(0)
case "manual": case "manual":
man() man()
case "vars":
if len(c.Vars) > 0 {
fmt.Printf("%-20s %s\n", "VARIABLE", "VALUE")
for k, v := range c.Vars {
fmt.Printf("%-20s -> %.2f\n", k, v)
}
} else {
fmt.Println("no vars registered")
}
default: default:
fmt.Println("unknown command or operator!") fmt.Println("unknown command or operator!")
} }
@@ -350,6 +393,10 @@ func (c *Calc) DoFuncall(funcname string) error {
return R.Err return R.Err
} }
// don't forget to backup!
c.stack.Backup()
// "pop"
if batch { if batch {
// get rid of stack // get rid of stack
c.stack.Clear() c.stack.Clear()
@@ -379,11 +426,16 @@ func (c *Calc) History(format string, args ...any) {
// print the result // print the result
func (c *Calc) Result() float64 { func (c *Calc) Result() float64 {
if !c.stdin { // we only print the result if it's either a final result or
fmt.Print("= ") // (if it is intermediate) if -i has been given
} if c.intermediate || !c.notdone {
// only needed in repl
if !c.stdin {
fmt.Print("= ")
}
fmt.Println(c.stack.Last()[0]) fmt.Println(c.stack.Last()[0])
}
return c.stack.Last()[0] return c.stack.Last()[0]
} }
@@ -394,12 +446,14 @@ func (c *Calc) Debug(msg string) {
} }
} }
func (c *Calc) luafunc(funcname string) { func (c *Calc) EvalLuaFunction(funcname string) {
// called from calc loop // called from calc loop
var x float64 var x float64
var err error var err error
switch c.interpreter.FuncNumArgs(funcname) { switch c.interpreter.FuncNumArgs(funcname) {
case 0:
fallthrough
case 1: case 1:
x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last()) x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last())
case 2: case 2:
@@ -417,7 +471,15 @@ func (c *Calc) luafunc(funcname string) {
c.stack.Backup() c.stack.Backup()
dopush := true
switch c.interpreter.FuncNumArgs(funcname) { switch c.interpreter.FuncNumArgs(funcname) {
case 0:
a := c.stack.Last()
if len(a) == 1 {
c.History("%s(%f) = %f", funcname, a, x)
}
dopush = false
case 1: case 1:
a := c.stack.Pop() a := c.stack.Pop()
c.History("%s(%f) = %f", funcname, a, x) c.History("%s(%f) = %f", funcname, a, x)
@@ -430,7 +492,30 @@ func (c *Calc) luafunc(funcname string) {
c.History("%s(*) = %f", funcname, x) c.History("%s(*) = %f", funcname, x)
} }
c.stack.Push(x) if dopush {
c.stack.Push(x)
}
c.Result() c.Result()
} }
func (c *Calc) PutVar(name string) {
last := c.stack.Last()
if len(last) == 1 {
c.Debug(fmt.Sprintf("register %.2f in %s", last[0], name))
c.Vars[name] = last[0]
} else {
fmt.Println("empty stack")
}
}
func (c *Calc) GetVar(name string) {
if _, ok := c.Vars[name]; ok {
c.Debug(fmt.Sprintf("retrieve %.2f from %s", c.Vars[name], name))
c.stack.Backup()
c.stack.Push(c.Vars[name])
} else {
fmt.Println("variable doesn't exist")
}
}

View File

@@ -20,6 +20,8 @@ package main
import ( import (
"fmt" "fmt"
"testing" "testing"
lua "github.com/yuin/gopher-lua"
) )
func TestCommentsAndWhitespace(t *testing.T) { func TestCommentsAndWhitespace(t *testing.T) {
@@ -104,6 +106,7 @@ func TestCalc(t *testing.T) {
exp float64 exp float64
batch bool batch bool
}{ }{
// ops
{ {
name: "plus", name: "plus",
cmd: `15 15 +`, cmd: `15 15 +`,
@@ -144,6 +147,8 @@ func TestCalc(t *testing.T) {
cmd: `400 20 %+`, cmd: `400 20 %+`,
exp: 480, exp: 480,
}, },
// math tests
{ {
name: "mod", name: "mod",
cmd: `9 2 mod`, cmd: `9 2 mod`,
@@ -164,6 +169,20 @@ func TestCalc(t *testing.T) {
cmd: `6 4 dim`, cmd: `6 4 dim`,
exp: 2, exp: 2,
}, },
// constants tests
{
name: "pitimes2",
cmd: `Pi 2 *`,
exp: 6.283185307179586,
},
{
name: "pi+sqrt2",
cmd: `Pi Sqrt2 +`,
exp: 4.555806215962888,
},
// batch tests
{ {
name: "batch-sum", name: "batch-sum",
cmd: `2 2 2 2 sum`, cmd: `2 2 2 2 sum`,
@@ -194,6 +213,34 @@ func TestCalc(t *testing.T) {
exp: 5, exp: 5,
batch: true, batch: true,
}, },
// stack tests
{
name: "use-vars",
cmd: `10 >TEN clear 5 <TEN *`,
exp: 50,
},
{
name: "reverse",
cmd: `100 500 reverse -`,
exp: 400,
},
{
name: "swap",
cmd: `2 16 swap /`,
exp: 8,
},
{
name: "clear batch",
cmd: "1 1 1 1 1 clear 1 1 sum",
exp: 2,
batch: true,
},
{
name: "undo",
cmd: `4 4 + undo *`,
exp: 16,
},
} }
for _, tt := range tests { for _, tt := range tests {
@@ -212,3 +259,55 @@ func TestCalc(t *testing.T) {
}) })
} }
} }
func TestCalcLua(t *testing.T) {
var tests = []struct {
function string
stack []float64
exp float64
}{
{
function: "lower",
stack: []float64{5, 6},
exp: 5.0,
},
{
function: "parallelresistance",
stack: []float64{100, 200, 300},
exp: 54.54545454545455,
},
}
calc := NewCalc()
L = lua.NewState(lua.Options{SkipOpenLibs: true})
defer L.Close()
luarunner := NewInterpreter("example.lua", false)
luarunner.InitLua()
calc.SetInt(luarunner)
for _, tt := range tests {
testname := fmt.Sprintf("lua-%s", tt.function)
t.Run(testname, func(t *testing.T) {
calc.stack.Clear()
for _, item := range tt.stack {
calc.stack.Push(item)
}
calc.EvalLuaFunction(tt.function)
got := calc.stack.Last()
if calc.stack.Len() != 1 {
t.Errorf("invalid stack size:\n+++ got: %d\n--- want: 1",
calc.stack.Len())
}
if got[0] != tt.exp {
t.Errorf("lua function %s failed:\n+++ got: %f\n--- want: %f",
tt.function, got, tt.exp)
}
})
}
}

38
example.lua Normal file
View File

@@ -0,0 +1,38 @@
-- simple function, return the lower number of the two operands
function lower(a,b)
if a < b then
return a
else
return b
end
end
-- calculate parallel resistance. Batch function (registered with -1,
-- see below). Takes a table as parameter.
--
-- Formula: 1/( (1/R1) + (1/R2) + ...)
function parallelresistance(list)
sumres = 0
for i, value in ipairs(list) do
sumres = sumres + 1 / value
end
return 1 / sumres
end
-- converter example
function inch2centimeter(inches)
return inches * 2.54
end
function init()
-- expects 2 args
register("lower", 2, "lower")
-- expects a list of all numbers on the stack, batch mode
register("parallelresistance", -1, "parallel resistance")
-- expects 1 arg, but doesn't pop()
register("inch2centimeter", 0)
end

View File

@@ -25,7 +25,8 @@ import (
) )
type Interpreter struct { type Interpreter struct {
debug bool debug bool
script string
} }
// LUA interpreter, instanciated in main() // LUA interpreter, instanciated in main()
@@ -42,8 +43,12 @@ type LuaFunction struct {
// have access to the interpreter instance // have access to the interpreter instance
var LuaFuncs map[string]LuaFunction var LuaFuncs map[string]LuaFunction
func NewInterpreter(script string, debug bool) *Interpreter {
return &Interpreter{debug: debug, script: script}
}
// initialize the lua environment properly // initialize the lua environment properly
func InitLua(config string, debug bool) *Interpreter { func (i *Interpreter) InitLua() {
// we only load a subset of lua Open modules and don't allow // we only load a subset of lua Open modules and don't allow
// net, system or io stuff // net, system or io stuff
for _, pair := range []struct { for _, pair := range []struct {
@@ -66,7 +71,7 @@ func InitLua(config string, debug bool) *Interpreter {
} }
// load the lua config (which we expect to contain init() and math functions) // load the lua config (which we expect to contain init() and math functions)
if err := L.DoFile(config); err != nil { if err := L.DoFile(i.script); err != nil {
panic(err) panic(err)
} }
@@ -84,8 +89,6 @@ func InitLua(config string, debug bool) *Interpreter {
}); err != nil { }); err != nil {
panic(err) panic(err)
} }
return &Interpreter{debug: debug}
} }
func (i *Interpreter) Debug(msg string) { func (i *Interpreter) Debug(msg string) {
@@ -113,6 +116,8 @@ func (i *Interpreter) CallLuaFunc(funcname string, items []float64) (float64, er
funcname, LuaFuncs[funcname].numargs)) funcname, LuaFuncs[funcname].numargs))
switch LuaFuncs[funcname].numargs { switch LuaFuncs[funcname].numargs {
case 0:
fallthrough
case 1: case 1:
// 1 arg variant // 1 arg variant
if err := L.CallByParam(lua.P{ if err := L.CallByParam(lua.P{

24
main.go
View File

@@ -30,19 +30,20 @@ import (
lua "github.com/yuin/gopher-lua" lua "github.com/yuin/gopher-lua"
) )
const VERSION string = "2.0.4" const VERSION string = "2.0.7"
const Usage string = `This is rpn, a reverse polish notation calculator cli. const Usage string = `This is rpn, a reverse polish notation calculator cli.
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-s, --stack show last 5 items of the stack (off by default) -s, --stack show last 5 items of the stack (off by default)
-m, --manual show manual -i --intermediate print intermediate results
-v, --version show version -m, --manual show manual
-h, --help show help -v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -59,7 +60,9 @@ func main() {
configfile := "" configfile := ""
flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode") flag.BoolVarP(&calc.batch, "batchmode", "b", false, "batch mode")
flag.BoolVarP(&calc.showstack, "showstack", "s", false, "show stack") 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(&enabledebug, "debug", "d", false, "debug mode")
flag.BoolVarP(&showversion, "version", "v", false, "show version") flag.BoolVarP(&showversion, "version", "v", false, "show version")
flag.BoolVarP(&showhelp, "help", "h", false, "show usage") flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
@@ -95,8 +98,9 @@ func main() {
// our config file is interpreted as lua code, only functions can // our config file is interpreted as lua code, only functions can
// be defined, init() will be called by InitLua(). // be defined, init() will be called by InitLua().
if _, err := os.Stat(configfile); err == nil { if _, err := os.Stat(configfile); err == nil {
I := InitLua(configfile, enabledebug) luarunner := NewInterpreter(configfile, enabledebug)
calc.SetInt(I) luarunner.InitLua()
calc.SetInt(luarunner)
} }
if len(flag.Args()) > 1 { if len(flag.Args()) > 1 {

44
rpn.go
View File

@@ -2,16 +2,19 @@ package main
var manpage = ` var manpage = `
NAME NAME
rpn - Reverse Polish Notation Calculator for the commandline rpn - Programmable command-line calculator using reverse polish notation
SYNOPSIS SYNOPSIS
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-v, --version show version -s, --stack show last 5 items of the stack (off by default)
-h, --help show help -i --intermediate print intermediate results
-m, --manual show manual
-v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -161,6 +164,11 @@ DESCRIPTION
help|? show this message help|? show this message
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack
Refer to https://pkg.go.dev/math for details about those functions. Refer to https://pkg.go.dev/math for details about those functions.
INTERACTIVE REPL INTERACTIVE REPL
@@ -193,6 +201,23 @@ INTERACTIVE REPL
ctrl-r ctrl-r
Search through history. Search through history.
COMMENTS
Lines starting with "#" are being ignored as comments. You can also
append comments to rpn input, e.g.:
# a comment
123 # another comment
In this case only 123 will be added to the stack.
VARIABLES
You can register the last item of the stack into a variable. Variable
names must be all caps. Use the ">NAME" command to put a value into
variable "NAME". Use "<NAME" to retrieve the value of variable "NAME"
and put it onto the stack.
The command vars can be used to get a list of all variables.
EXTENDING RPN USING LUA EXTENDING RPN USING LUA
You can use a lua script with lua functions to extend the calculator. By You can use a lua script with lua functions to extend the calculator. By
default the tool looks for "~/.rpn.lua". You can also specify a script default the tool looks for "~/.rpn.lua". You can also specify a script
@@ -219,8 +244,13 @@ EXTENDING RPN USING LUA
* function name * function name
* number of arguments expected (1,2 or -1 allowed), -1 means batch * number of arguments expected (see below)
mode.
Number of expected arguments can be:
- 0: expect 1 argument but do NOT modify the stack
- 1-n: do a singular calculation
- -1: batch mode work with all numbers on the stack
* help text * help text

36
rpn.pod
View File

@@ -1,16 +1,19 @@
=head1 NAME =head1 NAME
rpn - Reverse Polish Notation Calculator for the commandline rpn - Programmable command-line calculator using reverse polish notation
=head1 SYNOPSIS =head1 SYNOPSIS
Usage: rpn [-bdvh] [<operator>] Usage: rpn [-bdvh] [<operator>]
Options: Options:
-b, --batchmode enable batch mode -b, --batchmode enable batch mode
-d, --debug enable debug mode -d, --debug enable debug mode
-v, --version show version -s, --stack show last 5 items of the stack (off by default)
-h, --help show help -i --intermediate print intermediate results
-m, --manual show manual
-v, --version show version
-h, --help show help
When <operator> is given, batch mode ist automatically enabled. Use When <operator> is given, batch mode ist automatically enabled. Use
this only when working with stdin. E.g.: echo "2 3 4 5" | rpn + this only when working with stdin. E.g.: echo "2 3 4 5" | rpn +
@@ -169,6 +172,11 @@ Commands:
quit|exit|c-d|c-c exit program quit|exit|c-d|c-c exit program
Register variables:
>NAME Put last stack element into variable NAME
<NAME Retrieve variable NAME and put onto stack
Refer to https://pkg.go.dev/math for details about those functions. Refer to https://pkg.go.dev/math for details about those functions.
=head1 INTERACTIVE REPL =head1 INTERACTIVE REPL
@@ -223,6 +231,15 @@ append comments to rpn input, e.g.:
In this case only 123 will be added to the stack. In this case only 123 will be added to the stack.
=head1 VARIABLES
You can register the last item of the stack into a variable. Variable
names must be all caps. Use the ">NAME" command to put a value into
variable "NAME". Use "<NAME" to retrieve the value of variable "NAME"
and put it onto the stack.
The command B<vars> can be used to get a list of all variables.
=head1 EXTENDING RPN USING LUA =head1 EXTENDING RPN USING LUA
You can use a lua script with lua functions to extend the You can use a lua script with lua functions to extend the
@@ -256,8 +273,13 @@ function name
=item * =item *
number of arguments expected (1,2 or -1 allowed), -1 means batch number of arguments expected (see below)
mode.
Number of expected arguments can be:
- 0: expect 1 argument but do NOT modify the stack
- 1-n: do a singular calculation
- -1: batch mode work with all numbers on the stack
=item * =item *

View File

@@ -195,14 +195,24 @@ func (s *Stack) Backup() {
// make a backup, because the elements in list.List{} are pointers // make a backup, because the elements in list.List{} are pointers
// and lead to unexpected results. The methid here works reliably // and lead to unexpected results. The methid here works reliably
// at least. // at least.
s.mutex.Lock()
defer s.mutex.Unlock()
s.Debug(fmt.Sprintf("backing up %d items from rev %d",
s.linklist.Len(), s.rev))
s.backup = list.List{} s.backup = list.List{}
for e := s.linklist.Front(); e != nil; e = e.Next() { for e := s.linklist.Front(); e != nil; e = e.Next() {
s.backup.PushBack(e.Value) s.backup.PushBack(e.Value.(float64))
} }
s.backuprev = s.rev s.backuprev = s.rev
} }
func (s *Stack) Restore() { func (s *Stack) Restore() {
s.mutex.Lock()
defer s.mutex.Unlock()
if s.rev == 0 { if s.rev == 0 {
fmt.Println("error: stack is empty.") fmt.Println("error: stack is empty.")
return return
@@ -211,15 +221,26 @@ func (s *Stack) Restore() {
s.Debug(fmt.Sprintf("restoring stack to revision %d", s.backuprev)) s.Debug(fmt.Sprintf("restoring stack to revision %d", s.backuprev))
s.rev = s.backuprev s.rev = s.backuprev
s.linklist = s.backup
s.linklist = list.List{}
for e := s.backup.Front(); e != nil; e = e.Next() {
s.linklist.PushBack(e.Value.(float64))
}
} }
func (s *Stack) Reverse() { func (s *Stack) Reverse() {
newstack := list.List{} s.mutex.Lock()
defer s.mutex.Unlock()
items := []float64{}
for e := s.linklist.Front(); e != nil; e = e.Next() { for e := s.linklist.Front(); e != nil; e = e.Next() {
newstack.PushFront(e.Value) tail := s.linklist.Back()
items = append(items, tail.Value.(float64))
s.linklist.Remove(tail)
} }
s.linklist = newstack for i := len(items) - 1; i >= 0; i-- {
s.linklist.PushFront(items[i])
}
} }

View File

@@ -1,17 +0,0 @@
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()
register("add", 2, "addition")
register("test", 1, "test")
register("parallelresistance", 2, "parallel resistance")
end