Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fb611aa20 | |||
| 56d6d06d15 | |||
| d9f7e5b1ad | |||
| 0f54e01ef3 | |||
| 63f5db00b3 | |||
| 9745a5d909 | |||
| 0bb4c96481 | |||
| 1757298eb4 | |||
| 54041552d4 | |||
| 5302907dcf | |||
| eb4b17f078 | |||
| 33d70d6d1a | |||
| 9df9ad5dd1 | |||
| 34dc828ead | |||
| 29bc2c62a3 | |||
| 8eb2d77ea3 | |||
| 53bcf90d2a | |||
| f347b15146 | |||
| 115ce26ce9 | |||
| 227944b3fb | |||
| 0d01afcc9f | |||
| 00c76b41f1 | |||
| 08e0979cdd | |||
| f04f5822ec | |||
| 80d47879e9 | |||
| 985eb3d19d | |||
| 45734ab393 | |||
| c100cf349d |
@@ -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 {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
|
||||||
|
ctx := cloneContext(parentCtx)
|
||||||
|
ctx.SetParent(parentCtx)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if err = checkFunctionCall(ctx, funcName, ¶ms); err == nil {
|
||||||
|
if v, err = ctx.Call(funcName, params); err == nil {
|
||||||
|
exportObjects(parentCtx, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
@@ -67,7 +67,7 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
case string:
|
case string:
|
||||||
result = len(v) > 0
|
result = len(v) > 0
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "bool")
|
err = ErrCantConvert(name, v, "bool")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
result = int64(i)
|
result = int64(i)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "int")
|
err = ErrCantConvert(name, v, "int")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
case *FractionType:
|
case *FractionType:
|
||||||
result = v.toFloat()
|
result = v.toFloat()
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "float")
|
err = ErrCantConvert(name, v, "float")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -127,9 +127,9 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
var ok bool
|
var ok bool
|
||||||
if den, ok = args[1].(int64); !ok {
|
if den, ok = args[1].(int64); !ok {
|
||||||
err = errExpectedGot(name, "integer", args[1])
|
err = ErrExpectedGot(name, "integer", args[1])
|
||||||
} else if den == 0 {
|
} else if den == 0 {
|
||||||
err = errFuncDivisionByZero(name)
|
err = ErrFuncDivisionByZero(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -148,7 +148,7 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
case *FractionType:
|
case *FractionType:
|
||||||
result = v
|
result = v
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "float")
|
err = ErrCantConvert(name, v, "float")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -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.")
|
||||||
}
|
}
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
@@ -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()")
|
||||||
|
}
|
||||||
@@ -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()")
|
||||||
}
|
}
|
||||||
@@ -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 (
|
||||||
@@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
|||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = errMissingFilePath("openFile")
|
err = errMissingFilePath("appendFile")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -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("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
|
||||||
newFuncParam(paramFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
|
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
|
||||||
newFuncParam(paramFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{
|
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
|
||||||
newFuncParam(paramFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{
|
ctx.RegisterFunc("fileWrite", 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("fileRead", 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("fileClose", 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")
|
||||||
}
|
}
|
||||||
@@ -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,14 +63,14 @@ 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 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if count, err = toInt(args[2], name+"()"); err != nil {
|
if count, err = ToInt(args[2], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
|
||||||
newFuncParam(paramSeparator),
|
NewFuncParam(ParamSeparator),
|
||||||
newFuncParamFlag(paramItem, pfRepeat),
|
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
|
ctx.RegisterFunc("strSub", 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("strSplit", 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("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
|
||||||
newFuncParam(paramSource),
|
NewFuncParam(ParamSource),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{
|
ctx.RegisterFunc("strStartsWith", 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("strEndsWith", 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")
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-14
@@ -8,7 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func errTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
|
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
|
||||||
if maxArgs < 0 {
|
if maxArgs < 0 {
|
||||||
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
|
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
|
||||||
} else {
|
} else {
|
||||||
@@ -17,39 +17,39 @@ func errTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func errTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
|
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
|
||||||
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
|
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- General errors
|
// --- General errors
|
||||||
|
|
||||||
func errCantConvert(funcName string, value any, kind string) error {
|
func ErrCantConvert(funcName string, value any, kind string) error {
|
||||||
return fmt.Errorf("%s(): can't convert %s to %s", funcName, typeName(value), kind)
|
return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errExpectedGot(funcName string, kind string, value any) error {
|
func ErrExpectedGot(funcName string, kind string, value any) error {
|
||||||
return fmt.Errorf("%s() expected %s, got %s (%v)", funcName, kind, typeName(value), value)
|
return fmt.Errorf("%s(): expected %s, got %s (%v)", funcName, kind, TypeName(value), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errFuncDivisionByZero(funcName string) error {
|
func ErrFuncDivisionByZero(funcName string) error {
|
||||||
return fmt.Errorf("%s(): division by zero", funcName)
|
return fmt.Errorf("%s(): division by zero", funcName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errDivisionByZero() error {
|
func ErrDivisionByZero() error {
|
||||||
return fmt.Errorf("division by zero")
|
return fmt.Errorf("division by zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Parameter errors
|
// --- Parameter errors
|
||||||
|
|
||||||
func errMissingRequiredParameter(funcName, paramName string) error {
|
func ErrMissingRequiredParameter(funcName, paramName string) error {
|
||||||
return fmt.Errorf("%s() missing required parameter %q", funcName, paramName)
|
return fmt.Errorf("%s(): missing required parameter %q", funcName, paramName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errInvalidParameterValue(funcName, paramName string, paramValue any) error {
|
func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
|
||||||
return fmt.Errorf("%s() invalid value %s (%v) for parameter %q", funcName, typeName(paramValue), paramValue, paramName)
|
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
||||||
return fmt.Errorf("%s() the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, typeName(paramValue), paramValue)
|
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-13
@@ -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
@@ -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
@@ -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] == '@' }) {
|
||||||
|
|||||||
@@ -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
@@ -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
|
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-29
@@ -12,6 +12,12 @@ import (
|
|||||||
|
|
||||||
type DictType map[any]any
|
type DictType map[any]any
|
||||||
|
|
||||||
|
func MakeDict() (dict *DictType) {
|
||||||
|
d := make(DictType)
|
||||||
|
dict = &d
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func newDict(dictAny map[any]*term) (dict *DictType) {
|
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||||
var d DictType
|
var d DictType
|
||||||
if dictAny != nil {
|
if dictAny != nil {
|
||||||
@@ -26,42 +32,52 @@ func newDict(dictAny map[any]*term) (dict *DictType) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) {
|
func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
||||||
sb.WriteString(strings.Repeat("\t", indent))
|
indent := GetFormatIndent(opt)
|
||||||
sb.WriteString("{\n")
|
flags := GetFormatFlags(opt)
|
||||||
|
//sb.WriteString(strings.Repeat(" ", indent))
|
||||||
|
sb.WriteByte('{')
|
||||||
|
|
||||||
first := true
|
if len(*dict) > 0 {
|
||||||
for name, value := range *dict {
|
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||||
if first {
|
nest := strings.Repeat(" ", indent+1)
|
||||||
first = false
|
sb.WriteByte('\n')
|
||||||
} else {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString(strings.Repeat("\t", indent+1))
|
first := true
|
||||||
if key, ok := name.(string); ok {
|
for name, value := range *dict {
|
||||||
sb.WriteString(string('"') + key + string('"'))
|
if first {
|
||||||
} else {
|
first = false
|
||||||
sb.WriteString(fmt.Sprintf("%v", name))
|
} else {
|
||||||
}
|
sb.WriteByte(',')
|
||||||
sb.WriteString(": ")
|
sb.WriteByte('\n')
|
||||||
if f, ok := value.(Formatter); ok {
|
}
|
||||||
sb.WriteString(f.ToString(MultiLine))
|
|
||||||
} else if _, ok = value.(Functor); ok {
|
sb.WriteString(nest)
|
||||||
sb.WriteString("func(){}")
|
if key, ok := name.(string); ok {
|
||||||
} else {
|
sb.WriteString(string('"') + key + string('"'))
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", name))
|
||||||
|
}
|
||||||
|
sb.WriteString(": ")
|
||||||
|
if f, ok := value.(Formatter); ok {
|
||||||
|
sb.WriteString(f.ToString(innerOpt))
|
||||||
|
} else if _, ok = value.(Functor); ok {
|
||||||
|
sb.WriteString("func(){}")
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
sb.WriteString(strings.Repeat(" ", indent))
|
||||||
}
|
}
|
||||||
sb.WriteString(strings.Repeat("\t", indent))
|
sb.WriteString("}")
|
||||||
sb.WriteString("\n}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *DictType) ToString(opt FmtOpt) string {
|
func (dict *DictType) ToString(opt FmtOpt) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if opt&MultiLine != 0 {
|
flags := GetFormatFlags(opt)
|
||||||
dict.toMultiLine(&sb, 0)
|
if flags&MultiLine != 0 {
|
||||||
|
dict.toMultiLine(&sb, opt)
|
||||||
} else {
|
} else {
|
||||||
sb.WriteByte('{')
|
sb.WriteByte('{')
|
||||||
first := true
|
first := true
|
||||||
@@ -124,7 +140,12 @@ func (dict *DictType) merge(second *DictType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dict *DictType) setItem(key any, value any) (err error) {
|
func (dict *DictType) setItem(key any, value any) (err error) {
|
||||||
(*dict)[key]=value
|
(*dict)[key] = value
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
type DictFormat interface {
|
||||||
|
ToDict() *DictType
|
||||||
|
}
|
||||||
|
|||||||
+14
-2
@@ -6,7 +6,7 @@ package expr
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type FmtOpt uint16
|
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TTY FmtOpt = 1 << iota
|
TTY FmtOpt = 1 << iota
|
||||||
@@ -30,6 +30,18 @@ func TruncateString(s string) (trunc string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MakeFormatOptions(flags FmtOpt, indent int) FmtOpt {
|
||||||
|
return FmtOpt(indent<<16) | flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFormatFlags(opt FmtOpt) FmtOpt {
|
||||||
|
return opt & 0xFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFormatIndent(opt FmtOpt) int {
|
||||||
|
return int(opt >> 16)
|
||||||
|
}
|
||||||
|
|
||||||
type Formatter interface {
|
type Formatter interface {
|
||||||
ToString(options FmtOpt) string
|
ToString(options FmtOpt) string
|
||||||
}
|
}
|
||||||
@@ -51,7 +63,7 @@ type Typer interface {
|
|||||||
TypeName() string
|
TypeName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func typeName(v any) (name string) {
|
func TypeName(v any) (name string) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
name = "nil"
|
name = "nil"
|
||||||
} else if typer, ok := v.(Typer); ok {
|
} else if typer, ok := v.(Typer); ok {
|
||||||
|
|||||||
+4
-4
@@ -70,7 +70,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
}
|
}
|
||||||
for _, c := range dec[0:lsd] {
|
for _, c := range dec[0:lsd] {
|
||||||
if c < '0' || c > '9' {
|
if c < '0' || c > '9' {
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
return nil, ErrExpectedGot("fract", "digit", c)
|
||||||
}
|
}
|
||||||
num = num*10 + int64(c-'0')
|
num = num*10 + int64(c-'0')
|
||||||
den = den * 10
|
den = den * 10
|
||||||
@@ -81,7 +81,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
mul := int64(1)
|
mul := int64(1)
|
||||||
for _, c := range subParts[0] {
|
for _, c := range subParts[0] {
|
||||||
if c < '0' || c > '9' {
|
if c < '0' || c > '9' {
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
return nil, ErrExpectedGot("fract", "digit", c)
|
||||||
}
|
}
|
||||||
num = num*10 + int64(c-'0')
|
num = num*10 + int64(c-'0')
|
||||||
sub = sub*10 + int64(c-'0')
|
sub = sub*10 + int64(c-'0')
|
||||||
@@ -94,7 +94,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
p := subParts[1][0 : len(subParts[1])-1]
|
p := subParts[1][0 : len(subParts[1])-1]
|
||||||
for _, c := range p {
|
for _, c := range p {
|
||||||
if c < '0' || c > '9' {
|
if c < '0' || c > '9' {
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
return nil, ErrExpectedGot("fract", "digit", c)
|
||||||
}
|
}
|
||||||
num = num*10 + int64(c-'0')
|
num = num*10 + int64(c-'0')
|
||||||
den = den*10 + 9
|
den = den*10 + 9
|
||||||
@@ -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
@@ -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()")
|
|
||||||
}
|
|
||||||
+18
-66
@@ -21,7 +21,7 @@ func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
|
|||||||
if functor.info != nil {
|
if functor.info != nil {
|
||||||
s = functor.info.ToString(opt)
|
s = functor.info.ToString(opt)
|
||||||
} else {
|
} else {
|
||||||
s = "func() {<body>}"
|
s = "func() {}"
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
@@ -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,9 +178,9 @@ 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(" {}")
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-4
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
+3
-3
@@ -26,21 +26,21 @@ func NewListIterator(list *ListType, args []any) (it *ListIterator) {
|
|||||||
}
|
}
|
||||||
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
||||||
if argc >= 1 {
|
if argc >= 1 {
|
||||||
if i, err := toInt(args[0], "start index"); err == nil {
|
if i, err := ToInt(args[0], "start index"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = listLen + i
|
i = listLen + i
|
||||||
}
|
}
|
||||||
it.start = i
|
it.start = i
|
||||||
}
|
}
|
||||||
if argc >= 2 {
|
if argc >= 2 {
|
||||||
if i, err := toInt(args[1], "stop index"); err == nil {
|
if i, err := ToInt(args[1], "stop index"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = listLen + i
|
i = listLen + i
|
||||||
}
|
}
|
||||||
it.stop = i
|
it.stop = i
|
||||||
}
|
}
|
||||||
if argc >= 3 {
|
if argc >= 3 {
|
||||||
if i, err := toInt(args[2], "step"); err == nil {
|
if i, err := ToInt(args[2], "step"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = -i
|
i = -i
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-6
@@ -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,17 +34,42 @@ 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) {
|
||||||
|
indent := GetFormatIndent(opt)
|
||||||
|
flags := GetFormatFlags(opt)
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteByte('[')
|
sb.WriteByte('[')
|
||||||
if len(*ls) > 0 {
|
if len(*ls) > 0 {
|
||||||
if opt&MultiLine != 0 {
|
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||||
sb.WriteString("\n ")
|
nest := strings.Repeat(" ", indent+1)
|
||||||
|
|
||||||
|
if flags&MultiLine != 0 {
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
sb.WriteString(nest)
|
||||||
}
|
}
|
||||||
for i, item := range []any(*ls) {
|
for i, item := range []any(*ls) {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
if opt&MultiLine != 0 {
|
if flags&MultiLine != 0 {
|
||||||
sb.WriteString(",\n ")
|
sb.WriteString(",\n")
|
||||||
|
sb.WriteString(nest)
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
@@ -49,17 +78,20 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
|||||||
sb.WriteByte('"')
|
sb.WriteByte('"')
|
||||||
sb.WriteString(s)
|
sb.WriteString(s)
|
||||||
sb.WriteByte('"')
|
sb.WriteByte('"')
|
||||||
|
} else if formatter, ok := item.(Formatter); ok {
|
||||||
|
sb.WriteString(formatter.ToString(innerOpt))
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf("%v", item))
|
sb.WriteString(fmt.Sprintf("%v", item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opt&MultiLine != 0 {
|
if flags&MultiLine != 0 {
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
|
sb.WriteString(strings.Repeat(" ", indent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteByte(']')
|
sb.WriteByte(']')
|
||||||
s = sb.String()
|
s = sb.String()
|
||||||
if opt&Truncate != 0 && len(s) > TruncateSize {
|
if flags&Truncate != 0 && len(s) > TruncateSize {
|
||||||
s = TruncateString(s)
|
s = TruncateString(s)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+6
-5
@@ -26,18 +26,18 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro
|
|||||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||||
passedCount := len(*varParams)
|
passedCount := len(*varParams)
|
||||||
if info.MinArgs() > passedCount {
|
if info.MinArgs() > passedCount {
|
||||||
err = errTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||||
}
|
}
|
||||||
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
|
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
|
||||||
err = errTooMuchParams(name, info.MaxArgs(), len(*varParams))
|
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
|
||||||
}
|
}
|
||||||
if err == nil && owner != ctx {
|
if err == nil && owner != ctx {
|
||||||
ctx.RegisterFuncInfo(info)
|
ctx.RegisterFuncInfo(info)
|
||||||
@@ -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)
|
||||||
|
|||||||
+3
-3
@@ -28,7 +28,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
|
|||||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, typeName(keyListValue))
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||||
@@ -41,7 +41,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
|
|||||||
if index, ok := keyValue.(int64); ok {
|
if index, ok := keyValue.(int64); ok {
|
||||||
err = collection.setItem(index, value)
|
err = collection.setItem(index, value)
|
||||||
} else {
|
} else {
|
||||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, typeName(keyValue))
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||||
}
|
}
|
||||||
case *DictType:
|
case *DictType:
|
||||||
err = collection.setItem(keyValue, value)
|
err = collection.setItem(keyValue, value)
|
||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -25,7 +25,7 @@ func evalNot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, ok := toBool(rightValue); ok {
|
if b, ok := ToBool(rightValue); ok {
|
||||||
v = !b
|
v = !b
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(rightValue)
|
err = self.errIncompatibleType(rightValue)
|
||||||
@@ -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)
|
||||||
@@ -65,8 +65,8 @@ func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = toBool(leftValue)
|
leftBool, lok = ToBool(leftValue)
|
||||||
rightBool, rok = toBool(rightValue)
|
rightBool, rok = ToBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool && rightBool
|
v = leftBool && rightBool
|
||||||
@@ -87,13 +87,13 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := toBool(leftValue); !lok {
|
if leftBool, lok := ToBool(leftValue); !lok {
|
||||||
err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool)
|
err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool)
|
||||||
return
|
return
|
||||||
} else if !leftBool {
|
} else if !leftBool {
|
||||||
v = false
|
v = false
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if rightBool, rok := toBool(rightValue); rok {
|
if rightBool, rok := ToBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
@@ -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)
|
||||||
@@ -134,8 +134,8 @@ func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = toBool(leftValue)
|
leftBool, lok = ToBool(leftValue)
|
||||||
rightBool, rok = toBool(rightValue)
|
rightBool, rok = ToBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool || rightBool
|
v = leftBool || rightBool
|
||||||
@@ -156,13 +156,13 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := toBool(leftValue); !lok {
|
if leftBool, lok := ToBool(leftValue); !lok {
|
||||||
err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool)
|
err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool)
|
||||||
return
|
return
|
||||||
} else if leftBool {
|
} else if leftBool {
|
||||||
v = true
|
v = true
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if rightBool, rok := toBool(rightValue); rok {
|
if rightBool, rok := ToBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
|||||||
+2
-2
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+5
-2
@@ -31,10 +31,13 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sourceCtx != nil {
|
if sourceCtx != nil {
|
||||||
if formatter, ok := sourceCtx.(Formatter); ok {
|
if formatter, ok := sourceCtx.(DictFormat); ok {
|
||||||
|
v = formatter.ToDict()
|
||||||
|
} else 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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
|
|||||||
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
||||||
var v int
|
var v int
|
||||||
|
|
||||||
if v, err = toInt((*indexList)[0], "index expression"); err == nil {
|
if v, err = ToInt((*indexList)[0], "index expression"); err == nil {
|
||||||
if v < 0 && v >= -maxValue {
|
if v < 0 && v >= -maxValue {
|
||||||
v = maxValue + v
|
v = maxValue + v
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else if it, ok := childValue.(Iterator); ok {
|
} else if it, ok := childValue.(Iterator); ok {
|
||||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||||
count, _ := extIt.CallOperation(countName, nil)
|
count, _ := extIt.CallOperation(countName, nil)
|
||||||
v, _ = toInt(count, "")
|
v, _ = ToInt(count, "")
|
||||||
} else {
|
} else {
|
||||||
v = int64(it.Index() + 1)
|
v = int64(it.Index() + 1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-plugin.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------- plugin term
|
||||||
|
|
||||||
|
func newPluginTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priSign,
|
||||||
|
evalFunc: evalPlugin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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
@@ -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 {
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ func newNotEqualTerm(tk *Token) (inst *term) {
|
|||||||
|
|
||||||
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if v, err = evalEqual(ctx, self); err == nil {
|
if v, err = evalEqual(ctx, self); err == nil {
|
||||||
b, _ := toBool(v)
|
b, _ := ToBool(v)
|
||||||
v = !b
|
v = !b
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
// 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 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(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("DEPENDENCIES"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if deps := *sym.(*[]string); len(deps) > 0 {
|
||||||
|
// var count int
|
||||||
|
if err = loadModules(dirList, deps); err != nil {
|
||||||
|
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 loadModules(dirList []string, moduleNames []string) (err error) {
|
||||||
|
for _, name := range moduleNames {
|
||||||
|
if err1 := importPlugin(dirList, name); err1 != nil {
|
||||||
|
if !ImportInContext(name) {
|
||||||
|
err = err1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pluginRegister = make(map[string]*plugin.Plugin)
|
||||||
|
}
|
||||||
+9
-4
@@ -178,13 +178,18 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk = self.makeToken(SymDot, ch)
|
tk = self.makeToken(SymDot, ch)
|
||||||
}
|
}
|
||||||
case '\'':
|
case '\'':
|
||||||
tk = self.makeToken(SymQuote, ch)
|
if escape {
|
||||||
|
tk = self.makeToken(SymQuote, ch)
|
||||||
|
escape = false
|
||||||
|
} else {
|
||||||
|
tk = self.fetchString(ch)
|
||||||
|
}
|
||||||
case '"':
|
case '"':
|
||||||
if escape {
|
if escape {
|
||||||
tk = self.makeToken(SymDoubleQuote, ch)
|
tk = self.makeToken(SymDoubleQuote, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
tk = self.fetchString()
|
tk = self.fetchString(ch)
|
||||||
}
|
}
|
||||||
case '`':
|
case '`':
|
||||||
tk = self.makeToken(SymBackTick, ch)
|
tk = self.makeToken(SymBackTick, ch)
|
||||||
@@ -533,7 +538,7 @@ func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchString() (tk *Token) {
|
func (self *scanner) fetchString(termCh byte) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var ch, prev byte
|
var ch, prev byte
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -554,7 +559,7 @@ func (self *scanner) fetchString() (tk *Token) {
|
|||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
prev = 0
|
prev = 0
|
||||||
} else if ch == '"' {
|
} else if ch == termCh {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
prev = ch
|
prev = ch
|
||||||
|
|||||||
+58
-5
@@ -7,10 +7,11 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
// "strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
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),
|
||||||
@@ -41,11 +50,11 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
|
|||||||
ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
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 {
|
||||||
@@ -68,7 +77,7 @@ func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString(strings.Repeat("\t", indent))
|
sb.WriteString(strings.Repeat("\t", indent))
|
||||||
sb.WriteString("\n}\n")
|
sb.WriteString("\n}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func varsCtxToString(ctx ExprContext, indent int) string {
|
func varsCtxToString(ctx ExprContext, indent int) string {
|
||||||
@@ -97,15 +106,59 @@ func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
|||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString("\n}\n")
|
sb.WriteString("\n}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
varsCtxToBuilder(&sb, ctx, 0)
|
varsCtxToBuilder(&sb, ctx, 0)
|
||||||
|
sb.WriteByte('\n')
|
||||||
funcsCtxToBuilder(&sb, ctx, 0)
|
funcsCtxToBuilder(&sb, ctx, 0)
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
||||||
|
dict := ctx.ToDict()
|
||||||
|
return dict.ToString(opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) varsToDict(dict *DictType) *DictType {
|
||||||
|
names := ctx.EnumVars(nil)
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range ctx.EnumVars(nil) {
|
||||||
|
value, _ := ctx.GetVar(name)
|
||||||
|
if f, ok := value.(Formatter); ok {
|
||||||
|
(*dict)[name] = f.ToString(0)
|
||||||
|
} else if _, ok = value.(Functor); ok {
|
||||||
|
(*dict)[name] = "func(){}"
|
||||||
|
} else {
|
||||||
|
(*dict)[name] = fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dict
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) funcsToDict(dict *DictType) *DictType {
|
||||||
|
names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range names {
|
||||||
|
value, _ := ctx.GetFuncInfo(name)
|
||||||
|
if formatter, ok := value.(Formatter); ok {
|
||||||
|
(*dict)[name] = formatter.ToString(0)
|
||||||
|
} else {
|
||||||
|
(*dict)[name] = fmt.Sprintf("%v", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dict
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) ToDict() (dict *DictType) {
|
||||||
|
dict = MakeDict()
|
||||||
|
(*dict)["variables"] = ctx.varsToDict(MakeDict())
|
||||||
|
(*dict)["functions"] = ctx.funcsToDict(MakeDict())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
|
func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
|
||||||
v, exists = ctx.varStore[varName]
|
v, exists = ctx.varStore[varName]
|
||||||
|
|||||||
@@ -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,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.
|
||||||
|
|
||||||
// t_func-base_test.go
|
// t_builtin-base_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFuncBase(t *testing.T) {
|
func TestFuncBase(t *testing.T) {
|
||||||
section := "Func-Base"
|
section := "Builtin-Base"
|
||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`isNil(nil)`, true, nil},
|
/* 1 */ {`isNil(nil)`, true, nil},
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-import_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncImport(t *testing.T) {
|
||||||
|
section := "Builtin-Import"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`builtin "import"; import("./test-resources/test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
|
/* 2 */ {`builtin "import"; import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
|
/* 3 */ {`builtin "import"; importAll("./test-resources/test-funcs.expr"); six()`, int64(6), nil},
|
||||||
|
/* 4 */ {`builtin "import"; import("./test-resources/sample-export-all.expr"); six()`, int64(6), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Setenv("EXPR_PATH", "test-resources")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 69)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
// t_func-math-arith_test.go
|
// t_builtin-math-arith_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFuncMathArith(t *testing.T) {
|
func TestFuncMathArith(t *testing.T) {
|
||||||
section := "Func-Math-Arith"
|
section := "Builtin-Math-Arith"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
||||||
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-os-file_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncOs(t *testing.T) {
|
||||||
|
section := "Builtin-OS-File"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
||||||
|
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
||||||
|
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
||||||
|
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
||||||
|
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWrite(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||||
|
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileRead(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||||
|
/* 7 */ {`builtin "os.file"; word=fileRead(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
|
||||||
|
/* 7 */ {`builtin "os.file"; fileWrite(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 69)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-string_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncString(t *testing.T) {
|
||||||
|
section := "Builtin-String"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
||||||
|
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
||||||
|
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
||||||
|
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)},
|
||||||
|
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)},
|
||||||
|
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
||||||
|
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||||
|
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
|
||||||
|
/* 9 */ {`builtin "string"; strSub("0123456789", -3)`, "789", nil},
|
||||||
|
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
|
||||||
|
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
|
||||||
|
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
|
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)},
|
||||||
|
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
|
||||||
|
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
|
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)},
|
||||||
|
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||||
|
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)},
|
||||||
|
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)},
|
||||||
|
|
||||||
|
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||||
|
}
|
||||||
|
funcs: {
|
||||||
|
add(any=0 ...) -> number,
|
||||||
|
dec(any) -> decimal,
|
||||||
|
endsWithStr(source, suffix) -> boolean,
|
||||||
|
fract(any, denominator=1) -> fraction,
|
||||||
|
import( ...) -> any,
|
||||||
|
importAll( ...) -> any,
|
||||||
|
int(any) -> integer,
|
||||||
|
isBool(any) -> boolean,
|
||||||
|
isDec(any) -> boolean,
|
||||||
|
isDict(any) -> boolean,
|
||||||
|
isFloat(any) -> boolean,
|
||||||
|
isFract(any) -> boolean,
|
||||||
|
isInt(any) -> boolean,
|
||||||
|
isList(any) -> boolean,
|
||||||
|
isNil(any) -> boolean,
|
||||||
|
isString(any) -> boolean,
|
||||||
|
joinStr(separator, item="" ...) -> string,
|
||||||
|
mul(any=1 ...) -> number,
|
||||||
|
splitStr(source, separator="", count=-1) -> list of string,
|
||||||
|
startsWithStr(source, prefix) -> boolean,
|
||||||
|
subStr(source, start=0, count=-1) -> string,
|
||||||
|
trimStr(source) -> string
|
||||||
|
}
|
||||||
|
`, nil},*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 19)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
+2
-2
@@ -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)
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ func TestDictToStringMultiLine(t *testing.T) {
|
|||||||
var good bool
|
var good bool
|
||||||
section := "dict-ToString-ML"
|
section := "dict-ToString-ML"
|
||||||
want := `{
|
want := `{
|
||||||
"first": 1
|
"first": 1
|
||||||
}`
|
}`
|
||||||
args := map[any]*term{
|
args := map[any]*term{
|
||||||
"first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)),
|
"first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)),
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`0?{}`, nil, nil},
|
/* 1 */ {`0?{}`, nil, nil},
|
||||||
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||||
/* 3 */ {`builtin "os.file"; f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileRead(f); fileClose(f); line`, "uno", nil},
|
||||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||||
/* 6 */ {`
|
/* 6 */ {`
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// t_func-import_test.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFuncImport(t *testing.T) {
|
|
||||||
section := "Func-Import"
|
|
||||||
inputs := []inputType{
|
|
||||||
/* 1 */ {`builtin "import"; import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
|
||||||
/* 2 */ {`builtin "import"; import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
|
||||||
/* 3 */ {`builtin "import"; importAll("./test-funcs.expr"); six()`, int64(6), nil},
|
|
||||||
/* 4 */ {`builtin "import"; import("./sample-export-all.expr"); six()`, int64(6), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
|
||||||
parserTest(t, section, inputs)
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// t_func-os_test.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFuncOs(t *testing.T) {
|
|
||||||
section := "Func-OS"
|
|
||||||
inputs := []inputType{
|
|
||||||
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
|
||||||
/* 2 */ {`builtin "os.file"; handle=openFile("/etc/hosts"); closeFile(handle)`, true, nil},
|
|
||||||
/* 3 */ {`builtin "os.file"; handle=openFile("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
|
||||||
/* 4 */ {`builtin "os.file"; handle=createFile("/tmp/dummy"); closeFile(handle)`, true, nil},
|
|
||||||
/* 5 */ {`builtin "os.file"; handle=appendFile("/tmp/dummy"); writeFile(handle, "bye-bye"); closeFile(handle)`, true, nil},
|
|
||||||
/* 6 */ {`builtin "os.file"; handle=openFile("/tmp/dummy"); word=readFile(handle, "-"); closeFile(handle);word`, "bye", nil},
|
|
||||||
/* 7 */ {`builtin "os.file"; word=readFile(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
|
|
||||||
/* 7 */ {`builtin "os.file"; writeFile(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
|
|
||||||
}
|
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
|
||||||
parserTest(t, section, inputs)
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// t_func-string_test.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFuncString(t *testing.T) {
|
|
||||||
section := "Func-String"
|
|
||||||
|
|
||||||
inputs := []inputType{
|
|
||||||
/* 1 */ {`builtin "string"; joinStr("-", "one", "two", "three")`, "one-two-three", nil},
|
|
||||||
/* 2 */ {`builtin "string"; joinStr("-", ["one", "two", "three"])`, "one-two-three", nil},
|
|
||||||
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; joinStr("-", ls)`, "one-two-three", nil},
|
|
||||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; joinStr(1, ls)`, nil, errors.New(`joinStr() the "separator" parameter must be a string, got a integer (1)`)},
|
|
||||||
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; joinStr("-", ls)`, nil, errors.New(`joinStr() expected string, got integer (2)`)},
|
|
||||||
/* 6 */ {`builtin "string"; "<"+trimStr(" bye bye ")+">"`, "<bye bye>", nil},
|
|
||||||
/* 7 */ {`builtin "string"; subStr("0123456789", 1,2)`, "12", nil},
|
|
||||||
/* 8 */ {`builtin "string"; subStr("0123456789", -3,2)`, "78", nil},
|
|
||||||
/* 9 */ {`builtin "string"; subStr("0123456789", -3)`, "789", nil},
|
|
||||||
/* 10 */ {`builtin "string"; subStr("0123456789")`, "0123456789", nil},
|
|
||||||
/* 11 */ {`builtin "string"; startsWithStr("0123456789", "xyz", "012")`, true, nil},
|
|
||||||
/* 12 */ {`builtin "string"; startsWithStr("0123456789", "xyz", "0125")`, false, nil},
|
|
||||||
/* 13 */ {`builtin "string"; startsWithStr("0123456789")`, nil, errors.New(`startsWithStr(): too few params -- expected 2 or more, got 1`)},
|
|
||||||
/* 14 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
|
|
||||||
/* 15 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
|
|
||||||
/* 16 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`endsWithStr(): too few params -- expected 2 or more, got 1`)},
|
|
||||||
/* 17 */ {`builtin "string"; splitStr("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
|
||||||
/* 18 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got integer (1)`)},
|
|
||||||
/* 19 */ {`builtin "string"; joinStr()`, nil, errors.New(`joinStr(): too few params -- expected 1 or more, got 0`)},
|
|
||||||
|
|
||||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
|
||||||
}
|
|
||||||
funcs: {
|
|
||||||
add(any=0 ...) -> number,
|
|
||||||
dec(any) -> decimal,
|
|
||||||
endsWithStr(source, suffix) -> boolean,
|
|
||||||
fract(any, denominator=1) -> fraction,
|
|
||||||
import( ...) -> any,
|
|
||||||
importAll( ...) -> any,
|
|
||||||
int(any) -> integer,
|
|
||||||
isBool(any) -> boolean,
|
|
||||||
isDec(any) -> boolean,
|
|
||||||
isDict(any) -> boolean,
|
|
||||||
isFloat(any) -> boolean,
|
|
||||||
isFract(any) -> boolean,
|
|
||||||
isInt(any) -> boolean,
|
|
||||||
isList(any) -> boolean,
|
|
||||||
isNil(any) -> boolean,
|
|
||||||
isString(any) -> boolean,
|
|
||||||
joinStr(separator, item="" ...) -> string,
|
|
||||||
mul(any=1 ...) -> number,
|
|
||||||
splitStr(source, separator="", count=-1) -> list of string,
|
|
||||||
startsWithStr(source, prefix) -> boolean,
|
|
||||||
subStr(source, start=0, count=-1) -> string,
|
|
||||||
trimStr(source) -> string
|
|
||||||
}
|
|
||||||
`, nil},*/
|
|
||||||
}
|
|
||||||
|
|
||||||
//t.Setenv("EXPR_PATH", ".")
|
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 19)
|
|
||||||
parserTest(t, section, inputs)
|
|
||||||
}
|
|
||||||
+3
-3
@@ -43,8 +43,8 @@ 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() {}"
|
||||||
got := source.ToString(0)
|
got := source.ToString(0)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, 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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-9
@@ -8,15 +8,15 @@ import "testing"
|
|||||||
|
|
||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
||||||
/* 2 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||||
/* 3 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
|
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
|
||||||
/* 4 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
||||||
/* 5 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||||
/* 6 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||||
/* 7 */ {`builtin "math.arith"; include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
||||||
/* 8 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
|
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
|
||||||
/* 10 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
||||||
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||||
}
|
}
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
})
|
})
|
||||||
|
|||||||
+2
-2
@@ -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)
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
|
|||||||
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
||||||
|
|
||||||
if !eq /*gotResult != input.wantResult*/ {
|
if !eq /*gotResult != input.wantResult*/ {
|
||||||
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, typeName(gotResult), input.wantResult, typeName(input.wantResult))
|
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
|
||||||
good = false
|
good = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ func TestScanner(t *testing.T) {
|
|||||||
/* 22 */ {"@", SymAt, nil, nil},
|
/* 22 */ {"@", SymAt, nil, nil},
|
||||||
/* 23 */ {`#`, SymHash, nil, nil},
|
/* 23 */ {`#`, SymHash, nil, nil},
|
||||||
/* 24 */ {`%`, SymPercent, nil, nil},
|
/* 24 */ {`%`, SymPercent, nil, nil},
|
||||||
/* 25 */ {`'`, SymQuote, nil, nil},
|
/* 25 */ {`\'`, SymQuote, nil, nil},
|
||||||
/* 26 */ {`\"`, SymDoubleQuote, nil, nil},
|
/* 26 */ {`\"`, SymDoubleQuote, nil, nil},
|
||||||
/* 27 */ {`_`, SymUndescore, nil, nil},
|
/* 27 */ {`_`, SymUndescore, nil, nil},
|
||||||
/* 28 */ {`<>`, SymLessGreater, nil, nil},
|
/* 28 */ {`<>`, SymLessGreater, nil, nil},
|
||||||
|
|||||||
+5
-5
@@ -63,7 +63,7 @@ func TestIsString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count++
|
count++
|
||||||
b, ok := toBool(true)
|
b, ok := ToBool(true)
|
||||||
if !(ok && b) {
|
if !(ok && b) {
|
||||||
t.Errorf("%d: toBool(true) b=%v, ok=%v -> result = false, want true", count, b, ok)
|
t.Errorf("%d: toBool(true) b=%v, ok=%v -> result = false, want true", count, b, ok)
|
||||||
failed++
|
failed++
|
||||||
@@ -72,7 +72,7 @@ func TestIsString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count++
|
count++
|
||||||
b, ok = toBool("abc")
|
b, ok = ToBool("abc")
|
||||||
if !(ok && b) {
|
if !(ok && b) {
|
||||||
t.Errorf("%d: toBool(\"abc\") b=%v, ok=%v -> result = false, want true", count, b, ok)
|
t.Errorf("%d: toBool(\"abc\") b=%v, ok=%v -> result = false, want true", count, b, ok)
|
||||||
failed++
|
failed++
|
||||||
@@ -81,7 +81,7 @@ func TestIsString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count++
|
count++
|
||||||
b, ok = toBool([]int{})
|
b, ok = ToBool([]int{})
|
||||||
if ok {
|
if ok {
|
||||||
t.Errorf("%d: toBool([]) b=%v, ok=%v -> result = true, want false", count, b, ok)
|
t.Errorf("%d: toBool([]) b=%v, ok=%v -> result = true, want false", count, b, ok)
|
||||||
failed++
|
failed++
|
||||||
@@ -97,7 +97,7 @@ func TestToIntOk(t *testing.T) {
|
|||||||
wantValue := int(64)
|
wantValue := int(64)
|
||||||
wantErr := error(nil)
|
wantErr := error(nil)
|
||||||
|
|
||||||
gotValue, gotErr := toInt(source, "test")
|
gotValue, gotErr := ToInt(source, "test")
|
||||||
|
|
||||||
if gotErr != nil || gotValue != wantValue {
|
if gotErr != nil || gotValue != wantValue {
|
||||||
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
||||||
@@ -110,7 +110,7 @@ func TestToIntErr(t *testing.T) {
|
|||||||
wantValue := 0
|
wantValue := 0
|
||||||
wantErr := errors.New(`test expected integer, got uint64 (64)`)
|
wantErr := errors.New(`test expected integer, got uint64 (64)`)
|
||||||
|
|
||||||
gotValue, gotErr := toInt(source, "test")
|
gotValue, gotErr := ToInt(source, "test")
|
||||||
|
|
||||||
if gotErr.Error() != wantErr.Error() || gotValue != wantValue {
|
if gotErr.Error() != wantErr.Error() || gotValue != wantValue {
|
||||||
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
||||||
|
|||||||
@@ -156,15 +156,15 @@ func (self *term) toInt(computedValue any, valueDescription string) (i int, err
|
|||||||
if index64, ok := computedValue.(int64); ok {
|
if index64, ok := computedValue.(int64); ok {
|
||||||
i = int(index64)
|
i = int(index64)
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("%s, got %s (%v)", valueDescription, typeName(computedValue), computedValue)
|
err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||||
leftType := typeName(leftValue)
|
leftType := TypeName(leftValue)
|
||||||
leftText := getFormatted(leftValue, Truncate)
|
leftText := getFormatted(leftValue, Truncate)
|
||||||
rightType := typeName(rightValue)
|
rightType := TypeName(rightValue)
|
||||||
rightText := getFormatted(rightValue, Truncate)
|
rightText := getFormatted(rightValue, Truncate)
|
||||||
return self.tk.Errorf(
|
return self.tk.Errorf(
|
||||||
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
|
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
|
||||||
@@ -176,7 +176,7 @@ func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
|||||||
func (self *term) errIncompatibleType(value any) error {
|
func (self *term) errIncompatibleType(value any) error {
|
||||||
return self.tk.Errorf(
|
return self.tk.Errorf(
|
||||||
"prefix/postfix operator %q do not support operand '%v' [%s]",
|
"prefix/postfix operator %q do not support operand '%v' [%s]",
|
||||||
self.source(), value, typeName(value))
|
self.source(), value, TypeName(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) Errorf(template string, args ...any) (err error) {
|
func (self *term) Errorf(template string, args ...any) (err error) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
builtin ["os.file", "base"];
|
builtin ["os.file", "base"];
|
||||||
|
|
||||||
readInt=func(fh){
|
readInt=func(fh){
|
||||||
line=readFile(fh);
|
line=fileRead(fh);
|
||||||
line ? [nil] {nil} :: {int(line)}
|
line ? [nil] {nil} :: {int(line)}
|
||||||
};
|
};
|
||||||
|
|
||||||
ds={
|
ds={
|
||||||
"init":func(filename){
|
"init":func(filename){
|
||||||
fh=openFile(filename);
|
fh=fileOpen(filename);
|
||||||
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
|
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
|
||||||
fh
|
fh
|
||||||
},
|
},
|
||||||
@@ -20,7 +20,7 @@ ds={
|
|||||||
:: {@prev=current; @current=readInt(fh) but current}
|
:: {@prev=current; @current=readInt(fh) but current}
|
||||||
},
|
},
|
||||||
"clean":func(fh){
|
"clean":func(fh){
|
||||||
closeFile(fh)
|
fileClose(fh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func numAsFloat(v any) (f float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func toBool(v any) (b bool, ok bool) {
|
func ToBool(v any) (b bool, ok bool) {
|
||||||
ok = true
|
ok = true
|
||||||
switch x := v.(type) {
|
switch x := v.(type) {
|
||||||
case string:
|
case string:
|
||||||
@@ -201,13 +201,13 @@ func CloneFilteredMap[K comparable, V any](source map[K]V, filter func(key K) (a
|
|||||||
return CopyFilteredMap(dest, source, filter)
|
return CopyFilteredMap(dest, source, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toInt(value any, description string) (i int, err error) {
|
func ToInt(value any, description string) (i int, err error) {
|
||||||
if valueInt64, ok := value.(int64); ok {
|
if valueInt64, ok := value.(int64); ok {
|
||||||
i = int(valueInt64)
|
i = int(valueInt64)
|
||||||
} else if valueInt, ok := value.(int); ok {
|
} else if valueInt, ok := value.(int); ok {
|
||||||
i = valueInt
|
i = valueInt
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("%s expected integer, got %s (%v)", description, typeName(value), value)
|
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user