Compare commits

..

14 Commits

23 changed files with 273 additions and 195 deletions
+3 -2
View File
@@ -12,6 +12,7 @@ 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) {
@@ -54,7 +55,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("it_status", nil) localCtx.UnsafeSetVar(iterVarStatus, nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil { if it, op, err = parseRunArgs(localCtx, args); err != nil {
return return
@@ -84,7 +85,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("it_status") result, _ = localCtx.GetVar(iterVarStatus)
} }
return return
} }
+4 -4
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
} }
} }
+89 -73
View File
@@ -5,30 +5,45 @@
package expr package expr
import ( import (
"errors"
"io" "io"
) )
type dataCursor struct { type dataCursor struct {
ds map[string]Functor ds map[string]Functor
ctx ExprContext ctx ExprContext
index int initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
resource any index int
nextFunc Functor count int
cleanFunc Functor current any
resetFunc Functor lastErr error
currentFunc Functor resource any
nextFunc Functor
cleanFunc Functor
resetFunc Functor
} }
func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
index: -1, initState: true,
ctx: ctx.Clone(), index: -1,
count: 0,
current: nil,
lastErr: nil,
resource: resource,
ctx: ctx.Clone(),
nextFunc: ds[NextName],
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
} }
//dc.Next()
return return
} }
func (dc *dataCursor) Context() ExprContext {
return dc.ctx
}
func (dc *dataCursor) TypeName() string { func (dc *dataCursor) TypeName() string {
return "DataCursor" return "DataCursor"
} }
@@ -62,7 +77,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)
@@ -71,7 +86,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 {
@@ -93,15 +108,19 @@ 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 { _, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource})
dc.index = -1
}
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
//dc.Next()
} else { } else {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
} else { } else {
err = errNoOperation(resetName) err = errNoOperation(ResetName)
} }
success = err == nil success = err == nil
return return
@@ -111,107 +130,104 @@ 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 {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
} else { } else {
err = errors.New("no 'clean' function defined in the data-source") err = errNoOperation(CleanName)
} }
success = err == nil success = err == nil
return return
} }
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) dc.init()
if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
if dc.current != nil {
item = dc.current
} else {
err = io.EOF err = io.EOF
} }
exportObjects(dc.ctx, ctx)
return return
} }
// func (dc *dataCursor) _Next() (item any, err error) { // must return io.EOF after the last item
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
// if item == nil {
// err = io.EOF
// } else {
// dc.index++
// }
// }
// // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// exportObjects(dc.ctx, ctx)
// // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
// } else {
// err = errInvalidDataSource()
// }
// return
// }
// func (dc *dataCursor) _filter(item any) (filterdItem any, err error) {
// if filter, ok := dc.ds[filterName]; ok {
// ctx := cloneContext(dc.ctx)
// filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index})
// } else {
// filterdItem = item
// }
// return
// }
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) { func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
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
} }
} }
return return
} }
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) init() {
var accepted bool if dc.initState {
if dc.resource != nil { dc.initState = false
filter := dc.ds[filterName] dc.Next()
mapper := dc.ds[mapName] }
}
for item == nil && err == nil { func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
if dc.initState {
dc.init()
} else if err = dc.lastErr; err != nil {
return
}
current = dc.current
if dc.resource != nil {
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil { dc.index++
if item, dc.lastErr = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource, dc.index}); dc.lastErr == nil {
if item == nil { if item == nil {
err = io.EOF dc.lastErr = io.EOF
} else { } else {
dc.index++ accepted := true
if filter != nil { if filter != nil {
if accepted, err = dc.checkFilter(filter, item); err != nil || !accepted { if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil item = nil
} }
} }
if accepted {
dc.count++
}
if item != nil && mapper != nil { if item != nil && mapper != nil {
item, err = dc.mapItem(mapper, item) item, dc.lastErr = dc.mapItem(mapper, item)
} }
} }
} }
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
} }
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
} else { } else {
err = errInvalidDataSource() dc.lastErr = errInvalidDataSource()
} }
return return
} }
func (dc *dataCursor) Index() int { func (dc *dataCursor) Index() int {
return dc.index return dc.index - 1
}
func (dc *dataCursor) Count() int {
return dc.count
} }
+15
View File
@@ -18,7 +18,22 @@ 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))
+8
View File
@@ -75,7 +75,11 @@ 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
@@ -90,6 +94,10 @@ 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
+10 -9
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 {
@@ -28,6 +28,7 @@ type Iterator interface {
Next() (item any, err error) // must return io.EOF after the last item Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error) Current() (item any, err error)
Index() int Index() int
Count() int
} }
type ExtIterator interface { type ExtIterator interface {
+11 -6
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)
@@ -143,7 +143,12 @@ func (it *ListIterator) Index() int {
return it.index return it.index
} }
func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (bool, error) { func (it *ListIterator) Reset() (bool, error) {
it.index = it.start - it.step it.index = it.start - it.step
it.count = 0
return true, nil return true, nil
} }
+10 -13
View File
@@ -66,8 +66,8 @@ 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{NextName}
fieldsMask := 0b11 fieldsMask := 0b1
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
@@ -88,7 +88,6 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
missingFields = append(missingFields, field) missingFields = append(missingFields, field)
} }
} }
// err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} }
@@ -108,9 +107,10 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
} }
if ds != nil { if ds != nil {
dc := newDataCursor(ctx, ds) var dc *dataCursor
if initFunc, exists := ds[initName]; exists && initFunc != nil { if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any var args []any
var resource 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 {
return return
@@ -119,18 +119,15 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
args = []any{} args = []any{}
} }
initCtx := dc.ctx.Clone() initCtx := ctx.Clone()
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { if resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil {
return return
} }
exportObjects(dc.ctx, initCtx) dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} }
dc.nextFunc = ds[nextName]
dc.currentFunc = ds[currentName]
dc.cleanFunc = ds[cleanName]
dc.resetFunc = ds[resetName]
v = dc v = dc
} else if list, ok := firstChildValue.(*ListType); ok { } else if list, ok := firstChildValue.(*ListType); ok {
var args []any var args []any
+7 -7
View File
@@ -30,16 +30,16 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if IsDict(childValue) { } else if IsDict(childValue) {
// m, _ := childValue.(map[any]any)
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) { v = int64(it.Count())
count, _ := extIt.CallOperation(countName, nil) // if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
v, _ = ToGoInt(count, "") // count, _ := extIt.CallOperation(CountName, nil)
} else { // v, _ = ToGoInt(count, "")
v = int64(it.Index() + 1) // } else {
} // v = int64(it.Index() + 1)
// }
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
+2 -24
View File
@@ -4,10 +4,6 @@
// 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) {
@@ -22,31 +18,13 @@ 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 moduleSpec any var count int
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) if count, err = importPluginFromSearchPath(childValue); err == nil {
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
+15 -10
View File
@@ -18,6 +18,11 @@ func NewParser() (p *parser) {
return p return p
} }
func (parser *parser) Next(scanner *scanner) (tk *Token) {
for tk=scanner.Next(); tk.IsSymbol(SymComment); tk=scanner.Next() {}
return
}
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) { func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10) args := make([]*term, 0, 10)
itemExpected := false itemExpected := false
@@ -57,11 +62,11 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
itemExpected := false itemExpected := false
tk := scanner.Previous() tk := scanner.Previous()
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
tk = scanner.Next() tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) { if tk.IsSymbol(SymIdentifier) {
param := newTerm(tk) param := newTerm(tk)
args = append(args, param) args = append(args, param)
tk = scanner.Next() tk = parser.Next(scanner)
if tk.Sym == SymEqual { if tk.Sym == SymEqual {
var paramExpr *ast var paramExpr *ast
defaultParamsStarted = true defaultParamsStarted = true
@@ -86,7 +91,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
err = tk.ErrorExpectedGot(")") err = tk.ErrorExpectedGot(")")
} }
if err == nil { if err == nil {
tk = scanner.Next() tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) { if tk.IsSymbol(SymOpenBrace) {
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace) body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
} else { } else {
@@ -184,8 +189,8 @@ func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree
return return
} }
func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) { func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
tk := scanner.Next() tk := parser.Next(scanner)
if tk.Sym == SymError { if tk.Sym == SymError {
err = tk.Error() err = tk.Error()
return return
@@ -194,7 +199,7 @@ func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any,
return return
} }
if tk.Sym == SymInteger || tk.Sym == SymString { if tk.Sym == SymInteger || tk.Sym == SymString {
tkSep := scanner.Next() tkSep := parser.Next(scanner)
if tkSep.Sym != SymColon { if tkSep.Sym != SymColon {
err = tkSep.ErrorExpectedGot(":") err = tkSep.ErrorExpectedGot(":")
} else { } else {
@@ -213,7 +218,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
for lastSym != SymClosedBrace && lastSym != SymEos { for lastSym != SymClosedBrace && lastSym != SymEos {
var subTree *ast var subTree *ast
var key any var key any
if key, err = parser.parseDictKey(scanner, allowVarRef); err != nil { if key, err = parser.parseDictKey(scanner); err != nil {
break break
} else if key == nil { } else if key == nil {
tk := scanner.Previous() tk := scanner.Previous()
@@ -251,7 +256,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) { func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term var filterList *term
var caseExpr *ast var caseExpr *ast
tk := scanner.Next() tk := parser.Next(scanner)
startRow := tk.row startRow := tk.row
startCol := tk.col startCol := tk.col
if tk.Sym == SymOpenSquare { if tk.Sym == SymOpenSquare {
@@ -262,7 +267,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil { if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
return return
} }
tk = scanner.Next() tk = parser.Next(scanner)
startRow = tk.row startRow = tk.row
startCol = tk.col startCol = tk.col
} else if !defaultCase { } else if !defaultCase {
@@ -340,7 +345,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
tree = NewAst() tree = NewAst()
firstToken := true firstToken := true
// lastSym := SymUnknown // lastSym := SymUnknown
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() { for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
if tk.Sym == SymComment { if tk.Sym == SymComment {
continue continue
} }
+24
View File
@@ -6,6 +6,7 @@ package expr
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"plugin" "plugin"
"strings" "strings"
@@ -90,6 +91,29 @@ 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 {
+1 -23
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){it_status=it_status+item; true}, {"it_status":0})`, int64(6), nil}, /* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"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,25 +26,3 @@ 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")
// }
// }
+1 -1
View File
@@ -97,7 +97,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
if gotErr != wantErr { if gotErr != wantErr {
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) { if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr) t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
good = false good = false
} }
} }
+7 -2
View File
@@ -22,7 +22,7 @@ func TestDictParser(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`{}`, map[any]any{}, nil}, /* 1 */ {`{}`, map[any]any{}, nil},
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)}, /* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil}, /* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil}, /* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil}, /* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
@@ -32,7 +32,12 @@ func TestDictParser(t *testing.T) {
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil}, /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)}, /* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil}, /* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
} /* 12 */ {`m={
"a":1,
//"b":2,
"c":3
}`, map[any]any{"a": 1, "c": 3}, nil},
}
succeeded := 0 succeeded := 0
failed := 0 failed := 0
+3 -3
View File
@@ -19,8 +19,8 @@ func TestExpr(t *testing.T) {
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {` /* 6 */ {`
ds={ ds={
"init":func(end){@end=end; @current=0 but true}, "init":func(@end){@current=0 but true},
"current":func(){current}, //"current":func(){current},
"next":func(){ "next":func(){
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil} ((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
} }
@@ -32,6 +32,6 @@ func TestExpr(t *testing.T) {
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 3) //runTestSuiteSpec(t, section, inputs, 6)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -27,7 +27,7 @@ func TestFuncs(t *testing.T) {
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`}, /* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil}, /* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`}, /* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, `[1:24] expected "function-param-value", got ")"`}, /* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil}, /* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil}, /* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil}, // /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
+7 -7
View File
@@ -15,7 +15,7 @@ func TestNewListIterator(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "b" { } else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item) t.Errorf("expected %q, got %q", "b", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -27,7 +27,7 @@ func TestNewListIterator2(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "d" { } else if item != "d" {
t.Errorf("expcted %q, got %q", "d", item) t.Errorf("expected %q, got %q", "d", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -39,7 +39,7 @@ func TestNewListIterator3(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "b" { } else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item) t.Errorf("expected %q, got %q", "b", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -51,7 +51,7 @@ func TestNewIterList2(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -63,7 +63,7 @@ func TestNewIterList3(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -83,7 +83,7 @@ func TestNewIterList5(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "123" { } else if item != "123" {
t.Errorf("expcted %q, got %q", "123", item) t.Errorf("expected %q, got %q", "123", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -96,7 +96,7 @@ func TestNewIterList6(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
+2 -2
View File
@@ -11,7 +11,7 @@ func TestIteratorParser(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil}, /* 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}, /* 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}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
/* 4 */ {`include "test-resources/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 "test-resources/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 "test-resources/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},
@@ -27,6 +27,6 @@ func TestIteratorParser(t *testing.T) {
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil}, /* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
} }
//runTestSuiteSpec(t, section, inputs, 4) //runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+3 -3
View File
@@ -128,8 +128,8 @@ func TestGeneralParser(t *testing.T) {
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil}, /* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
/* 115 */ {`nil`, nil, nil}, /* 115 */ {`nil`, nil, nil},
/* 116 */ {`null`, nil, `undefined variable or function "null"`}, /* 116 */ {`null`, nil, `undefined variable or function "null"`},
/* 117 */ {`{"key"}`, nil, `[1:8] expected ":", got "}"`}, /* 117 */ {`{"key"}`, nil, "[1:8] expected `:`, got `}`"},
/* 118 */ {`{"key":}`, nil, `[1:9] expected "dictionary-value", got "}"`}, /* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
/* 119 */ {`{}`, &DictType{}, nil}, /* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil}, /* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil}, /* 121 */ {`1+1|2+0.5`, float64(2), nil},
@@ -144,6 +144,6 @@ func TestGeneralParser(t *testing.T) {
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 102) //runTestSuiteSpec(t, section, inputs, 130)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+9 -2
View File
@@ -9,8 +9,15 @@ import (
) )
func _TestImportPlugin(t *testing.T) { func _TestImportPlugin(t *testing.T) {
if err := importPlugin([]string{"test-resources"}, "json"); err != nil { t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
t.Errorf("importPlugin() failed: %v", err) t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
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)
} }
} }
+3 -3
View File
@@ -28,7 +28,7 @@ func (tk *Token) DevString() string {
func (tk *Token) String() string { func (tk *Token) String() string {
if tk.Value != nil { if tk.Value != nil {
if s, ok := tk.Value.(string); ok { if s, ok := tk.Value.(string); ok {
return fmt.Sprintf("%q", s) return s //fmt.Sprintf("%q", s)
} else { } else {
return fmt.Sprintf("%v", tk.Value) return fmt.Sprintf("%v", tk.Value)
} }
@@ -91,11 +91,11 @@ func (tk *Token) Errors(msg string) (err error) {
} }
func (tk *Token) ErrorExpectedGot(symbol string) (err error) { func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, tk) err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, tk)
return return
} }
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) { func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, got) err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
return return
} }
+38
View File
@@ -6,7 +6,11 @@ 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) {
@@ -221,3 +225,37 @@ 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
}