Compare commits

..

No commits in common. "f100adead3f34c911bf69757708bd401b7a4cbcf" and "b6952444ab234585e18d29ec11416eab82746554" have entirely different histories.

37 changed files with 138 additions and 965 deletions

View File

@ -23,11 +23,9 @@ func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Fu
return
}
if args[iterParamOperator] != nil {
if op, ok = args[iterParamOperator].(Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
return
}
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
return
}
var vars *DictType
@ -53,7 +51,7 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
var ok bool
var op Functor
var v any
// var usingDefaultOp = false
var usingDefaultOp = false
var params map[string]any
var item any
@ -62,27 +60,24 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
if it, op, err = parseRunArgs(localCtx, args); err != nil {
return
// } else if op == nil {
// op = NewGolangFunctor(printLnFunc)
// usingDefaultOp = true
} else if op == nil {
op = NewGolangFunctor(printLnFunc)
usingDefaultOp = true
}
for item, err = it.Next(); err == nil; item, err = it.Next() {
// if usingDefaultOp {
// params = map[string]any{ParamItem: []any{item}}
// } else {
// params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
// }
if op != nil {
if usingDefaultOp {
params = map[string]any{ParamItem: []any{item}}
} else {
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
if success, ok = ToBool(v); !success || !ok {
break
} else {
var success bool
if success, ok = ToBool(v); !success || !ok {
break
}
}
}
}
@ -91,9 +86,6 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
err = nil
}
if err == nil {
if op == nil {
ctx.UnsafeSetVar(iterVarStatus, it.Count())
}
result, _ = localCtx.GetVar(iterVarStatus)
}
return

View File

