Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93dac956fb | |||
| 307027d23d | |||
| 896844e772 | |||
| 9fc20077a1 | |||
| d2a4adebdd | |||
| fd8e32e12b | |||
| 1e62a51c15 | |||
| 0170baa0f5 | |||
| fe5c8e9619 | |||
| 7164e8816c | |||
| e0f3b028fc | |||
| 522b5983bd | |||
| f1e2163277 | |||
| bbdf498cf3 | |||
| f75c991ed2 | |||
| a7836143cc | |||
| ba9b9cb28f | |||
| ef1baa11a8 | |||
| cfddbd60b9 | |||
| 0760141caf | |||
| b9e780e659 | |||
| e41ddc664c | |||
| 3b641ac793 | |||
| a1a62b6794 | |||
| a18d92534f | |||
| ec0963e26f | |||
| be992131b1 | |||
| 0e3960321f | |||
| 61d34fef7d | |||
| 581f1585e6 |
+17
-19
@@ -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),
|
||||||
|
|||||||
+18
-3
@@ -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
|
||||||
|
|||||||
+2
-2
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -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)
|
||||||
@@ -53,3 +53,9 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
|
|||||||
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
||||||
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Operator errors
|
||||||
|
|
||||||
|
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||||
|
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
|
||||||
|
}
|
||||||
|
|||||||
+4
-2
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) TypeName() string {
|
||||||
|
return "DataCursor"
|
||||||
|
}
|
||||||
|
|
||||||
// func mapToString(m map[string]Functor) string {
|
// func mapToString(m map[string]Functor) string {
|
||||||
// var sb strings.Builder
|
// var sb strings.Builder
|
||||||
// sb.WriteByte('{')
|
// sb.WriteByte('{')
|
||||||
|
|||||||
+233
-120
@@ -9,6 +9,7 @@ Expressions calculator
|
|||||||
:icons: font
|
:icons: font
|
||||||
:icon-set: fi
|
:icon-set: fi
|
||||||
:numbered:
|
:numbered:
|
||||||
|
:data-uri:
|
||||||
//:table-caption: Tabella
|
//:table-caption: Tabella
|
||||||
//:figure-caption: Diagramma
|
//:figure-caption: Diagramma
|
||||||
:docinfo1:
|
:docinfo1:
|
||||||
@@ -19,10 +20,12 @@ Expressions calculator
|
|||||||
:rouge-style: gruvbox
|
:rouge-style: gruvbox
|
||||||
// :rouge-style: colorful
|
// :rouge-style: colorful
|
||||||
//:rouge-style: monokay
|
//:rouge-style: monokay
|
||||||
|
// Work around to manage double-column in back-tick quotes
|
||||||
|
:2c: ::
|
||||||
|
|
||||||
toc::[]
|
toc::[]
|
||||||
|
|
||||||
#TODO: Work in progress (last update on 2024/06/17, 16:31 a.m.)#
|
#TODO: Work in progress (last update on 2024/06/21, 05:40 a.m.)#
|
||||||
|
|
||||||
== Expr
|
== Expr
|
||||||
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
|
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
|
||||||
@@ -60,6 +63,8 @@ _Expr_ creates and keeps a inner _global context_ where it stores imported funct
|
|||||||
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
||||||
|
|
||||||
=== `dev-expr` test tool
|
=== `dev-expr` test tool
|
||||||
|
Before we begin to describe the syntax of _Expr_, it is worth introducing _dev-expr_ because it will be used to show many examples of expressions.
|
||||||
|
|
||||||
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provided an important aid for quickly testing of new features during their development.
|
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provided an important aid for quickly testing of new features during their development.
|
||||||
|
|
||||||
`dev-expr` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
`dev-expr` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
||||||
@@ -201,15 +206,20 @@ _dec-seq_ = _see-integer-literal-syntax_
|
|||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1.0` +
|
`>>>` [blue]`1.0` +
|
||||||
[green]`1` +
|
[green]`1`
|
||||||
|
|
||||||
`>>>` [blue]`0.123` +
|
`>>>` [blue]`0.123` +
|
||||||
[green]`0.123` +
|
[green]`0.123`
|
||||||
|
|
||||||
`>>>` [blue]`4.5e+3` +
|
`>>>` [blue]`4.5e+3` +
|
||||||
[green]`4500` +
|
[green]`4500`
|
||||||
|
|
||||||
`>>>` [blue]`4.5E-33` +
|
`>>>` [blue]`4.5E-33` +
|
||||||
[green]`4.5e-33` +
|
[green]`4.5e-33`
|
||||||
|
|
||||||
`>>>` [blue]`4.5E-3` +
|
`>>>` [blue]`4.5E-3` +
|
||||||
[green]`0.0045` +
|
[green]`0.0045`
|
||||||
|
|
||||||
`>>>` [blue]`4.5E10` +
|
`>>>` [blue]`4.5E10` +
|
||||||
[green]`4.5e+10`
|
[green]`4.5e+10`
|
||||||
|
|
||||||
@@ -239,35 +249,43 @@ _digit-seq_ = _see-integer-literal-syntax_
|
|||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
// [source,go]
|
|
||||||
// ----
|
|
||||||
`>>>` [blue]`1 | 2` +
|
`>>>` [blue]`1 | 2` +
|
||||||
[green]`1|2` +
|
[green]`1|2`
|
||||||
|
|
||||||
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||||
[green]`2|3` +
|
[green]`2|3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 + 2|3` +
|
`>>>` [blue]`1|2 + 2|3` +
|
||||||
[green]`7|6` +
|
[green]`7|6`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 * 2|3` +
|
`>>>` [blue]`1|2 * 2|3` +
|
||||||
[green]`1|3` +
|
[green]`1|3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 / 1|3` +
|
`>>>` [blue]`1|2 / 1|3` +
|
||||||
[green]`3|2` +
|
[green]`3|2`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
||||||
[green]`1.5` +
|
[green]`1.5`
|
||||||
|
|
||||||
`>>>` [blue]`-1|2` +
|
`>>>` [blue]`-1|2` +
|
||||||
[green]`-1|2` +
|
[green]`-1|2`
|
||||||
|
|
||||||
`>>>` [blue]`1|-2` [gray]_// Invalid sign specification_ +
|
`>>>` [blue]`1|-2` [gray]_// Invalid sign specification_ +
|
||||||
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_ +
|
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_
|
||||||
|
|
||||||
`>>>` [blue]`1|(-2)` +
|
`>>>` [blue]`1|(-2)` +
|
||||||
[green]`-1|2`
|
[green]`-1|2`
|
||||||
// ----
|
|
||||||
|
|
||||||
Fractions can be used together with integers and floats in expressions.
|
Fractions can be used together with integers and floats in expressions.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1|2 + 5` +
|
`>>>` [blue]`1|2 + 5` +
|
||||||
[green]`11|2` +
|
[green]`11|2`
|
||||||
|
|
||||||
`>>>` [blue]`4 - 1|2` +
|
`>>>` [blue]`4 - 1|2` +
|
||||||
[green]`7|2` +
|
[green]`7|2`
|
||||||
|
|
||||||
`>>>` [blue]`1.0 + 1|2` +
|
`>>>` [blue]`1.0 + 1|2` +
|
||||||
[green]`1.5`
|
[green]`1.5`
|
||||||
|
|
||||||
@@ -278,12 +296,15 @@ Strings are character sequences enclosed between two double quote [blue]`"`.
|
|||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`"I'm a string"` +
|
`>>>` [blue]`"I'm a string"` +
|
||||||
[green]`I'm a string` +
|
[green]`I'm a string`
|
||||||
|
|
||||||
`>>>` [blue]`"123abc?!"` +
|
`>>>` [blue]`"123abc?!"` +
|
||||||
[green]`123abc?!` +
|
[green]`123abc?!`
|
||||||
|
|
||||||
`>>>` [blue]`"123\nabc"` +
|
`>>>` [blue]`"123\nabc"` +
|
||||||
[green]`123` +
|
[green]`123` +
|
||||||
[green]`abc` +
|
[green]`abc`
|
||||||
|
|
||||||
`>>>` [blue]`"123\tabc"` +
|
`>>>` [blue]`"123\tabc"` +
|
||||||
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
||||||
|
|
||||||
@@ -304,25 +325,30 @@ The items of strings can be accessed using the square `[]` operator.
|
|||||||
|
|
||||||
.Item access syntax
|
.Item access syntax
|
||||||
====
|
====
|
||||||
_item_ = _string-expr_ "**[**" _integer-expr_ "**]**"
|
*_item_* = _string-expr_ "**[**" _integer-expr_ "**]**"
|
||||||
====
|
====
|
||||||
|
|
||||||
.Sub string syntax
|
.Sub-string syntax
|
||||||
====
|
====
|
||||||
_sub-string_ = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
*_sub-string_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
||||||
====
|
====
|
||||||
|
|
||||||
.String examples
|
.String examples
|
||||||
`>>>` [blue]`s="abcd"` [gray]_// assign the string to variable s_ +
|
`>>>` [blue]`s="abcd"` [gray]_// assign the string to variable s_ +
|
||||||
[green]`"abcd"` +
|
[green]`"abcd"`
|
||||||
|
|
||||||
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
|
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
|
||||||
[green]`"b"` +
|
[green]`"b"`
|
||||||
|
|
||||||
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ +
|
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ +
|
||||||
[green]`"d"` +
|
[green]`"d"`
|
||||||
|
|
||||||
`>>>` [blue]`\#s` [gray]_// number of chars_ +
|
`>>>` [blue]`\#s` [gray]_// number of chars_ +
|
||||||
[gren]`4` +
|
[gren]`4`
|
||||||
|
|
||||||
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
||||||
[green]`3` +
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`s[1:3]` [gray]_// chars from position 1 to position 3 excluded_ +
|
`>>>` [blue]`s[1:3]` [gray]_// chars from position 1 to position 3 excluded_ +
|
||||||
[grean]`"bc"`
|
[grean]`"bc"`
|
||||||
|
|
||||||
@@ -368,14 +394,16 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
|
|||||||
|
|
||||||
[CAUTION]
|
[CAUTION]
|
||||||
====
|
====
|
||||||
Currently, boolean operations are evaluated using _short cut evaluation_. This means that, if the left expression of the [blue]`and` and [blue]`or` operators is sufficient to establish the result of the whole operation, the right expression would not evaluated at all.
|
Currently, boolean operations are evaluated using _short cut evaluation_. This means that, if the left expression of the [blue]`and` and [blue]`or` operators is sufficient to establish the result of the whole operation, the right expression would not be evaluated at all.
|
||||||
|
|
||||||
.Example
|
.Example
|
||||||
[source,go]
|
[source,go]
|
||||||
----
|
----
|
||||||
2 > (a=1) or (a=8) > 0; a // <1>
|
2 > (a=1) or (a=8) > 0; a // <1>
|
||||||
----
|
----
|
||||||
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
||||||
|
|
||||||
|
|
||||||
|
TIP: `dev-expr` provides the _ctrl()_ function that allows to change this behaviour.
|
||||||
====
|
====
|
||||||
|
|
||||||
=== Lists
|
=== Lists
|
||||||
@@ -390,13 +418,17 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
|
|||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`[1,2,3]` [gray]_// List of integers_ +
|
`>>>` [blue]`[1,2,3]` [gray]_// List of integers_ +
|
||||||
[green]`[1, 2, 3]` +
|
[green]`[1, 2, 3]`
|
||||||
|
|
||||||
`>>>` [blue]`["one", "two", "three"]` [gray]_// List of strings_ +
|
`>>>` [blue]`["one", "two", "three"]` [gray]_// List of strings_ +
|
||||||
[green]`["one", "two", "three"]` +
|
[green]`["one", "two", "three"]`
|
||||||
|
|
||||||
`>>>` [blue]`["one", 2, false, 4.1]` [gray]_// List of mixed-types_ +
|
`>>>` [blue]`["one", 2, false, 4.1]` [gray]_// List of mixed-types_ +
|
||||||
[green]`["one", 2, false, 4.1]` +
|
[green]`["one", 2, false, 4.1]`
|
||||||
|
|
||||||
`>>>` [blue]`["one"+1, 2.0*(9-2)]` [gray]_// List of expressions_ +
|
`>>>` [blue]`["one"+1, 2.0*(9-2)]` [gray]_// List of expressions_ +
|
||||||
[green]`["one1", 14]` +
|
[green]`["one1", 14]`
|
||||||
|
|
||||||
`>>>` [blue]`[ [1,"one"], [2,"two"]]` [gray]_// List of lists_ +
|
`>>>` [blue]`[ [1,"one"], [2,"two"]]` [gray]_// List of lists_ +
|
||||||
[green]`[[1, "one"], [2, "two"]]`
|
[green]`[[1, "one"], [2, "two"]]`
|
||||||
|
|
||||||
@@ -409,37 +441,52 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
|
|||||||
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
|
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
|
||||||
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_
|
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_
|
||||||
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_
|
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_
|
||||||
| [blue]`.` | _List item_ | Item at given position | [blue]`[1,2.3].1` -> _2_
|
| [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_
|
||||||
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
|
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
|
||||||
[blue]`6 in [1,2,3]` -> _false_
|
[blue]`6 in [1,2,3]` -> _false_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
The items of array can be accessed using the dot `.` operator.
|
Array's items can be accessed using the index `[]` operator.
|
||||||
|
|
||||||
.Item access syntax
|
.Item access syntax
|
||||||
====
|
====
|
||||||
_item_ = _list-expr_ "**.**" _integer-expr_
|
*_item_* = _list-expr_ "**[**" _integer-expr_ "**]**"
|
||||||
|
====
|
||||||
|
|
||||||
|
.Sub-array (or slice of array) syntax
|
||||||
|
====
|
||||||
|
*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
||||||
====
|
====
|
||||||
|
|
||||||
.Items of list
|
.Items of list
|
||||||
`>>>` [blue]`[1,2,3].1` +
|
`>>>` [blue]`[1,2,3].1` +
|
||||||
[green]`2` +
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`list=[1,2,3]; list.1` +
|
`>>>` [blue]`list=[1,2,3]; list.1` +
|
||||||
[green]`2` +
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`["one","two","three"].1` +
|
`>>>` [blue]`["one","two","three"].1` +
|
||||||
[green]`two` +
|
[green]`two`
|
||||||
|
|
||||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
||||||
[green]`two` +
|
[green]`two`
|
||||||
|
|
||||||
`>>>` [blue]`list.(-1)` +
|
`>>>` [blue]`list.(-1)` +
|
||||||
[green]`three` +
|
[green]`three`
|
||||||
|
|
||||||
`>>>` [blue]`list.(10)` +
|
`>>>` [blue]`list.(10)` +
|
||||||
[red]`Eval Error: [1:9] index 10 out of bounds` +
|
[red]`Eval Error: [1:9] index 10 out of bounds`
|
||||||
|
|
||||||
`>>>` [blue]`#list` +
|
`>>>` [blue]`#list` +
|
||||||
[green]`3` +
|
[green]`3`
|
||||||
`>>>` [blue]`index=2; ["a", "b", "c", "d"].index` +
|
|
||||||
|
`>>>` [blue]`index=2; ["a", "b", "c", "d"][index]` +
|
||||||
[green]`c`
|
[green]`c`
|
||||||
|
|
||||||
|
|
||||||
|
`>>>` [blue]`["a", "b", "c", "d"][2:]` +
|
||||||
|
[green]`["c", "d"]`
|
||||||
|
|
||||||
|
|
||||||
=== Dictionaries
|
=== Dictionaries
|
||||||
The _dictionary_, or _dict_, data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_.
|
The _dictionary_, or _dict_, data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_.
|
||||||
@@ -453,14 +500,11 @@ _empty-dict_ = "**{}**" +
|
|||||||
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" +
|
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" +
|
||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
|
||||||
`>>>` [blue]`{1:"one", 2:"two"}` +
|
|
||||||
[green]`{1: "one", 2: "two"}` +
|
|
||||||
`>>>` [blue]`{"one":1, "two": 2}` +
|
|
||||||
[green]`{"one": 1, "two": 2}` +
|
|
||||||
`>>>` [blue]`{"sum":1+2+3, "prod":1*2*3}` +
|
|
||||||
[green]`{"sum": 6, "prod": 6}`
|
|
||||||
|
|
||||||
|
.Item access syntax
|
||||||
|
====
|
||||||
|
*_item_* = _dict-expr_ "**[**" _key-expr_ "**]**"
|
||||||
|
====
|
||||||
|
|
||||||
.Dict operators
|
.Dict operators
|
||||||
[cols="^2,^2,4,5"]
|
[cols="^2,^2,4,5"]
|
||||||
@@ -468,11 +512,27 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
|
|||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
|
|
||||||
| [blue]`+` | _Join_ | Joins two dicts | [blue]`{1:"one"}+{6:"six"}` -> _{1: "one", 6: "six"}_
|
| [blue]`+` | _Join_ | Joins two dicts | [blue]`{1:"one"}+{6:"six"}` -> _{1: "one", 6: "six"}_
|
||||||
| [blue]`.` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}."two"` -> _2_
|
| [blue]`[]` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}["two"]` -> _2_
|
||||||
| [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ +
|
| [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ +
|
||||||
[blue]`"six" in {"one":1, "two":2}` -> _false_
|
[blue]`"six" in {"one":1, "two":2}` -> _false_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
`>>>` [blue]`{1:"one", 2:"two"}` +
|
||||||
|
[green]`{1: "one", 2: "two"}`
|
||||||
|
|
||||||
|
`>>>` [blue]`{"one":1, "two": 2}` +
|
||||||
|
[green]`{"one": 1, "two": 2}`
|
||||||
|
|
||||||
|
`>>>` [blue]`{"sum":1+2+3, "prod":1*2*3}` +
|
||||||
|
[green]`{"sum": 6, "prod": 6}`
|
||||||
|
|
||||||
|
`>>>` [blue]`{"one":1, "two":2}["two"]` +
|
||||||
|
[green]`2`
|
||||||
|
|
||||||
|
`>>>` [blue]`d={"one":1, "two":2}; d["six"]=6; d` +
|
||||||
|
[green]`{"two": 2, "one": 1, "six": 6}`
|
||||||
|
|
||||||
|
|
||||||
== Variables
|
== Variables
|
||||||
_Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
_Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
||||||
@@ -488,17 +548,23 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
|||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`a=1` +
|
`>>>` [blue]`a=1` +
|
||||||
[green]`1` +
|
[green]`1`
|
||||||
|
|
||||||
`>>>` [blue]`a_b=1+2` +
|
`>>>` [blue]`a_b=1+2` +
|
||||||
[green]`1+2` +
|
[green]`1+2`
|
||||||
|
|
||||||
`>>>` [blue]`a_b` +
|
`>>>` [blue]`a_b` +
|
||||||
[green]`3` +
|
[green]`3`
|
||||||
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value has the approximation error typical of the float data-type_ +
|
|
||||||
[green]`31.200000000000003` +
|
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value has the typical approximation error of the float data-type_ +
|
||||||
|
[green]`31.200000000000003`
|
||||||
|
|
||||||
`>>>` [blue]`x = 1; y = 2*x` +
|
`>>>` [blue]`x = 1; y = 2*x` +
|
||||||
[green]`2` +
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`_a=2` +
|
`>>>` [blue]`_a=2` +
|
||||||
[red]`Parse Error: [1:2] unexpected token "_"` +
|
[red]`Parse Error: [1:2] unexpected token "_"`
|
||||||
|
|
||||||
`>>>` [blue]`1=2` +
|
`>>>` [blue]`1=2` +
|
||||||
[red]`Parse Error: assign operator ("=") must be preceded by a variable`
|
[red]`Parse Error: assign operator ("=") must be preceded by a variable`
|
||||||
|
|
||||||
@@ -508,6 +574,11 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
|||||||
=== [blue]`;` operator
|
=== [blue]`;` operator
|
||||||
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
|
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
|
||||||
|
|
||||||
|
.Mult-expression syntax
|
||||||
|
====
|
||||||
|
*_multi-expression_* = _expression_ {"**;**" _expression_ }
|
||||||
|
====
|
||||||
|
|
||||||
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_.
|
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_.
|
||||||
|
|
||||||
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
||||||
@@ -566,38 +637,47 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
|
|||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1 ? {"a"} : {"b"}` +
|
`>>>` [blue]`1 ? {"a"} : {"b"}` +
|
||||||
[green]`b` +
|
[green]`b`
|
||||||
`>>>` [blue]`10 ? {"a"} : {"b"} :: {"c"}` +
|
|
||||||
[green]`c' +
|
`>>>` [blue]`10 ? {"a"} : {"b"} {2c} {"c"}` +
|
||||||
[green]`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}` +
|
[green]`c`
|
||||||
[green]`b` +
|
|
||||||
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}` +
|
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2c} {"c"}` +
|
||||||
[red]`Parse Error: [1:34] case list in default clause` +
|
[green]`b`
|
||||||
[green]`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} :: {"c"}` +
|
|
||||||
[green]`b` +
|
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2c} [10] {"c"}` +
|
||||||
`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} :: {"c"}` +
|
[red]`Parse Error: [1:34] case list in default clause`
|
||||||
[green]`b` +
|
|
||||||
|
`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} {2c} {"c"}` +
|
||||||
|
[green]`b`
|
||||||
|
|
||||||
|
`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} {2c} {"c"}` +
|
||||||
|
[green]`b`
|
||||||
|
|
||||||
`>>>` [blue]`10 ? {"a"} : {"b"}` +
|
`>>>` [blue]`10 ? {"a"} : {"b"}` +
|
||||||
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
||||||
|
|
||||||
|
|
||||||
=== Variable default value [blue]`??` and [blue]`?=`
|
=== Variable default value [blue]`??` and [blue]`?=`
|
||||||
The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is define; otherwise they return the value of the right expression.
|
The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||||
|
|
||||||
IMPORTANT: If the left variable is defined, the right expression is not evuated at all.
|
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
||||||
|
|
||||||
The [blue]`??` do not change the status of the left variable.
|
The [blue]`??` operator do not change the status of the left variable.
|
||||||
|
|
||||||
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`var ?? (1+2)`'
|
`>>>` [blue]`var ?? (1+2)`' +
|
||||||
[green]`3` +
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`var` +
|
`>>>` [blue]`var` +
|
||||||
[red]`Eval Error: undefined variable or function "var"` +
|
[red]`Eval Error: undefined variable or function "var"`
|
||||||
|
|
||||||
`>>>` [blue]`var ?= (1+2)` +
|
`>>>` [blue]`var ?= (1+2)` +
|
||||||
[green]`3` +
|
[green]`3`
|
||||||
`>>>` [blue]`var` +
|
|
||||||
|
`>>>` [blue]`var`
|
||||||
[green]`3`
|
[green]`3`
|
||||||
|
|
||||||
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
||||||
@@ -610,57 +690,87 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
|===
|
|===
|
||||||
| Priority | Operators | Position | Operation | Operands and results
|
| Priority | Operators | Position | Operation | Operands and results
|
||||||
|
|
||||||
.2+|*ITEM*| [blue]`.` | _Infix_ | _List item_| _list_ `"."` _integer_ -> _any_
|
.2+|*ITEM*| [blue]`[`...`]` | _Postfix_ | _List item_| _list_ `[` _integer_ `]` -> _any_
|
||||||
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `"."` _any_ -> _any_
|
| [blue]`[`...`]` | _Postfix_ | _Dict item_ | _dict_ `[` _any_ `]` -> _any_
|
||||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
|
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
||||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
|
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `++` -> _any_
|
||||||
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
|
.2+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
||||||
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`"+"`\|`"-"`) _number_ -> _number_
|
| [blue]`?=` | _Infix_ | _Default/assign value_| _variable_ `?=` _any-expr_ -> _any_
|
||||||
| [blue]`#` | _Prefix_ | _Lenght-of_ | `"#"` _collection_ -> _integer_
|
.1+| *ITER*^1^| [blue]`()` | _Prefix_ | _Iterator value_ | `()` _iterator_ -> _any_
|
||||||
| [blue]`#` | _Prefix_ | _Size-of_ | `"#"` _iterator_ -> _integer_
|
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `!` -> _integer_
|
||||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `"*"` _number_ -> _number_
|
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_
|
||||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `"*"` _integer_ -> _string_
|
| [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_
|
||||||
| [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_
|
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
||||||
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_
|
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
||||||
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_
|
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
||||||
.6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_
|
.1+|*FRACT*| [blue]`\|` | _Infix_ | _Fraction_ | _integer_ `\|` _integer_ -> _fraction_
|
||||||
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_
|
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||||
| [blue]`+` | _Infix_ | _List-join_ | _list_ `"+"` _list_ -> _list_
|
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
||||||
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `"+"` _dict_ -> _dict_
|
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
||||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_
|
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `./` _number_ -> _float_
|
||||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_
|
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `%` _integer_ -> _integer_
|
||||||
.8+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_
|
.6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `+` _number_ -> _number_
|
||||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_
|
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `+` (_string_\|_number_) -> _string_
|
||||||
| [blue]`>` | _Infix_ | _greater_ | _comparable_ `">"` _comparable_ -> _boolean_
|
| [blue]`+` | _Infix_ | _List-join_ | _list_ `+` _list_ -> _list_
|
||||||
| [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_
|
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
||||||
| [blue]`==` | _Infix_ | _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_
|
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
||||||
| [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_
|
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
||||||
| [blue]`in` | _Infix_ | _member-of-list_ | _any_ `"in"` _list_ -> _boolean_
|
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
||||||
| [blue]`in` | _Infix_ | _key-of-dict_ | _any_ `"in"` _dict_ -> _boolean_
|
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
||||||
.1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | `"not"` _boolean_ -> _boolean_
|
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
||||||
.2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ `"and"` _boolean_ -> _boolean_
|
| [blue]`>=` | _Infix_ | _Greater-equal_ | _comparable_ `>=` _comparable_ -> _boolean_
|
||||||
| [blue]`&&` | _Infix_ | _and_ | _boolean_ `"&&"` _boolean_ -> _boolean_
|
| [blue]`==` | _Infix_ | _Equal_ | _comparable_ `==` _comparable_ -> _boolean_
|
||||||
.2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ `"or"` _boolean_ -> _boolean_
|
| [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_
|
||||||
| [blue]`\|\|` | _Infix_ | _or_ | _boolean_ `"\|\|"` _boolean_ -> _boolean_
|
| [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_
|
||||||
.3+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_
|
| [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_
|
||||||
| [blue]`>>` | _Infix_ | _front-insert_ | _any_ ">>" _list_ -> _list_
|
.1+|*NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
|
||||||
| [blue]`<<` | _Infix_ | _back-insert_ | _list_ "<<" _any_ -> _list_
|
.2+|*AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
|
||||||
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_
|
| [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_
|
||||||
|
.2+|*OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
|
||||||
|
| [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_
|
||||||
|
.3+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
|
||||||
|
| [blue]`>>` | _Infix_ | _Front-insert_ | _any_ `>>` _list_ -> _list_
|
||||||
|
| [blue]`<<` | _Infix_ | _Back-insert_ | _list_ `<<` _any_ -> _list_
|
||||||
|
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
|
||||||
|
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
^1^ Experimental
|
||||||
|
|
||||||
|
|
||||||
== Functions
|
== Functions
|
||||||
Functions in _Expr_ are very similar to functions available in many programming languages. Actually, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
|
Functions in _Expr_ are very similar to functions available in many programming languages. Actually, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
|
||||||
|
|
||||||
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
|
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
|
||||||
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to _import_ the module in which they are defined.
|
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to _import_ the module in which they are defined.
|
||||||
|
|
||||||
In _Expr_ functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator [blue]`@` it is possibile to export local definition to the calling context.
|
|
||||||
|
=== _Expr_ function definition
|
||||||
|
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters.
|
||||||
|
|
||||||
|
. Expr's function definition syntax
|
||||||
|
====
|
||||||
|
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**"
|
||||||
|
_param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ]
|
||||||
|
_required-param-list_ = _identifier_ { "**,**" _identifier_ }
|
||||||
|
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ }
|
||||||
|
_optional-param_ = _identifier_ "**=**" _any-expr_
|
||||||
|
====
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
#TODO#
|
||||||
|
|
||||||
|
=== _Golang_ function definition
|
||||||
|
Description of how to define Golan functions and how to bind them to _Expr_ are topics treated in another document that I'll write, one day, maybe.
|
||||||
|
|
||||||
=== Function calls
|
=== Function calls
|
||||||
#TODO: function calls operations#
|
#TODO: function calls operations#
|
||||||
|
|
||||||
=== Function definitions
|
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator [blue]`@` it is possibile to export local definition to the calling context.
|
||||||
#TODO: function definitions operations#
|
|
||||||
|
|
||||||
|
== Iterators
|
||||||
|
#TODO: function calls operations#
|
||||||
|
|
||||||
== Builtins
|
== Builtins
|
||||||
#TODO: builtins#
|
#TODO: builtins#
|
||||||
@@ -671,4 +781,7 @@ In _Expr_ functions compute values in a local context (scope) that do not make e
|
|||||||
[blue]_import([grey]#<source-file>#)_ loads the multi-expression contained in the specified source and returns its value.
|
[blue]_import([grey]#<source-file>#)_ loads the multi-expression contained in the specified source and returns its value.
|
||||||
|
|
||||||
|
|
||||||
|
== Plugins
|
||||||
|
#TODO: plugins#
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+446
-196
File diff suppressed because one or more lines are too long
+49
-32
@@ -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,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(", ")
|
||||||
}
|
}
|
||||||
@@ -180,7 +186,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
+7
-3
@@ -26,21 +26,21 @@ func NewListIterator(list *ListType, args []any) (it *ListIterator) {
|
|||||||
}
|
}
|
||||||
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
||||||
if argc >= 1 {
|
if argc >= 1 {
|
||||||
if i, err := ToInt(args[0], "start index"); err == nil {
|
if i, err := 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
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,10 @@ func (it *ListIterator) String() string {
|
|||||||
return fmt.Sprintf("$(#%d)", l)
|
return fmt.Sprintf("$(#%d)", l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) TypeName() string {
|
||||||
|
return "ListIterator"
|
||||||
|
}
|
||||||
|
|
||||||
func (it *ListIterator) HasOperation(name string) bool {
|
func (it *ListIterator) HasOperation(name string) bool {
|
||||||
yes := name == resetName || name == indexName || name == countName
|
yes := name == resetName || name == indexName || name == countName
|
||||||
return yes
|
return yes
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
|
Typer
|
||||||
Next() (item any, err error) // must return io.EOF after the last item
|
Next() (item any, err error) // must return io.EOF after the last item
|
||||||
Current() (item any, err error)
|
Current() (item any, err error)
|
||||||
Index() int
|
Index() int
|
||||||
|
|||||||
+2
-6
@@ -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
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-coalesce.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
//-------- null coalesce term
|
|
||||||
|
|
||||||
func newNullCoalesceTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priCoalesce,
|
|
||||||
evalFunc: evalNullCoalesce,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
var rightValue any
|
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := self.children[0]
|
|
||||||
if leftTerm.tk.Sym != SymVariable {
|
|
||||||
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
|
||||||
v = leftValue
|
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
|
||||||
v = rightValue
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- coalesce assign term
|
|
||||||
|
|
||||||
func newCoalesceAssignTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priCoalesce,
|
|
||||||
evalFunc: evalAssignCoalesce,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
var rightValue any
|
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := self.children[0]
|
|
||||||
if leftTerm.tk.Sym != SymVariable {
|
|
||||||
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
|
||||||
v = leftValue
|
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
|
||||||
if functor, ok := rightValue.(Functor); ok {
|
|
||||||
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
|
||||||
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
v = rightValue
|
|
||||||
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
|
|
||||||
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-default.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- default term
|
||||||
|
|
||||||
|
func newDefaultTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priCoalesce,
|
||||||
|
evalFunc: evalDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalDefault(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var rightValue any
|
||||||
|
|
||||||
|
if err = self.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := self.children[0]
|
||||||
|
if leftTerm.tk.Sym != SymVariable {
|
||||||
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
|
err = ErrLeftOperandMustBeVariable(leftTerm, self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
|
v = leftValue
|
||||||
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
v = rightValue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- alternate term
|
||||||
|
|
||||||
|
func newAlternateTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priCoalesce,
|
||||||
|
evalFunc: evalAlternate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalAlternate(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var rightValue any
|
||||||
|
|
||||||
|
if err = self.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := self.children[0]
|
||||||
|
if leftTerm.tk.Sym != SymVariable {
|
||||||
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
|
err = ErrLeftOperandMustBeVariable(leftTerm, self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
|
||||||
|
if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
v = rightValue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v = leftValue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- default assign term
|
||||||
|
|
||||||
|
func newDefaultAssignTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priCoalesce,
|
||||||
|
evalFunc: evalAssignDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalAssignDefault(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var rightValue any
|
||||||
|
|
||||||
|
if err = self.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := self.children[0]
|
||||||
|
if leftTerm.tk.Sym != SymVariable {
|
||||||
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
|
err = ErrLeftOperandMustBeVariable(leftTerm, self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
|
v = leftValue
|
||||||
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
if functor, ok := rightValue.(Functor); ok {
|
||||||
|
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
v = rightValue
|
||||||
|
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
|
||||||
|
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
|
||||||
|
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
|
||||||
|
}
|
||||||
+3
-1
@@ -41,7 +41,9 @@ func evalInclude(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
} else if IsString(childValue) {
|
} else if IsString(childValue) {
|
||||||
filePath, _ := childValue.(string)
|
filePath, _ := childValue.(string)
|
||||||
v, err = EvalFile(ctx, filePath)
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = self.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
|
|||||||
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
||||||
var v int
|
var v int
|
||||||
|
|
||||||
if v, err = ToInt((*indexList)[0], "index expression"); err == nil {
|
if v, err = ToGoInt((*indexList)[0], "index expression"); err == nil {
|
||||||
if v < 0 && v >= -maxValue {
|
if v < 0 && v >= -maxValue {
|
||||||
v = maxValue + v
|
v = maxValue + v
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else if it, ok := childValue.(Iterator); ok {
|
} else if it, ok := childValue.(Iterator); ok {
|
||||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||||
count, _ := extIt.CallOperation(countName, nil)
|
count, _ := extIt.CallOperation(countName, nil)
|
||||||
v, _ = ToInt(count, "")
|
v, _ = ToGoInt(count, "")
|
||||||
} else {
|
} else {
|
||||||
v = int64(it.Index() + 1)
|
v = int64(it.Index() + 1)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -202,8 +202,10 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
case '?':
|
case '?':
|
||||||
if next, _ := self.peek(); next == '?' {
|
if next, _ := self.peek(); next == '?' {
|
||||||
tk = self.moveOn(SymDoubleQuestion, ch, next)
|
tk = self.moveOn(SymDoubleQuestion, ch, next)
|
||||||
} else if next, _ := self.peek(); next == '=' {
|
} else if next == '=' {
|
||||||
tk = self.moveOn(SymQuestionEqual, ch, next)
|
tk = self.moveOn(SymQuestionEqual, ch, next)
|
||||||
|
} else if next == '!' {
|
||||||
|
tk = self.moveOn(SymQuestionExclam, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymQuestion, ch)
|
tk = self.makeToken(SymQuestion, ch)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-77
@@ -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()
|
||||||
|
|||||||
@@ -57,16 +57,17 @@ const (
|
|||||||
SymTilde // 46: '~'
|
SymTilde // 46: '~'
|
||||||
SymDoubleQuestion // 47: '??'
|
SymDoubleQuestion // 47: '??'
|
||||||
SymQuestionEqual // 48: '?='
|
SymQuestionEqual // 48: '?='
|
||||||
SymDoubleAt // 49: '@@'
|
SymQuestionExclam // 49: '?!'
|
||||||
SymDoubleColon // 50: '::'
|
SymDoubleAt // 50: '@@'
|
||||||
SymInsert // 51: '>>'
|
SymDoubleColon // 51: '::'
|
||||||
SymAppend // 52: '<<'
|
SymInsert // 52: '>>'
|
||||||
SymCaret // 53: '^'
|
SymAppend // 53: '<<'
|
||||||
SymDollarRound // 54: '$('
|
SymCaret // 54: '^'
|
||||||
SymOpenClosedRound // 55: '()'
|
SymDollarRound // 55: '$('
|
||||||
SymDoubleDollar // 56: '$$'
|
SymOpenClosedRound // 56: '()'
|
||||||
SymDoubleDot // 57: '..'
|
SymDoubleDollar // 57: '$$'
|
||||||
SymTripleDot // 58: '...'
|
SymDoubleDot // 58: '..'
|
||||||
|
SymTripleDot // 59: '...'
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
|
|||||||
@@ -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, `[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`},
|
||||||
|
/* 9 */ {`true and false`, false, nil},
|
||||||
|
/* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`},
|
||||||
|
/* 11 */ {`[] and false`, nil, `[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, `[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`},
|
||||||
|
/* 14 */ {`[] or false`, nil, `[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)
|
||||||
|
}
|
||||||
+10
-12
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,9 +20,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 6 */ {`int(3.1)`, int64(3), nil},
|
/* 6 */ {`int(3.1)`, int64(3), nil},
|
||||||
/* 7 */ {`int(3.9)`, int64(3), nil},
|
/* 7 */ {`int(3.9)`, int64(3), nil},
|
||||||
/* 8 */ {`int("432")`, int64(432), nil},
|
/* 8 */ {`int("432")`, int64(432), nil},
|
||||||
/* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)},
|
/* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
|
||||||
/* 10 */ {`int("432", 4)`, nil, errors.New(`int(): too much params -- expected 1, got 2`)},
|
/* 10 */ {`int("432", 4)`, nil, `int(): too much params -- expected 1, got 2`},
|
||||||
/* 11 */ {`int(nil)`, nil, errors.New(`int(): can't convert nil to int`)},
|
/* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
|
||||||
/* 12 */ {`isInt(2+1)`, true, nil},
|
/* 12 */ {`isInt(2+1)`, true, nil},
|
||||||
/* 13 */ {`isInt(3.1)`, false, nil},
|
/* 13 */ {`isInt(3.1)`, false, nil},
|
||||||
/* 14 */ {`isFloat(3.1)`, true, nil},
|
/* 14 */ {`isFloat(3.1)`, true, nil},
|
||||||
@@ -42,9 +41,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 30 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
|
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
||||||
/* 31 */ {`dec()`, nil, errors.New(`dec(): too few params -- expected 1, got 0`)},
|
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||||
/* 32 */ {`dec(1,2,3)`, nil, errors.New(`dec(): too much params -- expected 1, got 3`)},
|
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too much params -- expected 1, got 3`},
|
||||||
/* 33 */ {`isBool(false)`, true, nil},
|
/* 33 */ {`isBool(false)`, true, nil},
|
||||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
||||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||||
@@ -53,15 +52,14 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 38 */ {`bool(1.0)`, true, nil},
|
/* 38 */ {`bool(1.0)`, true, nil},
|
||||||
/* 39 */ {`bool("1")`, true, nil},
|
/* 39 */ {`bool("1")`, true, nil},
|
||||||
/* 40 */ {`bool(false)`, false, nil},
|
/* 40 */ {`bool(false)`, false, nil},
|
||||||
/* 41 */ {`bool([1])`, nil, errors.New(`bool(): can't convert list to bool`)},
|
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
||||||
/* 44 */ {`dec([1])`, nil, errors.New(`dec(): can't convert list to float`)},
|
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||||
// /* 45 */ {`string([1])`, nil, errors.New(`string(): can't convert list to string`)},
|
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 2)
|
runTestSuite(t, section, inputs)
|
||||||
parserTest(t, section, inputs)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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, 1)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
|
|||||||
|
|
||||||
t.Setenv("EXPR_PATH", "test-resources")
|
t.Setenv("EXPR_PATH", "test-resources")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ func TestFuncMathArith(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
||||||
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
||||||
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)},
|
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, `unknown function mulX()`},
|
||||||
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
|
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
|
||||||
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
|
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
|
||||||
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
|
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
|
||||||
@@ -24,6 +23,6 @@ func TestFuncMathArith(t *testing.T) {
|
|||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,16 +13,23 @@ func TestFuncOs(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
||||||
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
||||||
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, `open /etc/hostsX: no such file or directory`},
|
||||||
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
||||||
/* 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, `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, `fileWriteText(): invalid file handle`},
|
||||||
|
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, `fileOpen(): too few params -- expected 1, got 0`},
|
||||||
|
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, `fileOpen(): missing or invalid file path`},
|
||||||
|
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, `fileCreate(): missing or invalid file path`},
|
||||||
|
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, `fileAppend(): missing or invalid file path`},
|
||||||
|
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `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, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,8 +15,8 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
||||||
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
||||||
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
||||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)},
|
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
|
||||||
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)},
|
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
||||||
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
||||||
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||||
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
|
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
|
||||||
@@ -25,13 +24,13 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
|
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
|
||||||
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
|
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
|
||||||
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
|
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)},
|
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, `strStartsWith(): too few params -- expected 2 or more, got 1`},
|
||||||
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
|
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
|
||||||
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
|
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)},
|
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`},
|
||||||
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||||
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)},
|
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
||||||
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)},
|
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
||||||
|
|
||||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||||
}
|
}
|
||||||
@@ -64,6 +63,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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_common_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inputType struct {
|
||||||
|
source string
|
||||||
|
wantResult any
|
||||||
|
wantErr any
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWantedError(input *inputType) error {
|
||||||
|
var wantErr error
|
||||||
|
var ok bool
|
||||||
|
if wantErr, ok = input.wantErr.(error); !ok {
|
||||||
|
if msg, ok := input.wantErr.(string); ok {
|
||||||
|
wantErr = errors.New(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wantErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
|
||||||
|
var expr Expr
|
||||||
|
var gotResult any
|
||||||
|
var gotErr error
|
||||||
|
|
||||||
|
wantErr := getWantedError(input)
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = NewSimpleStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
logTest(t, count, section, input.source, input.wantResult, 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 != wantErr {
|
||||||
|
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
|
||||||
|
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, 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
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,23 +17,24 @@ func TestFractionsParser(t *testing.T) {
|
|||||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
||||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
||||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
||||||
/* 7 */ {`1|"5"`, nil, errors.New(`denominator must be integer, got string (5)`)},
|
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
||||||
/* 8 */ {`"1"|5`, nil, errors.New(`numerator must be integer, got string (1)`)},
|
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
||||||
/* 9 */ {`1|+5`, nil, errors.New(`[1:3] infix operator "|" requires two non-nil operands, got 1`)},
|
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
||||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
||||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
||||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
||||||
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
||||||
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 15 */ {`1|0`, nil, errors.New(`division by zero`)},
|
/* 15 */ {`1|0`, nil, `division by zero`},
|
||||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||||
/* 17 */ {`fract("")`, (*FractionType)(nil), errors.New(`bad syntax`)},
|
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||||
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)},
|
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||||
|
/* 22 */ {`string(1|2)`, "1|2", nil},
|
||||||
}
|
}
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFractionToStringSimple(t *testing.T) {
|
func TestFractionToStringSimple(t *testing.T) {
|
||||||
|
|||||||
+10
-9
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,25 +16,27 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
||||||
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
||||||
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
/* 6 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)},
|
/* 6 */ {`@x="hello"; @x`, nil, `[1:3] variable references are not allowed in top level expressions: "@x"`},
|
||||||
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
||||||
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
||||||
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, `undefined variable or function "x"`},
|
||||||
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
||||||
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
||||||
/* 14 */ {`two=func(){2}; two(123)`, nil, errors.New(`two(): too much params -- expected 0, got 1`)},
|
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
|
||||||
/* 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, `[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, `[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) {
|
||||||
@@ -44,7 +45,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)
|
||||||
|
|||||||
+2
-3
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,11 +16,11 @@ func TestCollections(t *testing.T) {
|
|||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, errors.New(`[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`)},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -7,6 +7,7 @@ package expr
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
|
section := "Iterator"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
||||||
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||||
@@ -22,5 +23,6 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// /* 1 */ {`0?{}`, nil, nil},
|
// /* 1 */ {`0?{}`, nil, nil},
|
||||||
// }
|
// }
|
||||||
parserTest(t, "Iterator", inputs)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-13
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,14 +21,14 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||||
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||||
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
/* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
|
||||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||||
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
||||||
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
||||||
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
||||||
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, errors.New(`[1:34] index 10 out of bounds`)},
|
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`},
|
||||||
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||||
@@ -37,18 +36,18 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
||||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)},
|
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, errors.New(`[1:18] infix operator "<<" requires two non-nil operands, got 1`)},
|
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||||
/* 28 */ {`2 << 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`)},
|
/* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
||||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, errors.New(`[1:6] infix operator ">>" requires two non-nil operands, got 0`)},
|
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||||
/* 30 */ {`2 >> 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`)},
|
/* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
||||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||||
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||||
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, errors.New(`index 5 out of bounds (0, 1)`)},
|
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||||
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, errors.New(`[1:12] index/key specification expected, got [] [list]`)},
|
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||||
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, errors.New(`[1:12] index/key is nil`)},
|
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
|
||||||
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||||
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
||||||
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
||||||
@@ -57,6 +56,6 @@ func TestListParser(t *testing.T) {
|
|||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
parserTest(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-112
@@ -5,18 +5,9 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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"
|
||||||
|
|
||||||
@@ -57,13 +48,13 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 34 */ {`(((1)))`, int64(1), nil},
|
/* 34 */ {`(((1)))`, int64(1), nil},
|
||||||
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
|
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
|
||||||
/* 36 */ {`0 || 0.0 && "hello"`, false, nil},
|
/* 36 */ {`0 || 0.0 && "hello"`, false, nil},
|
||||||
/* 37 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)},
|
/* 37 */ {`"s" + true`, nil, `[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`},
|
||||||
/* 38 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)},
|
/* 38 */ {`+false`, nil, `[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`},
|
||||||
/* 39 */ {`false // very simple expression`, false, nil},
|
/* 39 */ {`false // very simple expression`, false, nil},
|
||||||
/* 40 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)},
|
/* 40 */ {`1 + // Missing right operator`, nil, `[1:4] infix operator "+" requires two non-nil operands, got 1`},
|
||||||
/* 41 */ {"", nil, nil},
|
/* 41 */ {"", nil, nil},
|
||||||
/* 42 */ {"4!", int64(24), nil},
|
/* 42 */ {"4!", int64(24), nil},
|
||||||
/* 43 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)},
|
/* 43 */ {"(-4)!", nil, `factorial of a negative integer (-4) is not allowed`},
|
||||||
/* 44 */ {"-4!", int64(-24), nil},
|
/* 44 */ {"-4!", int64(-24), nil},
|
||||||
/* 45 */ {"1.5 < 7", true, nil},
|
/* 45 */ {"1.5 < 7", true, nil},
|
||||||
/* 46 */ {"1.5 > 7", false, nil},
|
/* 46 */ {"1.5 > 7", false, nil},
|
||||||
@@ -75,20 +66,20 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 52 */ {`"1.5" > "7"`, false, nil},
|
/* 52 */ {`"1.5" > "7"`, false, nil},
|
||||||
/* 53 */ {`"1.5" == "7"`, false, nil},
|
/* 53 */ {`"1.5" == "7"`, false, nil},
|
||||||
/* 54 */ {`"1.5" != "7"`, true, nil},
|
/* 54 */ {`"1.5" != "7"`, true, nil},
|
||||||
/* 55 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)},
|
/* 55 */ {"1.5 < ", nil, `[1:6] infix operator "<" requires two non-nil operands, got 1`},
|
||||||
/* 56 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)},
|
/* 56 */ {"1.5 > ", nil, `[1:6] infix operator ">" requires two non-nil operands, got 1`},
|
||||||
/* 57 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)},
|
/* 57 */ {"1.5 <= ", nil, `[1:6] infix operator "<=" requires two non-nil operands, got 1`},
|
||||||
/* 58 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)},
|
/* 58 */ {"1.5 >= ", nil, `[1:6] infix operator ">=" requires two non-nil operands, got 1`},
|
||||||
/* 59 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)},
|
/* 59 */ {"1.5 != ", nil, `[1:6] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 60 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)},
|
/* 60 */ {"1.5 == ", nil, `[1:6] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 61 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)},
|
/* 61 */ {`"1.5" < `, nil, `[1:8] infix operator "<" requires two non-nil operands, got 1`},
|
||||||
/* 62 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)},
|
/* 62 */ {`"1.5" > `, nil, `[1:8] infix operator ">" requires two non-nil operands, got 1`},
|
||||||
/* 63 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)},
|
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 64 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)},
|
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 66 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)},
|
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
||||||
/* 67 */ {"4 / 0", nil, errors.New(`division by zero`)},
|
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||||
/* 68 */ {"4.0 / 0", nil, errors.New(`division by zero`)},
|
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
/* 70 */ {`123`, int64(123), nil},
|
/* 70 */ {`123`, int64(123), nil},
|
||||||
/* 71 */ {`1.`, float64(1.0), nil},
|
/* 71 */ {`1.`, float64(1.0), nil},
|
||||||
@@ -100,7 +91,7 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 77 */ {`5 % 2`, int64(1), nil},
|
/* 77 */ {`5 % 2`, int64(1), nil},
|
||||||
/* 78 */ {`5 % (-2)`, int64(1), nil},
|
/* 78 */ {`5 % (-2)`, int64(1), nil},
|
||||||
/* 79 */ {`-5 % 2`, int64(-1), nil},
|
/* 79 */ {`-5 % 2`, int64(-1), nil},
|
||||||
/* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`)},
|
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
||||||
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
||||||
@@ -113,12 +104,12 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 90 */ {`x=2 but x*10`, int64(20), nil},
|
/* 90 */ {`x=2 but x*10`, int64(20), nil},
|
||||||
/* 91 */ {`false and true`, false, nil},
|
/* 91 */ {`false and true`, false, nil},
|
||||||
/* 92 */ {`false and (x==2)`, false, nil},
|
/* 92 */ {`false and (x==2)`, false, nil},
|
||||||
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, `undefined variable or function "x"`},
|
||||||
/* 94 */ {`false or true`, true, nil},
|
/* 94 */ {`false or true`, true, nil},
|
||||||
/* 95 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 95 */ {`false or (x==2)`, nil, `undefined variable or function "x"`},
|
||||||
/* 96 */ {`a=5; a`, int64(5), nil},
|
/* 96 */ {`a=5; a`, int64(5), nil},
|
||||||
/* 97 */ {`2=5`, nil, errors.New(`[1:2] left operand of "=" must be a variable or a collection's item`)},
|
/* 97 */ {`2=5`, nil, `[1:2] left operand of "=" must be a variable or a collection's item`},
|
||||||
/* 98 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable or a collection's item`)},
|
/* 98 */ {`2+a=5`, nil, `[1:3] left operand of "=" must be a variable or a collection's item`},
|
||||||
/* 99 */ {`2+(a=5)`, int64(7), nil},
|
/* 99 */ {`2+(a=5)`, int64(7), nil},
|
||||||
/* 100 */ {`x ?? "default"`, "default", nil},
|
/* 100 */ {`x ?? "default"`, "default", nil},
|
||||||
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
||||||
@@ -128,98 +119,31 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
|
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
|
||||||
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
|
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
|
||||||
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
|
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
|
||||||
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)},
|
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, `[1:34] case list in default clause`},
|
||||||
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
|
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
|
||||||
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
|
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
|
||||||
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)},
|
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, `[1:3] no case catches the value (10) of the selection expression`},
|
||||||
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)},
|
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, `[1:22] selector-case outside of a selector context`},
|
||||||
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
||||||
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
||||||
/* 115 */ {`nil`, nil, nil},
|
/* 115 */ {`nil`, nil, nil},
|
||||||
/* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
|
/* 116 */ {`null`, nil, `undefined variable or function "null"`},
|
||||||
/* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
|
/* 117 */ {`{"key"}`, nil, `[1:8] expected ":", got "}"`},
|
||||||
/* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)},
|
/* 118 */ {`{"key":}`, nil, `[1:9] expected "dictionary-value", got "}"`},
|
||||||
/* 119 */ {`{}`, &DictType{}, nil},
|
/* 119 */ {`{}`, &DictType{}, nil},
|
||||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
||||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
||||||
/* 123 */ {`1|(2-2)`, nil, errors.New(`division by zero`)},
|
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
||||||
|
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||||
|
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||||
|
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
||||||
|
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||||
|
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||||
|
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user