import() function added
This commit is contained in:
parent
6aada9f7e4
commit
4683a08da2
120
func-import.go
Normal file
120
func-import.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// func-import.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ENV_EXPR_PATH = "EXPR_PATH"
|
||||||
|
|
||||||
|
func importFunc(ctx exprContext, name string, args []any) (result any, err error) {
|
||||||
|
var dirList []string
|
||||||
|
|
||||||
|
dirList = addEnvImportDirs(dirList)
|
||||||
|
dirList = addPresetImportDirs(ctx, dirList)
|
||||||
|
result, err = doImport(ctx, name, dirList, NewFlatArrayIterator(args))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
||||||
|
if !(isString(paramValue) /*|| isList(paramValue)*/) {
|
||||||
|
err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func addEnvImportDirs(dirList []string) []string {
|
||||||
|
if dirSpec, exists := os.LookupEnv(ENV_EXPR_PATH); exists {
|
||||||
|
dirs := strings.Split(dirSpec, ":")
|
||||||
|
if dirList == nil {
|
||||||
|
dirList = dirs
|
||||||
|
} else {
|
||||||
|
dirList = append(dirList, dirs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirList
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPresetImportDirs(ctx exprContext, dirList []string) []string {
|
||||||
|
if dirSpec, exists := getPresetString(ctx, preset_import_path); exists {
|
||||||
|
dirs := strings.Split(dirSpec, ":")
|
||||||
|
if dirList == nil {
|
||||||
|
dirList = dirs
|
||||||
|
} else {
|
||||||
|
dirList = append(dirList, dirs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirList
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFile(filePath string) bool {
|
||||||
|
info, err := os.Stat(filePath)
|
||||||
|
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||||
|
for _, dir := range dirList {
|
||||||
|
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
||||||
|
filePath = fullPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPathRelative(filePath string) bool {
|
||||||
|
unixPath := filepath.ToSlash(filePath)
|
||||||
|
return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
|
||||||
|
if path.IsAbs(filename) || isPathRelative(filename) {
|
||||||
|
if isFile(filename) {
|
||||||
|
filePath = filename
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filePath = searchAmongPath(filename, dirList)
|
||||||
|
}
|
||||||
|
if len(filePath) == 0 {
|
||||||
|
err = fmt.Errorf("source file %q not found", filename)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func doImport(ctx exprContext, name string, dirList []string, it Iterator) (result any, err error) {
|
||||||
|
var v any
|
||||||
|
var sourceFilepath string
|
||||||
|
|
||||||
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if err = checkStringParamExpected(name, v, it.Index()); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var file *os.File
|
||||||
|
if file, err = os.Open(sourceFilepath); err == nil {
|
||||||
|
defer file.Close()
|
||||||
|
var expr *ast
|
||||||
|
scanner := NewScanner(file, DefaultTranslations())
|
||||||
|
parser := NewParser(ctx)
|
||||||
|
if expr, err = parser.parse(scanner); err == nil {
|
||||||
|
_, err = expr.Eval(ctx, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil && err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func importImportFunc(ctx exprContext) {
|
||||||
|
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
||||||
|
}
|
32
func-math.go
32
func-math.go
@ -6,38 +6,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// func addFunc(ctx exprContext, name string, args []any) (result any, err error) {
|
|
||||||
// var sumAsFloat = false
|
|
||||||
// var floatSum float64 = 0.0
|
|
||||||
// var intSum int64 = 0
|
|
||||||
|
|
||||||
// for i, v := range args {
|
|
||||||
// if !isNumber(v) {
|
|
||||||
// err = fmt.Errorf("add(): param nr %d has wrong type %T, number expected", i+1, v)
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !sumAsFloat && isFloat(v) {
|
|
||||||
// sumAsFloat = true
|
|
||||||
// floatSum = float64(intSum)
|
|
||||||
// }
|
|
||||||
// if sumAsFloat {
|
|
||||||
// floatSum += numAsFloat(v)
|
|
||||||
// } else {
|
|
||||||
// iv, _ := v.(int64)
|
|
||||||
// intSum += iv
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if err == nil {
|
|
||||||
// if sumAsFloat {
|
|
||||||
// result = floatSum
|
|
||||||
// } else {
|
|
||||||
// result = intSum
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
||||||
if !(isNumber(paramValue) || isList(paramValue)) {
|
if !(isNumber(paramValue) || isList(paramValue)) {
|
||||||
err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue)
|
err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue)
|
||||||
|
@ -130,11 +130,16 @@ func TestParser(t *testing.T) {
|
|||||||
/* 109 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
/* 109 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
||||||
/* 110 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
/* 110 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
||||||
/* 111 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
/* 111 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
|
/* 112 */ {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
|
/* 113 */ {`import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
}
|
}
|
||||||
|
check_env_expr_path := 113
|
||||||
|
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
|
// {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
// {`add(1,2,3)`, int64(6), nil},
|
// {`add(1,2,3)`, int64(6), nil},
|
||||||
// {`a=5; a`, int64(5), nil},
|
// {`a=5; a`, int64(5), nil},
|
||||||
// {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
// {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
||||||
@ -150,6 +155,7 @@ func TestParser(t *testing.T) {
|
|||||||
ctx.SetValue("var1", int64(123))
|
ctx.SetValue("var1", int64(123))
|
||||||
ctx.SetValue("var2", "abc")
|
ctx.SetValue("var2", "abc")
|
||||||
importMathFuncs(ctx)
|
importMathFuncs(ctx)
|
||||||
|
importImportFunc(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
||||||
@ -178,6 +184,9 @@ func TestParser(t *testing.T) {
|
|||||||
succeeded++
|
succeeded++
|
||||||
} else {
|
} else {
|
||||||
failed++
|
failed++
|
||||||
|
if i+1 == check_env_expr_path {
|
||||||
|
t.Logf(`NOTICE: Test nr %d requires EXPR_PATH environment variable with value "."`, check_env_expr_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
||||||
@ -190,13 +199,6 @@ func TestListParser(t *testing.T) {
|
|||||||
wantErr error
|
wantErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctx := newTestContext()
|
|
||||||
ctx := NewSimpleFuncStore()
|
|
||||||
ctx.SetValue("var1", int64(123))
|
|
||||||
ctx.SetValue("var2", "abc")
|
|
||||||
// ctx.addFunc("add", addFunc)
|
|
||||||
importMathFuncs(ctx)
|
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// {`add(1,2,3)`, int64(6), nil},
|
// {`add(1,2,3)`, int64(6), nil},
|
||||||
// }
|
// }
|
||||||
@ -220,12 +222,17 @@ func TestListParser(t *testing.T) {
|
|||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
parser := NewParser(ctx)
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
var expr *ast
|
var expr *ast
|
||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
|
||||||
|
ctx := NewSimpleFuncStore()
|
||||||
|
ctx.SetValue("var1", int64(123))
|
||||||
|
ctx.SetValue("var2", "abc")
|
||||||
|
importMathFuncs(ctx)
|
||||||
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
|
15
preset.go
15
preset.go
@ -7,10 +7,17 @@ import "strings"
|
|||||||
const (
|
const (
|
||||||
preset_last_result = "_last"
|
preset_last_result = "_last"
|
||||||
preset_bool_shortcut = "_bool_shortcut"
|
preset_bool_shortcut = "_bool_shortcut"
|
||||||
|
preset_import_path = "_import_path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initial values
|
||||||
|
const (
|
||||||
|
init_import_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initDefaultVars(ctx exprContext) {
|
func initDefaultVars(ctx exprContext) {
|
||||||
ctx.SetValue(preset_bool_shortcut, true)
|
ctx.SetValue(preset_bool_shortcut, true)
|
||||||
|
ctx.SetValue(preset_import_path, init_import_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func enable(ctx exprContext, name string) {
|
func enable(ctx exprContext, name string) {
|
||||||
@ -37,3 +44,11 @@ func isEnabled(ctx exprContext, name string) (status bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPresetString(ctx exprContext, name string) (s string, exists bool) {
|
||||||
|
var v any
|
||||||
|
if v, exists = ctx.GetValue(name); exists {
|
||||||
|
s, exists = v.(string)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
12
test-funcs.expr
Normal file
12
test-funcs.expr
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
test-funcs.expr: example source file
|
||||||
|
*/
|
||||||
|
|
||||||
|
// double(x): returns 2*x
|
||||||
|
double=func(x){2*x};
|
||||||
|
|
||||||
|
// Define variable 'a' wth value 5
|
||||||
|
a=5;
|
||||||
|
|
||||||
|
// two(): returns 2
|
||||||
|
two=func() {2};
|
Loading…
Reference in New Issue
Block a user