Compare commits

..

14 Commits

Author SHA1 Message Date
29bc2c62a3 first plugin support.
Module organization requires a better structure to decouple definitions and implementations
2024-06-09 07:41:56 +02:00
8eb2d77ea3 improved position of some common functions 2024-06-09 07:38:29 +02:00
53bcf90d2a operator-builtin.go: some error messages improved 2024-06-09 07:36:12 +02:00
f347b15146 Control vars are now stored in the globalCtx only.
However, it is still allowed to store control var in a local context in special situation
2024-06-08 05:49:28 +02:00
115ce26ce9 IsOptional() function of ExprFuncParam renamed as IdDefault(). A new IsOptional() function created to check if a param is optional without requiring a default value 2024-06-07 09:45:02 +02:00
227944b3fb Removed eval() function from ast interface and implementation. Check on preset control variables is now done in initDefaultVars() 2024-06-07 09:41:10 +02:00
0d01afcc9f member ctx removed from struct parser because it is unused 2024-06-07 09:03:42 +02:00
00c76b41f1 list-type.go: two special constructors, MakeList() and ListFromStrigns(), added 2024-06-07 09:02:14 +02:00
08e0979cdd import-utils.go: addPresetDirs() replaced by addSearchDirs() 2024-06-07 09:01:18 +02:00
f04f5822ec common-type-names.go: added type name list-of 2024-06-07 08:59:04 +02:00
80d47879e9 t_list_test.go: removed commented code 2024-06-07 08:54:59 +02:00
985eb3d19d Merge branch 'main' into feat_plugin 2024-06-06 05:33:56 +02:00
45734ab393 new test file t_iter-list.go 2024-06-06 05:33:35 +02:00
c100cf349d some identier exported; new file import-utils.go 2024-06-06 05:31:35 +02:00
36 changed files with 658 additions and 335 deletions

9
ast.go
View File

@ -10,7 +10,6 @@ import (
type Expr interface {
Eval(ctx ExprContext) (result any, err error)
eval(ctx ExprContext, preset bool) (result any, err error)
String() string
}
@ -106,16 +105,10 @@ func (self *ast) Finish() {
}
func (self *ast) Eval(ctx ExprContext) (result any, err error) {
return self.eval(ctx, true)
}
func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
self.Finish()
if self.root != nil {
if preset {
initDefaultVars(ctx)
}
// initDefaultVars(ctx)
if self.forest != nil {
for _, root := range self.forest {
if result, err = root.compute(ctx); err == nil {

View File

@ -5,14 +5,16 @@
package expr
const (
typeAny = "any"
typeBoolean = "boolean"
typeFloat = "float"
typeFraction = "fraction"
typeHandle = "handle"
typeInt = "integer"
typeItem = "item"
typeNumber = "number"
typePair = "pair"
typeString = "string"
TypeAny = "any"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeHandle = "handle"
TypeInt = "integer"
TypeItem = "item"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)

View File

@ -22,13 +22,11 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
if name[0] == '@' {
name = name[1:]
}
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
// ctx.RegisterFuncInfo(name, info)
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
}
func exportObjects(destCtx, sourceCtx ExprContext) {
exportAll := isEnabled(sourceCtx, control_export_all)
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
// Export variables
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {

View File

@ -16,6 +16,7 @@ type Functor interface {
type ExprFuncParam interface {
Name() string
Type() string
IsDefault() bool
IsOptional() bool
IsRepeat() bool
DefaultValue() any
@ -36,6 +37,8 @@ type ExprFunc interface {
type ExprContext interface {
Clone() ExprContext
Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)

View File

@ -4,13 +4,13 @@
// control.go
package expr
import "strings"
// Preset control variables
const (
ControlLastResult = "last"
ControlBoolShortcut = "_bool_shortcut"
ControlImportPath = "_import_path"
ControlPreset = "_preset"
ControlLastResult = "last"
ControlBoolShortcut = "_bool_shortcut"
ControlSearchPath = "_search_path"
ControlParentContext = "_parent_context"
)
// Other control variables
@ -20,43 +20,14 @@ const (
// Initial values
const (
init_import_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
)
func initDefaultVars(ctx ExprContext) {
if _, exists := ctx.GetVar(ControlPreset); exists {
return
}
ctx.SetVar(ControlPreset, true)
ctx.SetVar(ControlBoolShortcut, true)
ctx.SetVar(ControlImportPath, init_import_path)
}
func enable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") {
ctx.SetVar(name, true)
} else {
ctx.SetVar("_"+name, true)
}
}
func disable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") {
ctx.SetVar(name, false)
} else {
ctx.SetVar("_"+name, false)
}
}
func isEnabled(ctx ExprContext, name string) (status bool) {
if v, exists := ctx.GetVar(name); exists {
if b, ok := v.(bool); ok {
status = b
}
}
return
}
func getControlString(ctx ExprContext, name string) (s string, exists bool) {
var v any
if v, exists = ctx.GetVar(name); exists {
s, exists = v.(string)
}
return
ctx.SetVar(ControlSearchPath, init_search_path)
}

View File

@ -212,7 +212,7 @@ func anyToFract(v any) (f *FractionType, err error) {
}
}
if f == nil {
err = errExpectedGot("fract", typeFraction, v)
err = errExpectedGot("fract", TypeFraction, v)
}
return
}

View File

@ -159,25 +159,25 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{
newFuncParam(paramValue),
NewFuncParam(paramValue),
}
ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams)
ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams)
ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams)
ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams)
ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{
newFuncParam(paramValue),
newFuncParamFlagDef("denominator", pfOptional, 1),
ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams)
ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams)
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
NewFuncParam(paramValue),
NewFuncParamFlagDef("denominator", PfDefault, 1),
})
}

