Compare commits
14 Commits
3b641ac793
...
fe5c8e9619
Author | SHA1 | Date | |
---|---|---|---|
fe5c8e9619 | |||
7164e8816c | |||
e0f3b028fc | |||
522b5983bd | |||
f1e2163277 | |||
bbdf498cf3 | |||
f75c991ed2 | |||
a7836143cc | |||
ba9b9cb28f | |||
ef1baa11a8 | |||
cfddbd60b9 | |||
0760141caf | |||
b9e780e659 | |||
e41ddc664c |
@ -17,19 +17,31 @@ type exprFunctor struct {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
||||||
return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
var defCtx ExprContext
|
||||||
|
if ctx != nil {
|
||||||
|
// if ctx.GetParent() != nil {
|
||||||
|
// defCtx = ctx.Clone()
|
||||||
|
// defCtx.SetParent(ctx)
|
||||||
|
// } else {
|
||||||
|
defCtx = ctx
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
||||||
|
return functor.defCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
if functor.defCtx != nil {
|
// if functor.defCtx != nil {
|
||||||
ctx.Merge(functor.defCtx)
|
// ctx.Merge(functor.defCtx)
|
||||||
}
|
// }
|
||||||
|
|
||||||
for i, p := range functor.params {
|
for i, p := range functor.params {
|
||||||
if i < len(args) {
|
if i < len(args) {
|
||||||
arg := args[i]
|
arg := args[i]
|
||||||
if funcArg, ok := arg.(Functor); ok {
|
if funcArg, ok := arg.(Functor); ok {
|
||||||
// ctx.RegisterFunc(p, functor, 0, -1)
|
|
||||||
paramSpecs := funcArg.GetParams()
|
paramSpecs := funcArg.GetParams()
|
||||||
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
||||||
} else {
|
} else {
|
||||||
@ -42,17 +54,3 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
|
|||||||
result, err = functor.expr.Eval(ctx)
|
result, err = functor.expr.Eval(ctx)
|
||||||
return
|
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
|
|
||||||
// }
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -120,6 +121,32 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func stringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
switch v := args[0].(type) {
|
||||||
|
case int64:
|
||||||
|
result = strconv.FormatInt(v, 10)
|
||||||
|
case float64:
|
||||||
|
result = strconv.FormatFloat(v, 'g', -1, 64)
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
result = "true"
|
||||||
|
} else {
|
||||||
|
result = "false"
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
result = v
|
||||||
|
case *FractionType:
|
||||||
|
result = v.ToString(0)
|
||||||
|
case Formatter:
|
||||||
|
result = v.ToString(0)
|
||||||
|
case fmt.Stringer:
|
||||||
|
result = v.String()
|
||||||
|
default:
|
||||||
|
err = ErrCantConvert(name, v, "string")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[0].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
@ -175,6 +202,7 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
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("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
|
||||||
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
|
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
|
||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
NewFuncParamFlagDef("denominator", PfDefault, 1),
|
NewFuncParamFlagDef("denominator", PfDefault, 1),
|
||||||
|
@ -4,11 +4,26 @@
|
|||||||
// builtin-fmt.go
|
// builtin-fmt.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getStdout(ctx ExprContext) io.Writer {
|
||||||
|
var w io.Writer
|
||||||
|
if wany, exists := ctx.GetVar(ControlStdout); exists && wany != nil {
|
||||||
|
w, _ = wany.(io.Writer)
|
||||||
|
}
|
||||||
|
if w == nil {
|
||||||
|
w = os.Stdout
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
var n int
|
var n int
|
||||||
if n, err = fmt.Print(args...); err == nil {
|
if n, err = fmt.Fprint(getStdout(ctx), args...); err == nil {
|
||||||
result = int64(n)
|
result = int64(n)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -16,7 +31,7 @@ func printFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
|
|
||||||
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
var n int
|
var n int
|
||||||
if n, err = fmt.Println(args...); err == nil {
|
if n, err = fmt.Fprintln(getStdout(ctx), args...); err == nil {
|
||||||
result = int64(n)
|
result = int64(n)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -66,11 +66,11 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
|
|||||||
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 = ToGoInt(args[1], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if count, err = ToInt(args[2], name+"()"); err != nil {
|
if count, err = ToGoInt(args[2], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,15 +36,15 @@ 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)
|
||||||
|
@ -10,6 +10,7 @@ type Functor interface {
|
|||||||
SetFunc(info ExprFunc)
|
SetFunc(info ExprFunc)
|
||||||
GetFunc() ExprFunc
|
GetFunc() ExprFunc
|
||||||
GetParams() []ExprFuncParam
|
GetParams() []ExprFuncParam
|
||||||
|
GetDefinitionContext() ExprContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Function Param Info
|
// ---- Function Param Info
|
||||||
@ -31,12 +32,14 @@ type ExprFunc interface {
|
|||||||
Functor() Functor
|
Functor() Functor
|
||||||
Params() []ExprFuncParam
|
Params() []ExprFuncParam
|
||||||
ReturnType() string
|
ReturnType() string
|
||||||
|
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
|
||||||
|
AllocContext(parentCtx ExprContext) (ctx ExprContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----Expression Context
|
// ----Expression Context
|
||||||
type ExprContext interface {
|
type ExprContext interface {
|
||||||
Clone() ExprContext
|
Clone() ExprContext
|
||||||
Merge(ctx ExprContext)
|
// Merge(ctx ExprContext)
|
||||||
SetParent(ctx ExprContext)
|
SetParent(ctx ExprContext)
|
||||||
GetParent() (ctx ExprContext)
|
GetParent() (ctx ExprContext)
|
||||||
GetVar(varName string) (value any, exists bool)
|
GetVar(varName string) (value any, exists bool)
|
||||||
@ -47,7 +50,6 @@ type ExprContext interface {
|
|||||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||||
Call(name string, args []any) (result any, err error)
|
Call(name string, args []any) (result any, err error)
|
||||||
// RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
|
||||||
RegisterFuncInfo(info ExprFunc)
|
RegisterFuncInfo(info ExprFunc)
|
||||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
|
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
|
||||||
}
|
}
|
||||||
|
13
control.go
13
control.go
@ -11,6 +11,7 @@ const (
|
|||||||
ControlBoolShortcut = "_bool_shortcut"
|
ControlBoolShortcut = "_bool_shortcut"
|
||||||
ControlSearchPath = "_search_path"
|
ControlSearchPath = "_search_path"
|
||||||
ControlParentContext = "_parent_context"
|
ControlParentContext = "_parent_context"
|
||||||
|
ControlStdout = "_stdout"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Other control variables
|
// Other control variables
|
||||||
@ -23,11 +24,17 @@ const (
|
|||||||
init_search_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 SetCtrl(ctx ExprContext, name string, value any) (current any) {
|
||||||
|
current, _ = ctx.GetVar(name)
|
||||||
|
ctx.UnsafeSetVar(name, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func initDefaultVars(ctx ExprContext) {
|
func initDefaultVars(ctx ExprContext) {
|
||||||
if _, exists := ctx.GetVar(ControlPreset); exists {
|
if _, exists := ctx.GetVar(ControlPreset); exists {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.SetVar(ControlPreset, true)
|
ctx.UnsafeSetVar(ControlPreset, true)
|
||||||
ctx.SetVar(ControlBoolShortcut, true)
|
ctx.UnsafeSetVar(ControlBoolShortcut, true)
|
||||||
ctx.SetVar(ControlSearchPath, init_search_path)
|
ctx.UnsafeSetVar(ControlSearchPath, init_search_path)
|
||||||
}
|
}
|
||||||
|
77
function.go
77
function.go
@ -42,6 +42,10 @@ func (functor *baseFunctor) GetFunc() ExprFunc {
|
|||||||
return functor.info
|
return functor.info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (functor *baseFunctor) GetDefinitionContext() ExprContext {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Function Parameters
|
// ---- Function Parameters
|
||||||
type paramFlags uint16
|
type paramFlags uint16
|
||||||
|
|
||||||
@ -94,12 +98,14 @@ func (param *funcParamInfo) DefaultValue() any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Functions
|
// --- Functions
|
||||||
|
|
||||||
|
// funcInfo implements ExprFunc
|
||||||
type funcInfo struct {
|
type funcInfo struct {
|
||||||
name string
|
name string
|
||||||
minArgs int
|
minArgs int
|
||||||
maxArgs int
|
maxArgs int
|
||||||
functor Functor
|
functor Functor
|
||||||
params []ExprFuncParam
|
formalParams []ExprFuncParam
|
||||||
returnType string
|
returnType string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,18 +132,18 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
info = &funcInfo{
|
info = &funcInfo{
|
||||||
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
|
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
|
||||||
}
|
}
|
||||||
functor.SetFunc(info)
|
functor.SetFunc(info)
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
// func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
||||||
return newFuncInfo("unnamed", functor, returnType, params)
|
// return newFuncInfo("unnamed", functor, returnType, params)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (info *funcInfo) Params() []ExprFuncParam {
|
func (info *funcInfo) Params() []ExprFuncParam {
|
||||||
return info.params
|
return info.formalParams
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *funcInfo) ReturnType() string {
|
func (info *funcInfo) ReturnType() string {
|
||||||
@ -152,8 +158,8 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
|
|||||||
sb.WriteString(info.Name())
|
sb.WriteString(info.Name())
|
||||||
}
|
}
|
||||||
sb.WriteByte('(')
|
sb.WriteByte('(')
|
||||||
if info.params != nil {
|
if info.formalParams != nil {
|
||||||
for i, p := range info.params {
|
for i, p := range info.formalParams {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
@ -200,41 +206,52 @@ func (info *funcInfo) Functor() Functor {
|
|||||||
return info.functor
|
return info.functor
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Call a function ---
|
func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
|
||||||
|
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
|
||||||
|
ctx = defCtx.Clone()
|
||||||
|
ctx.SetParent(defCtx)
|
||||||
|
} else {
|
||||||
|
ctx = parentCtx.Clone()
|
||||||
|
ctx.SetParent(parentCtx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
|
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
|
||||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
passedCount := len(*varActualParams)
|
||||||
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() {
|
|
||||||
if i >= passedCount {
|
for i := passedCount; i < len(info.formalParams); i++ {
|
||||||
|
p := info.formalParams[i]
|
||||||
if !p.IsDefault() {
|
if !p.IsDefault() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
*varParams = append(*varParams, p.DefaultValue())
|
*varActualParams = append(*varActualParams, p.DefaultValue())
|
||||||
}
|
}
|
||||||
|
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
|
||||||
|
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
|
||||||
}
|
}
|
||||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
|
|
||||||
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
|
if err == nil {
|
||||||
|
ctx = info.AllocContext(parentCtx)
|
||||||
}
|
}
|
||||||
if err == nil && owner != ctx {
|
return
|
||||||
ctx.RegisterFuncInfo(info)
|
}
|
||||||
|
|
||||||
|
// ----- Call a function ---
|
||||||
|
|
||||||
|
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
|
||||||
|
if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
|
||||||
|
var ctx ExprContext
|
||||||
|
if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
|
||||||
|
functor := info.Functor()
|
||||||
|
result, err = functor.Invoke(ctx, name, actualParams)
|
||||||
|
exportObjectsToParent(ctx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallFunction(parentCtx ExprContext, name string, params []any) (result any, err error) {
|
|
||||||
ctx := cloneContext(parentCtx)
|
|
||||||
ctx.SetParent(parentCtx)
|
|
||||||
|
|
||||||
if err = checkFunctionCall(ctx, name, ¶ms); err == nil {
|
|
||||||
result, err = ctx.Call(name, params)
|
|
||||||
exportObjectsToParent(ctx)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -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 := ToGoInt(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 := ToGoInt(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 := ToGoInt(args[2], "step"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = -i
|
i = -i
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,6 @@ func evalNot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
func newAndTerm(tk *Token) (inst *term) {
|
func newAndTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priAnd,
|
priority: priAnd,
|
||||||
@ -88,7 +86,7 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue))
|
||||||
return
|
return
|
||||||
} else if !leftBool {
|
} else if !leftBool {
|
||||||
v = false
|
v = false
|
||||||
@ -107,8 +105,6 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
func newOrTerm(tk *Token) (inst *term) {
|
func newOrTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priOr,
|
priority: priOr,
|
||||||
@ -157,7 +153,7 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue))
|
||||||
return
|
return
|
||||||
} else if leftBool {
|
} else if leftBool {
|
||||||
v = true
|
v = true
|
||||||
|
@ -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 = ToGoInt((*indexList)[0], "index expression"); err == nil {
|
||||||
if v < 0 && v >= -maxValue {
|
if v < 0 && v >= -maxValue {
|
||||||
v = maxValue + v
|
v = maxValue + v
|
||||||
}
|
}
|
||||||
|
@ -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, _ = ToGoInt(count, "")
|
||||||
} else {
|
} else {
|
||||||
v = int64(it.Index() + 1)
|
v = int64(it.Index() + 1)
|
||||||
}
|
}
|
||||||
|
@ -36,88 +36,21 @@ func (ctx *SimpleStore) GetParent() ExprContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) Clone() ExprContext {
|
func (ctx *SimpleStore) Clone() ExprContext {
|
||||||
return &SimpleStore{
|
clone := &SimpleStore{
|
||||||
varStore: CloneFilteredMap(ctx.varStore, filterRefName),
|
varStore: CloneFilteredMap(ctx.varStore, filterRefName),
|
||||||
funcStore: CloneFilteredMap(ctx.funcStore, filterRefName),
|
funcStore: CloneFilteredMap(ctx.funcStore, filterRefName),
|
||||||
}
|
}
|
||||||
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) Merge(src ExprContext) {
|
// func (ctx *SimpleStore) Merge(src ExprContext) {
|
||||||
for _, name := range src.EnumVars(filterRefName) {
|
// for _, name := range src.EnumVars(filterRefName) {
|
||||||
ctx.varStore[name], _ = src.GetVar(name)
|
// ctx.varStore[name], _ = src.GetVar(name)
|
||||||
}
|
// }
|
||||||
for _, name := range src.EnumFuncs(filterRefName) {
|
// for _, name := range src.EnumFuncs(filterRefName) {
|
||||||
ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
// ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/*
|
|
||||||
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
|
||||||
sb.WriteString("vars: {\n")
|
|
||||||
first := true
|
|
||||||
for _, name := range ctx.EnumVars(nil) {
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
value, _ := ctx.GetVar(name)
|
|
||||||
sb.WriteString(strings.Repeat("\t", indent+1))
|
|
||||||
sb.WriteString(name)
|
|
||||||
sb.WriteString(": ")
|
|
||||||
if f, ok := value.(Formatter); ok {
|
|
||||||
sb.WriteString(f.ToString(0))
|
|
||||||
} else if _, ok = value.(Functor); ok {
|
|
||||||
sb.WriteString("func(){}")
|
|
||||||
// } else if _, ok = value.(map[any]any); ok {
|
|
||||||
// sb.WriteString("dict{}")
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteString(strings.Repeat("\t", indent))
|
|
||||||
sb.WriteString("\n}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func varsCtxToString(ctx ExprContext, indent int) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
varsCtxToBuilder(&sb, ctx, indent)
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
|
||||||
sb.WriteString("funcs: {\n")
|
|
||||||
first := true
|
|
||||||
names := ctx.EnumFuncs(func(name string) bool { return true })
|
|
||||||
slices.Sort(names)
|
|
||||||
for _, name := range names {
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
value, _ := ctx.GetFuncInfo(name)
|
|
||||||
sb.WriteString(strings.Repeat("\t", indent+1))
|
|
||||||
if formatter, ok := value.(Formatter); ok {
|
|
||||||
sb.WriteString(formatter.ToString(0))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteString("\n}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
varsCtxToBuilder(&sb, ctx, 0)
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
funcsCtxToBuilder(&sb, ctx, 0)
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
||||||
dict := ctx.ToDict()
|
dict := ctx.ToDict()
|
||||||
|
66
t_bool_test.go
Normal file
66
t_bool_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_bool_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBool(t *testing.T) {
|
||||||
|
section := "Bool"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`true`, true, nil},
|
||||||
|
/* 2 */ {`false`, false, nil},
|
||||||
|
/* 3 */ {`not false`, true, nil},
|
||||||
|
/* 4 */ {`not 1`, false, nil},
|
||||||
|
/* 5 */ {`not "true"`, false, nil},
|
||||||
|
/* 6 */ {`not "false"`, false, nil},
|
||||||
|
/* 7 */ {`not ""`, true, nil},
|
||||||
|
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`)},
|
||||||
|
/* 9 */ {`true and false`, false, nil},
|
||||||
|
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)},
|
||||||
|
/* 11 */ {`[] and false`, nil, errors.New(`got list as left operand type of 'AND' operator, it must be bool`)},
|
||||||
|
/* 12 */ {`true or false`, true, nil},
|
||||||
|
/* 13 */ {`true or []`, true, nil},
|
||||||
|
/* 14 */ {`[] or false`, nil, errors.New(`got list as left operand type of 'OR' operator, it must be bool`)},
|
||||||
|
/* 13 */ //{`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoolNoShortcut(t *testing.T) {
|
||||||
|
section := "Bool-NoShortcut"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`true`, true, nil},
|
||||||
|
/* 2 */ {`false`, false, nil},
|
||||||
|
/* 3 */ {`not false`, true, nil},
|
||||||
|
/* 4 */ {`not 1`, false, nil},
|
||||||
|
/* 5 */ {`not "true"`, false, nil},
|
||||||
|
/* 6 */ {`not "false"`, false, nil},
|
||||||
|
/* 7 */ {`not ""`, true, nil},
|
||||||
|
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`)},
|
||||||
|
/* 9 */ {`true and false`, false, nil},
|
||||||
|
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)},
|
||||||
|
/* 11 */ {`[] and false`, nil, errors.New(`[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`)},
|
||||||
|
/* 12 */ {`true or false`, true, nil},
|
||||||
|
/* 13 */ {`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
|
||||||
|
/* 14 */ {`[] or false`, nil, errors.New(`[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
ctx := NewSimpleStore()
|
||||||
|
current := SetCtrl(ctx, ControlBoolShortcut, false)
|
||||||
|
|
||||||
|
// runCtxTestSuiteSpec(t, ctx, section, inputs, 1)
|
||||||
|
runCtxTestSuite(t, ctx, section, inputs)
|
||||||
|
|
||||||
|
SetCtrl(ctx, ControlBoolShortcut, current)
|
||||||
|
}
|
@ -63,5 +63,5 @@ func TestFuncBase(t *testing.T) {
|
|||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 2)
|
// parserTestSpec(t, section, inputs, 2)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
48
t_builtin-fmt_test.go
Normal file
48
t_builtin-fmt_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-fmt.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "errors"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncFmt(t *testing.T) {
|
||||||
|
section := "Builtin-Fmt"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`builtin "fmt"; print("ciao")`, int64(4), nil},
|
||||||
|
/* 2 */ {`builtin "fmt"; println(" ciao")`, int64(6), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 19)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFmt(t *testing.T) {
|
||||||
|
section := "Builtin-Fmt"
|
||||||
|
|
||||||
|
text := "ciao mondo"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
ctx := NewSimpleStore()
|
||||||
|
currentStdout := SetCtrl(ctx, ControlStdout, &b)
|
||||||
|
|
||||||
|
runCtxTestSuite(t, ctx, section, inputs)
|
||||||
|
|
||||||
|
SetCtrl(ctx, ControlStdout, currentStdout)
|
||||||
|
if b.String() != text+"\n" {
|
||||||
|
t.Errorf("println(): Got: %q, Want: %q", b.String(), text+"\n")
|
||||||
|
}
|
||||||
|
}
|
@ -20,5 +20,5 @@ func TestFuncImport(t *testing.T) {
|
|||||||
t.Setenv("EXPR_PATH", "test-resources")
|
t.Setenv("EXPR_PATH", "test-resources")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// parserTestSpec(t, section, inputs, 69)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,5 @@ func TestFuncMathArith(t *testing.T) {
|
|||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// parserTestSpec(t, section, inputs, 69)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,18 @@ func TestFuncOs(t *testing.T) {
|
|||||||
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||||
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
|
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||||
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
|
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
|
||||||
/* 7 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
|
/* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
|
||||||
|
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, errors.New(`fileOpen(): too few params -- expected 1, got 0`)},
|
||||||
|
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, errors.New(`fileOpen(): missing or invalid file path`)},
|
||||||
|
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, errors.New(`fileCreate(): missing or invalid file path`)},
|
||||||
|
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, errors.New(`fileAppend(): missing or invalid file path`)},
|
||||||
|
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, errors.New(`fileClose(): invalid file handle`)},
|
||||||
|
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
||||||
|
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, errors.New(`fileReadTextAll(): invalid file handle 123 [int64]`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// parserTestSpec(t, section, inputs, 69)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,6 @@ func TestFuncString(t *testing.T) {
|
|||||||
|
|
||||||
//t.Setenv("EXPR_PATH", ".")
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 19)
|
// runTestSuiteSpec(t, section, inputs, 19)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
113
t_common_test.go
Normal file
113
t_common_test.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_common_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inputType struct {
|
||||||
|
source string
|
||||||
|
wantResult any
|
||||||
|
wantErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
|
||||||
|
succeeded := 0
|
||||||
|
failed := 0
|
||||||
|
for _, count := range spec {
|
||||||
|
good := doTest(t, ctx, section, &inputs[count-1], count)
|
||||||
|
|
||||||
|
if good {
|
||||||
|
succeeded++
|
||||||
|
} else {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTestSuiteSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
|
||||||
|
runCtxTestSuiteSpec(t, nil, section, inputs, spec...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inputType) {
|
||||||
|
|
||||||
|
succeeded := 0
|
||||||
|
failed := 0
|
||||||
|
|
||||||
|
for i, input := range inputs {
|
||||||
|
good := doTest(t, ctx, section, &input, i+1)
|
||||||
|
if good {
|
||||||
|
succeeded++
|
||||||
|
} else {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||||
|
}
|
||||||
|
func runTestSuite(t *testing.T, section string, inputs []inputType) {
|
||||||
|
runCtxTestSuite(t, nil, section, inputs)
|
||||||
|
/*
|
||||||
|
succeeded := 0
|
||||||
|
failed := 0
|
||||||
|
|
||||||
|
for i, input := range inputs {
|
||||||
|
good := doTest(t, nil, section, &input, i+1)
|
||||||
|
if good {
|
||||||
|
succeeded++
|
||||||
|
} else {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
|
||||||
|
var expr Expr
|
||||||
|
var gotResult any
|
||||||
|
var gotErr error
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = NewSimpleStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
logTest(t, count, section, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := reflect.DeepEqual(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))
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotErr != input.wantErr {
|
||||||
|
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
||||||
|
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr)
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
||||||
|
if wantErr == nil {
|
||||||
|
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
||||||
|
} else {
|
||||||
|
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
|
||||||
|
}
|
||||||
|
}
|
@ -33,5 +33,5 @@ func TestExpr(t *testing.T) {
|
|||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 3)
|
// parserTestSpec(t, section, inputs, 3)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func TestFractionsParser(t *testing.T) {
|
|||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)},
|
||||||
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)},
|
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)},
|
||||||
}
|
}
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFractionToStringSimple(t *testing.T) {
|
func TestFractionToStringSimple(t *testing.T) {
|
||||||
|
@ -29,13 +29,15 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
|
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
|
||||||
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, errors.New(`[1:16] can't mix default and non-default parameters`)},
|
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, errors.New(`[1:16] can't mix default and non-default parameters`)},
|
||||||
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
|
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
|
||||||
|
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
|
||||||
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 17)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
|
func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
@ -23,5 +23,5 @@ func TestCollections(t *testing.T) {
|
|||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 5)
|
// parserTestSpec(t, section, inputs, 5)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -22,5 +22,5 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// /* 1 */ {`0?{}`, nil, nil},
|
// /* 1 */ {`0?{}`, nil, nil},
|
||||||
// }
|
// }
|
||||||
parserTest(t, "Iterator", inputs)
|
runTestSuite(t, "Iterator", inputs)
|
||||||
}
|
}
|
||||||
|
@ -58,5 +58,5 @@ func TestListParser(t *testing.T) {
|
|||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 17)
|
// parserTestSpec(t, section, inputs, 17)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,9 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type inputType struct {
|
|
||||||
source string
|
|
||||||
wantResult any
|
|
||||||
wantErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGeneralParser(t *testing.T) {
|
func TestGeneralParser(t *testing.T) {
|
||||||
section := "Parser"
|
section := "Parser"
|
||||||
|
|
||||||
@ -148,78 +140,5 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
// parserTestSpec(t, section, inputs, 102)
|
// parserTestSpec(t, section, inputs, 102)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
|
||||||
|
|
||||||
func parserTestSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
|
|
||||||
succeeded := 0
|
|
||||||
failed := 0
|
|
||||||
for _, count := range spec {
|
|
||||||
good := doTest(t, section, &inputs[count-1], count)
|
|
||||||
|
|
||||||
if good {
|
|
||||||
succeeded++
|
|
||||||
} else {
|
|
||||||
failed++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parserTest(t *testing.T, section string, inputs []inputType) {
|
|
||||||
|
|
||||||
succeeded := 0
|
|
||||||
failed := 0
|
|
||||||
|
|
||||||
for i, input := range inputs {
|
|
||||||
good := doTest(t, section, &input, i+1)
|
|
||||||
if good {
|
|
||||||
succeeded++
|
|
||||||
} else {
|
|
||||||
failed++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doTest(t *testing.T, section string, input *inputType, count int) (good bool) {
|
|
||||||
var expr Expr
|
|
||||||
var gotResult any
|
|
||||||
var gotErr error
|
|
||||||
|
|
||||||
ctx := NewSimpleStore()
|
|
||||||
parser := NewParser()
|
|
||||||
|
|
||||||
logTest(t, count, section, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
eq := reflect.DeepEqual(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))
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotErr != input.wantErr {
|
|
||||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
|
||||||
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
|
||||||
if wantErr == nil {
|
|
||||||
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
|
||||||
} else {
|
|
||||||
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
31
t_plugin_test.go
Normal file
31
t_plugin_test.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_plugin_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func _TestImportPlugin(t *testing.T) {
|
||||||
|
if err := importPlugin([]string{"test-resources"}, "json"); err != nil {
|
||||||
|
t.Errorf("importPlugin() failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPluginExists(t *testing.T) {
|
||||||
|
name := "json"
|
||||||
|
exists := pluginExists(name)
|
||||||
|
t.Logf("pluginExists(%v): %v", name, exists)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakePluginName(t *testing.T) {
|
||||||
|
name := "json"
|
||||||
|
want := "expr-" + name + "-plugin.so"
|
||||||
|
|
||||||
|
if got := makePluginName(name); got != want {
|
||||||
|
t.Errorf("makePluginName(%q) failed: Got: %q, Want: %q", name, got, want)
|
||||||
|
}
|
||||||
|
}
|
@ -48,5 +48,5 @@ func TestRelational(t *testing.T) {
|
|||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 31)
|
// parserTestSpec(t, section, inputs, 31)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,5 @@ func TestStringsParser(t *testing.T) {
|
|||||||
/* 5 */ {`"abc"[1]`, `b`, nil},
|
/* 5 */ {`"abc"[1]`, `b`, nil},
|
||||||
/* 6 */ {`#"abc"`, int64(3), nil},
|
/* 6 */ {`#"abc"`, int64(3), nil},
|
||||||
}
|
}
|
||||||
parserTest(t, "String", inputs)
|
runTestSuite(t, "String", inputs)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,6 @@ func TestSomething(t *testing.T) {
|
|||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 1)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,7 +98,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 := ToGoInt(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 +111,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 := ToGoInt(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",
|
||||||
@ -154,3 +155,12 @@ func TestAnyInteger(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopyMap(t *testing.T) {
|
||||||
|
source := map[string]int{"one": 1, "two": 2, "three": 3}
|
||||||
|
dest := make(map[string]int)
|
||||||
|
result := CopyMap(dest, source)
|
||||||
|
if !reflect.DeepEqual(result, source) {
|
||||||
|
t.Errorf("utils.CopyMap() failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIN
test-resources/expr-json-plugin.so
Normal file
BIN
test-resources/expr-json-plugin.so
Normal file
Binary file not shown.
BIN
test-resources/expr-json-plugin.so.debug
Normal file
BIN
test-resources/expr-json-plugin.so.debug
Normal file
Binary file not shown.
12
utils.go
12
utils.go
@ -135,6 +135,7 @@ func anyInteger(v any) (i int64, ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fromGenericAny(v any) (exprAny any, ok bool) {
|
func fromGenericAny(v any) (exprAny any, ok bool) {
|
||||||
|
if v != nil {
|
||||||
if exprAny, ok = v.(bool); ok {
|
if exprAny, ok = v.(bool); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -153,6 +154,7 @@ func fromGenericAny(v any) (exprAny any, ok bool) {
|
|||||||
if exprAny, ok = v.(*ListType); ok {
|
if exprAny, ok = v.(*ListType); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,10 +178,10 @@ func CopyMap[K comparable, V any](dest, source map[K]V) map[K]V {
|
|||||||
return dest
|
return dest
|
||||||
}
|
}
|
||||||
|
|
||||||
func CloneMap[K comparable, V any](source map[K]V) map[K]V {
|
// func CloneMap[K comparable, V any](source map[K]V) map[K]V {
|
||||||
dest := make(map[K]V, len(source))
|
// dest := make(map[K]V, len(source))
|
||||||
return CopyMap(dest, source)
|
// return CopyMap(dest, source)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func CopyFilteredMap[K comparable, V any](dest, source map[K]V, filter func(key K) (accept bool)) map[K]V {
|
func CopyFilteredMap[K comparable, V any](dest, source map[K]V, filter func(key K) (accept bool)) map[K]V {
|
||||||
// fmt.Printf("--- Clone with filter %p\n", filter)
|
// fmt.Printf("--- Clone with filter %p\n", filter)
|
||||||
@ -201,7 +203,7 @@ 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 ToGoInt(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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user