Compare commits

..

No commits in common. "1a772597cbfddfbac94d653f7f93f2d7041414e3" and "79889cd8e17972e03fe8108fe3018b24d1e6963b" have entirely different histories.

14 changed files with 92 additions and 145 deletions

View File

@ -12,7 +12,6 @@ import (
const ( const (
iterParamOperator = "operator" iterParamOperator = "operator"
iterParamVars = "vars" iterParamVars = "vars"
iterVarStatus = "status"
) )
func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) { func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) {
@ -55,7 +54,7 @@ func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var item any var item any
localCtx := ctx.Clone() localCtx := ctx.Clone()
localCtx.UnsafeSetVar(iterVarStatus, nil) localCtx.UnsafeSetVar("it_status", nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil { if it, op, err = parseRunArgs(localCtx, args); err != nil {
return return
@ -85,7 +84,7 @@ func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
err = nil err = nil
} }
if err == nil { if err == nil {
result, _ = localCtx.GetVar(iterVarStatus) result, _ = localCtx.GetVar("it_status")
} }
return return
} }

View File

@ -34,8 +34,8 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doAdd(ctx, name, subIter, count, level); err != nil { if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) { if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil { if _, err = extIter.CallOperation(cleanName, nil); err != nil {
return return
} }
} }
@ -107,8 +107,8 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doMul(ctx, name, subIter, count, level); err != nil { if v, err = doMul(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) { if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil { if _, err = extIter.CallOperation(cleanName, nil); err != nil {
return return
} }
} }

View File