View File

@ -23,11 +23,11 @@ func printLnFunc(ctx ExprContext, name string, args []any) (result any, err erro
}
func ImportFmtFuncs(ctx ExprContext) {
ctx.RegisterFunc("print", newGolangFunctor(printFunc), typeInt, []ExprFuncParam{
newFuncParamFlag(paramItem, pfRepeat),
ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{
NewFuncParamFlag(paramItem, PfRepeat),
})
ctx.RegisterFunc("println", newGolangFunctor(printLnFunc), typeInt, []ExprFuncParam{
newFuncParamFlag(paramItem, pfRepeat),
ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{
NewFuncParamFlag(paramItem, PfRepeat),
})
}

View File

@ -5,100 +5,25 @@
package expr
import (
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
)
const ENV_EXPR_PATH = "EXPR_PATH"
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return importGeneral(ctx, name, args)
}
func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
enable(ctx, control_export_all)
CtrlEnable(ctx, control_export_all)
return importGeneral(ctx, name, args)
}
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
var dirList []string
dirList = addEnvImportDirs(dirList)
dirList = addPresetImportDirs(ctx, dirList)
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
result, err = doImport(ctx, name, dirList, NewArrayIterator(args))
return
}
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue)
}
return
}
func addEnvImportDirs(dirList []string) []string {
if dirSpec, exists := os.LookupEnv(ENV_EXPR_PATH); exists {
dirs := strings.Split(dirSpec, ":")
if dirList == nil {
dirList = dirs
} else {
dirList = append(dirList, dirs...)
}
}
return dirList
}
func addPresetImportDirs(ctx ExprContext, dirList []string) []string {
if dirSpec, exists := getControlString(ctx, ControlImportPath); exists {
dirs := strings.Split(dirSpec, ":")
if dirList == nil {
dirList = dirs
} else {
dirList = append(dirList, dirs...)
}
}
return dirList
}
func isFile(filePath string) bool {
info, err := os.Stat(filePath)
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
}
func searchAmongPath(filename string, dirList []string) (filePath string) {
for _, dir := range dirList {
if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath
break
}
}
return
}
func isPathRelative(filePath string) bool {
unixPath := filepath.ToSlash(filePath)
return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../")
}
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) {
filePath = filename
}
} else {
filePath = searchAmongPath(filename, dirList)
}
if len(filePath) == 0 {
err = fmt.Errorf("source file %q not found", filename)
}
return
}
func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) {
var v any
var sourceFilepath string
@ -115,9 +40,9 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
defer file.Close()
var expr *ast
scanner := NewScanner(file, DefaultTranslations())
parser := NewParser(ctx)
parser := NewParser()
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
result, err = expr.eval(ctx, false)
result, err = expr.Eval(ctx)
}
if err != nil {
break
@ -137,11 +62,11 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
}
func ImportImportFuncs(ctx ExprContext) {
ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(paramFilepath, pfRepeat),
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{
NewFuncParamFlag(paramFilepath, PfRepeat),
})
ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(paramFilepath, pfRepeat),
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{
NewFuncParamFlag(paramFilepath, PfRepeat),
})
}

