mirror of
https://codeberg.org/scip/rpnc.git
synced 2025-12-18 13:01:08 +01:00
code re-organization, added more commands and more flexible lua api
This commit is contained in:
225
go/calc.go
225
go/calc.go
@@ -1,14 +1,32 @@
|
|||||||
|
/*
|
||||||
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
lua "github.com/yuin/gopher-lua"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Calc struct {
|
type Calc struct {
|
||||||
@@ -18,25 +36,38 @@ type Calc struct {
|
|||||||
stack *Stack
|
stack *Stack
|
||||||
history []string
|
history []string
|
||||||
completer readline.AutoCompleter
|
completer readline.AutoCompleter
|
||||||
L *lua.LState
|
interpreter *Interpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// help for lua functions will be added dynamically
|
||||||
const Help string = `Available commands:
|
const Help string = `Available commands:
|
||||||
batch enable batch mode
|
batch toggle batch mode
|
||||||
debug enable debug output
|
debug toggle debug output
|
||||||
dump display the stack contents
|
dump display the stack contents
|
||||||
clear clear the whole stack
|
clear clear the whole stack
|
||||||
shift remove the last element of the stack
|
shift remove the last element of the stack
|
||||||
history display calculation history
|
history display calculation history
|
||||||
help show this message
|
help|? show this message
|
||||||
|
quit|exit|c-d|c-c exit program
|
||||||
|
|
||||||
Available operators:
|
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:
|
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 {
|
func GetCompleteCustomFunctions() func(string) []string {
|
||||||
return func(line string) []string {
|
return func(line string) []string {
|
||||||
funcs := []string{}
|
funcs := []string{}
|
||||||
@@ -58,12 +89,15 @@ func NewCalc() *Calc {
|
|||||||
readline.PcItem("dump"),
|
readline.PcItem("dump"),
|
||||||
readline.PcItem("reverse"),
|
readline.PcItem("reverse"),
|
||||||
readline.PcItem("debug"),
|
readline.PcItem("debug"),
|
||||||
|
readline.PcItem("undebug"),
|
||||||
readline.PcItem("clear"),
|
readline.PcItem("clear"),
|
||||||
readline.PcItem("batch"),
|
readline.PcItem("batch"),
|
||||||
readline.PcItem("shift"),
|
readline.PcItem("shift"),
|
||||||
readline.PcItem("undo"),
|
readline.PcItem("undo"),
|
||||||
readline.PcItem("help"),
|
readline.PcItem("help"),
|
||||||
readline.PcItem("history"),
|
readline.PcItem("history"),
|
||||||
|
readline.PcItem("exit"),
|
||||||
|
readline.PcItem("quit"),
|
||||||
|
|
||||||
// ops
|
// ops
|
||||||
readline.PcItem("+"),
|
readline.PcItem("+"),
|
||||||
@@ -91,41 +125,69 @@ func NewCalc() *Calc {
|
|||||||
readline.PcItem("sqrt"),
|
readline.PcItem("sqrt"),
|
||||||
readline.PcItem("remainder"),
|
readline.PcItem("remainder"),
|
||||||
readline.PcItem("avg"),
|
readline.PcItem("avg"),
|
||||||
|
readline.PcItem("mean"), // alias for avg
|
||||||
|
readline.PcItem("min"),
|
||||||
|
readline.PcItem("max"),
|
||||||
readline.PcItem("median"),
|
readline.PcItem("median"),
|
||||||
)
|
)
|
||||||
|
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setup the interpreter, called from main()
|
||||||
|
func (c *Calc) SetInt(I *Interpreter) {
|
||||||
|
c.interpreter = I
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Calc) ToggleDebug() {
|
func (c *Calc) ToggleDebug() {
|
||||||
c.debug = !c.debug
|
c.debug = !c.debug
|
||||||
c.stack.ToggleDebug()
|
c.stack.ToggleDebug()
|
||||||
|
fmt.Printf("debugging set to %t\n", c.debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Calc) ToggleBatch() {
|
func (c *Calc) ToggleBatch() {
|
||||||
c.batch = !c.batch
|
c.batch = !c.batch
|
||||||
|
fmt.Printf("batchmode set to %t\n", c.batch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Calc) ToggleStdin() {
|
func (c *Calc) ToggleStdin() {
|
||||||
c.stdin = !c.stdin
|
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) {
|
func (c *Calc) Eval(line string) {
|
||||||
line = strings.TrimSpace(line)
|
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 == "" {
|
if line == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for luafunc := range LuaFuncs {
|
space := regexp.MustCompile(`\s+`)
|
||||||
luafuncs = append(luafuncs, luafunc)
|
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) {
|
for _, item := range space.Split(line, -1) {
|
||||||
@@ -135,39 +197,53 @@ func (c *Calc) Eval(line string) {
|
|||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
c.stack.Push(num)
|
c.stack.Push(num)
|
||||||
} else {
|
} else {
|
||||||
if simple.MatchString(line) {
|
if simple.MatchString(item) {
|
||||||
|
// simple ops like + or x
|
||||||
c.simple(item[0])
|
c.simple(item[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if contains(constants, item) {
|
if contains(constants, item) {
|
||||||
|
// put the constant onto the stack
|
||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
c.stack.Push(const2num(item))
|
c.stack.Push(const2num(item))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if contains(functions, item) {
|
if contains(functions, item) {
|
||||||
|
// go builtin math function, if implemented
|
||||||
c.mathfunc(item)
|
c.mathfunc(item)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if contains(batch, item) {
|
if contains(batch, item) {
|
||||||
|
// math functions only supported in batch mode like max or mean
|
||||||
c.batchfunc(item)
|
c.batchfunc(item)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if contains(luafuncs, item) {
|
if contains(luafuncs, item) {
|
||||||
|
// user provided custom lua functions
|
||||||
c.luafunc(item)
|
c.luafunc(item)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// management commands
|
||||||
switch item {
|
switch item {
|
||||||
|
case "?":
|
||||||
|
fallthrough
|
||||||
case "help":
|
case "help":
|
||||||
fmt.Println(Help)
|
fmt.Println(Help)
|
||||||
|
fmt.Println("Lua functions:")
|
||||||
|
for name, function := range LuaFuncs {
|
||||||
|
fmt.Printf("%-20s %s\n", name, function.help)
|
||||||
|
}
|
||||||
case "dump":
|
case "dump":
|
||||||
c.stack.Dump()
|
c.stack.Dump()
|
||||||
case "debug":
|
case "debug":
|
||||||
c.ToggleDebug()
|
c.ToggleDebug()
|
||||||
|
case "undebug":
|
||||||
|
c.debug = false
|
||||||
case "batch":
|
case "batch":
|
||||||
c.ToggleBatch()
|
c.ToggleBatch()
|
||||||
case "clear":
|
case "clear":
|
||||||
@@ -185,8 +261,10 @@ func (c *Calc) Eval(line string) {
|
|||||||
for _, entry := range c.history {
|
for _, entry := range c.history {
|
||||||
fmt.Println(entry)
|
fmt.Println(entry)
|
||||||
}
|
}
|
||||||
case "^":
|
case "exit":
|
||||||
c.exp()
|
fallthrough
|
||||||
|
case "quit":
|
||||||
|
os.Exit(0)
|
||||||
default:
|
default:
|
||||||
fmt.Println("unknown command or operator!")
|
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) {
|
func (c *Calc) History(format string, args ...any) {
|
||||||
c.history = append(c.history, fmt.Sprintf(format, args...))
|
c.history = append(c.history, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print the result
|
||||||
func (c *Calc) Result() float64 {
|
func (c *Calc) Result() float64 {
|
||||||
if !c.stdin {
|
if !c.stdin {
|
||||||
fmt.Print("= ")
|
fmt.Print("= ")
|
||||||
@@ -208,6 +289,13 @@ func (c *Calc) Result() float64 {
|
|||||||
return c.stack.Last()
|
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) {
|
func (c *Calc) simple(op byte) {
|
||||||
c.stack.Backup()
|
c.stack.Backup()
|
||||||
|
|
||||||
@@ -216,15 +304,15 @@ func (c *Calc) simple(op byte) {
|
|||||||
a := c.stack.Pop()
|
a := c.stack.Pop()
|
||||||
var x float64
|
var x float64
|
||||||
|
|
||||||
if c.debug {
|
c.Debug(fmt.Sprintf("evaluating: %.2f %c %.2f", a, op, b))
|
||||||
fmt.Printf("DEBUG: evaluating: %.2f %c %.2f\n", a, op, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case '+':
|
case '+':
|
||||||
x = a + b
|
x = a + b
|
||||||
case '-':
|
case '-':
|
||||||
x = a - b
|
x = a - b
|
||||||
|
case 'x':
|
||||||
|
fallthrough // alias for *
|
||||||
case '*':
|
case '*':
|
||||||
x = a * b
|
x = a * b
|
||||||
case '/':
|
case '/':
|
||||||
@@ -233,6 +321,8 @@ func (c *Calc) simple(op byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
x = a / b
|
x = a / b
|
||||||
|
case '^':
|
||||||
|
x = math.Pow(a, b)
|
||||||
default:
|
default:
|
||||||
panic("invalid operator!")
|
panic("invalid operator!")
|
||||||
}
|
}
|
||||||
@@ -246,32 +336,11 @@ func (c *Calc) simple(op byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = c.Result()
|
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calc using go math lib functions
|
||||||
func (c *Calc) mathfunc(funcname string) {
|
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()
|
c.stack.Backup()
|
||||||
|
|
||||||
for c.stack.Len() > 0 {
|
for c.stack.Len() > 0 {
|
||||||
@@ -283,6 +352,8 @@ func (c *Calc) mathfunc(funcname string) {
|
|||||||
x = math.Sqrt(a)
|
x = math.Sqrt(a)
|
||||||
c.History("sqrt(%f) = %f", a, x)
|
c.History("sqrt(%f) = %f", a, x)
|
||||||
|
|
||||||
|
case "mod":
|
||||||
|
fallthrough // alias
|
||||||
case "remainder":
|
case "remainder":
|
||||||
b := c.stack.Pop()
|
b := c.stack.Pop()
|
||||||
a := 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) {
|
func (c *Calc) batchfunc(funcname string) {
|
||||||
if !c.batch {
|
if !c.batch {
|
||||||
fmt.Println("error: only available in batch mode")
|
fmt.Println("error: only available in batch mode")
|
||||||
@@ -344,6 +416,8 @@ func (c *Calc) batchfunc(funcname string) {
|
|||||||
x = all[middle]
|
x = all[middle]
|
||||||
c.History("median(all)")
|
c.History("median(all)")
|
||||||
|
|
||||||
|
case "mean":
|
||||||
|
fallthrough // alias
|
||||||
case "avg":
|
case "avg":
|
||||||
var sum float64
|
var sum float64
|
||||||
|
|
||||||
@@ -353,6 +427,28 @@ func (c *Calc) batchfunc(funcname string) {
|
|||||||
|
|
||||||
x = sum / float64(count)
|
x = sum / float64(count)
|
||||||
c.History("avg(all)")
|
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.stack.Push(x)
|
||||||
@@ -360,20 +456,41 @@ func (c *Calc) batchfunc(funcname string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Calc) luafunc(funcname string) {
|
func (c *Calc) luafunc(funcname string) {
|
||||||
// we may need to put them onto the stack afterwards!
|
// called from calc loop
|
||||||
c.stack.Backup()
|
var x float64
|
||||||
b := c.stack.Pop()
|
var err error
|
||||||
a := c.stack.Pop()
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
c.stack.Push(a)
|
|
||||||
c.stack.Push(b)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
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.stack.Push(x)
|
||||||
|
|
||||||
c.Result()
|
c.Result()
|
||||||
|
|||||||
@@ -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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,31 +24,59 @@ import (
|
|||||||
lua "github.com/yuin/gopher-lua"
|
lua "github.com/yuin/gopher-lua"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Interpreter struct {
|
||||||
|
debug bool
|
||||||
|
}
|
||||||
|
|
||||||
// LUA interpreter, instanciated in main()
|
// LUA interpreter, instanciated in main()
|
||||||
var L *lua.LState
|
var L *lua.LState
|
||||||
|
|
||||||
var LuaFuncs map[string]int
|
// holds a user provided lua function
|
||||||
|
type LuaFunction struct {
|
||||||
// FIXME: add 2nd var with help string
|
name string
|
||||||
// called from lua to register a 1 arg math function
|
help string
|
||||||
func RegisterFuncOneArg(L *lua.LState) int {
|
numargs int
|
||||||
function := L.ToString(1)
|
|
||||||
LuaFuncs[function] = 1
|
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from lua to register a 1 arg math function
|
// must be global since init() is being called from lua which doesn't
|
||||||
func RegisterFuncTwoArg(L *lua.LState) int {
|
// have access to the interpreter instance
|
||||||
function := L.ToString(1)
|
var LuaFuncs map[string]LuaFunction
|
||||||
LuaFuncs[function] = 2
|
|
||||||
return 1
|
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitLua(L *lua.LState) {
|
// load the lua config (which we expect to contain init() and math functions)
|
||||||
LuaFuncs = map[string]int{}
|
if err := L.DoFile(config); err != nil {
|
||||||
L.SetGlobal("RegisterFuncOneArg", L.NewFunction(RegisterFuncOneArg))
|
panic(err)
|
||||||
L.SetGlobal("RegisterFuncTwoArg", L.NewFunction(RegisterFuncTwoArg))
|
}
|
||||||
|
|
||||||
|
// 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{
|
if err := L.CallByParam(lua.P{
|
||||||
Fn: L.GetGlobal("init"),
|
Fn: L.GetGlobal("init"),
|
||||||
NRet: 0,
|
NRet: 0,
|
||||||
@@ -39,26 +84,68 @@ func InitLua(L *lua.LState) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &Interpreter{debug: debug}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallLuaFunc(L *lua.LState, funcname string, a float64, b float64) (float64, error) {
|
func (i *Interpreter) Debug(msg string) {
|
||||||
if LuaFuncs[funcname] == 1 {
|
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
|
// 1 arg variant
|
||||||
if err := L.CallByParam(lua.P{
|
if err := L.CallByParam(lua.P{
|
||||||
Fn: L.GetGlobal(funcname),
|
Fn: L.GetGlobal(funcname),
|
||||||
NRet: 1,
|
NRet: 1,
|
||||||
Protect: true,
|
Protect: true,
|
||||||
}, lua.LNumber(a)); err != nil {
|
}, lua.LNumber(items[0])); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
} else {
|
case 2:
|
||||||
// 2 arg variant
|
// 2 arg variant
|
||||||
if err := L.CallByParam(lua.P{
|
if err := L.CallByParam(lua.P{
|
||||||
Fn: L.GetGlobal(funcname),
|
Fn: L.GetGlobal(funcname),
|
||||||
NRet: 1,
|
NRet: 1,
|
||||||
Protect: true,
|
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
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,5 +156,22 @@ func CallLuaFunc(L *lua.LState, funcname string, a float64, b float64) (float64,
|
|||||||
return float64(res), nil
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
72
go/main.go
72
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
@@ -38,7 +56,8 @@ func main() {
|
|||||||
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")
|
||||||
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()
|
flag.Parse()
|
||||||
|
|
||||||
@@ -56,43 +75,28 @@ func main() {
|
|||||||
calc.ToggleDebug()
|
calc.ToggleDebug()
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(configfile); err == nil {
|
// the lua state object is global, instanciate it early
|
||||||
// FIXME: put into interpreter.go, probably with its own obj
|
|
||||||
// then just Interpreter.Init(configfile) should suffice
|
|
||||||
L = lua.NewState(lua.Options{SkipOpenLibs: true})
|
L = lua.NewState(lua.Options{SkipOpenLibs: true})
|
||||||
defer L.Close()
|
defer L.Close()
|
||||||
|
|
||||||
// we only load a subset of lua Open modules and don't allow
|
// our config file is interpreted as lua code, only functions can
|
||||||
// net, system or io stuff
|
// be defined, init() will be called by InitLua().
|
||||||
for _, pair := range []struct {
|
if _, err := os.Stat(configfile); err == nil {
|
||||||
n string
|
I := InitLua(configfile, enabledebug)
|
||||||
f lua.LGFunction
|
calc.SetInt(I)
|
||||||
}{
|
|
||||||
{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 {
|
if len(flag.Args()) > 1 {
|
||||||
panic(err)
|
// commandline calc operation, no readline etc needed
|
||||||
}
|
// called like rpn 2 2 +
|
||||||
|
calc.stdin = true
|
||||||
InitLua(L)
|
calc.Eval(strings.Join(flag.Args(), " "))
|
||||||
calc.L = L
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interactive mode, need readline
|
||||||
rl, err := readline.NewEx(&readline.Config{
|
rl, err := readline.NewEx(&readline.Config{
|
||||||
Prompt: "\033[31m»\033[0m ",
|
Prompt: calc.Prompt(),
|
||||||
HistoryFile: os.Getenv("HOME") + "/.rpn-history",
|
HistoryFile: os.Getenv("HOME") + "/.rpn-history",
|
||||||
HistoryLimit: 500,
|
HistoryLimit: 500,
|
||||||
AutoComplete: calc.completer,
|
AutoComplete: calc.completer,
|
||||||
@@ -108,19 +112,27 @@ func main() {
|
|||||||
rl.CaptureExitSignal()
|
rl.CaptureExitSignal()
|
||||||
|
|
||||||
if inputIsStdin() {
|
if inputIsStdin() {
|
||||||
|
// commands are coming on stdin, however we will still enter
|
||||||
|
// the same loop since readline just reads fine from stdin
|
||||||
calc.ToggleStdin()
|
calc.ToggleStdin()
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
// primary program repl
|
||||||
line, err := rl.Readline()
|
line, err := rl.Readline()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
calc.Eval(line)
|
calc.Eval(line)
|
||||||
|
rl.SetPrompt(calc.Prompt())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(flag.Args()) > 0 {
|
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])
|
calc.Eval(flag.Args()[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
go/stack.go
104
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,8 +23,14 @@ import (
|
|||||||
"sync"
|
"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 {
|
type Stack struct {
|
||||||
dll list.List
|
linklist list.List
|
||||||
backup list.List
|
backup list.List
|
||||||
debug bool
|
debug bool
|
||||||
rev int
|
rev int
|
||||||
@@ -15,8 +38,15 @@ type Stack struct {
|
|||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: maybe use a separate stack object for backup so that it has
|
||||||
|
// its own revision etc
|
||||||
func NewStack() *Stack {
|
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) {
|
func (s *Stack) Debug(msg string) {
|
||||||
@@ -33,6 +63,7 @@ func (s *Stack) Bump() {
|
|||||||
s.rev++
|
s.rev++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append an item to the stack
|
||||||
func (s *Stack) Push(x float64) {
|
func (s *Stack) Push(x float64) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
@@ -40,20 +71,21 @@ func (s *Stack) Push(x float64) {
|
|||||||
s.Debug(fmt.Sprintf(" push to stack: %.2f", x))
|
s.Debug(fmt.Sprintf(" push to stack: %.2f", x))
|
||||||
|
|
||||||
s.Bump()
|
s.Bump()
|
||||||
s.dll.PushBack(x)
|
s.linklist.PushBack(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove and return an item from the stack
|
||||||
func (s *Stack) Pop() float64 {
|
func (s *Stack) Pop() float64 {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
if s.dll.Len() == 0 {
|
if s.linklist.Len() == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
tail := s.dll.Back()
|
tail := s.linklist.Back()
|
||||||
val := tail.Value
|
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))
|
||||||
|
|
||||||
@@ -61,31 +93,65 @@ func (s *Stack) Pop() float64 {
|
|||||||
return val.(float64)
|
return val.(float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// just remove the last item, do not return it
|
||||||
func (s *Stack) Shift() {
|
func (s *Stack) Shift() {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
if s.dll.Len() == 0 {
|
if s.linklist.Len() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tail := s.dll.Back()
|
tail := s.linklist.Back()
|
||||||
s.dll.Remove(tail)
|
s.linklist.Remove(tail)
|
||||||
|
|
||||||
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
|
s.Debug(fmt.Sprintf("remove from stack: %.2f", tail.Value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// just return the last item, do not remove it
|
||||||
func (s *Stack) Last() float64 {
|
func (s *Stack) Last() float64 {
|
||||||
if s.dll.Back() == nil {
|
if s.linklist.Back() == nil {
|
||||||
return 0
|
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() {
|
func (s *Stack) Dump() {
|
||||||
fmt.Printf("Stack revision %d (%p):\n", s.rev, &s.dll)
|
fmt.Printf("Stack revision %d (%p):\n", s.rev, &s.linklist)
|
||||||
for e := s.dll.Front(); e != nil; e = e.Next() {
|
for e := s.linklist.Front(); e != nil; e = e.Next() {
|
||||||
fmt.Println(e.Value)
|
fmt.Println(e.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +166,11 @@ func (s *Stack) Dump() {
|
|||||||
func (s *Stack) Clear() {
|
func (s *Stack) Clear() {
|
||||||
s.Debug("DEBUG: clearing stack")
|
s.Debug("DEBUG: clearing stack")
|
||||||
|
|
||||||
s.dll = list.List{}
|
s.linklist = list.List{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Len() int {
|
func (s *Stack) Len() int {
|
||||||
return s.dll.Len()
|
return s.linklist.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Backup() {
|
func (s *Stack) Backup() {
|
||||||
@@ -113,7 +179,7 @@ func (s *Stack) Backup() {
|
|||||||
// 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.backup = list.List{}
|
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.backup.PushBack(e.Value)
|
||||||
}
|
}
|
||||||
s.backuprev = s.rev
|
s.backuprev = s.rev
|
||||||
@@ -128,15 +194,15 @@ 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.dll = s.backup
|
s.linklist = s.backup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stack) Reverse() {
|
func (s *Stack) Reverse() {
|
||||||
newstack := list.List{}
|
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)
|
newstack.PushFront(e.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dll = newstack
|
s.linklist = newstack
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,16 @@ function add(a,b)
|
|||||||
return a + b
|
return a + b
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function test(a)
|
||||||
|
return a
|
||||||
|
end
|
||||||
|
|
||||||
function parallelresistance(a,b)
|
function parallelresistance(a,b)
|
||||||
return 1.0 / (a * b)
|
return 1.0 / (a * b)
|
||||||
end
|
end
|
||||||
|
|
||||||
function init()
|
function init()
|
||||||
RegisterFuncTwoArg("add")
|
register("add", 2, "addition")
|
||||||
RegisterFuncTwoArg("parallelresistance")
|
register("test", 1, "test")
|
||||||
|
register("parallelresistance", 2, "parallel resistance")
|
||||||
end
|
end
|
||||||
|
|||||||
18
go/util.go
18
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
|
// find an item in a list
|
||||||
func contains(s []string, e string) bool {
|
func contains(s []string, e string) bool {
|
||||||
for _, a := range s {
|
for _, a := range s {
|
||||||
if a == e {
|
if a == e {
|
||||||
|
|||||||
Reference in New Issue
Block a user