Compare commits

...

4 Commits

20 changed files with 129 additions and 90 deletions

View File

@ -42,3 +42,17 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
result, err = functor.expr.Eval(ctx) result, err = functor.expr.Eval(ctx)
return return
} }
func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
if err == nil {
if err = checkFunctionCall(ctx, funcName, &params); err == nil {
if v, err = ctx.Call(funcName, params); err == nil {
exportObjects(parentCtx, ctx)
}
}
}
return
}

View File

@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
} }
} else { } else {
err = errMissingFilePath("openFile") err = errMissingFilePath("appendFile")
} }
return return
} }
@ -187,24 +187,24 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("fileWrite", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""), NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
}) })
ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileRead", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"), NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
}) })
ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
}) })
} }

View File

@ -25,11 +25,11 @@ func errTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
// --- General errors // --- General errors
func errCantConvert(funcName string, value any, kind string) error { func errCantConvert(funcName string, value any, kind string) error {
return fmt.Errorf("%s(): can't convert %s to %s", funcName, typeName(value), kind) return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
} }
func errExpectedGot(funcName string, kind string, value any) error { func errExpectedGot(funcName string, kind string, value any) error {
return fmt.Errorf("%s() expected %s, got %s (%v)", funcName, kind, typeName(value), value) return fmt.Errorf("%s() expected %s, got %s (%v)", funcName, kind, TypeName(value), value)
} }
func errFuncDivisionByZero(funcName string) error { func errFuncDivisionByZero(funcName string) error {
@ -47,9 +47,9 @@ func errMissingRequiredParameter(funcName, paramName string) error {
} }
func errInvalidParameterValue(funcName, paramName string, paramValue any) error { func errInvalidParameterValue(funcName, paramName string, paramValue any) error {
return fmt.Errorf("%s() invalid value %s (%v) for parameter %q", funcName, typeName(paramValue), paramValue, paramName) return fmt.Errorf("%s() invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
} }
func errWrongParamType(funcName, paramName, paramType string, paramValue any) error { func errWrongParamType(funcName, paramName, paramType string, paramValue any) error {
return fmt.Errorf("%s() the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, typeName(paramValue), paramValue) return fmt.Errorf("%s() the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
} }

View File

@ -12,6 +12,12 @@ import (
type DictType map[any]any type DictType map[any]any
func MakeDict() (dict *DictType) {
d := make(DictType)
dict = &d
return
}
func newDict(dictAny map[any]*term) (dict *DictType) { func newDict(dictAny map[any]*term) (dict *DictType) {
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
@ -124,7 +130,6 @@ func (dict *DictType) merge(second *DictType) {
} }
func (dict *DictType) setItem(key any, value any) (err error) { func (dict *DictType) setItem(key any, value any) (err error) {
(*dict)[key]=value (*dict)[key] = value
return return
} }

View File

@ -51,7 +51,7 @@ type Typer interface {
TypeName() string TypeName() string
} }
func typeName(v any) (name string) { func TypeName(v any) (name string) {
if v == nil { if v == nil {
name = "nil" name = "nil"
} else if typer, ok := v.(Typer); ok { } else if typer, ok := v.(Typer); ok {

View File

@ -20,7 +20,7 @@ const (
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) { if !(IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, typeName(paramValue)) err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, TypeName(paramValue))
} }
return return
} }

View File

@ -28,7 +28,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
if keyListValue, err = keyListTerm.compute(ctx); err != nil { if keyListValue, err = keyListTerm.compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, typeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
return return
} }
if keyValue = (*keyList)[0]; keyValue == nil { if keyValue = (*keyList)[0]; keyValue == nil {
@ -41,7 +41,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
err = collection.setItem(index, value) err = collection.setItem(index, value)
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, typeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
} }
case *DictType: case *DictType:
err = collection.setItem(keyValue, value) err = collection.setItem(keyValue, value)

View File

@ -41,7 +41,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }

View File

@ -5,9 +5,7 @@
package expr package expr
import ( import (
"fmt"
"io" "io"
"plugin"
) )
//-------- plugin term //-------- plugin term
@ -22,48 +20,6 @@ func newPluginTerm(tk *Token) (inst *term) {
} }
} }
func importPlugin(ctx ExprContext, dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
var sym plugin.Symbol
var moduleName string
var importFunc func(ctx ExprContext)
var ok bool
decoratedName := fmt.Sprintf("expr-%s-plugin.so", name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
}
if p, err = plugin.Open(filePath); err != nil {
return
}
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
return
}
if moduleName = *sym.(*string); moduleName == "" {
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
return
}
if sym, err = p.Lookup("ImportFuncs"); err != nil {
return
}
if importFunc, ok = sym.(func(ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return
}
registerPlugin(moduleName, p)
importFunc(globalCtx)
return
}
func evalPlugin(ctx ExprContext, self *term) (v any, err error) { func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
var childValue any var childValue any
var moduleSpec any var moduleSpec any
@ -77,12 +33,12 @@ func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
it := NewAnyIterator(childValue) it := NewAnyIterator(childValue)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok { if module, ok := moduleSpec.(string); ok {
if err = importPlugin(ctx, dirList, module); err != nil { if err = importPlugin(dirList, module); err != nil {
break break
} }
count++ count++
} else { } else {
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }

View File

@ -25,6 +25,70 @@ func pluginExists(name string) (exists bool) {
return return
} }
func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
var sym plugin.Symbol
var moduleName string
var importFunc func(ExprContext)
var ok bool
decoratedName := fmt.Sprintf("expr-%s-plugin.so", name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
}
if p, err = plugin.Open(filePath); err != nil {
return
}
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
return
}
if moduleName = *sym.(*string); moduleName == "" {
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
return
}
if sym, err = p.Lookup("DEPENDENCIES"); err != nil {
return
}
if deps := *sym.(*[]string); len(deps) > 0 {
// var count int
if err = loadModules(dirList, deps); err != nil {
return
}
}
if sym, err = p.Lookup("ImportFuncs"); err != nil {
return
}
if importFunc, ok = sym.(func(ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return
}
registerPlugin(moduleName, p)
importFunc(globalCtx)
return
}
func loadModules(dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil {
if !ImportInContext(name) {
err = err1
break
}
}
}
return
}
func init() { func init() {
pluginRegister = make(map[string]*plugin.Plugin) pluginRegister = make(map[string]*plugin.Plugin)
} }