View File

@ -167,12 +167,12 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
}
func ImportMathFuncs(ctx ExprContext) {
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(0)),
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, int64(0)),
})
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(1)),
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, int64(1)),
})
}

View File

@ -187,25 +187,25 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
}
func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(paramFilepath),
ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(paramFilepath),
})
ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(paramFilepath),
ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(paramFilepath),
})
ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{
newFuncParam(paramFilepath),
ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(paramFilepath),
})
ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{
newFuncParam(typeHandle),
newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""),
ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
})
ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
newFuncParam(typeHandle),
newFuncParamFlagDef("limitCh", pfOptional, "\n"),
ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
})
ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
newFuncParam(typeHandle),
ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle),
})
}

View File

@ -21,7 +21,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
if s, ok := v.(string); ok {
sb.WriteString(s)
} else {
err = errExpectedGot(funcName, typeString, v)
err = errExpectedGot(funcName, TypeString, v)
return
}
}
@ -51,7 +51,7 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
}
} else {
err = errWrongParamType(name, paramSeparator, typeString, args[0])
err = errWrongParamType(name, paramSeparator, TypeString, args[0])
}
return
}
@ -63,7 +63,7 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
var ok bool
if source, ok = args[0].(string); !ok {
return nil, errWrongParamType(name, paramSource, typeString, args[0])
return nil, errWrongParamType(name, paramSource, TypeString, args[0])
}
if start, err = toInt(args[1], name+"()"); err != nil {
@ -91,7 +91,7 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
var ok bool
if source, ok = args[0].(string); !ok {
return nil, errWrongParamType(name, paramSource, typeString, args[0])
return nil, errWrongParamType(name, paramSource, TypeString, args[0])
}
result = strings.TrimSpace(source)
return
@ -104,7 +104,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
result = false
if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, paramSource, typeString, args[0])
return result, errWrongParamType(name, paramSource, TypeString, args[0])
}
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok {
@ -127,7 +127,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
result = false
if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, paramSource, typeString, args[0])
return result, errWrongParamType(name, paramSource, TypeString, args[0])
}
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok {
@ -150,7 +150,7 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
var ok bool
if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, paramSource, typeString, args[0])
return result, errWrongParamType(name, paramSource, TypeString, args[0])
}
if sep, ok = args[1].(string); !ok {
@ -182,37 +182,37 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
// Import above functions in the context
func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSeparator),
newFuncParamFlag(paramItem, pfRepeat),
ctx.RegisterFunc("joinStr", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(paramSeparator),
NewFuncParamFlag(paramItem, PfRepeat),
})
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSource),
newFuncParamFlagDef(paramStart, pfOptional, int64(0)),
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)),
ctx.RegisterFunc("subStr", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(paramSource),
NewFuncParamFlagDef(paramStart, PfDefault, int64(0)),
NewFuncParamFlagDef(paramCount, PfDefault, int64(-1)),
})
ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{
newFuncParam(paramSource),
newFuncParamFlagDef(paramSeparator, pfOptional, ""),
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)),
ctx.RegisterFunc("splitStr", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
NewFuncParam(paramSource),
NewFuncParamFlagDef(paramSeparator, PfDefault, ""),
NewFuncParamFlagDef(paramCount, PfDefault, int64(-1)),
})
ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
newFuncParam(paramSource),
ctx.RegisterFunc("trimStr", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(paramSource),
})
ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{
newFuncParam(paramSource),
newFuncParam(paramPrefix),
newFuncParamFlag("other "+paramPrefix, pfRepeat),
ctx.RegisterFunc("startsWithStr", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(paramSource),
NewFuncParam(paramPrefix),
NewFuncParamFlag("other "+paramPrefix, PfRepeat),
})
ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{
newFuncParam(paramSource),
newFuncParam(paramSuffix),
newFuncParamFlag("other "+paramSuffix, pfRepeat),
ctx.RegisterFunc("endsWithStr", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(paramSource),
NewFuncParam(paramSuffix),
NewFuncParamFlag("other "+paramSuffix, PfRepeat),
})
}