@ -1,149 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"fmt"
"io"
"slices"
)
const paramHandleOrPath = "handle-or-path"
const fileReadTextIteratorType = "fileReadTextIterator"
type fileReadTextIterator struct {
osReader *osReader
index int
count int
line string
autoClose bool
}
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
}
func (it *fileReadTextIterator) TypeName() string {
return fileReadTextIteratorType
}
func (it *fileReadTextIterator) String() string {
if it.osReader != nil && it.osReader.fh != nil {
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
}
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
}
func (it *fileReadTextIterator) Count() int {
return it.count
}
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.osReader.fh != nil {
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
it.index++
it.count++
item = it.line[0 : len(it.line)-1]
} else if it.autoClose {
it.Clean()
}
}
return
}
func (it *fileReadTextIterator) Current() (item any, err error) {
if len(it.line) > 0 {
item = it.line[0 : len(it.line)-1]
}
return
}
func (it *fileReadTextIterator) Index() int {
return it.index
}
func (it *fileReadTextIterator) Reset() (err error) {
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
it.index = -1
it.count = 0
it.line = ""
}
return
}
func (it *fileReadTextIterator) HasOperation(name string) bool {
return slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName}, name)
}
func (it *fileReadTextIterator) Clean() (err error) {
if it.osReader.fh != nil {
if err = it.osReader.fh.Close(); err == nil {
it.osReader = nil
}
}
return nil
}
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case NextName:
v, err = it.Next()
case ResetName:
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
v, err = it.Current()
case CountName:
v = it.count
default:
err = errNoOperation(name)
}
return
}
func fileReadIteratorFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle *osReader
var invalidFileHandle any
var ok, autoClose bool
result = nil
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
var handleAny any
if handleAny, err = openFileFunc(ctx, name, map[string]any{ParamFilepath: fileName}); err != nil {
return
}
if handleAny != nil {
handle = handleAny.(*osReader)
autoClose = true
}
} else {
invalidFileHandle = args[paramHandleOrPath]
}
}
if handle != nil {
result = newReadTextIterator(handle, autoClose)
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
// func ImportOsIterFuncs(ctx ExprContext) {
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
// NewFuncParam(paramHandleOrPath),
// })
// }
// func init() {
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
// }

View File

@ -250,10 +250,6 @@ func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamHandle),
})
ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
NewFuncParam(paramHandleOrPath),
})
}
func init() {

View File

@ -13,7 +13,6 @@ const (
TypeFileHandle = "file-handle"
TypeInt = "integer"
TypeItem = "item"
TypeIterator = "iterator"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"

View File

@ -68,54 +68,42 @@ func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone
var s string
var argAny any
dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
it = &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
if len(args) > 0 {
argAny = args[0]
} else {
argAny = "default"
}
if s, err = ToGoString(argAny, "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
case "d", "desc":
sortType = sortTypeDesc
case "n", "none", "nosort", "no-sort":
sortType = sortTypeNone
case "", "default":
sortType = sortTypeDefault
default:
err = fmt.Errorf("invalid sort type %q", s)
}
if err == nil {
if len(args) > 1 {
argAny = args[1]
} else {
argAny = "default"
if s, err = ToGoString(args[0], "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
case "d", "desc":
sortType = sortTypeDesc
case "n", "none", "nosort", "no-sort":
sortType = sortTypeNone
case "", "default":
sortType = sortTypeDefault
default:
err = fmt.Errorf("invalid sort type %q", s)
}
if s, err = ToGoString(argAny, "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
dictIt.iterMode = dictIterModeKeys
case "v", "value", "values":
dictIt.iterMode = dictIterModeValues
case "i", "item", "items":
dictIt.iterMode = dictIterModeItems
case "", "default":
dictIt.iterMode = dictIterModeKeys
default:
err = fmt.Errorf("invalid iteration mode %q", s)
if err == nil && len(args) > 1 {
if s, err = ToGoString(args[1], "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
it.iterMode = dictIterModeKeys
case "v", "value", "values":
it.iterMode = dictIterModeValues
case "i", "item", "items":
it.iterMode = dictIterModeItems
case "", "default":
it.iterMode = dictIterModeKeys
default:
err = fmt.Errorf("invalid iteration mode %q", s)
}
}
}
}
}
dictIt.makeKeys(*dict, sortType)
return dictIt, err
it.makeKeys(*dict, sortType)
return
}
func NewMapIterator(m map[any]any) (it *DictIterator) {
@ -129,7 +117,7 @@ func (it *DictIterator) String() string {
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$({#%d})", l)
return fmt.Sprintf("$(#%d)", l)
}
func (it *DictIterator) TypeName() string {

View File

@ -1087,8 +1087,6 @@ The "base" builtin module provides functions for type checking and type conversi
.Other functions
* <<_eval,eval()>>
* <<_set,set()>>
* <<_unset,unset()>>
* <<_var,var()>>
@ -1320,32 +1318,6 @@ This function allows you to define variables whose names must include special ch
`>>>` [blue]`var("gain%", var("$x")+1)` +
[green]`13`
===== set
Syntax: +
`{4sp}set(<string-expr>, <expr>) -> any`
This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.
It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.
.Examples
`>>>` [blue]`set("$x", 100)` +
[green]`100` +
`>>>` [blue]`var("$x")` +
[green]`100` +
===== unset()
Syntax: +
`{4sp}unset(<string-expr>) -> any`
This function allows you to unset a variable whose name can include special characters. The parameter is the name of the variable to unset.
.Examples
`>>>` [blue]`unset("$x")` +
[green]`nil` +
`>>>` [blue]`var("$x")` +
[red]`Eval Error: var(): unknown variable "$x"`
==== Module "fmt"
===== print()
@ -1353,31 +1325,18 @@ This function allows you to unset a variable whose name can include special char
===== println()
==== Module "import"
Module actiovation: +
===== _import()_
Syntax: +
`{4sp}import(<source-file>)`
Loads the multi-expression contained in the specified source and returns its value.
[blue]_import([grey]#<source-file>#)_ -- loads the multi-expression contained in the specified source and returns its value.
===== _importAll()_
==== Module "iterator"
===== run()
Syntax: +
`{4sp}run(<iterator>, <operator>, <vars>) -> any`
Iterates over the specified iterator and applies the specified operator to the current value of the iterator.
==== Module "math.arith"
Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.
* <<_add,add()>>
* <<_mul,mul()>>
===== add()
Syntax: +
`{4sp}add(<num-expr1>, <num-expr2>, ...) -> any` +

View File

@ -76,12 +76,6 @@ func isFile(filePath string) bool {
func searchAmongPath(filename string, dirList []string) (filePath string) {
var err error
suffix := SHAREDLIBRARY_EXTENSION
if strings.HasSuffix(filename, ".debug") {
suffix += ".debug"
}
for _, dir := range dirList {
if dir, err = ExpandPath(dir); err != nil {
continue
@ -90,12 +84,6 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
filePath = fullPath
break
}
subdir := strings.TrimSuffix(filename, suffix)
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
filePath = fullPath
break
}
}
return
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iter-factory.go
package expr
func NewIterator(value any) (it Iterator, err error) {
if value == nil {
return NewArrayIterator([]any{}), nil
}
switch v := value.(type) {
case *ListType:
it = NewListIterator(v, nil)
case *DictType:
it, err = NewDictIterator(v, nil)
case []any:
it = NewArrayIterator(v)
case Iterator:
it = v
default:
it = NewArrayIterator([]any{value})
}
return
}

View File

@ -27,7 +27,6 @@ const (
type Iterator interface {
Typer
fmt.Stringer
Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error)
Index() int

View File

@ -1,9 +0,0 @@
//go:build darwin
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-darwin.go
package expr
const SHAREDLIBRARY_EXTENSION = ".dylib"

View File

@ -1,9 +0,0 @@
//go:build linux
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-linux.go
package expr
const SHAREDLIBRARY_EXTENSION = ".so"

View File

@ -1,9 +0,0 @@
//go:build windows
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-windows.go
package expr
const SHAREDLIBRARY_EXTENSION = ".dll"

View File

@ -62,12 +62,27 @@ func NewArrayIterator(array []any) (it *ListIterator) {
return
}
func NewAnyIterator(value any) (it *ListIterator) {
if value == nil {
it = NewArrayIterator([]any{})
} else if list, ok := value.(*ListType); ok {
it = NewListIterator(list, nil)
} else if array, ok := value.([]any); ok {
it = NewArrayIterator(array)
} else if it1, ok := value.(*ListIterator); ok {
it = it1
} else {
it = NewArrayIterator([]any{value})
}
return
}
func (it *ListIterator) String() string {
var l = 0
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$([#%d])", l)
return fmt.Sprintf("$(#%d)", l)
}
func (it *ListIterator) TypeName() string {

View File

@ -26,9 +26,9 @@ func newList(listAny []any) (list *ListType) {
func NewList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
// for i, item := range listAny {
// ls[i] = item
// }
// for i, item := range listAny {
// ls[i] = item
// }
copy(ls, listAny)
list = &ls
}
@ -53,14 +53,14 @@ func ListFromStrings(stringList []string) (list *ListType) {
}
func (ls *ListType) ToString(opt FmtOpt) (s string) {
indent := GetFormatIndent(opt)
flags := GetFormatFlags(opt)
indent := GetFormatIndent(opt)
flags := GetFormatFlags(opt)
var sb strings.Builder
sb.WriteByte('[')
if len(*ls) > 0 {
innerOpt := MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
innerOpt := MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
if flags&MultiLine != 0 {
sb.WriteByte('\n')
@ -129,20 +129,6 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
return
}
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls1) == len(ls2) {
answer = true
for index, i1 := range *ls1 {
// if i1 != (ls2)[index] {
if !reflect.DeepEqual(i1, ls2[index]) {
answer = false
break
}
}
}
return
}
func (list *ListType) indexDeepSameCmp(target any) (index int) {
var eq bool
var err error
@ -207,6 +193,3 @@ func (list *ListType) setItem(index int64, value any) (err error) {
return
}
func (list *ListType) appendItem(value any) {
*list = append(*list, value)
}

View File

@ -31,10 +31,7 @@ func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
count, err = ImportInContextByGlobPattern(module)
} else {
var moduleSpec any
var it Iterator
if it, err = NewIterator(childValue); err != nil {
return
}
it := NewAnyIterator(childValue)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if ImportInContext(module) {

View File

@ -1,70 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-digest.go
package expr
import (
"fmt"
"io"
)
//-------- digest term
func newDigestTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalDigest,
}
}
func evalDigest(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it Iterator
var item, lastValue any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", TypeName(leftValue))
}
lastValue = nil
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("_index", it.Index())
ctx.SetVar("_count", it.Count())
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
if rightValue == nil {
break
} else {
lastValue = rightValue
}
}
ctx.DeleteVar("_count")
ctx.DeleteVar("_index")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = lastValue
return
}
// init
func init() {
registerTermConstructor(SymKwDigest, newDigestTerm)
}

View File

@ -1,72 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-filter.go
package expr
import (
"fmt"
"io"
)
//-------- map term
func newFilterTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalFilter,
}
}
func evalFilter(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of FILTER must be an iterable data-source; got %s", TypeName(leftValue))
}
values := newListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("_index", it.Index())
ctx.SetVar("_count", it.Count())
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
if success, valid := ToBool(rightValue); valid {
if success {
values.appendItem(item)
}
} else {
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
}
}
ctx.DeleteVar("_count")
ctx.DeleteVar("_index")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwFilter, newFilterTerm)
}

View File

@ -1,65 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-join.go
package expr
import (
"fmt"
"io"
)
//-------- join term
func newJoinTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalJoin,
}
}
func evalJoin(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var itLeft, itRight Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return
}
if rightValue, err = opTerm.children[1].compute(ctx); err != nil {
return
}
if itLeft, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", TypeName(leftValue))
}
if itRight, err = NewIterator(rightValue); err != nil {
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", TypeName(rightValue))
}
values := newListA()
for _, it := range []Iterator{itLeft, itRight} {
for item, err = it.Next(); err == nil; item, err = it.Next() {
values.appendItem(item)
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwJoin, newJoinTerm)
}

View File

@ -1,66 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-map.go
package expr
import (
"fmt"
"io"
)
//-------- map term
func newMapTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalMap,
}
}
func evalMap(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue))
}
values := newListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("_index", it.Index())
ctx.SetVar("_count", it.Count())
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
values.appendItem(rightValue)
}
ctx.DeleteVar("_count")
ctx.DeleteVar("_index")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwMap, newMapTerm)
}