View File

@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`0?{}`, nil, nil}, /* 1 */ {`0?{}`, nil, nil},
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil}, /* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
/* 3 */ {`builtin "os.file"; f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil}, /* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileRead(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {` /* 6 */ {`

View File

@ -13,13 +13,13 @@ func TestFuncOs(t *testing.T) {
section := "Func-OS" section := "Func-OS"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "os.file"`, int64(1), nil}, /* 1 */ {`builtin "os.file"`, int64(1), nil},
/* 2 */ {`builtin "os.file"; handle=openFile("/etc/hosts"); closeFile(handle)`, true, nil}, /* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=openFile("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)}, /* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
/* 4 */ {`builtin "os.file"; handle=createFile("/tmp/dummy"); closeFile(handle)`, true, nil}, /* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=appendFile("/tmp/dummy"); writeFile(handle, "bye-bye"); closeFile(handle)`, true, nil}, /* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWrite(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=openFile("/tmp/dummy"); word=readFile(handle, "-"); closeFile(handle);word`, "bye", nil}, /* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileRead(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=readFile(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)}, /* 7 */ {`builtin "os.file"; word=fileRead(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
/* 7 */ {`builtin "os.file"; writeFile(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)}, /* 7 */ {`builtin "os.file"; fileWrite(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")

View File

@ -8,15 +8,15 @@ import "testing"
func TestIteratorParser(t *testing.T) { func TestIteratorParser(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
/* 2 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, /* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
/* 3 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
/* 4 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, /* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, /* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, /* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, /* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil}, /* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
/* 10 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil}, /* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil}, /* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
} }
// inputs1 := []inputType{ // inputs1 := []inputType{

View File

@ -203,7 +203,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
eq := reflect.DeepEqual(gotResult, input.wantResult) eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ { if !eq /*gotResult != input.wantResult*/ {
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, typeName(gotResult), input.wantResult, typeName(input.wantResult)) t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
good = false good = false
} }

View File

@ -156,15 +156,15 @@ func (self *term) toInt(computedValue any, valueDescription string) (i int, err
if index64, ok := computedValue.(int64); ok { if index64, ok := computedValue.(int64); ok {
i = int(index64) i = int(index64)
} else { } else {
err = self.Errorf("%s, got %s (%v)", valueDescription, typeName(computedValue), computedValue) err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
} }
return return
} }
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := typeName(leftValue) leftType := TypeName(leftValue)
leftText := getFormatted(leftValue, Truncate) leftText := getFormatted(leftValue, Truncate)
rightType := typeName(rightValue) rightType := TypeName(rightValue)
rightText := getFormatted(rightValue, Truncate) rightText := getFormatted(rightValue, Truncate)
return self.tk.Errorf( return self.tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q", "left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
@ -176,7 +176,7 @@ func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
func (self *term) errIncompatibleType(value any) error { func (self *term) errIncompatibleType(value any) error {
return self.tk.Errorf( return self.tk.Errorf(
"prefix/postfix operator %q do not support operand '%v' [%s]", "prefix/postfix operator %q do not support operand '%v' [%s]",
self.source(), value, typeName(value)) self.source(), value, TypeName(value))
} }
func (self *term) Errorf(template string, args ...any) (err error) { func (self *term) Errorf(template string, args ...any) (err error) {

View File

@ -1,13 +1,13 @@
builtin ["os.file", "base"]; builtin ["os.file", "base"];
readInt=func(fh){ readInt=func(fh){
line=readFile(fh); line=fileRead(fh);
line ? [nil] {nil} :: {int(line)} line ? [nil] {nil} :: {int(line)}
}; };
ds={ ds={
"init":func(filename){ "init":func(filename){
fh=openFile(filename); fh=fileOpen(filename);
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current }; fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
fh fh
}, },
@ -20,7 +20,7 @@ ds={
:: {@prev=current; @current=readInt(fh) but current} :: {@prev=current; @current=readInt(fh) but current}
}, },
"clean":func(fh){ "clean":func(fh){
closeFile(fh) fileClose(fh)
} }
} }

View File

@ -207,7 +207,7 @@ func toInt(value any, description string) (i int, err error) {
} else if valueInt, ok := value.(int); ok { } else if valueInt, ok := value.(int); ok {
i = valueInt i = valueInt
} else { } else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, typeName(value), value) err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
} }
return return
} }