View File

@ -48,7 +48,7 @@ type golangFunctor struct {
f FuncTemplate
}
func newGolangFunctor(f FuncTemplate) *golangFunctor {
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f}
}
@ -83,7 +83,7 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
if funcArg, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1)
paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, typeAny, paramSpecs)
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
} else {
ctx.UnsafeSetVar(p.Name(), arg)
}
@ -91,7 +91,7 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
ctx.UnsafeSetVar(p.Name(), nil)
}
}
result, err = functor.expr.eval(ctx, false)
result, err = functor.expr.Eval(ctx)
return
}
@ -99,8 +99,9 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
type paramFlags uint16
const (
pfOptional paramFlags = 1 << iota
pfRepeat
PfDefault paramFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
@ -109,15 +110,15 @@ type funcParamInfo struct {
defaultValue any
}
func newFuncParam(name string) ExprFuncParam {
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
func NewFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
@ -126,15 +127,19 @@ func (param *funcParamInfo) Name() string {
}
func (param *funcParamInfo) Type() string {
return "any"
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & pfOptional) != 0
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & pfRepeat) != 0
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
@ -159,7 +164,7 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
}
if p.IsOptional() {
if p.IsDefault() || p.IsOptional() {
maxArgs++
} else if maxArgs == minArgs {
minArgs++
@ -207,7 +212,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
}
sb.WriteString(p.Name())
if p.IsOptional() {
if p.IsDefault() {
sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"')
@ -226,7 +231,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
if len(info.returnType) > 0 {
sb.WriteString(info.returnType)
} else {
sb.WriteString(typeAny)
sb.WriteString(TypeAny)
}
sb.WriteString(" {<body>}")
return sb.String()

View File

@ -4,7 +4,10 @@
// global-context.go
package expr
import "path/filepath"
import (
"path/filepath"
"strings"
)
var globalCtx *SimpleStore
@ -63,7 +66,78 @@ func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, owne
return
}
func GlobalCtrlSet(name string, newValue any) (currentValue any) {
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
currentValue, _ = globalCtx.GetVar(name)
globalCtx.SetVar(name, newValue)
return currentValue
}
func GlobalCtrlGet(name string) (currentValue any) {
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
currentValue, _ = globalCtx.GetVar(name)
return currentValue
}
func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
if v, exists := ctx.GetVar(name); exists && IsBool(v) {
currentStatus, _ = v.(bool)
}
ctx.SetVar(name, true)
return currentStatus
}
func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
if v, exists := ctx.GetVar(name); exists && IsBool(v) {
currentStatus, _ = v.(bool)
}
ctx.SetVar(name, false)
return currentStatus
}
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
var v any
var exists bool
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
if v, exists = ctx.GetVar(name); !exists {
v, exists = globalCtx.GetVar(name)
}
if exists {
if b, ok := v.(bool); ok {
status = b
}
}
return
}
func getControlString(name string) (s string, exists bool) {
var v any
if v, exists = globalCtx.GetVar(name); exists {
s, exists = v.(string)
}
return
}
func init() {
globalCtx = NewSimpleStore()
initDefaultVars(globalCtx)
ImportBuiltinsFuncs(globalCtx)
}

