Compare commits
No commits in common. "1757298eb491c858dd0b0f7eebc5f02f7ab9ec86" and "33d70d6d1a23c3d588122eacb5dbfc0b60d69917" have entirely different histories.
1757298eb4
...
33d70d6d1a
@ -42,17 +42,3 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
|
||||
result, err = functor.expr.Eval(ctx)
|
||||
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, ¶ms); err == nil {
|
||||
if v, err = ctx.Call(funcName, params); err == nil {
|
||||
exportObjects(parentCtx, ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||
}
|
||||
} else {
|
||||
err = errMissingFilePath("appendFile")
|
||||
err = errMissingFilePath("openFile")
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -187,24 +187,24 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
|
||||
}
|
||||
|
||||
func ImportOsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
|
||||
ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
|
||||
ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
|
||||
ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
})
|
||||
ctx.RegisterFunc("fileWrite", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
|
||||
ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
|
||||
})
|
||||
ctx.RegisterFunc("fileRead", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
|
||||
ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
|
||||
})
|
||||
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||
ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(TypeHandle),
|
||||
})
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ func errTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
|
||||
// --- General errors
|
||||
|
||||
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 {
|
||||
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 {
|
||||
@ -47,9 +47,9 @@ func errMissingRequiredParameter(funcName, paramName string) 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 {
|
||||
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)
|
||||
}
|
||||
|
@ -12,12 +12,6 @@ import (
|
||||
|
||||
type DictType map[any]any
|
||||
|
||||
func MakeDict() (dict *DictType) {
|
||||
d := make(DictType)
|
||||
dict = &d
|
||||
return
|
||||
}
|
||||
|
||||
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||
var d DictType
|
||||
if dictAny != nil {
|
||||
@ -130,6 +124,7 @@ func (dict *DictType) merge(second *DictType) {
|
||||
}
|
||||
|
||||
func (dict *DictType) setItem(key any, value any) (err error) {
|
||||
(*dict)[key] = value
|
||||
(*dict)[key]=value
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
builtin ["os.file", "base"];
|
||||
|
||||
readInt=func(fh){
|
||||
line=fileRead(fh);
|
||||
line=readFile(fh);
|
||||
line ? [nil] {nil} :: {int(line)}
|
||||
};
|
||||
|
||||
ds={
|
||||
"init":func(filename){
|
||||
fh=fileOpen(filename);
|
||||
fh=openFile(filename);
|
||||
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
|
||||
fh
|
||||
},
|
||||
@ -20,7 +20,7 @@ ds={
|
||||
:: {@prev=current; @current=readInt(fh) but current}
|
||||
},
|
||||
"clean":func(fh){
|
||||
fileClose(fh)
|
||||
closeFile(fh)
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ type Typer interface {
|
||||
TypeName() string
|
||||
}
|
||||
|
||||
func TypeName(v any) (name string) {
|
||||
func typeName(v any) (name string) {
|
||||
if v == nil {
|
||||
name = "nil"
|
||||
} else if typer, ok := v.(Typer); ok {
|
||||
|
@ -20,7 +20,7 @@ const (
|
||||
|
||||
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 %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
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
|
||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||
return
|
||||
} 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
|
||||
}
|
||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||
@ -41,7 +41,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
|
||||
if index, ok := keyValue.(int64); ok {
|
||||
err = collection.setItem(index, value)
|
||||
} 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:
|
||||
err = collection.setItem(keyValue, value)
|
||||
|
@ -41,7 +41,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
||||
break
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"plugin"
|
||||
)
|
||||
|
||||
//-------- plugin term
|
||||
@ -20,6 +22,48 @@ 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) {
|
||||
var childValue any
|
||||
var moduleSpec any
|
||||
@ -33,12 +77,12 @@ func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
|
||||
it := NewAnyIterator(childValue)
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if err = importPlugin(dirList, module); err != nil {
|
||||
if err = importPlugin(ctx, dirList, module); err != nil {
|
||||
break
|
||||
}
|
||||
count++
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
64
plugins.go
64
plugins.go
@ -25,70 +25,6 @@ func pluginExists(name string) (exists bool) {
|
||||
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() {
|
||||
pluginRegister = make(map[string]*plugin.Plugin)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`0?{}`, nil, nil},
|
||||
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileRead(f); fileClose(f); line`, "uno", nil},
|
||||
/* 3 */ {`builtin "os.file"; f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||
/* 6 */ {`
|
||||
|
@ -13,13 +13,13 @@ func TestFuncOs(t *testing.T) {
|
||||
section := "Func-OS"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
||||
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
||||
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
||||
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
||||
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWrite(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileRead(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||
/* 7 */ {`builtin "os.file"; word=fileRead(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
|
||||
/* 7 */ {`builtin "os.file"; fileWrite(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
|
||||
/* 2 */ {`builtin "os.file"; handle=openFile("/etc/hosts"); closeFile(handle)`, true, nil},
|
||||
/* 3 */ {`builtin "os.file"; handle=openFile("/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},
|
||||
/* 5 */ {`builtin "os.file"; handle=appendFile("/tmp/dummy"); writeFile(handle, "bye-bye"); closeFile(handle)`, true, nil},
|
||||
/* 6 */ {`builtin "os.file"; handle=openFile("/tmp/dummy"); word=readFile(handle, "-"); closeFile(handle);word`, "bye", nil},
|
||||
/* 7 */ {`builtin "os.file"; word=readFile(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
|
||||
/* 7 */ {`builtin "os.file"; writeFile(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
@ -8,15 +8,15 @@ import "testing"
|
||||
|
||||
func TestIteratorParser(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
||||
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
||||
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
||||
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
|
||||
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
||||
/* 1 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
||||
/* 2 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||
/* 3 */ {`include "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},
|
||||
/* 5 */ {`builtin "math.arith"; include "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},
|
||||
/* 7 */ {`builtin "math.arith"; include "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},
|
||||
/* 10 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
||||
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||
}
|
||||
// inputs1 := []inputType{
|
||||
|
@ -203,7 +203,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
|
||||
eq := reflect.DeepEqual(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
|
||||
}
|
||||
|
||||
|
8
term.go
8
term.go
@ -156,15 +156,15 @@ func (self *term) toInt(computedValue any, valueDescription string) (i int, err
|
||||
if index64, ok := computedValue.(int64); ok {
|
||||
i = int(index64)
|
||||
} else {
|
||||
err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
|
||||
err = self.Errorf("%s, got %s (%v)", valueDescription, typeName(computedValue), computedValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||
leftType := TypeName(leftValue)
|
||||
leftType := typeName(leftValue)
|
||||
leftText := getFormatted(leftValue, Truncate)
|
||||
rightType := TypeName(rightValue)
|
||||
rightType := typeName(rightValue)
|
||||
rightText := getFormatted(rightValue, Truncate)
|
||||
return self.tk.Errorf(
|
||||
"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 {
|
||||
return self.tk.Errorf(
|
||||
"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) {
|
||||
|
2
utils.go
2
utils.go
@ -207,7 +207,7 @@ func toInt(value any, description string) (i int, err error) {
|
||||
} else if valueInt, ok := value.(int); ok {
|
||||
i = valueInt
|
||||
} 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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user