Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 315f5b22d3 | |||
| dfae593e86 | |||
| d572f3a129 | |||
| c461fd138e | |||
| 6ecbe2feb1 | |||
| 80d3c6ec7d | |||
| e09806c716 | |||
| 1a772597cb | |||
| 33b3e1fc29 | |||
| 4e3f5cfbc6 | |||
| e35d4e3f70 | |||
| b4529499d6 | |||
| 7745dc24e2 | |||
| be25385d02 | |||
| 79889cd8e1 | |||
| 234759158c | |||
| 00fda29606 | |||
| 2a2840bdf2 | |||
| 06373f5126 | |||
| e69dad5fb5 | |||
| 7459057df9 | |||
| 176969c956 | |||
| cde2efacf1 | |||
| d7a7b3218c | |||
| be3bb12f28 | |||
| 032916d4fa | |||
| f3cc0cc7ad | |||
| 905337f963 | |||
| 9a95a837f6 | |||
| 8547248ea2 |
@@ -19,6 +19,10 @@ func NewAst() *ast {
|
|||||||
return &ast{}
|
return &ast{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (expr *ast) TypeName() string {
|
||||||
|
return "Expression"
|
||||||
|
}
|
||||||
|
|
||||||
func (expr *ast) ToForest() {
|
func (expr *ast) ToForest() {
|
||||||
if expr.root != nil {
|
if expr.root != nil {
|
||||||
if expr.forest == nil {
|
if expr.forest == nil {
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFuncto
|
|||||||
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (functor *exprFunctor) TypeName() string {
|
||||||
|
return "ExprFunctor"
|
||||||
|
}
|
||||||
|
|
||||||
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
||||||
return functor.defCtx
|
return functor.defCtx
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ func NewGolangFunctor(f FuncTemplate) *golangFunctor {
|
|||||||
return &golangFunctor{f: f}
|
return &golangFunctor{f: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (functor *golangFunctor) TypeName() string {
|
||||||
|
return "GoFunctor"
|
||||||
|
}
|
||||||
|
|
||||||
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
return functor.f(ctx, name, args)
|
return functor.f(ctx, name, args)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// builtin-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
iterParamOperator = "operator"
|
||||||
|
iterParamVars = "vars"
|
||||||
|
iterVarStatus = "status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if it, ok = args[0].(Iterator); !ok {
|
||||||
|
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[0], TypeName(args[0]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
if op, ok = args[1].(Functor); !ok || op == nil {
|
||||||
|
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[1], TypeName(args[1]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(args) > 2 {
|
||||||
|
var vars *DictType
|
||||||
|
if vars, ok = args[2].(*DictType); !ok || vars == nil {
|
||||||
|
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[2], TypeName(args[2]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for key, value := range *vars {
|
||||||
|
var varName string
|
||||||
|
if varName, ok = key.(string); ok {
|
||||||
|
localCtx.UnsafeSetVar(varName, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var it Iterator
|
||||||
|
var ok bool
|
||||||
|
var op Functor
|
||||||
|
var v any
|
||||||
|
var usingDefaultOp = false
|
||||||
|
var params []any
|
||||||
|
var item any
|
||||||
|
|
||||||
|
localCtx := ctx.Clone()
|
||||||
|
localCtx.UnsafeSetVar(iterVarStatus, nil)
|
||||||
|
|
||||||
|
if it, op, err = parseRunArgs(localCtx, args); err != nil {
|
||||||
|
return
|
||||||
|
} else if op == nil {
|
||||||
|
op = NewGolangFunctor(printLnFunc)
|
||||||
|
usingDefaultOp = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
|
if usingDefaultOp {
|
||||||
|
params = []any{item}
|
||||||
|
} else {
|
||||||
|
params = []any{it.Index(), item}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err = op.Invoke(localCtx, iterParamOperator, params); err != nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
var success bool
|
||||||
|
if success, ok = ToBool(v); !success || !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
result, _ = localCtx.GetVar(iterVarStatus)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportIterFuncs(ctx ExprContext) {
|
||||||
|
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParam(ParamIterator),
|
||||||
|
NewFuncParamFlag(iterParamOperator, PfOptional),
|
||||||
|
NewFuncParamFlag(iterParamVars, PfOptional),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -19,9 +19,10 @@ const (
|
|||||||
ParamEllipsis = "..."
|
ParamEllipsis = "..."
|
||||||
ParamFilepath = "filepath"
|
ParamFilepath = "filepath"
|
||||||
ParamDirpath = "dirpath"
|
ParamDirpath = "dirpath"
|
||||||
|
ParamIterator = "iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// to be moved in its own source file
|
// to be moved in its own source file
|
||||||
const (
|
const (
|
||||||
ConstLastIndex = 0xFFFF_FFFF
|
ConstLastIndex = 0xFFFF_FFFF
|
||||||
)
|
)
|
||||||
|
|||||||
+102
-61
@@ -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,82 +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
|
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
|
||||||
if dc.resource != nil {
|
var v any
|
||||||
ctx := cloneContext(dc.ctx)
|
var ok bool
|
||||||
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
|
ctx := cloneContext(dc.ctx)
|
||||||
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
|
if v, err = filter.Invoke(ctx, FilterName, []any{item, dc.index}); err == nil && v != nil {
|
||||||
if item == nil {
|
if accepted, ok = v.(bool); !ok {
|
||||||
err = io.EOF
|
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
|
||||||
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterName = "filter"
|
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
|
||||||
func (dc *dataCursor) filter(item any) (filterdItem any, err error) {
|
ctx := cloneContext(dc.ctx)
|
||||||
if filter, ok := dc.ds[filterName]; ok {
|
mappedItem, err = mapper.Invoke(ctx, MapName, []any{item, dc.index})
|
||||||
ctx := cloneContext(dc.ctx)
|
|
||||||
filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index});
|
|
||||||
} else {
|
|
||||||
filterdItem = item
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
|
func (dc *dataCursor) init() {
|
||||||
|
if dc.initState {
|
||||||
|
dc.initState = false
|
||||||
|
dc.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
filter := dc.ds[FilterName]
|
||||||
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
|
mapper := dc.ds[MapName]
|
||||||
for item == nil && err == nil {
|
var item any
|
||||||
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
|
for item == nil && dc.lastErr == nil {
|
||||||
|
ctx := cloneContext(dc.ctx)
|
||||||
|
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
|
||||||
item, err = dc.filter(item)
|
if filter != nil {
|
||||||
|
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
||||||
|
item = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if accepted {
|
||||||
|
dc.count++
|
||||||
|
}
|
||||||
|
if item != nil && mapper != nil {
|
||||||
|
item, dc.lastErr = dc.mapItem(mapper, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exportObjects(dc.ctx, ctx)
|
||||||
|
}
|
||||||
|
dc.current = item
|
||||||
|
if dc.lastErr != nil {
|
||||||
|
dc.index--
|
||||||
|
dc.Clean()
|
||||||
}
|
}
|
||||||
// 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 {
|
} 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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -14,8 +14,15 @@ type ExprContext interface {
|
|||||||
GetLast() any
|
GetLast() any
|
||||||
SetVar(varName string, value any)
|
SetVar(varName string, value any)
|
||||||
UnsafeSetVar(varName string, value any)
|
UnsafeSetVar(varName string, value any)
|
||||||
|
|
||||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||||
|
VarCount() int
|
||||||
|
DeleteVar(varName string)
|
||||||
|
|
||||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
|
FuncCount() int
|
||||||
|
DeleteFunc(funcName string)
|
||||||
|
|
||||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||||
Call(name string, args []any) (result any, err error)
|
Call(name string, args []any) (result any, err error)
|
||||||
RegisterFuncInfo(info ExprFunc)
|
RegisterFuncInfo(info ExprFunc)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
// ---- Functor interface
|
// ---- Functor interface
|
||||||
type Functor interface {
|
type Functor interface {
|
||||||
|
Typer
|
||||||
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
|
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
|
||||||
SetFunc(info ExprFunc)
|
SetFunc(info ExprFunc)
|
||||||
GetFunc() ExprFunc
|
GetFunc() ExprFunc
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
// ----Expression interface
|
// ----Expression interface
|
||||||
type Expr interface {
|
type Expr interface {
|
||||||
|
Typer
|
||||||
Eval(ctx ExprContext) (result any, err error)
|
Eval(ctx ExprContext) (result any, err error)
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
-7
@@ -12,13 +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"
|
||||||
|
MapName = "map"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
@@ -26,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 {
|
||||||
|
|||||||
+13
-4
@@ -90,17 +90,21 @@ func (it *ListIterator) TypeName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) HasOperation(name string) bool {
|
func (it *ListIterator) HasOperation(name string) bool {
|
||||||
yes := name == resetName || name == indexName || name == countName
|
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 resetName:
|
case NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case ResetName:
|
||||||
v, err = it.Reset()
|
v, err = it.Reset()
|
||||||
case indexName:
|
case IndexName:
|
||||||
v = int64(it.Index())
|
v = int64(it.Index())
|
||||||
case countName:
|
case CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case CountName:
|
||||||
v = it.count
|
v = it.count
|
||||||
default:
|
default:
|
||||||
err = errNoOperation(name)
|
err = errNoOperation(name)
|
||||||
@@ -139,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
@@ -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
|
||||||
|
|||||||
+3
-3
@@ -11,7 +11,7 @@ func newDefaultTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priCoalesce,
|
priority: priDefault,
|
||||||
evalFunc: evalDefault,
|
evalFunc: evalDefault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ func newAlternateTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priCoalesce,
|
priority: priDefault,
|
||||||
evalFunc: evalAlternate,
|
evalFunc: evalAlternate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ func newDefaultAssignTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priCoalesce,
|
priority: priDefault,
|
||||||
evalFunc: evalAssignDefault,
|
evalFunc: evalAssignDefault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -25,8 +25,8 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if IsList(childValue) {
|
if IsList(childValue) {
|
||||||
list, _ := childValue.([]any)
|
list, _ := childValue.(*ListType)
|
||||||
for i, filePathSpec := range list {
|
for i, filePathSpec := range *list {
|
||||||
if filePath, ok := filePathSpec.(string); ok {
|
if filePath, ok := filePathSpec.(string); ok {
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
count++
|
count++
|
||||||
@@ -47,8 +47,9 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err != nil {
|
||||||
v = count
|
//v = count
|
||||||
|
v = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,5 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||||
|
registerTermConstructor(SymCaret, newIterValueTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-unset.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
//-------- unset term
|
||||||
|
|
||||||
|
func newUnsetTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priSign,
|
||||||
|
evalFunc: evalUnset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, err error) {
|
||||||
|
if name, ok := item.(string); ok {
|
||||||
|
var size int
|
||||||
|
if strings.HasSuffix(name, "()") {
|
||||||
|
size = ctx.FuncCount()
|
||||||
|
ctx.DeleteFunc(strings.TrimRight(name, "()"))
|
||||||
|
deleted = ctx.FuncCount() < size
|
||||||
|
} else {
|
||||||
|
size = ctx.VarCount()
|
||||||
|
ctx.DeleteVar(name)
|
||||||
|
deleted = ctx.VarCount() < size
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleType(item)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var childValue any
|
||||||
|
var deleted bool
|
||||||
|
|
||||||
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
if IsList(childValue) {
|
||||||
|
list, _ := childValue.(*ListType)
|
||||||
|
for _, item := range *list {
|
||||||
|
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
|
||||||
|
break
|
||||||
|
} else if deleted {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if deleted, err = deleteContextItem(ctx, opTerm, childValue); err == nil && deleted {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = int64(count)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymKwUnset, newUnsetTerm)
|
||||||
|
}
|
||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
+4
-4
@@ -269,11 +269,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk = scanner.makeToken(SymDollar, ch)
|
tk = scanner.makeToken(SymDollar, ch)
|
||||||
}
|
}
|
||||||
case '(':
|
case '(':
|
||||||
if next, _ := scanner.peek(); next == ')' {
|
// if next, _ := scanner.peek(); next == ')' {
|
||||||
tk = scanner.moveOn(SymOpenClosedRound, ch, next)
|
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
|
||||||
} else {
|
// } else {
|
||||||
tk = scanner.makeToken(SymOpenRound, ch)
|
tk = scanner.makeToken(SymOpenRound, ch)
|
||||||
}
|
// }
|
||||||
case ')':
|
case ')':
|
||||||
tk = scanner.makeToken(SymClosedRound, ch)
|
tk = scanner.makeToken(SymClosedRound, ch)
|
||||||
case '[':
|
case '[':
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varN
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) VarCount() int {
|
||||||
|
return len(ctx.varStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) DeleteVar(varName string) {
|
||||||
|
delete(ctx.varStore, varName)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
||||||
info, exists = ctx.funcStore[name]
|
info, exists = ctx.funcStore[name]
|
||||||
return
|
return
|
||||||
@@ -163,6 +171,14 @@ func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (fun
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) FuncCount() int {
|
||||||
|
return len(ctx.funcStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) DeleteFunc(funcName string) {
|
||||||
|
delete(ctx.funcStore, funcName)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
|
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
|
||||||
if info, exists := GetLocalFuncInfo(ctx, name); exists {
|
if info, exists := GetLocalFuncInfo(ctx, name); exists {
|
||||||
functor := info.Functor()
|
functor := info.Functor()
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ const (
|
|||||||
SymKwIn
|
SymKwIn
|
||||||
SymKwInclude
|
SymKwInclude
|
||||||
SymKwNil
|
SymKwNil
|
||||||
|
SymKwUnset
|
||||||
)
|
)
|
||||||
|
|
||||||
var keywords map[string]Symbol
|
var keywords map[string]Symbol
|
||||||
@@ -123,5 +124,6 @@ func init() {
|
|||||||
"NOT": SymKwNot,
|
"NOT": SymKwNot,
|
||||||
"OR": SymKwOr,
|
"OR": SymKwOr,
|
||||||
"NIL": SymKwNil,
|
"NIL": SymKwNil,
|
||||||
|
"UNSET": SymKwUnset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncRun(t *testing.T) {
|
||||||
|
section := "Builtin-Iterator"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 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},
|
||||||
|
/* 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},
|
||||||
|
/* 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]`},
|
||||||
|
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
//runTestSuiteSpec(t, section, inputs, 3)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
+1
-1
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -27,16 +27,17 @@ 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 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
||||||
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 20)
|
//runTestSuiteSpec(t, section, inputs, 20)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -9,10 +9,10 @@ import "testing"
|
|||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
section := "Iterator"
|
section := "Iterator"
|
||||||
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},
|
||||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||||
@@ -20,9 +20,13 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
||||||
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||||
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
|
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
|
||||||
// /* 12 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(1), nil},
|
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
|
||||||
|
/* 13 */ {`it=$([1,2,3,4],1,3); it++; it++;`, int64(3), nil},
|
||||||
|
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
|
||||||
|
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
|
||||||
|
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 12)
|
//runTestSuiteSpec(t, section, inputs, 3)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_operator_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOperator(t *testing.T) {
|
||||||
|
section := "Operator"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
|
||||||
|
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
|
||||||
|
/* 3 */ {`f=func(){3}; unset "f()"`, int64(1), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
//runTestSuiteSpec(t, section, inputs, 3)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
+3
-3
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const (
|
|||||||
priSign
|
priSign
|
||||||
priFact
|
priFact
|
||||||
priIterValue
|
priIterValue
|
||||||
priCoalesce
|
priDefault
|
||||||
priIncDec
|
priIncDec
|
||||||
priDot
|
priDot
|
||||||
priValue
|
priValue
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
ds={
|
||||||
|
"init":func(end){@end=end; @current=0 but true},
|
||||||
|
"current":func(){current},
|
||||||
|
"next":func(){
|
||||||
|
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
|
||||||
|
}
|
||||||
|
, "filter":func(item,index) { item > 0 }
|
||||||
|
, "map": func(item,index) { item * 2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Example
|
||||||
|
;
|
||||||
|
it=$(ds,3);
|
||||||
|
add(it)
|
||||||
|
*/
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user