View File

@ -31,11 +31,11 @@ func pluginExists(name string) (exists bool) {
func makePluginName(name string) (decorated string) {
var template string
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
template = "expr-%s-plugin%s"
template = "expr-%s-plugin.so"
} else {
template = "expr-%s-plugin%s.debug"
template = "expr-%s-plugin.so.debug"
}
decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION)
decorated = fmt.Sprintf(template, name)
return
}
@ -93,12 +93,10 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
func importPluginFromSearchPath(name any) (count int, err error) {
var moduleSpec any
var it Iterator
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count = 0
if it, err = NewIterator(name); err != nil {
return
}
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 {

View File

@ -210,14 +210,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.makeToken(SymQuote, ch)
escape = false
} else {
tk = scanner.fetchString(ch, true)
tk = scanner.fetchString(ch)
}
case '"':
if escape {
tk = scanner.makeToken(SymDoubleQuote, ch)
escape = false
} else {
tk = scanner.fetchString(ch, true)
tk = scanner.fetchString(ch)
}
case '`':
tk = scanner.makeToken(SymBackTick, ch)
@ -315,14 +315,6 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk.source += ")"
} else if next == '$' {
tk = scanner.moveOn(SymDoubleDollar, ch, next)
} else if next == '{' {
scanner.readChar()
if tk = scanner.fetchString('}', false); tk != nil {
tk.Sym = SymIdentifier
}
} else if next == '_' || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
scanner.readChar()
tk = scanner.fetchIdentifier(next)
} else {
tk = scanner.makeToken(SymDollar, ch)
}
@ -598,7 +590,7 @@ func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (
return
}
func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
var err error
var ch, prev byte
var sb strings.Builder
@ -636,11 +628,7 @@ func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
}
} else {
txt := sb.String()
if addQuote {
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
} else {
tk = scanner.makeValueToken(SymString, txt, txt)
}
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
}
return
}