View File

@ -16,10 +16,10 @@ func EvalString(ctx ExprContext, source string) (result any, err error) {
r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations())
parser := NewParser(ctx)
parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil {
result, err = tree.eval(ctx, true)
result, err = tree.Eval(ctx)
}
return
}
@ -38,10 +38,10 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
for _, arg := range args {
if isFunc(arg.Value) {
if f, ok := arg.Value.(FuncTemplate); ok {
functor := newGolangFunctor(f)
functor := NewGolangFunctor(f)
// ctx.RegisterFunc(arg.Name, functor, 0, -1)
ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{
NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, 0),
})
} else {
err = fmt.Errorf("invalid function specification: %q", arg.Name)
@ -68,7 +68,7 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
var tree *ast
scanner := NewScanner(r, DefaultTranslations())
parser := NewParser(ctx)
parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil {
result, err = tree.Eval(ctx)

104
import-utils.go Normal file
View File

@ -0,0 +1,104 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// import-utils.go
package expr
import (
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
)
const (
ENV_EXPR_SOURCE_PATH = "EXPR_PATH"
ENV_EXPR_PLUGIN_PATH = "EXPR_PLUGIN_PATH"
)
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, typeName(paramValue))
}
return
}
func addSourceEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
}
func addPluginEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
}
func addEnvImportDirs(envVarName string, dirList []string) []string {
if dirSpec, exists := os.LookupEnv(envVarName); exists {
dirs := strings.Split(dirSpec, ":")
if dirList == nil {
dirList = dirs
} else {
dirList = append(dirList, dirs...)
}
}
return dirList
}
func addSearchDirs(endingPath string, dirList []string) []string {
if dirSpec, exists := getControlString(ControlSearchPath); exists {
dirs := strings.Split(dirSpec, ":")
if dirList == nil {
dirList = dirs
} else {
if len(endingPath) > 0 {
for _, d := range dirs {
dirList = append(dirList, path.Join(d, endingPath))
}
} else {
dirList = append(dirList, dirs...)
}
}
}
return dirList
}
func buildSearchDirList(endingPath, envVarName string) (dirList []string) {
dirList = addEnvImportDirs(envVarName, dirList)
dirList = addSearchDirs(endingPath, dirList)
return
}
func isFile(filePath string) bool {
info, err := os.Stat(filePath)
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
}
func searchAmongPath(filename string, dirList []string) (filePath string) {
for _, dir := range dirList {
if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath
break
}
}
return
}
func isPathRelative(filePath string) bool {
unixPath := filepath.ToSlash(filePath)
return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../")
}
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) {
filePath = filename
}
} else {
filePath = searchAmongPath(filename, dirList)
}
if len(filePath) == 0 {
err = fmt.Errorf("file %q not found", filename)
}
return
}

View File

