Compare commits

...

17 Commits

Author SHA1 Message Date
camoroso 33d70d6d1a splitted go and expr function bindings in dedicated source files 2024-06-09 10:41:06 +02:00
camoroso 9df9ad5dd1 func-*.go modules renamed as builtin-*.go.
Also changed and exported some identiefier relatet to the builtin feature
2024-06-09 10:28:51 +02:00
camoroso 34dc828ead exported some identifier 2024-06-09 10:13:37 +02:00
camoroso 29bc2c62a3 first plugin support.
Module organization requires a better structure to decouple definitions and implementations
2024-06-09 07:41:56 +02:00
camoroso 8eb2d77ea3 improved position of some common functions 2024-06-09 07:38:29 +02:00
camoroso 53bcf90d2a operator-builtin.go: some error messages improved 2024-06-09 07:36:12 +02:00
camoroso 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
camoroso 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
camoroso 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
camoroso 0d01afcc9f member ctx removed from struct parser because it is unused 2024-06-07 09:03:42 +02:00
camoroso 00c76b41f1 list-type.go: two special constructors, MakeList() and ListFromStrigns(), added 2024-06-07 09:02:14 +02:00
camoroso 08e0979cdd import-utils.go: addPresetDirs() replaced by addSearchDirs() 2024-06-07 09:01:18 +02:00
camoroso f04f5822ec common-type-names.go: added type name list-of 2024-06-07 08:59:04 +02:00
camoroso 80d47879e9 t_list_test.go: removed commented code 2024-06-07 08:54:59 +02:00
camoroso 985eb3d19d Merge branch 'main' into feat_plugin 2024-06-06 05:33:56 +02:00
camoroso 45734ab393 new test file t_iter-list.go 2024-06-06 05:33:35 +02:00
camoroso c100cf349d some identier exported; new file import-utils.go 2024-06-06 05:31:35 +02:00
43 changed files with 861 additions and 528 deletions
+1 -8
View File
@@ -10,7 +10,6 @@ import (
type Expr interface { type Expr interface {
Eval(ctx ExprContext) (result any, err error) Eval(ctx ExprContext) (result any, err error)
eval(ctx ExprContext, preset bool) (result any, err error)
String() string String() string
} }
@@ -106,16 +105,10 @@ func (self *ast) Finish() {
} }
func (self *ast) Eval(ctx ExprContext) (result any, err error) { 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() self.Finish()
if self.root != nil { if self.root != nil {
if preset { // initDefaultVars(ctx)
initDefaultVars(ctx)
}
if self.forest != nil { if self.forest != nil {
for _, root := range self.forest { for _, root := range self.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package expr
// ---- Linking with Expr functions
type exprFunctor struct {
baseFunctor
params []ExprFuncParam
expr Expr
defCtx ExprContext
}
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
// }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
return &exprFunctor{expr: e, params: params, defCtx: ctx}
}
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
if functor.defCtx != nil {
ctx.Merge(functor.defCtx)
}
for i, p := range functor.params {
if i < len(args) {
arg := args[i]
if funcArg, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1)
paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
} else {
ctx.UnsafeSetVar(p.Name(), arg)
}
} else {
ctx.UnsafeSetVar(p.Name(), nil)
}
}
result, err = functor.expr.Eval(ctx)
return
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bind-go-function.go
package expr
// ---- Linking with Go functions
type golangFunctor struct {
baseFunctor
f FuncTemplate
}
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f}
}
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
return functor.f(ctx, name, args)
}
+18 -18
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// func-builtins.go // builtin-base.go
package expr package expr
import ( import (
@@ -159,28 +159,28 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
func ImportBuiltinsFuncs(ctx ExprContext) { func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{ anyParams := []ExprFuncParam{
newFuncParam(paramValue), NewFuncParam(ParamValue),
} }
ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams) ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams) ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams) ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams) ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams) ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams) ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams) ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams) ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams) ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams) ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams)
ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams) ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams)
ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams) ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams)
ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{ ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
newFuncParam(paramValue), NewFuncParam(ParamValue),
newFuncParamFlagDef("denominator", pfOptional, 1), NewFuncParamFlagDef("denominator", PfDefault, 1),
}) })
} }
func init() { func init() {
registerImport("base", ImportBuiltinsFuncs, "Base expression tools like isNil(), int(), etc.") RegisterBuiltinModule("base", ImportBuiltinsFuncs, "Base expression tools like isNil(), int(), etc.")
} }
+6 -6
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// func-fmt.go // builtin-fmt.go
package expr package expr
import "fmt" import "fmt"
@@ -23,14 +23,14 @@ func printLnFunc(ctx ExprContext, name string, args []any) (result any, err erro
} }
func ImportFmtFuncs(ctx ExprContext) { func ImportFmtFuncs(ctx ExprContext) {
ctx.RegisterFunc("print", newGolangFunctor(printFunc), typeInt, []ExprFuncParam{ ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{
newFuncParamFlag(paramItem, pfRepeat), NewFuncParamFlag(ParamItem, PfRepeat),
}) })
ctx.RegisterFunc("println", newGolangFunctor(printLnFunc), typeInt, []ExprFuncParam{ ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{
newFuncParamFlag(paramItem, pfRepeat), NewFuncParamFlag(ParamItem, PfRepeat),
}) })
} }
func init() { func init() {
registerImport("fmt", ImportFmtFuncs, "String and console formatting functions") RegisterBuiltinModule("fmt", ImportFmtFuncs, "String and console formatting functions")
} }
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-import.go
package expr
import (
"io"
"os"
)
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) {
CtrlEnable(ctx, control_export_all)
return importGeneral(ctx, name, args)
}
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
result, err = doImport(ctx, name, dirList, NewArrayIterator(args))
return
}
func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) {
var v any
var sourceFilepath string
for v, err = it.Next(); err == nil; v, err = it.Next() {
if err = checkStringParamExpected(name, v, it.Index()); err != nil {
break
}
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
break
}
var file *os.File
if file, err = os.Open(sourceFilepath); err == nil {
defer file.Close()
var expr *ast
scanner := NewScanner(file, DefaultTranslations())
parser := NewParser()
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
result, err = expr.Eval(ctx)
}
if err != nil {
break
}
} else {
break
}
}
if err != nil {
if err == io.EOF {
err = nil
} else {
result = nil
}
}
return
}
func ImportImportFuncs(ctx ExprContext) {
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat),
})
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat),
})
}
func init() {
RegisterBuiltinModule("import", ImportImportFuncs, "Functions import() and include()")
}
+6 -6
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// funcs-math.go // builtin-math-arith.go
package expr package expr
import ( import (
@@ -167,15 +167,15 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
} }
func ImportMathFuncs(ctx ExprContext) { func ImportMathFuncs(ctx ExprContext) {
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{ ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(0)), NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
}) })
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{ ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(1)), NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
}) })
} }
func init() { func init() {
registerImport("math.arith", ImportMathFuncs, "Functions add() and mul()") RegisterBuiltinModule("math.arith", ImportMathFuncs, "Functions add() and mul()")
} }
+16 -16
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// func-os.go // builtin-os-file.go
package expr package expr
import ( import (
@@ -187,28 +187,28 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{ ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
newFuncParam(paramFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{ ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
newFuncParam(paramFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{ ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
newFuncParam(paramFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{ ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
newFuncParam(typeHandle), NewFuncParam(TypeHandle),
newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""), NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
}) })
ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
newFuncParam(typeHandle), NewFuncParam(TypeHandle),
newFuncParamFlagDef("limitCh", pfOptional, "\n"), NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
}) })
ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{ ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
newFuncParam(typeHandle), NewFuncParam(TypeHandle),
}) })
} }
func init() { func init() {
registerImport("os.file", ImportOsFuncs, "Operating system file functions") RegisterBuiltinModule("os.file", ImportOsFuncs, "Operating system file functions")
} }
+31 -31
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// func-string.go // builtin-string.go
package expr package expr
import ( import (
@@ -21,7 +21,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
sb.WriteString(s) sb.WriteString(s)
} else { } else {
err = errExpectedGot(funcName, typeString, v) err = errExpectedGot(funcName, TypeString, v)
return return
} }
} }
@@ -45,13 +45,13 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
} else if it, ok := args[1].(Iterator); ok { } else if it, ok := args[1].(Iterator); ok {
result, err = doJoinStr(name, sep, it) result, err = doJoinStr(name, sep, it)
} else { } else {
err = errInvalidParameterValue(name, paramParts, args[1]) err = errInvalidParameterValue(name, ParamParts, args[1])
} }
} else { } else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:])) result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
} }
} else { } else {
err = errWrongParamType(name, paramSeparator, typeString, args[0]) err = errWrongParamType(name, ParamSeparator, TypeString, args[0])
} }
return return
} }
@@ -63,7 +63,7 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
var ok bool var ok bool
if source, ok = args[0].(string); !ok { 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 { 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 var ok bool
if source, ok = args[0].(string); !ok { 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) result = strings.TrimSpace(source)
return return
@@ -104,7 +104,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
result = false result = false
if source, ok = args[0].(string); !ok { 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:] { for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { if target, ok := targetSpec.(string); ok {
@@ -127,7 +127,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
result = false result = false
if source, ok = args[0].(string); !ok { 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:] { for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { 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 var ok bool
if source, ok = args[0].(string); !ok { 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 { if sep, ok = args[1].(string); !ok {
@@ -182,42 +182,42 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
// Import above functions in the context // Import above functions in the context
func ImportStringFuncs(ctx ExprContext) { func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("joinStr", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
newFuncParam(paramSeparator), NewFuncParam(ParamSeparator),
newFuncParamFlag(paramItem, pfRepeat), NewFuncParamFlag(ParamItem, PfRepeat),
}) })
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("subStr", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
newFuncParam(paramSource), NewFuncParam(ParamSource),
newFuncParamFlagDef(paramStart, pfOptional, int64(0)), NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)),
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)), NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{ ctx.RegisterFunc("splitStr", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
newFuncParam(paramSource), NewFuncParam(ParamSource),
newFuncParamFlagDef(paramSeparator, pfOptional, ""), NewFuncParamFlagDef(ParamSeparator, PfDefault, ""),
newFuncParamFlagDef(paramCount, pfOptional, int64(-1)), NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{ ctx.RegisterFunc("trimStr", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
newFuncParam(paramSource), NewFuncParam(ParamSource),
}) })
ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{ ctx.RegisterFunc("startsWithStr", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
newFuncParam(paramSource), NewFuncParam(ParamSource),
newFuncParam(paramPrefix), NewFuncParam(ParamPrefix),
newFuncParamFlag("other "+paramPrefix, pfRepeat), NewFuncParamFlag("other "+ParamPrefix, PfRepeat),
}) })
ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{ ctx.RegisterFunc("endsWithStr", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
newFuncParam(paramSource), NewFuncParam(ParamSource),
newFuncParam(paramSuffix), NewFuncParam(ParamSuffix),
newFuncParamFlag("other "+paramSuffix, pfRepeat), NewFuncParamFlag("other "+ParamSuffix, PfRepeat),
}) })
} }
// Register the import function in the import-register. // Register the import function in the import-register.
// That will allow to import all function of this module by the "builtin" operator." // That will allow to import all function of this module by the "builtin" operator."
func init() { func init() {
registerImport("string", ImportStringFuncs, "string utilities") RegisterBuiltinModule("string", ImportStringFuncs, "string utilities")
} }
+48
View File
@@ -0,0 +1,48 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtins-register.go
package expr
import (
"fmt"
)
type builtinModule struct {
importFunc func(ExprContext)
description string
imported bool
}
func newBuiltinModule(importFunc func(ExprContext), description string) *builtinModule {
return &builtinModule{importFunc, description, false}
}
var builtinModuleRegister map[string]*builtinModule
func RegisterBuiltinModule(name string, importFunc func(ExprContext), description string) {
if builtinModuleRegister == nil {
builtinModuleRegister = make(map[string]*builtinModule)
}
if _, exists := builtinModuleRegister[name]; exists {
panic(fmt.Errorf("module %q already registered", name))
}
builtinModuleRegister[name] = newBuiltinModule(importFunc, description)
}
func IterateBuiltinModules(op func(name, description string, imported bool) bool) {
if op != nil {
for name, mod := range builtinModuleRegister {
if !op(name, mod.description, mod.imported) {
break
}
}
}
}
// ----
func init() {
if builtinModuleRegister == nil {
builtinModuleRegister = make(map[string]*builtinModule)
}
}
+13 -13
View File
@@ -5,17 +5,17 @@
package expr package expr
const ( const (
paramCount = "count" ParamCount = "count"
paramItem = "item" ParamItem = "item"
paramParts = "parts" ParamParts = "parts"
paramSeparator = "separator" ParamSeparator = "separator"
paramSource = "source" ParamSource = "source"
paramSuffix = "suffix" ParamSuffix = "suffix"
paramPrefix = "prefix" ParamPrefix = "prefix"
paramStart = "start" ParamStart = "start"
paramEnd = "end" ParamEnd = "end"
paramValue = "value" ParamValue = "value"
paramEllipsis = "..." ParamEllipsis = "..."
paramFilepath = "filepath" ParamFilepath = "filepath"
paramDirpath = "dirpath" ParamDirpath = "dirpath"
) )
+12 -10
View File
@@ -5,14 +5,16 @@
package expr package expr
const ( const (
typeAny = "any" TypeAny = "any"
typeBoolean = "boolean" TypeBoolean = "boolean"
typeFloat = "float" TypeFloat = "float"
typeFraction = "fraction" TypeFraction = "fraction"
typeHandle = "handle" TypeHandle = "handle"
typeInt = "integer" TypeInt = "integer"
typeItem = "item" TypeItem = "item"
typeNumber = "number" TypeNumber = "number"
typePair = "pair" TypePair = "pair"
typeString = "string" TypeString = "string"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
) )
+1 -3
View File
@@ -22,13 +22,11 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
if name[0] == '@' { if name[0] == '@' {
name = name[1:] 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()) ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
} }
func exportObjects(destCtx, sourceCtx ExprContext) { 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) // fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
// Export variables // Export variables
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
+3
View File
@@ -16,6 +16,7 @@ type Functor interface {
type ExprFuncParam interface { type ExprFuncParam interface {
Name() string Name() string
Type() string Type() string
IsDefault() bool
IsOptional() bool IsOptional() bool
IsRepeat() bool IsRepeat() bool
DefaultValue() any DefaultValue() any
@@ -36,6 +37,8 @@ type ExprFunc interface {
type ExprContext interface { type ExprContext interface {
Clone() ExprContext Clone() ExprContext
Merge(ctx ExprContext) Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
SetVar(varName string, value any) SetVar(varName string, value any)
UnsafeSetVar(varName string, value any) UnsafeSetVar(varName string, value any)
+11 -40
View File
@@ -4,13 +4,13 @@
// control.go // control.go
package expr package expr
import "strings"
// Preset control variables // Preset control variables
const ( const (
ControlLastResult = "last" ControlPreset = "_preset"
ControlBoolShortcut = "_bool_shortcut" ControlLastResult = "last"
ControlImportPath = "_import_path" ControlBoolShortcut = "_bool_shortcut"
ControlSearchPath = "_search_path"
ControlParentContext = "_parent_context"
) )
// Other control variables // Other control variables
@@ -20,43 +20,14 @@ const (
// Initial values // Initial values
const ( 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) { func initDefaultVars(ctx ExprContext) {
if _, exists := ctx.GetVar(ControlPreset); exists {
return
}
ctx.SetVar(ControlPreset, true)
ctx.SetVar(ControlBoolShortcut, true) ctx.SetVar(ControlBoolShortcut, true)
ctx.SetVar(ControlImportPath, init_import_path) ctx.SetVar(ControlSearchPath, init_search_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
} }
+1 -1
View File
@@ -212,7 +212,7 @@ func anyToFract(v any) (f *FractionType, err error) {
} }
} }
if f == nil { if f == nil {
err = errExpectedGot("fract", typeFraction, v) err = errExpectedGot("fract", TypeFraction, v)
} }
return return
} }
-150
View File
@@ -1,150 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// func-import.go
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)
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)
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
for v, err = it.Next(); err == nil; v, err = it.Next() {
if err = checkStringParamExpected(name, v, it.Index()); err != nil {
break
}
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
break
}
var file *os.File
if file, err = os.Open(sourceFilepath); err == nil {
defer file.Close()
var expr *ast
scanner := NewScanner(file, DefaultTranslations())
parser := NewParser(ctx)
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
result, err = expr.eval(ctx, false)
}
if err != nil {
break
}
} else {
break
}
}
if err != nil {
if err == io.EOF {
err = nil
} else {
result = nil
}
}
return
}
func ImportImportFuncs(ctx ExprContext) {
ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(paramFilepath, pfRepeat),
})
ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{
newFuncParamFlag(paramFilepath, pfRepeat),
})
}
func init() {
registerImport("import", ImportImportFuncs, "Functions import() and include()")
}
+16 -64
View File
@@ -42,65 +42,13 @@ func (functor *baseFunctor) GetFunc() ExprFunc {
return functor.info return functor.info
} }
// ---- Linking with Go functions
type golangFunctor struct {
baseFunctor
f FuncTemplate
}
func newGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f}
}
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
return functor.f(ctx, name, args)
}
// ---- Linking with Expr functions
type exprFunctor struct {
baseFunctor
params []ExprFuncParam
expr Expr
defCtx ExprContext
}
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
// }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
return &exprFunctor{expr: e, params: params, defCtx: ctx}
}
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
if functor.defCtx != nil {
ctx.Merge(functor.defCtx)
}
for i, p := range functor.params {
if i < len(args) {
arg := args[i]
if funcArg, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1)
paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, typeAny, paramSpecs)
} else {
ctx.UnsafeSetVar(p.Name(), arg)
}
} else {
ctx.UnsafeSetVar(p.Name(), nil)
}
}
result, err = functor.expr.eval(ctx, false)
return
}
// ---- Function Parameters // ---- Function Parameters
type paramFlags uint16 type paramFlags uint16
const ( const (
pfOptional paramFlags = 1 << iota PfDefault paramFlags = 1 << iota
pfRepeat PfOptional
PfRepeat
) )
type funcParamInfo struct { type funcParamInfo struct {
@@ -109,15 +57,15 @@ type funcParamInfo struct {
defaultValue any defaultValue any
} }
func newFuncParam(name string) ExprFuncParam { func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name} return &funcParamInfo{name: name}
} }
func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam { func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags} 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} return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
} }
@@ -126,15 +74,19 @@ func (param *funcParamInfo) Name() string {
} }
func (param *funcParamInfo) Type() 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 { func (param *funcParamInfo) IsOptional() bool {
return (param.flags & pfOptional) != 0 return (param.flags & PfOptional) != 0
} }
func (param *funcParamInfo) IsRepeat() bool { func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & pfRepeat) != 0 return (param.flags & PfRepeat) != 0
} }
func (param *funcParamInfo) DefaultValue() any { func (param *funcParamInfo) DefaultValue() any {
@@ -159,7 +111,7 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
if maxArgs == -1 { if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) 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++ maxArgs++
} else if maxArgs == minArgs { } else if maxArgs == minArgs {
minArgs++ minArgs++
@@ -207,7 +159,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
} }
sb.WriteString(p.Name()) sb.WriteString(p.Name())
if p.IsOptional() { if p.IsDefault() {
sb.WriteByte('=') sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok { if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"') sb.WriteByte('"')
@@ -226,7 +178,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
if len(info.returnType) > 0 { if len(info.returnType) > 0 {
sb.WriteString(info.returnType) sb.WriteString(info.returnType)
} else { } else {
sb.WriteString(typeAny) sb.WriteString(TypeAny)
} }
sb.WriteString(" {<body>}") sb.WriteString(" {<body>}")
return sb.String() return sb.String()
+78 -4
View File
@@ -4,13 +4,16 @@
// global-context.go // global-context.go
package expr package expr
import "path/filepath" import (
"path/filepath"
"strings"
)
var globalCtx *SimpleStore var globalCtx *SimpleStore
func ImportInContext(name string) (exists bool) { func ImportInContext(name string) (exists bool) {
var mod *module var mod *builtinModule
if mod, exists = moduleRegister[name]; exists { if mod, exists = builtinModuleRegister[name]; exists {
mod.importFunc(globalCtx) mod.importFunc(globalCtx)
mod.imported = true mod.imported = true
} }
@@ -19,7 +22,7 @@ func ImportInContext(name string) (exists bool) {
func ImportInContextByGlobPattern(pattern string) (count int, err error) { func ImportInContextByGlobPattern(pattern string) (count int, err error) {
var matched bool var matched bool
for name, mod := range moduleRegister { for name, mod := range builtinModuleRegister {
if matched, err = filepath.Match(pattern, name); err == nil { if matched, err = filepath.Match(pattern, name); err == nil {
if matched { if matched {
count++ count++
@@ -63,7 +66,78 @@ func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, owne
return 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() { func init() {
globalCtx = NewSimpleStore() globalCtx = NewSimpleStore()
initDefaultVars(globalCtx)
ImportBuiltinsFuncs(globalCtx) ImportBuiltinsFuncs(globalCtx)
} }
+6 -6
View File
@@ -16,10 +16,10 @@ func EvalString(ctx ExprContext, source string) (result any, err error) {
r := strings.NewReader(source) r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
parser := NewParser(ctx) parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil { if tree, err = parser.Parse(scanner); err == nil {
result, err = tree.eval(ctx, true) result, err = tree.Eval(ctx)
} }
return return
} }
@@ -38,10 +38,10 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
for _, arg := range args { for _, arg := range args {
if isFunc(arg.Value) { if isFunc(arg.Value) {
if f, ok := arg.Value.(FuncTemplate); ok { 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, 0, -1)
ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{ ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0), NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0),
}) })
} else { } else {
err = fmt.Errorf("invalid function specification: %q", arg.Name) 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) { func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
var tree *ast var tree *ast
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
parser := NewParser(ctx) parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil { if tree, err = parser.Parse(scanner); err == nil {
result, err = tree.Eval(ctx) result, err = tree.Eval(ctx)
+104
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
}
+21
View File
@@ -20,6 +20,10 @@ func newListA(listAny ...any) (list *ListType) {
} }
func newList(listAny []any) (list *ListType) { func newList(listAny []any) (list *ListType) {
return NewList(listAny)
}
func NewList(listAny []any) (list *ListType) {
if listAny != nil { if listAny != nil {
ls := make(ListType, len(listAny)) ls := make(ListType, len(listAny))
for i, item := range listAny { for i, item := range listAny {
@@ -30,6 +34,23 @@ func newList(listAny []any) (list *ListType) {
return 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) { func (ls *ListType) ToString(opt FmtOpt) (s string) {
var sb strings.Builder var sb strings.Builder
sb.WriteByte('[') sb.WriteByte('[')
-48
View File
@@ -1,48 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// module-register.go
package expr
import (
"fmt"
)
type module struct {
importFunc func(ExprContext)
description string
imported bool
}
func newModule(importFunc func(ExprContext), description string) *module {
return &module{importFunc, description, false}
}
var moduleRegister map[string]*module
func registerImport(name string, importFunc func(ExprContext), description string) {
if moduleRegister == nil {
moduleRegister = make(map[string]*module)
}
if _, exists := moduleRegister[name]; exists {
panic(fmt.Errorf("module %q already registered", name))
}
moduleRegister[name] = newModule(importFunc, description)
}
func IterateModules(op func(name, description string, imported bool) bool) {
if op != nil {
for name, mod := range moduleRegister {
if !op(name, mod.description, mod.imported) {
break
}
}
}
}
// ----
func init() {
if moduleRegister == nil {
moduleRegister = make(map[string]*module)
}
}
+4 -3
View File
@@ -30,7 +30,7 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro
} }
for i, p := range info.Params() { for i, p := range info.Params() {
if i >= passedCount { if i >= passedCount {
if !p.IsOptional() { if !p.IsDefault() {
break break
} }
*varParams = append(*varParams, p.DefaultValue()) *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) { func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
ctx := cloneContext(parentCtx) ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
name, _ := self.tk.Value.(string) name, _ := self.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5) params := make([]any, len(self.children), len(self.children)+5)
for i, tree := range self.children { for i, tree := range self.children {
@@ -91,12 +92,12 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
var defValue any var defValue any
flags := paramFlags(0) flags := paramFlags(0)
if len(param.children) > 0 { if len(param.children) > 0 {
flags |= pfOptional flags |= PfDefault
if defValue, err = param.children[0].compute(ctx); err != nil { if defValue, err = param.children[0].compute(ctx); err != nil {
return return
} }
} }
info := newFuncParamFlagDef(param.source(), flags, defValue) info := NewFuncParamFlagDef(param.source(), flags, defValue)
paramList = append(paramList, info) paramList = append(paramList, info)
} }
v = newExprFunctor(expr, paramList, ctx) v = newExprFunctor(expr, paramList, ctx)
+1 -1
View File
@@ -81,7 +81,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
} else if funcDef, ok := functor.(*exprFunctor); ok { } else if funcDef, ok := functor.(*exprFunctor); ok {
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) 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 { } else {
err = self.Errorf("unknown function %s()", rightChild.source()) err = self.Errorf("unknown function %s()", rightChild.source())
} }
+2 -2
View File
@@ -48,7 +48,7 @@ func newAndTerm(tk *Token) (inst *term) {
} }
func evalAnd(ctx ExprContext, self *term) (v any, err error) { func evalAnd(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, ControlBoolShortcut) { if CtrlIsEnabled(ctx, ControlBoolShortcut) {
v, err = evalAndWithShortcut(ctx, self) v, err = evalAndWithShortcut(ctx, self)
} else { } else {
v, err = evalAndWithoutShortcut(ctx, self) 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) { func evalOr(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, ControlBoolShortcut) { if CtrlIsEnabled(ctx, ControlBoolShortcut) {
v, err = evalOrWithShortcut(ctx, self) v, err = evalOrWithShortcut(ctx, self)
} else { } else {
v, err = evalOrWithoutShortcut(ctx, self) v, err = evalOrWithoutShortcut(ctx, self)
+2 -2
View File
@@ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
if ImportInContext(module) { if ImportInContext(module) {
count++ count++
} else { } else {
err = self.Errorf("unknown module %q", module) err = self.Errorf("unknown builtin module %q", module)
break break
} }
} else { } 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 break
} }
} }
+2 -2
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 { } else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok { if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) //ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{ ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
newFuncParamFlag(paramValue, pfOptional|pfRepeat), NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
}) })
} else { } else {
v = rightValue v = rightValue
+2 -1
View File
@@ -34,7 +34,8 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
if formatter, ok := sourceCtx.(Formatter); ok { if formatter, ok := sourceCtx.(Formatter); ok {
v = formatter.ToString(0) v = formatter.ToString(0)
} else { } 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) d := make(map[string]any)
for _, key := range keys { for _, key := range keys {
d[key], _ = sourceCtx.GetVar(key) d[key], _ = sourceCtx.GetVar(key)
+1 -1
View File
@@ -17,7 +17,7 @@ func newExportAllTerm(tk *Token) (inst *term) {
} }
func evalExportAll(ctx ExprContext, self *term) (v any, err error) { func evalExportAll(ctx ExprContext, self *term) (v any, err error) {
enable(ctx, control_export_all) CtrlEnable(ctx, control_export_all)
return return
} }
+102
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)
}
+1 -1
View File
@@ -12,7 +12,7 @@ type intPair struct {
} }
func (p *intPair) TypeName() string { func (p *intPair) TypeName() string {
return typePair return TypePair
} }
func (p *intPair) ToString(opt FmtOpt) string { func (p *intPair) ToString(opt FmtOpt) string {
+3 -3
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) { func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
caseData, _ := caseSel.(*selectorCase) caseData, _ := caseSel.(*selectorCase)
if caseData.filterList == nil { if caseData.filterList == nil {
selectedValue, err = caseData.caseExpr.eval(ctx, false) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
} else if filterList, ok := caseData.filterList.value().([]*term); ok { } else if filterList, ok := caseData.filterList.value().([]*term); ok {
if len(filterList) == 0 && exprValue == int64(caseIndex) { if len(filterList) == 0 && exprValue == int64(caseIndex) {
selectedValue, err = caseData.caseExpr.eval(ctx, false) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
} else { } else {
var caseValue any var caseValue any
for _, caseTerm := range filterList { for _, caseTerm := range filterList {
if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue { 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 match = true
break break
} }
+2 -5
View File
@@ -11,13 +11,10 @@ import (
//-------- parser //-------- parser
type parser struct { type parser struct {
ctx ExprContext
} }
func NewParser(ctx ExprContext) (p *parser) { func NewParser() (p *parser) {
p = &parser{ p = &parser{}
ctx: ctx,
}
return p return p
} }
+30
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)
}
+10 -1
View File
@@ -11,6 +11,7 @@ import (
) )
type SimpleStore struct { type SimpleStore struct {
parent ExprContext
varStore map[string]any varStore map[string]any
funcStore map[string]ExprFunc funcStore map[string]ExprFunc
} }
@@ -26,6 +27,14 @@ func NewSimpleStore() *SimpleStore {
func filterRefName(name string) bool { return name[0] != '@' } func filterRefName(name string) bool { return name[0] != '@' }
func filterPrivName(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 { func (ctx *SimpleStore) Clone() ExprContext {
return &SimpleStore{ return &SimpleStore{
varStore: CloneFilteredMap(ctx.varStore, filterRefName), varStore: CloneFilteredMap(ctx.varStore, filterRefName),
@@ -45,7 +54,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) { func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString("vars: {\n") sb.WriteString("vars: {\n")
first := true first := true
for _, name := range ctx.EnumVars(filterPrivName) { for _, name := range ctx.EnumVars(nil) {
if first { if first {
first = false first = false
} else { } else {
+2
View File
@@ -101,6 +101,7 @@ const (
SymKwBut SymKwBut
SymKwFunc SymKwFunc
SymKwBuiltin SymKwBuiltin
SymKwPlugin
SymKwIn SymKwIn
SymKwInclude SymKwInclude
SymKwNil SymKwNil
@@ -113,6 +114,7 @@ func init() {
keywords = map[string]Symbol{ keywords = map[string]Symbol{
"AND": SymKwAnd, "AND": SymKwAnd,
"BUILTIN": SymKwBuiltin, "BUILTIN": SymKwBuiltin,
"PLUGIN": SymKwPlugin,
"BUT": SymKwBut, "BUT": SymKwBut,
"FUNC": SymKwFunc, "FUNC": SymKwFunc,
"IN": SymKwIn, "IN": SymKwIn,
+1 -1
View File
@@ -49,7 +49,7 @@ func TestDictParser(t *testing.T) {
ctx.SetVar("var1", int64(123)) ctx.SetVar("var1", int64(123))
ctx.SetVar("var2", "abc") ctx.SetVar("var2", "abc")
ImportMathFuncs(ctx) ImportMathFuncs(ctx)
parser := NewParser(ctx) parser := NewParser()
logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr) logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr)
+2 -2
View File
@@ -43,7 +43,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
} }
func TestFunctionToStringSimple(t *testing.T) { func TestFunctionToStringSimple(t *testing.T) {
source := newGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := "func() {<body>}" want := "func() {<body>}"
got := source.ToString(0) got := source.ToString(0)
if got != want { if got != want {
@@ -52,7 +52,7 @@ func TestFunctionToStringSimple(t *testing.T) {
} }
func TestFunctionGetFunc(t *testing.T) { func TestFunctionGetFunc(t *testing.T) {
source := newGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := ExprFunc(nil) want := ExprFunc(nil)
got := source.GetFunc() got := source.GetFunc()
if got != want { if got != want {
+162
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)
}
}
-77
View File
@@ -6,19 +6,12 @@ package expr
import ( import (
"errors" "errors"
"strings"
"testing" "testing"
) )
func TestListParser(t *testing.T) { func TestListParser(t *testing.T) {
section := "List" section := "List"
/* type inputType struct {
source string
wantResult any
wantErr error
}
*/
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`[]`, newListA(), nil}, /* 1 */ {`[]`, newListA(), nil},
/* 2 */ {`[1,2,3]`, newListA(int64(1), int64(2), int64(3)), 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) // parserTestSpec(t, section, inputs, 17)
parserTest(t, section, inputs) 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)
} }
+1 -1
View File
@@ -12,7 +12,7 @@ func TestIterateModules(t *testing.T) {
section := "Module-Register" section := "Module-Register"
mods := make([]string, 0, 100) mods := make([]string, 0, 100)
IterateModules(func(name, description string, imported bool) bool { IterateBuiltinModules(func(name, description string, imported bool) bool {
mods = append(mods, name) mods = append(mods, name)
return true return true
}) })
+1 -1
View File
@@ -188,7 +188,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
var gotErr error var gotErr error
ctx := NewSimpleStore() ctx := NewSimpleStore()
parser := NewParser(ctx) parser := NewParser()
logTest(t, count, section, input.source, input.wantResult, input.wantErr) logTest(t, count, section, input.source, input.wantResult, input.wantErr)