View File

@ -135,10 +135,6 @@ func init() {
SymKwNot: {"not", symClassOperator, posInfix},
SymKwOr: {"or", symClassOperator, posInfix},
SymKwBut: {"but", symClassOperator, posInfix},
SymKwMap: {"map", symClassOperator, posInfix},
SymKwFilter: {"filter", symClassOperator, posInfix},
SymKwDigest: {"digest", symClassOperator, posInfix},
SymKwJoin: {"join", symClassOperator, posInfix},
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
SymKwPlugin: {"plugin", symClassOperator, posPrefix},

View File

@ -119,10 +119,6 @@ const (
SymKwPlugin
SymKwIn
SymKwInclude
SymKwMap
SymKwFilter
SymKwDigest
SymKwJoin
SymKwNil
SymKwUnset
)
@ -139,13 +135,9 @@ func init() {
"FUNC": SymKwFunc,
"IN": SymKwIn,
"INCLUDE": SymKwInclude,
"MAP": SymKwMap,
"FILTER": SymKwFilter,
"NOT": SymKwNot,
"OR": SymKwOr,
"NIL": SymKwNil,
"UNSET": SymKwUnset,
"DIGEST": SymKwDigest,
"JOIN": SymKwJoin,
}
}

View File

@ -21,11 +21,10 @@ func TestFuncRun(t *testing.T) {
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileReadIterator("test-file.txt"); run(it)`, nil, nil},
}
//t.Setenv("EXPR_PATH", ".")
runTestSuiteSpec(t, section, inputs, 10)
// runTestSuite(t, section, inputs)
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}

View File

@ -5,7 +5,6 @@
package expr
import (
"io"
"testing"
)
@ -27,16 +26,10 @@ func TestFuncOs(t *testing.T) {
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
/* 16 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++`, "uno", nil},
/* 17 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()},
/* 18 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it.clean`, nil, nil},
/* 19 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); string(it)`, `$(fileReadTextIterator@"test-file.txt")`, nil},
/* 20 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle); string(handle)`, `reader`, nil},
/* 21 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle); string(handle)`, `writer`, nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 19)
//runTestSuiteSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs)
}

View File

@ -72,7 +72,6 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
var expr Expr
var gotResult any
var gotErr error
var eq, eqDone bool
wantErr := getWantedError(input)
@ -91,18 +90,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
gotResult, gotErr = expr.Eval(ctx)
}
if input.wantResult != nil && gotResult != nil {
if ls1, ok := input.wantResult.(*ListType); ok {
if ls2, ok := gotResult.(*ListType); ok {
eq = ls1.Equals(*ls2)
eqDone = true
}
}
}
if !eqDone {
eq = reflect.DeepEqual(gotResult, input.wantResult)
}
eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ {
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))

View File

@ -43,11 +43,9 @@ func TestExpr(t *testing.T) {
it++;
it++
`, int64(1), nil},
/* 20 */ {`a=2; ${a}`, int64(2), nil},
/* 21 */ {`$_=2; $_`, int64(2), nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 21)
// runTestSuiteSpec(t, section, inputs, 18)
runTestSuite(t, section, inputs)
}