@ -20,7 +20,7 @@ type dataCursor struct {
currentFunc Functor currentFunc Functor
} }
func NewDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
index: -1, index: -1,
@ -29,10 +29,6 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
return return
} }
func (dc *dataCursor) Context() ExprContext {
return dc.ctx
}
func (dc *dataCursor) TypeName() string { func (dc *dataCursor) TypeName() string {
return "DataCursor" return "DataCursor"
} }
@ -66,7 +62,7 @@ func (dc *dataCursor) String() string {
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) { func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == IndexName exists = name == indexName
if !exists { if !exists {
f, ok := dc.ds[name] f, ok := dc.ds[name]
exists = ok && isFunctor(f) exists = ok && isFunctor(f)
@ -75,7 +71,7 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
} }
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) {
if name == IndexName { if name == indexName {
value = int64(dc.Index()) value = int64(dc.Index())
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) { } else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc { if functor == dc.cleanFunc {
@ -97,7 +93,7 @@ func (dc *dataCursor) Reset() (success bool, err error) {
if dc.resetFunc != nil { if dc.resetFunc != nil {
if dc.resource != nil { if dc.resource != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if _, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource}); err == nil { if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil {
dc.index = -1 dc.index = -1
} }
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
@ -105,7 +101,7 @@ func (dc *dataCursor) Reset() (success bool, err error) {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
} else { } else {
err = errNoOperation(ResetName) err = errNoOperation(resetName)
} }
success = err == nil success = err == nil
return return
@ -115,7 +111,7 @@ func (dc *dataCursor) Clean() (success bool, err error) {
if dc.cleanFunc != nil { if dc.cleanFunc != nil {
if dc.resource != nil { if dc.resource != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
_, err = dc.cleanFunc.Invoke(ctx, CleanName, []any{dc.resource}) _, err = dc.cleanFunc.Invoke(ctx, cleanName, []any{dc.resource})
dc.resource = nil dc.resource = nil
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
} else { } else {
@ -130,7 +126,7 @@ func (dc *dataCursor) Clean() (success bool, err error) {
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if item, err = dc.currentFunc.Invoke(ctx, CurrentName, []any{}); err == nil && item == nil { if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
err = io.EOF err = io.EOF
} }
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
@ -171,7 +167,7 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
var v any var v any
var ok bool var ok bool
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if v, err = filter.Invoke(ctx, FilterName, []any{item, dc.index}); err == nil && v != nil { if v, err = filter.Invoke(ctx, filterName, []any{item, dc.index}); err == nil && v != nil {
if accepted, ok = v.(bool); !ok { if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
} }
@ -181,19 +177,19 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
} }
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) { func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
mappedItem, err = mapper.Invoke(ctx, MapName, []any{item, dc.index}); mappedItem, err = mapper.Invoke(ctx, mapName, []any{item, dc.index});
return return
} }
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
var accepted bool var accepted bool
if dc.resource != nil { if dc.resource != nil {
filter := dc.ds[FilterName] filter := dc.ds[filterName]
mapper := dc.ds[MapName] mapper := dc.ds[mapName]
for item == nil && err == nil { for item == nil && err == nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if item, err = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource}); err == nil { if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
if item == nil { if item == nil {
err = io.EOF err = io.EOF
} else { } else {

View File

@ -18,22 +18,7 @@ func MakeDict() (dict *DictType) {
return return
} }
func NewDict(dictAny map[any]any) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func newDict(dictAny map[any]*term) (dict *DictType) { func newDict(dictAny map[any]*term) (dict *DictType) {
// TODO Change wi a call to NewDict()
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
d = make(DictType, len(dictAny)) d = make(DictType, len(dictAny))

View File

@ -75,11 +75,7 @@ func isFile(filePath string) bool {
} }
func searchAmongPath(filename string, dirList []string) (filePath string) { func searchAmongPath(filename string, dirList []string) (filePath string) {
var err error
for _, dir := range dirList { for _, dir := range dirList {
if dir, err = ExpandPath(dir); err != nil {
continue
}
if fullPath := path.Join(dir, filename); isFile(fullPath) { if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath filePath = fullPath
break break
@ -94,10 +90,6 @@ func isPathRelative(filePath string) bool {
} }
func makeFilepath(filename string, dirList []string) (filePath string, err error) { func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if filename, err = ExpandPath(filename); err != nil {
return
}
if path.IsAbs(filename) || isPathRelative(filename) { if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) { if isFile(filename) {
filePath = filename filePath = filename

View File

@ -12,15 +12,15 @@ import (
// Operator names // Operator names
const ( const (
InitName = "init" initName = "init"
CleanName = "clean" cleanName = "clean"
ResetName = "reset" resetName = "reset"
NextName = "next" nextName = "next"
CurrentName = "current" currentName = "current"
IndexName = "index" indexName = "index"
CountName = "count" countName = "count"
FilterName = "filter" filterName = "filter"
MapName = "map" mapName = "map"
) )
type Iterator interface { type Iterator interface {

View File

@ -90,21 +90,21 @@ func (it *ListIterator) TypeName() string {
} }
func (it *ListIterator) HasOperation(name string) bool { func (it *ListIterator) HasOperation(name string) bool {
yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName yes := name == nextName || name == resetName || name == indexName || name == countName || name == currentName
return yes return yes
} }
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) { func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
switch name { switch name {
case NextName: case nextName:
v, err = it.Next() v, err = it.Next()
case ResetName: case resetName:
v, err = it.Reset() v, err = it.Reset()
case IndexName: case indexName:
v = int64(it.Index()) v = int64(it.Index())
case CurrentName: case currentName:
v, err = it.Current() v, err = it.Current()
case CountName: case countName:
v = it.count v = it.count
default: default:
err = errNoOperation(name) err = errNoOperation(name)

View File

@ -66,7 +66,7 @@ func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error)
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
if dictAny, ok := firstChildValue.(*DictType); ok { if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{CurrentName, NextName} requiredFields := []string{currentName, nextName}
fieldsMask := 0b11 fieldsMask := 0b11
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]Functor)
@ -108,8 +108,8 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
} }
if ds != nil { if ds != nil {
dc := NewDataCursor(ctx, ds) dc := newDataCursor(ctx, ds)
if initFunc, exists := ds[InitName]; exists && initFunc != nil { if initFunc, exists := ds[initName]; exists && initFunc != nil {
var args []any var args []any
if len(opTerm.children) > 1 { if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil { if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
@ -120,16 +120,16 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
} }
initCtx := dc.ctx.Clone() initCtx := dc.ctx.Clone()
if dc.resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil { if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
return return
} }
exportObjects(dc.ctx, initCtx) exportObjects(dc.ctx, initCtx)
} }
dc.nextFunc = ds[NextName] dc.nextFunc = ds[nextName]
dc.currentFunc = ds[CurrentName] dc.currentFunc = ds[currentName]
dc.cleanFunc = ds[CleanName] dc.cleanFunc = ds[cleanName]
dc.resetFunc = ds[ResetName] dc.resetFunc = ds[resetName]
v = dc v = dc
} else if list, ok := firstChildValue.(*ListType); ok { } else if list, ok := firstChildValue.(*ListType); ok {

View File

@ -34,8 +34,8 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
m, _ := childValue.(*DictType) m, _ := childValue.(*DictType)
v = int64(len(*m)) v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) { if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
count, _ := extIt.CallOperation(CountName, nil) count, _ := extIt.CallOperation(countName, nil)
v, _ = ToGoInt(count, "") v, _ = ToGoInt(count, "")
} else { } else {
v = int64(it.Index() + 1) v = int64(it.Index() + 1)

View File

@ -4,6 +4,10 @@
// operator-plugin.go // operator-plugin.go
package expr package expr
import (
"io"
)
//-------- plugin term //-------- plugin term
func newPluginTerm(tk *Token) (inst *term) { func newPluginTerm(tk *Token) (inst *term) {
@ -18,13 +22,31 @@ func newPluginTerm(tk *Token) (inst *term) {
func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) { func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var count int var moduleSpec any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if count, err = importPluginFromSearchPath(childValue); err == nil { dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count := 0
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 {
break
}
count++
} else {
err = opTerm.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
if err == nil {
v = int64(count) v = int64(count)
} }
return return

View File

@ -6,7 +6,6 @@ package expr
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"plugin" "plugin"
"strings" "strings"
@ -91,29 +90,6 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
return return
} }
func importPluginFromSearchPath(name any) (count int, err error) {
var moduleSpec any
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count = 0
it := NewAnyIterator(name)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil {
break
}
count++
} else {
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
return
}
func loadModules(dirList []string, moduleNames []string) (err error) { func loadModules(dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames { for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil { if err1 := importPlugin(dirList, name); err1 != nil {

View File

@ -14,7 +14,7 @@ func TestFuncRun(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil}, /* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil}, /* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil}, /* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){it_status=it_status+item; true}, {"it_status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil}, /* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`}, /* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, `paramter "operator" must be a function, passed <nil> [nil]`}, /* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, `paramter "operator" must be a function, passed <nil> [nil]`},
@ -26,3 +26,25 @@ func TestFuncRun(t *testing.T) {
//runTestSuiteSpec(t, section, inputs, 3) //runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
// func TestFmt(t *testing.T) {
// section := "Builtin-Fmt"
// text := "ciao mondo"
// inputs := []inputType{
// /* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
// }
// // t.Setenv("EXPR_PATH", ".")
// var b bytes.Buffer
// ctx := NewSimpleStore()
// currentStdout := SetCtrl(ctx, ControlStdout, &b)
// runCtxTestSuite(t, ctx, section, inputs)
// SetCtrl(ctx, ControlStdout, currentStdout)
// if b.String() != text+"\n" {
// t.Errorf("println(): Got: %q, Want: %q", b.String(), text+"\n")
// }
// }

View File

@ -9,15 +9,8 @@ import (
) )
func _TestImportPlugin(t *testing.T) { func _TestImportPlugin(t *testing.T) {
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go") if err := importPlugin([]string{"test-resources"}, "json"); err != nil {
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin") t.Errorf("importPlugin() failed: %v", err)
gotCount, gotErr := importPluginFromSearchPath("json")
if gotCount != 1 {
t.Errorf("Import count: got=%d, want=1", gotCount)
}
if gotErr != nil {
t.Errorf("importPlugin() failed: %v", gotErr)
} }
} }

View File

@ -6,11 +6,7 @@ package expr
import ( import (
"fmt" "fmt"
"os"
"os/user"
"path"
"reflect" "reflect"
"strings"
) )
func IsString(v any) (ok bool) { func IsString(v any) (ok bool) {
@ -225,37 +221,3 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
} }
return result return result
} }
func ExpandPath(sourcePath string) (expandedPath string, err error) {
for expandedPath = os.ExpandEnv(sourcePath); expandedPath != sourcePath; expandedPath = os.ExpandEnv(sourcePath) {
sourcePath = expandedPath
}
if strings.HasPrefix(sourcePath, "~") {
var home, userName, remainder string
slashPos := strings.IndexRune(sourcePath, '/')
if slashPos > 0 {
userName = sourcePath[1:slashPos]
remainder = sourcePath[slashPos:]
} else {
userName = sourcePath[1:]
}
if len(userName) == 0 {
home, err = os.UserHomeDir()
if err != nil {
return
}
} else {
var userInfo *user.User
userInfo, err = user.Lookup(userName)
if err != nil {
return
}
home = userInfo.HomeDir
}
expandedPath = path.Join(home, remainder)
}
return
}