@ -20,6 +20,10 @@ func newListA(listAny ...any) (list *ListType) {
}
func newList(listAny []any) (list *ListType) {
return NewList(listAny)
}
func NewList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
for i, item := range listAny {
@ -30,6 +34,23 @@ func newList(listAny []any) (list *ListType) {
return
}
func MakeList(length, capacity int) (list *ListType) {
if capacity < length {
capacity = length
}
ls := make(ListType, length, capacity)
list = &ls
return
}
func ListFromStrings(stringList []string) (list *ListType) {
list = MakeList(len(stringList), 0)
for i, s := range stringList {
(*list)[i] = s
}
return
}
func (ls *ListType) ToString(opt FmtOpt) (s string) {
var sb strings.Builder
sb.WriteByte('[')

View File

@ -30,7 +30,7 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro
}
for i, p := range info.Params() {
if i >= passedCount {
if !p.IsOptional() {
if !p.IsDefault() {
break
}
*varParams = append(*varParams, p.DefaultValue())
@ -50,6 +50,7 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
name, _ := self.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5)
for i, tree := range self.children {
@ -91,12 +92,12 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
var defValue any
flags := paramFlags(0)
if len(param.children) > 0 {
flags |= pfOptional
flags |= PfDefault
if defValue, err = param.children[0].compute(ctx); err != nil {
return
}
}
info := newFuncParamFlagDef(param.source(), flags, defValue)
info := NewFuncParamFlagDef(param.source(), flags, defValue)
paramList = append(paramList, info)
}
v = newExprFunctor(expr, paramList, ctx)

View File

@ -81,7 +81,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
} else if funcDef, ok := functor.(*exprFunctor); ok {
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs)
} else {
err = self.Errorf("unknown function %s()", rightChild.source())
}

View File

@ -48,7 +48,7 @@ func newAndTerm(tk *Token) (inst *term) {
}
func evalAnd(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, ControlBoolShortcut) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
v, err = evalAndWithShortcut(ctx, self)
} else {
v, err = evalAndWithoutShortcut(ctx, self)
@ -117,7 +117,7 @@ func newOrTerm(tk *Token) (inst *term) {
}
func evalOr(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, ControlBoolShortcut) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
v, err = evalOrWithShortcut(ctx, self)
} else {
v, err = evalOrWithoutShortcut(ctx, self)

View File

@ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
if ImportInContext(module) {
count++
} else {
err = self.Errorf("unknown module %q", module)
err = self.Errorf("unknown builtin module %q", module)
break
}
} else {
err = self.Errorf("expected string at item nr %d, got %T", it.Index()+1, moduleSpec)
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, typeName(moduleSpec))
break
}
}

View File

@ -67,8 +67,8 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{
newFuncParamFlag(paramValue, pfOptional|pfRepeat),
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(paramValue, PfDefault|PfRepeat),
})
} else {
v = rightValue

View File

@ -34,7 +34,8 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
if formatter, ok := sourceCtx.(Formatter); ok {
v = formatter.ToString(0)
} else {
keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
keys := sourceCtx.EnumVars(nil)
d := make(map[string]any)
for _, key := range keys {
d[key], _ = sourceCtx.GetVar(key)

View File

@ -17,7 +17,7 @@ func newExportAllTerm(tk *Token) (inst *term) {
}
func evalExportAll(ctx ExprContext, self *term) (v any, err error) {
enable(ctx, control_export_all)
CtrlEnable(ctx, control_export_all)
return
}

102
operator-plugin.go Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-plugin.go
package expr
import (
"fmt"
"io"
"plugin"
)
//-------- plugin term
func newPluginTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priSign,
evalFunc: evalPlugin,
}
}
func importPlugin(ctx ExprContext, dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
var sym plugin.Symbol
var moduleName string
var importFunc func(ctx ExprContext)
var ok bool
decoratedName := fmt.Sprintf("expr-%s-plugin.so", name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
}
if p, err = plugin.Open(filePath); err != nil {
return
}
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
return
}
if moduleName = *sym.(*string); moduleName == "" {
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
return
}
if sym, err = p.Lookup("ImportFuncs"); err != nil {
return
}
if importFunc, ok = sym.(func(ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return
}
registerPlugin(moduleName, p)
importFunc(globalCtx)
return
}
func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
var childValue any
var moduleSpec any
if childValue, err = self.evalPrefix(ctx); err != nil {
return
}
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count := 0
it := NewAnyIterator(childValue)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(ctx, dirList, module); err != nil {
break
}
count++
} else {
err = self.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)
}
return
}
// init
func init() {
registerTermConstructor(SymKwPlugin, newPluginTerm)
}

View File