View File

@ -59,7 +59,7 @@ func TestNewIterList2(t *testing.T) {
func TestNewIterList3(t *testing.T) {
list := []any{"a", "b", "c", "d"}
it, _ := NewIterator(list)
it := NewAnyIterator(list)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
@ -71,7 +71,7 @@ func TestNewIterList3(t *testing.T) {
func TestNewIterList4(t *testing.T) {
list := any(nil)
it, _ := NewIterator(list)
it := NewAnyIterator(list)
if _, err := it.Next(); err != io.EOF {
t.Errorf("error: %v", err)
}
@ -79,7 +79,7 @@ func TestNewIterList4(t *testing.T) {
func TestNewIterList5(t *testing.T) {
list := "123"
it, _ := NewIterator(list)
it := NewAnyIterator(list)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "123" {
@ -91,8 +91,8 @@ func TestNewIterList5(t *testing.T) {
func TestNewIterList6(t *testing.T) {
list := newListA("a", "b", "c", "d")
it1, _ := NewIterator(list)
it, _ := NewIterator(it1)
it1 := NewAnyIterator(list)
it := NewAnyIterator(it1)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
@ -103,9 +103,9 @@ func TestNewIterList6(t *testing.T) {
}
func TestNewString(t *testing.T) {
list := "123"
it, _ := NewIterator(list)
if s := it.String(); s != "$([#1])" {
t.Errorf("expected $([#1]), got %s", s)
it := NewAnyIterator(list)
if s := it.String(); s != "$(#1)" {
t.Errorf("expected $(#1), got %s", s)
}
}

View File

@ -31,11 +31,8 @@ func TestIteratorParser(t *testing.T) {
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, NewList([]any{int64(1), "one"}), nil},
/* 23 */ {`builtin "os.file"; fileReadIterator("test-file.txt") map ${_index}`, NewList([]any{int64(0), int64(1)}), nil},
/* 24 */ {`builtin "os.file"; #(fileReadIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
/* 25 */ {`builtin "os.file"; #(fileReadIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
}
// runTestSuiteSpec(t, section, inputs, 23)
// runTestSuiteSpec(t, section, inputs, 20)
runTestSuite(t, section, inputs)
}

View File

@ -30,19 +30,13 @@ func TestOperator(t *testing.T) {
/* 17 */ {`~true`, nil, `[1:2] prefix/postfix operator "~" do not support operand 'true' [bool]`},
/* 18 */ {`1^2`, int64(3), nil},
/* 19 */ {`3^2`, int64(1), nil},
/* 20 */ {`a=1; a^=2`, int64(3), nil},
/* 21 */ {`a=1; ++a`, int64(2), nil},
/* 22 */ {`a=1; --a`, int64(0), nil},
/* 23 */ {`[1,2,3] map var("_")`, newList([]any{int64(1), int64(2), int64(3)}), nil},
/* 24 */ {`[1,2,3] map $_`, newList([]any{int64(1), int64(2), int64(3)}), nil},
/* 25 */ {`[1,2,3,4] filter ($_ % 2 == 0)`, newList([]any{int64(2), int64(4)}), nil},
/* 26 */ {`max=0; [2,3,1] digest max=(($_ > max) ? {$_} :: {max})`, int64(3), nil},
/* 27 */ {`["a","b"] join ["x"]`, newList([]any{"a", "b", "x"}), nil},
/* 28 */ {`["a","b"] join ["x"-true]`, nil, `[1:21] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
/* 19 */ {`a=1; a^=2`, int64(3), nil},
/* 20 */ {`a=1; ++a`, int64(2), nil},
/* 21 */ {`a=1; --a`, int64(0), nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 28)
// runTestSuiteSpec(t, section, inputs, 4)
runTestSuite(t, section, inputs)
}

View File

@ -30,29 +30,9 @@ func TestPluginExists(t *testing.T) {
func TestMakePluginName(t *testing.T) {
name := "json"
want := "expr-" + name + "-plugin" + SHAREDLIBRARY_EXTENSION
want := "expr-" + name + "-plugin.so"
if got := makePluginName(name); got != want {
t.Errorf("makePluginName(%q) failed: Got: %q, Want: %q", name, got, want)
}
}
// func TestLoadPluginName(t *testing.T) {
// name := "json"
// // want := "expr-" + name + "-plugin
// want := 1
// os.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
// // os.Setenv("EXPR_PLUGIN_PATH", "${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
// os.Setenv("EXPR_PLUGIN_PATH", "${PLUGINS}")
// got, err := importPluginFromSearchPath(name)
// if err != nil {
// t.Errorf("importPluginFromSearchPath(%q) failed: %v", name, err)
// return
// }
// if got != want {
// t.Errorf("importPluginFromSearchPath(%q) failed: Got: %q, Want: %q", name, got, want)
// }
// }

View File

@ -1,118 +0,0 @@
//go:build unix
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_utils_test.go
package expr
import (
"errors"
"os/user"
"path"
"testing"
)
func TestExpandPathRootOk(t *testing.T) {
source := "~root"
wantValue := "/root"
// wantErr := errors.New(`test expected string, got list ([])`)
wantErr := error(nil)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestExpandPathRootSubDirOk(t *testing.T) {
source := "~root/test"
wantValue := "/root/test"
// wantErr := errors.New(`test expected string, got list ([])`)
wantErr := error(nil)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestExpandPathUser(t *testing.T) {
u, _ := user.Current()
source := "~"
wantValue := path.Join("/home", u.Username)
// wantErr := errors.New(`test expected string, got list ([])`)
wantErr := error(nil)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestExpandPathEnv(t *testing.T) {
u, _ := user.Current()
source := "$HOME"
wantValue := path.Join("/home", u.Username)
// wantErr := errors.New(`test expected string, got list ([])`)
wantErr := error(nil)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestExpandPathErr(t *testing.T) {
source := "~fake-user/test"
wantValue := "~fake-user/test"
wantErr := errors.New(`user: unknown user fake-user`)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestExpandPathUserErr(t *testing.T) {
u, _ := user.Current()
source := "~"
wantValue := path.Join("/home", u.Username)
// wantErr := errors.New(`test expected string, got list ([])`)
wantErr := error(nil)
gotValue, gotErr := ExpandPath(source)
if gotErr != nil {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ExpandPath(%v) gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}

View File

@ -164,48 +164,3 @@ func TestCopyMap(t *testing.T) {
t.Errorf("utils.CopyMap() failed")
}
}
func TestToStringOk(t *testing.T) {
source := "ciao"
wantValue := "ciao"
wantErr := error(nil)
gotValue, gotErr := ToGoString(source, "test")
if gotErr != nil {
t.Error(gotErr)
} else if gotValue != wantValue {
t.Errorf("toGoString(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestToStringErr(t *testing.T) {
source := newListA()
wantValue := ""
wantErr := errors.New(`test expected string, got list ([])`)
gotValue, gotErr := ToGoString(source, "test")
if gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf(`ToGoString(%v, "test") gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
} else if gotValue != wantValue {
t.Errorf(`ToString(%v, "test") gotValue=%q, gotErr=%v -> wantValue=%q, wantErr=%v`,
source, gotValue, gotErr, wantValue, wantErr)
}
}
func TestIsFunctorSucc(t *testing.T) {
f := NewGolangFunctor(isNilFunc)
if !isFunctor(f) {
t.Errorf("isNilFunc() evalued as not a functor")
}
}
func TestIsFunctorFail(t *testing.T) {
f := int64(1)
if isFunctor(f) {
t.Errorf("int evalued as a functor")
}
}

View File

@ -13,7 +13,6 @@ type termPriority uint32
const (
priNone termPriority = iota
priRange
priIterOp // map, filter, digest, etc
priBut
priAssign
priInsert

View File

@ -1,48 +0,0 @@
//go:build unix
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// utils-unix.go
package expr
import (
"os"
"os/user"
"path"
"strings"
)
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
}

View File

@ -1,18 +0,0 @@
//go:build windows
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// utils-unix.go
package expr
import (
"os"
)
func ExpandPath(sourcePath string) (expandedPath string, err error) {
for expandedPath = os.ExpandEnv(sourcePath); expandedPath != sourcePath; expandedPath = os.ExpandEnv(sourcePath) {
sourcePath = expandedPath
}
return
}

View File

@ -6,7 +6,11 @@ package expr
import (
"fmt"
"os"
"os/user"
"path"
"reflect"
"strings"
)
func IsString(v any) (ok bool) {
@ -230,3 +234,37 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
}
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
}