10 Commits

Author SHA1 Message Date
2b79f3f9ca updated docs 2023-11-05 17:39:15 +01:00
11753fc984 added the remainder of the go math functions 2023-11-05 17:38:57 +01:00
5adc3b30ab bump version 2023-11-05 13:01:50 +01:00
56a4000b67 fixed debug output 2023-11-05 13:00:26 +01:00
c4c60651d1 moved all math functions and operators to funcalls
So now if you want to add a new operator or math function all you have
to do is to add it to func.go. Conpletion will be generated from it.
2023-11-05 12:55:59 +01:00
5189d351c6 save more typing with handy types 2023-11-04 19:55:45 +01:00
3649408d17 made Shift() and Last() use flexible number of items to work on 2023-11-04 19:41:28 +01:00
a0c0a27a35 add Funcalls to Calc 2023-11-04 19:41:11 +01:00
052a9ae4d0 added more generic function management 2023-11-04 19:40:53 +01:00
48154ce6b1 fix man link, fix release generator 2023-11-02 19:36:42 +01:00
9 changed files with 697 additions and 229 deletions

View File

@@ -76,4 +76,4 @@ buildall:
./mkrel.sh $(tool) $(VERSION)
release: buildall
gh release create $(VERSION) --generate-notes releases/*
gh release create v$(VERSION) --generate-notes releases/*

View File

@@ -254,9 +254,7 @@ hesitate to ask me about it, I'll add it.
The documentation is provided as a unix man-page. It will be
automatically installed if you install from source. However, you can
read the man-page online:
https://github.com/TLINDEN/rpnc/blob/main/rpn.pod
[read the man-page online](https://github.com/TLINDEN/rpnc/blob/master/rpn.pod)
Or if you cloned the repository you can read it this way (perl needs
to be installed though): `perldoc rpn.pod`.

329
calc.go
View File

@@ -30,19 +30,19 @@ import (
)
type Calc struct {
debug bool
batch bool
stdin bool
stack *Stack
history []string
completer readline.AutoCompleter
interpreter *Interpreter
Operators *regexp.Regexp
Space *regexp.Regexp
Constants []string
MathFunctions []string
BatchFunctions []string
LuaFunctions []string
debug bool
batch bool
stdin bool
stack *Stack
history []string
completer readline.AutoCompleter
interpreter *Interpreter
Space *regexp.Regexp
Constants []string
LuaFunctions []string
Funcalls Funcalls
BatchFuncalls Funcalls
}
// help for lua functions will be added dynamically
@@ -56,31 +56,32 @@ history display calculation history
help|? show this message
quit|exit|c-d|c-c exit program
Available operators:
basic operators: + - x /
Operators:
basic operators: + - x * / ^ (* is an alias of x)
Available math functions:
sqrt square root
mod remainder of division (alias: remainder)
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 functions:
% percent
%- substract percent
%+ add percent
Math operators:
^ power`
Math functions (see https://pkg.go.dev/math):
mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
y1 copysign dim hypot
Batch functions:
sum sum of all values (alias: +)
max max of all values
min min of all values
mean mean of all values (alias: avg)
median median of all values`
// commands, constants and operators, defined here to feed completion
// and our mode switch in Eval() dynamically
const (
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit`
Operators string = `+ - * x / ^ % %- %+`
MathFunctions string = `sqrt remainder`
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
BatchFunctions string = `median avg mean max min`
Commands string = `dump reverse debug undebug clear batch shift undo help history manual exit quit`
Constants string = `Pi Phi Sqrt2 SqrtE SqrtPi SqrtPhi Ln2 Log2E Ln10 Log10E`
)
// That way we can add custom functions to completion
@@ -93,41 +94,45 @@ func GetCompleteCustomFunctions() func(string) []string {
}
completions = append(completions, strings.Split(Commands, " ")...)
completions = append(completions, strings.Split(Operators, " ")...)
completions = append(completions, strings.Split(MathFunctions, " ")...)
completions = append(completions, strings.Split(Constants, " ")...)
return completions
}
}
func (c *Calc) GetCompleteCustomFuncalls() func(string) []string {
return func(line string) []string {
completions := []string{}
for function := range c.Funcalls {
completions = append(completions, function)
}
for function := range c.BatchFuncalls {
completions = append(completions, function)
}
return completions
}
}
func NewCalc() *Calc {
c := Calc{stack: NewStack(), debug: false}
c.Funcalls = DefineFunctions()
c.BatchFuncalls = DefineBatchFunctions()
c.completer = readline.NewPrefixCompleter(
// custom lua functions
readline.PcItemDynamic(GetCompleteCustomFunctions()),
readline.PcItemDynamic(c.GetCompleteCustomFuncalls()),
)
// pre-calculate mode switching regexes
reg := `^[`
for _, op := range strings.Split(Operators, " ") {
switch op {
case "x":
reg += op
default:
reg += `\` + op
}
}
reg += `]$`
c.Operators = regexp.MustCompile(reg)
c.Space = regexp.MustCompile(`\s+`)
// pre-calculate mode switching arrays
c.Constants = strings.Split(Constants, " ")
c.MathFunctions = strings.Split(MathFunctions, " ")
c.BatchFunctions = strings.Split(BatchFunctions, " ")
for name := range LuaFuncs {
c.LuaFunctions = append(c.LuaFunctions, name)
@@ -187,11 +192,19 @@ func (c *Calc) Eval(line string) {
c.stack.Backup()
c.stack.Push(num)
} else {
if c.Operators.MatchString(item) {
// simple ops like + or x
c.simple(item[0])
continue
}
/*
if contains(c.MathFunctions, item) {
// go builtin math function, if implemented
c.mathfunc(item)
continue
}
if contains(c.BatchFunctions, item) {
// math functions only supported in batch mode like max or mean
c.batchfunc(item)
continue
}
*/
if contains(c.Constants, item) {
// put the constant onto the stack
@@ -200,16 +213,29 @@ func (c *Calc) Eval(line string) {
continue
}
if contains(c.MathFunctions, item) {
// go builtin math function, if implemented
c.mathfunc(item)
if _, ok := c.Funcalls[item]; ok {
if err := c.DoFuncall(item); err != nil {
fmt.Println(err)
} else {
c.Result()
}
continue
}
if contains(c.BatchFunctions, item) {
// math functions only supported in batch mode like max or mean
c.batchfunc(item)
continue
if c.batch {
if _, ok := c.BatchFuncalls[item]; ok {
if err := c.DoFuncall(item); err != nil {
fmt.Println(err)
} else {
c.Result()
}
continue
}
} else {
if _, ok := c.BatchFuncalls[item]; ok {
fmt.Println("only supported in batch mode")
continue
}
}
if contains(c.LuaFunctions, item) {
@@ -264,6 +290,69 @@ func (c *Calc) Eval(line string) {
}
}
// Execute a math function, check if it is defined just in case
func (c *Calc) DoFuncall(funcname string) error {
var function *Funcall
if c.batch {
function = c.BatchFuncalls[funcname]
} else {
function = c.Funcalls[funcname]
}
if function == nil {
panic("function not defined but in completion list")
}
var args Numbers
batch := false
if function.Expectargs == -1 {
// batch mode, but always < stack len, so check first
args = c.stack.All()
batch = true
} else {
// this is way better behavior than just using 0 in place of
// non-existing stack items
if c.stack.Len() < function.Expectargs {
return errors.New("stack doesn't provide enough arguments")
}
args = c.stack.Last(function.Expectargs)
}
c.Debug(fmt.Sprintf("calling %s with args: %v", funcname, args))
// the actual lambda call, so to say. We provide a slice of
// the requested size, fetched from the stack (but not popped
// yet!)
R := function.Func(args)
if R.Err != nil {
// leave the stack untouched in case of any error
return R.Err
}
if batch {
// get rid of stack
c.stack.Clear()
} else {
// remove operands
c.stack.Shift(function.Expectargs)
}
// save result
c.stack.Push(R.Res)
// thanks a lot
c.SetHistory(funcname, args, R.Res)
return nil
}
// we need to add a history entry for each operation
func (c *Calc) SetHistory(op string, args Numbers, res float64) {
c.History("%s %s -> %f", list2str(args), op, res)
}
// just a textual representation of math operations, viewable with the
// history command
func (c *Calc) History(format string, args ...any) {
@@ -276,9 +365,9 @@ func (c *Calc) Result() float64 {
fmt.Print("= ")
}
fmt.Println(c.stack.Last())
fmt.Println(c.stack.Last()[0])
return c.stack.Last()
return c.stack.Last()[0]
}
func (c *Calc) Debug(msg string) {
@@ -331,122 +420,6 @@ func (c *Calc) simple(op byte) {
c.Result()
}
// calc using go math lib functions
func (c *Calc) mathfunc(funcname string) {
c.stack.Backup()
for c.stack.Len() > 0 {
var x float64
switch funcname {
case "sqrt":
a := c.stack.Pop()
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()
x = math.Remainder(a, b)
c.History("remainderf(%f / %f) = %f", a, b, x)
case "%":
b := c.stack.Pop()
a := c.stack.Pop()
x = (a / 100) * b
c.History("%f percent of %f = %f", b, a, x)
case "%-":
b := c.stack.Pop()
a := c.stack.Pop()
x = a - ((a / 100) * b)
c.History("%f minus %f percent of %f = %f", a, b, a, x)
case "%+":
b := c.stack.Pop()
a := c.stack.Pop()
x = a + ((a / 100) * b)
c.History("%f plus %f percent of %f = %f", a, b, a, x)
}
c.stack.Push(x)
if !c.batch {
break
}
}
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")
}
c.stack.Backup()
var x float64
count := c.stack.Len()
switch funcname {
case "median":
all := []float64{}
for c.stack.Len() > 0 {
all = append(all, c.stack.Pop())
}
middle := count / 2
x = all[middle]
c.History("median(all)")
case "mean":
fallthrough // alias
case "avg":
var sum float64
for c.stack.Len() > 0 {
sum += c.stack.Pop()
}
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)
_ = c.Result()
}
func (c *Calc) luafunc(funcname string) {
// called from calc loop
var x float64
@@ -454,9 +427,9 @@ func (c *Calc) luafunc(funcname string) {
switch c.interpreter.FuncNumArgs(funcname) {
case 1:
x, err = c.interpreter.CallLuaFunc(funcname, []float64{c.stack.Last()})
x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last())
case 2:
x, err = c.interpreter.CallLuaFunc(funcname, c.stack.LastTwo())
x, err = c.interpreter.CallLuaFunc(funcname, c.stack.Last(2))
case -1:
x, err = c.interpreter.CallLuaFunc(funcname, c.stack.All())
default:

460
funcs.go Normal file
View File

@@ -0,0 +1,460 @@
/*
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"
"math"
)
type R struct {
Res float64
Err error
}
type Numbers []float64
type Function func(Numbers) R
// every function we are able to call must be of type Funcall, which
// needs to specify how many numbers it expects and the actual go
// function to be executed.
//
// The function has to take a float slice as argument and return a
// float and an error object. The float slice is guaranteed to have
// the expected number of arguments.
//
// However, Lua functions are handled differently, see interpreter.go.
type Funcall struct {
Expectargs int // -1 means batch only mode, you'll get the whole stack as arg
Func Function
}
// will hold all hard coded functions and operators
type Funcalls map[string]*Funcall
// convenience function, create a new Funcall object, if expectargs
// was not specified, 2 is assumed.
func NewFuncall(function Function, expectargs ...int) *Funcall {
expect := 2
if len(expectargs) > 0 {
expect = expectargs[0]
}
return &Funcall{
Expectargs: expect,
Func: function,
}
}
// Convenience function, create new result
func NewR(n float64, e error) R {
return R{Res: n, Err: e}
}
// the actual functions, called once during initialization.
func DefineFunctions() Funcalls {
f := map[string]*Funcall{
// simple operators, they all expect 2 args
"+": NewFuncall(
func(arg Numbers) R {
return NewR(arg[0]+arg[1], nil)
},
),
"-": NewFuncall(
func(arg Numbers) R {
return NewR(arg[0]-arg[1], nil)
},
),
"x": NewFuncall(
func(arg Numbers) R {
return NewR(arg[0]*arg[1], nil)
},
),
"/": NewFuncall(
func(arg Numbers) R {
if arg[1] == 0 {
return NewR(0, errors.New("division by null"))
}
return NewR(arg[0]/arg[1], nil)
},
),
"^": NewFuncall(
func(arg Numbers) R {
return NewR(math.Pow(arg[0], arg[1]), nil)
},
),
"%": NewFuncall(
func(arg Numbers) R {
return NewR((arg[0]/100)*arg[1], nil)
},
),
"%-": NewFuncall(
func(arg Numbers) R {
return NewR(arg[0]-((arg[0]/100)*arg[1]), nil)
},
),
"%+": NewFuncall(
func(arg Numbers) R {
return NewR(arg[0]+((arg[0]/100)*arg[1]), nil)
},
),
"mod": NewFuncall(
func(arg Numbers) R {
return NewR(math.Remainder(arg[0], arg[1]), nil)
},
),
"sqrt": NewFuncall(
func(arg Numbers) R {
return NewR(math.Sqrt(arg[0]), nil)
},
1),
"abs": NewFuncall(
func(arg Numbers) R {
return NewR(math.Abs(arg[0]), nil)
},
1),
"acos": NewFuncall(
func(arg Numbers) R {
return NewR(math.Acos(arg[0]), nil)
},
1),
"acosh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Acosh(arg[0]), nil)
},
1),
"asin": NewFuncall(
func(arg Numbers) R {
return NewR(math.Asin(arg[0]), nil)
},
1),
"asinh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Asinh(arg[0]), nil)
},
1),
"atan": NewFuncall(
func(arg Numbers) R {
return NewR(math.Atan(arg[0]), nil)
},
1),
"atan2": NewFuncall(
func(arg Numbers) R {
return NewR(math.Atan2(arg[0], arg[1]), nil)
},
2),
"atanh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Atanh(arg[0]), nil)
},
1),
"cbrt": NewFuncall(
func(arg Numbers) R {
return NewR(math.Cbrt(arg[0]), nil)
},
1),
"ceil": NewFuncall(
func(arg Numbers) R {
return NewR(math.Ceil(arg[0]), nil)
},
1),
"cos": NewFuncall(
func(arg Numbers) R {
return NewR(math.Cos(arg[0]), nil)
},
1),
"cosh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Cosh(arg[0]), nil)
},
1),
"erf": NewFuncall(
func(arg Numbers) R {
return NewR(math.Erf(arg[0]), nil)
},
1),
"erfc": NewFuncall(
func(arg Numbers) R {
return NewR(math.Erfc(arg[0]), nil)
},
1),
"erfcinv": NewFuncall(
func(arg Numbers) R {
return NewR(math.Erfcinv(arg[0]), nil)
},
1),
"erfinv": NewFuncall(
func(arg Numbers) R {
return NewR(math.Erfinv(arg[0]), nil)
},
1),
"exp": NewFuncall(
func(arg Numbers) R {
return NewR(math.Exp(arg[0]), nil)
},
1),
"exp2": NewFuncall(
func(arg Numbers) R {
return NewR(math.Exp2(arg[0]), nil)
},
1),
"expm1": NewFuncall(
func(arg Numbers) R {
return NewR(math.Expm1(arg[0]), nil)
},
1),
"floor": NewFuncall(
func(arg Numbers) R {
return NewR(math.Floor(arg[0]), nil)
},
1),
"gamma": NewFuncall(
func(arg Numbers) R {
return NewR(math.Gamma(arg[0]), nil)
},
1),
"ilogb": NewFuncall(
func(arg Numbers) R {
return NewR(float64(math.Ilogb(arg[0])), nil)
},
1),
"j0": NewFuncall(
func(arg Numbers) R {
return NewR(math.J0(arg[0]), nil)
},
1),
"j1": NewFuncall(
func(arg Numbers) R {
return NewR(math.J1(arg[0]), nil)
},
1),
"log": NewFuncall(
func(arg Numbers) R {
return NewR(math.Log(arg[0]), nil)
},
1),
"log10": NewFuncall(
func(arg Numbers) R {
return NewR(math.Log10(arg[0]), nil)
},
1),
"log1p": NewFuncall(
func(arg Numbers) R {
return NewR(math.Log1p(arg[0]), nil)
},
1),
"log2": NewFuncall(
func(arg Numbers) R {
return NewR(math.Log2(arg[0]), nil)
},
1),
"logb": NewFuncall(
func(arg Numbers) R {
return NewR(math.Logb(arg[0]), nil)
},
1),
"pow": NewFuncall(
func(arg Numbers) R {
return NewR(math.Pow(arg[0], arg[1]), nil)
},
2),
"round": NewFuncall(
func(arg Numbers) R {
return NewR(math.Round(arg[0]), nil)
},
1),
"roundtoeven": NewFuncall(
func(arg Numbers) R {
return NewR(math.RoundToEven(arg[0]), nil)
},
1),
"sin": NewFuncall(
func(arg Numbers) R {
return NewR(math.Sin(arg[0]), nil)
},
1),
"sinh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Sinh(arg[0]), nil)
},
1),
"tan": NewFuncall(
func(arg Numbers) R {
return NewR(math.Tan(arg[0]), nil)
},
1),
"tanh": NewFuncall(
func(arg Numbers) R {
return NewR(math.Tanh(arg[0]), nil)
},
1),
"trunc": NewFuncall(
func(arg Numbers) R {
return NewR(math.Trunc(arg[0]), nil)
},
1),
"y0": NewFuncall(
func(arg Numbers) R {
return NewR(math.Y0(arg[0]), nil)
},
1),
"y1": NewFuncall(
func(arg Numbers) R {
return NewR(math.Y1(arg[0]), nil)
},
1),
"copysign": NewFuncall(
func(arg Numbers) R {
return NewR(math.Copysign(arg[0], arg[1]), nil)
},
2),
"dim": NewFuncall(
func(arg Numbers) R {
return NewR(math.Dim(arg[0], arg[1]), nil)
},
2),
"hypot": NewFuncall(
func(arg Numbers) R {
return NewR(math.Hypot(arg[0], arg[1]), nil)
},
2),
}
// aliases
f["*"] = f["x"]
f["remainder"] = f["mod"]
return f
}
func DefineBatchFunctions() Funcalls {
f := map[string]*Funcall{
"median": NewFuncall(
func(args Numbers) R {
middle := len(args) / 2
return NewR(args[middle], nil)
},
-1),
"mean": NewFuncall(
func(args Numbers) R {
var sum float64
for _, item := range args {
sum += item
}
return NewR(sum/float64(len(args)), nil)
},
-1),
"min": NewFuncall(
func(args Numbers) R {
var min float64
min, args = args[0], args[1:]
for _, item := range args {
if item < min {
min = item
}
}
return NewR(min, nil)
},
-1),
"max": NewFuncall(
func(args Numbers) R {
var max float64
max, args = args[0], args[1:]
for _, item := range args {
if item > max {
max = item
}
}
return NewR(max, nil)
},
-1),
"sum": NewFuncall(
func(args Numbers) R {
var sum float64
for _, item := range args {
sum += item
}
return NewR(sum, nil)
},
-1),
}
// aliases
f["+"] = f["sum"]
f["avg"] = f["mean"]
return f
}

View File

@@ -30,7 +30,7 @@ import (
lua "github.com/yuin/gopher-lua"
)
const VERSION string = "2.0.0"
const VERSION string = "2.0.1"
const Usage string = `This is rpn, a reverse polish notation calculator cli.

34
rpn.go
View File

@@ -116,20 +116,36 @@ DESCRIPTION
You can use the shift command to remove the last number from the stack.
BUILTIN OPERATORS AND FUNCTIONS
Basic operators: + - x /
Basic operators:
Math functions:
+ add
- substract
/ divide
x multiply (alias: *)
^ power
Percent functions:
sqrt square root
mod remainder of division (alias: remainder)
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
^ power
Batch functions:
sum sum of all values (alias: +)
max max of all values
min min of all values
mean mean of all values (alias: avg)
median median of all values
Math functions:
mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
y1 copysign dim hypot
Refer to https://pkg.go.dev/math for details about those functions.
EXTENDING RPN USING LUA
You can use a lua script with lua functions to extend the calculator. By

34
rpn.pod
View File

@@ -123,20 +123,36 @@ stack.
=head2 BUILTIN OPERATORS AND FUNCTIONS
Basic operators: + - x /
Basic operators:
Math functions:
+ add
- substract
/ divide
x multiply (alias: *)
^ power
Percent functions:
sqrt square root
mod remainder of division (alias: remainder)
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
^ power
Batch functions:
sum sum of all values (alias: +)
max max of all values
min min of all values
mean mean of all values (alias: avg)
median median of all values
Math functions:
mod sqrt abs acos acosh asin asinh atan atan2 atanh cbrt ceil cos cosh
erf erfc erfcinv erfinv exp exp2 expm1 floor gamma ilogb j0 j1 log
log10 log1p log2 logb pow round roundtoeven sin sinh tan tanh trunc y0
y1 copysign dim hypot
Refer to https://pkg.go.dev/math for details about those functions.
=head1 EXTENDING RPN USING LUA

View File

@@ -94,46 +94,43 @@ func (s *Stack) Pop() float64 {
}
// just remove the last item, do not return it
func (s *Stack) Shift() {
func (s *Stack) Shift(num ...int) {
s.mutex.Lock()
defer s.mutex.Unlock()
count := 1
if len(num) > 0 {
count = num[0]
}
if s.linklist.Len() == 0 {
return
}
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.linklist.Back() == nil {
return 0
for i := 0; i < count; i++ {
tail := s.linklist.Back()
s.linklist.Remove(tail)
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
}
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 {
// Return the last num items from the stack w/o modifying it.
func (s *Stack) Last(num ...int) []float64 {
items := []float64{}
if s.linklist.Back() == nil {
return items
i := s.Len()
count := 1
if len(num) > 0 {
count = num[0]
}
last := s.Pop()
items = append(items, last)
items = append(items, s.linklist.Back().Value.(float64))
for e := s.linklist.Front(); e != nil; e = e.Next() {
if i <= count {
items = append(items, e.Value.(float64))
}
i--
}
s.Push(last)
return items
}
@@ -164,7 +161,7 @@ func (s *Stack) Dump() {
}
func (s *Stack) Clear() {
s.Debug("DEBUG: clearing stack")
s.Debug("clearing stack")
s.linklist = list.List{}
}

10
util.go
View File

@@ -17,7 +17,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import "math"
import (
"fmt"
"math"
"strings"
)
// find an item in a list
func contains(s []string, e string) bool {
@@ -55,3 +59,7 @@ func const2num(name string) float64 {
return 0
}
}
func list2str(list Numbers) string {
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(list)), " "), "[]")
}