Compare commits
	
		
			No commits in common. "fe5c8e9619109ae5f4deee1f52fa3aed310f90d0" and "3b641ac7931655bf86a882b72ea2f64035695332" have entirely different histories.
		
	
	
		
			fe5c8e9619
			...
			3b641ac793
		
	
		
@ -17,31 +17,19 @@ type exprFunctor struct {
 | 
				
			|||||||
// }
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
 | 
					func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
 | 
				
			||||||
	var defCtx ExprContext
 | 
						return &exprFunctor{expr: e, params: params, defCtx: ctx}
 | 
				
			||||||
	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 {
 | 
				
			||||||
@ -54,3 +42,17 @@ 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,7 +5,6 @@
 | 
				
			|||||||
package expr
 | 
					package expr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -121,32 +120,6 @@ 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:
 | 
				
			||||||
@ -202,7 +175,6 @@ 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,26 +4,11 @@
 | 
				
			|||||||
// builtin-fmt.go
 | 
					// builtin-fmt.go
 | 
				
			||||||
package expr
 | 
					package expr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import "fmt"
 | 
				
			||||||
	"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.Fprint(getStdout(ctx), args...); err == nil {
 | 
						if n, err = fmt.Print(args...); err == nil {
 | 
				
			||||||
		result = int64(n)
 | 
							result = int64(n)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
@ -31,7 +16,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.Fprintln(getStdout(ctx), args...); err == nil {
 | 
						if n, err = fmt.Println(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 = ToGoInt(args[1], name+"()"); err != nil {
 | 
						if start, err = ToInt(args[1], name+"()"); err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if count, err = ToGoInt(args[2], name+"()"); err != nil {
 | 
						if count, err = ToInt(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,7 +10,6 @@ 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
 | 
				
			||||||
@ -32,14 +31,12 @@ 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)
 | 
				
			||||||
@ -50,6 +47,7 @@ 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,7 +11,6 @@ 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
 | 
				
			||||||
@ -24,17 +23,11 @@ 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.UnsafeSetVar(ControlPreset, true)
 | 
						ctx.SetVar(ControlPreset, true)
 | 
				
			||||||
	ctx.UnsafeSetVar(ControlBoolShortcut, true)
 | 
						ctx.SetVar(ControlBoolShortcut, true)
 | 
				
			||||||
	ctx.UnsafeSetVar(ControlSearchPath, init_search_path)
 | 
						ctx.SetVar(ControlSearchPath, init_search_path)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										107
									
								
								function.go
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								function.go
									
									
									
									
									
								
							@ -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(){}"
 | 
							s = "func() {}"
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s
 | 
						return s
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -42,10 +42,6 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,15 +94,13 @@ 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
 | 
				
			||||||
	formalParams []ExprFuncParam
 | 
						params     []ExprFuncParam
 | 
				
			||||||
	returnType   string
 | 
						returnType string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
 | 
					func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
 | 
				
			||||||
@ -132,18 +126,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, formalParams: params,
 | 
							name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: 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.formalParams
 | 
						return info.params
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (info *funcInfo) ReturnType() string {
 | 
					func (info *funcInfo) ReturnType() string {
 | 
				
			||||||
@ -158,8 +152,8 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
 | 
				
			|||||||
		sb.WriteString(info.Name())
 | 
							sb.WriteString(info.Name())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sb.WriteByte('(')
 | 
						sb.WriteByte('(')
 | 
				
			||||||
	if info.formalParams != nil {
 | 
						if info.params != nil {
 | 
				
			||||||
		for i, p := range info.formalParams {
 | 
							for i, p := range info.params {
 | 
				
			||||||
			if i > 0 {
 | 
								if i > 0 {
 | 
				
			||||||
				sb.WriteString(", ")
 | 
									sb.WriteString(", ")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -186,7 +180,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		sb.WriteString(TypeAny)
 | 
							sb.WriteString(TypeAny)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sb.WriteString("{}")
 | 
						sb.WriteString(" {}")
 | 
				
			||||||
	return sb.String()
 | 
						return sb.String()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -206,52 +200,41 @@ func (info *funcInfo) Functor() Functor {
 | 
				
			|||||||
	return info.functor
 | 
						return info.functor
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
 | 
					 | 
				
			||||||
	passedCount := len(*varActualParams)
 | 
					 | 
				
			||||||
	if info.MinArgs() > passedCount {
 | 
					 | 
				
			||||||
		err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i := passedCount; i < len(info.formalParams); i++ {
 | 
					 | 
				
			||||||
		p := info.formalParams[i]
 | 
					 | 
				
			||||||
		if !p.IsDefault() {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		*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 {
 | 
					 | 
				
			||||||
		ctx = info.AllocContext(parentCtx)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ----- Call a function ---
 | 
					// ----- Call a function ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
 | 
					func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
 | 
				
			||||||
	if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
 | 
						if info, exists, owner := GetFuncInfo(ctx, name); exists {
 | 
				
			||||||
		var ctx ExprContext
 | 
							passedCount := len(*varParams)
 | 
				
			||||||
		if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
 | 
							if info.MinArgs() > passedCount {
 | 
				
			||||||
			functor := info.Functor()
 | 
								err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
 | 
				
			||||||
			result, err = functor.Invoke(ctx, name, actualParams)
 | 
							}
 | 
				
			||||||
			exportObjectsToParent(ctx)
 | 
							for i, p := range info.Params() {
 | 
				
			||||||
 | 
								if i >= passedCount {
 | 
				
			||||||
 | 
									if !p.IsDefault() {
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									*varParams = append(*varParams, p.DefaultValue())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
 | 
				
			||||||
 | 
								err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err == nil && owner != ctx {
 | 
				
			||||||
 | 
								ctx.RegisterFuncInfo(info)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} 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 := ToGoInt(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 := ToGoInt(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 := ToGoInt(args[2], "step"); err == nil {
 | 
									if i, err := ToInt(args[2], "step"); err == nil {
 | 
				
			||||||
					if i < 0 {
 | 
										if i < 0 {
 | 
				
			||||||
						i = -i
 | 
											i = -i
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
				
			|||||||
@ -37,7 +37,9 @@ 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,
 | 
				
			||||||
@ -86,7 +88,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 %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue))
 | 
							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
 | 
				
			||||||
@ -104,7 +106,9 @@ 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,
 | 
				
			||||||
@ -153,7 +157,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 %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue))
 | 
							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
 | 
				
			||||||
 | 
				
			|||||||
@ -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 = ToGoInt((*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
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -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, _ = ToGoInt(count, "")
 | 
								v, _ = ToInt(count, "")
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			v = int64(it.Index() + 1)
 | 
								v = int64(it.Index() + 1)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -36,21 +36,88 @@ func (ctx *SimpleStore) GetParent() ExprContext {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ctx *SimpleStore) Clone() ExprContext {
 | 
					func (ctx *SimpleStore) Clone() ExprContext {
 | 
				
			||||||
	clone := &SimpleStore{
 | 
						return &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()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,66 +0,0 @@
 | 
				
			|||||||
// 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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,48 +0,0 @@
 | 
				
			|||||||
// 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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,18 +19,11 @@ 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`)},
 | 
				
			||||||
		/*   8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
 | 
							/*   7 */ {`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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -64,6 +64,6 @@ func TestFuncString(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	//t.Setenv("EXPR_PATH", ".")
 | 
						//t.Setenv("EXPR_PATH", ".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// runTestSuiteSpec(t, section, inputs, 19)
 | 
						// parserTestSpec(t, section, inputs, 19)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										113
									
								
								t_common_test.go
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								t_common_test.go
									
									
									
									
									
								
							@ -1,113 +0,0 @@
 | 
				
			|||||||
// 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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(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`)},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestFractionToStringSimple(t *testing.T) {
 | 
					func TestFractionToStringSimple(t *testing.T) {
 | 
				
			||||||
 | 
				
			|||||||
@ -29,15 +29,13 @@ 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", ".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// runTestSuiteSpec(t, section, inputs, 17)
 | 
						// parserTestSpec(t, section, inputs, 17)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(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) {
 | 
				
			||||||
@ -46,7 +44,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestFunctionToStringSimple(t *testing.T) {
 | 
					func TestFunctionToStringSimple(t *testing.T) {
 | 
				
			||||||
	source := NewGolangFunctor(dummy)
 | 
						source := NewGolangFunctor(dummy)
 | 
				
			||||||
	want := "func(){}"
 | 
						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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -22,5 +22,5 @@ func TestIteratorParser(t *testing.T) {
 | 
				
			|||||||
	// inputs1 := []inputType{
 | 
						// inputs1 := []inputType{
 | 
				
			||||||
	// 	/*   1 */ {`0?{}`, nil, nil},
 | 
						// 	/*   1 */ {`0?{}`, nil, nil},
 | 
				
			||||||
	// }
 | 
						// }
 | 
				
			||||||
	runTestSuite(t, "Iterator", inputs)
 | 
						parserTest(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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,9 +6,17 @@ 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"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -140,5 +148,78 @@ func TestGeneralParser(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// t.Setenv("EXPR_PATH", ".")
 | 
						// t.Setenv("EXPR_PATH", ".")
 | 
				
			||||||
	// parserTestSpec(t, section, inputs, 102)
 | 
						// parserTestSpec(t, section, inputs, 102)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,31 +0,0 @@
 | 
				
			|||||||
// 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)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(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},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	runTestSuite(t, "String", inputs)
 | 
						parserTest(t, "String", inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,6 @@ func TestSomething(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// t.Setenv("EXPR_PATH", ".")
 | 
						// t.Setenv("EXPR_PATH", ".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// runTestSuiteSpec(t, section, inputs, 1)
 | 
						// parserTestSpec(t, section, inputs, 1)
 | 
				
			||||||
	runTestSuite(t, section, inputs)
 | 
						parserTest(t, section, inputs)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,6 @@ package expr
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -98,7 +97,7 @@ func TestToIntOk(t *testing.T) {
 | 
				
			|||||||
	wantValue := int(64)
 | 
						wantValue := int(64)
 | 
				
			||||||
	wantErr := error(nil)
 | 
						wantErr := error(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gotValue, gotErr := ToGoInt(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",
 | 
				
			||||||
@ -111,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 := ToGoInt(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",
 | 
				
			||||||
@ -155,12 +154,3 @@ 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")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										46
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								utils.go
									
									
									
									
									
								
							@ -135,25 +135,23 @@ 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
 | 
						}
 | 
				
			||||||
		}
 | 
						if exprAny, ok = v.(string); ok {
 | 
				
			||||||
		if exprAny, ok = v.(string); ok {
 | 
							return
 | 
				
			||||||
			return
 | 
						}
 | 
				
			||||||
		}
 | 
						if exprAny, ok = anyInteger(v); ok {
 | 
				
			||||||
		if exprAny, ok = anyInteger(v); ok {
 | 
							return
 | 
				
			||||||
			return
 | 
						}
 | 
				
			||||||
		}
 | 
						if exprAny, ok = anyFloat(v); ok {
 | 
				
			||||||
		if exprAny, ok = anyFloat(v); ok {
 | 
							return
 | 
				
			||||||
			return
 | 
						}
 | 
				
			||||||
		}
 | 
						if exprAny, ok = v.(*DictType); ok {
 | 
				
			||||||
		if exprAny, ok = v.(*DictType); ok {
 | 
							return
 | 
				
			||||||
			return
 | 
						}
 | 
				
			||||||
		}
 | 
						if exprAny, ok = v.(*ListType); ok {
 | 
				
			||||||
		if exprAny, ok = v.(*ListType); ok {
 | 
							return
 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -178,10 +176,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)
 | 
				
			||||||
@ -203,7 +201,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 ToGoInt(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 {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user