@ -12,7 +12,7 @@ type intPair struct {
}
func (p *intPair) TypeName() string {
return typePair
return TypePair
}
func (p *intPair) ToString(opt FmtOpt) string {

View File

@ -19,17 +19,17 @@ func newSelectorTerm(tk *Token) (inst *term) {
func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
caseData, _ := caseSel.(*selectorCase)
if caseData.filterList == nil {
selectedValue, err = caseData.caseExpr.eval(ctx, false)
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
if len(filterList) == 0 && exprValue == int64(caseIndex) {
selectedValue, err = caseData.caseExpr.eval(ctx, false)
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
} else {
var caseValue any
for _, caseTerm := range filterList {
if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue {
selectedValue, err = caseData.caseExpr.eval(ctx, false)
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
break
}

View File

@ -11,13 +11,10 @@ import (
//-------- parser
type parser struct {
ctx ExprContext
}
func NewParser(ctx ExprContext) (p *parser) {
p = &parser{
ctx: ctx,
}
func NewParser() (p *parser) {
p = &parser{}
return p
}

30
plugins.go Normal file
View File

@ -0,0 +1,30 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// plugin.go
package expr
import (
"fmt"
"plugin"
)
var pluginRegister map[string]*plugin.Plugin
func registerPlugin(name string, p *plugin.Plugin) (err error) {
if pluginExists(name) {
err = fmt.Errorf("plugin %q already loaded", name)
} else {
pluginRegister[name] = p
}
return
}
func pluginExists(name string) (exists bool) {
_, exists = pluginRegister[name]
return
}
func init() {
pluginRegister = make(map[string]*plugin.Plugin)
}

View File

@ -11,6 +11,7 @@ import (
)
type SimpleStore struct {
parent ExprContext
varStore map[string]any
funcStore map[string]ExprFunc
}
@ -26,6 +27,14 @@ func NewSimpleStore() *SimpleStore {
func filterRefName(name string) bool { return name[0] != '@' }
func filterPrivName(name string) bool { return name[0] != '_' }
func (ctx *SimpleStore) SetParent(parentCtx ExprContext) {
ctx.parent = parentCtx
}
func (ctx *SimpleStore) GetParent() ExprContext {
return ctx.parent
}
func (ctx *SimpleStore) Clone() ExprContext {
return &SimpleStore{
varStore: CloneFilteredMap(ctx.varStore, filterRefName),
@ -45,7 +54,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString("vars: {\n")
first := true
for _, name := range ctx.EnumVars(filterPrivName) {
for _, name := range ctx.EnumVars(nil) {
if first {
first = false
} else {

View File

@ -101,6 +101,7 @@ const (
SymKwBut
SymKwFunc
SymKwBuiltin
SymKwPlugin
SymKwIn
SymKwInclude
SymKwNil
@ -113,6 +114,7 @@ func init() {
keywords = map[string]Symbol{
"AND": SymKwAnd,
"BUILTIN": SymKwBuiltin,
"PLUGIN": SymKwPlugin,
"BUT": SymKwBut,
"FUNC": SymKwFunc,
"IN": SymKwIn,

View File

@ -49,7 +49,7 @@ func TestDictParser(t *testing.T) {
ctx.SetVar("var1", int64(123))
ctx.SetVar("var2", "abc")
ImportMathFuncs(ctx)
parser := NewParser(ctx)
parser := NewParser()
logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr)

View File

@ -43,7 +43,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
}
func TestFunctionToStringSimple(t *testing.T) {
source := newGolangFunctor(dummy)
source := NewGolangFunctor(dummy)
want := "func() {<body>}"
got := source.ToString(0)
if got != want {
@ -52,7 +52,7 @@ func TestFunctionToStringSimple(t *testing.T) {
}
func TestFunctionGetFunc(t *testing.T) {
source := newGolangFunctor(dummy)
source := NewGolangFunctor(dummy)
want := ExprFunc(nil)
got := source.GetFunc()
if got != want {

162
t_iter-list_test.go Normal file
View File

@ -0,0 +1,162 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_iter-list_test.go
package expr
import (
"io"
"testing"
)
func TestNewListIterator(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewListIterator2(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{3, 1, -1})
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "d" {
t.Errorf("expcted %q, got %q", "d", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewListIterator3(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, -1, 1})
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewIterList2(t *testing.T) {
list := []any{"a", "b", "c", "d"}
it := NewArrayIterator(list)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewIterList3(t *testing.T) {
list := []any{"a", "b", "c", "d"}
it := NewAnyIterator(list)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewIterList4(t *testing.T) {
list := any(nil)
it := NewAnyIterator(list)
if _, err := it.Next(); err != io.EOF {
t.Errorf("error: %v", err)
}
}
func TestNewIterList5(t *testing.T) {
list := "123"
it := NewAnyIterator(list)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "123" {
t.Errorf("expcted %q, got %q", "123", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewIterList6(t *testing.T) {
list := newListA("a", "b", "c", "d")
it1 := NewAnyIterator(list)
it := NewAnyIterator(it1)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
}
func TestNewString(t *testing.T) {
list := "123"
it := NewAnyIterator(list)
if s := it.String(); s != "$(#1)" {
t.Errorf("expected $(#1), got %s", s)
}
}
func TestHasOperation(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
hasOp := it.HasOperation("reset")
if !hasOp {
t.Errorf("HasOperation(reset) must be true, got false")
}
}
func TestCallOperationReset(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
if v, err := it.CallOperation("reset", nil); err != nil {
t.Errorf("Error on CallOperation(reset): %v", err)
} else {
t.Logf("Reset result: %v", v)
}
}
func TestCallOperationIndex(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
if v, err := it.CallOperation("index", nil); err != nil {
t.Errorf("Error on CallOperation(index): %v", err)
} else {
t.Logf("Index result: %v", v)
}
}
func TestCallOperationCount(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
if v, err := it.CallOperation("count", nil); err != nil {
t.Errorf("Error on CallOperation(count): %v", err)
} else {
t.Logf("Count result: %v", v)
}
}
func TestCallOperationUnknown(t *testing.T) {
list := newListA("a", "b", "c", "d")
it := NewListIterator(list, []any{1, 3, 1})
if v, err := it.CallOperation("unknown", nil); err == nil {
t.Errorf("Expected error on CallOperation(unknown), got %v", v)
}
}

View File

@ -6,19 +6,12 @@ package expr
import (
"errors"
"strings"
"testing"
)
func TestListParser(t *testing.T) {
section := "List"
/* type inputType struct {
source string
wantResult any
wantErr error
}
*/
inputs := []inputType{
/* 1 */ {`[]`, newListA(), nil},
/* 2 */ {`[1,2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
@ -64,74 +57,4 @@ func TestListParser(t *testing.T) {
// parserTestSpec(t, section, inputs, 17)
parserTest(t, section, inputs)
return
succeeded := 0
failed := 0
// inputs1 := []inputType{
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
// }
for i, input := range inputs {
var expr *ast
var gotResult any
var gotErr error
ctx := NewSimpleStore()
ImportMathFuncs(ctx)
parser := NewParser(ctx)
logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations())
good := true
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
gotResult, gotErr = expr.Eval(ctx)
}
if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) {
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
good = false
}
if gotList, okGot := gotResult.([]any); okGot {
if wantList, okWant := input.wantResult.([]any); okWant {
if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) {
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
good = false
} else {
equal := len(gotList) == len(wantList)
if equal {
for i, gotItem := range gotList {
wantItem := wantList[i]
equal = gotItem == wantItem
if !equal {
break
}
}
}
if !equal {
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
good = false
}
}
}
}
if gotErr != input.wantErr {
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
good = false
}
}
if good {
succeeded++
} else {
failed++
}
}
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
}

View File

@ -188,7 +188,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
var gotErr error
ctx := NewSimpleStore()
parser := NewParser(ctx)
parser := NewParser()
logTest(t, count, section, input.source, input.wantResult, input.wantErr)