Compare commits
17 Commits
v0.2.0
...
b9ea96f649
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ea96f649 | |||
| 838ab9fd7e | |||
| ad6b4b872f | |||
| c75e6485e0 | |||
| 7b80f7f03b | |||
| 7f9fd570b2 | |||
| b17d2250a4 | |||
| dda10749d0 | |||
| 07ca84170e | |||
| 55e136e9bc | |||
| e493c40c7b | |||
| 95605232ab | |||
| 8f396a35de | |||
| bd323efedf | |||
| a9b143d012 | |||
| 024ff42be0 | |||
| 8ab2c28343 |
+84
-20
@@ -63,35 +63,37 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := expr.NewSimpleVarStore()
|
||||
ctx.SetValue("var", int64(4))
|
||||
ctx.SetVar("var", 4)
|
||||
|
||||
source := `(3-1)*(10/5) == var`
|
||||
|
||||
r := strings.NewReader(source)
|
||||
scanner := expr.NewScanner(r, DefaultTranslations())
|
||||
r := strings.NewReader(source)
|
||||
scanner := expr.NewScanner(r, expr.DefaultTranslations())
|
||||
parser := expr.NewParser(ctx)
|
||||
|
||||
if ast, err := parser.Parse(scanner); err == nil {
|
||||
if result, err := ast.Eval(ctx); err == nil {
|
||||
fmt.Printf("%q -> %v [%T]\n", source, result, result)
|
||||
} else {
|
||||
fmt.Println("Error calculating the expression:", err)
|
||||
}
|
||||
fmt.Printf("%q -> %v [%T]\n", source, result, result)
|
||||
} else {
|
||||
fmt.Println("Error calculating the expression:", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Error parsing the expression:", err)
|
||||
}
|
||||
fmt.Println("Error parsing the expression:", err)
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The above program is equivalent to the following one.
|
||||
|
||||
[source,go]
|
||||
----
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
@@ -99,18 +101,43 @@ import (
|
||||
|
||||
func main() {
|
||||
ctx := expr.NewSimpleVarStore()
|
||||
ctx.SetValue("var", int64(4))
|
||||
ctx.SetVar("var", 4)
|
||||
|
||||
source := `(3-1)*(10/5) == var`
|
||||
|
||||
if result, err := expr.EvalString(ctx, source); err == nil {
|
||||
fmt.Printf("%q -> %v [%T]\n", source, result, result)
|
||||
} else {
|
||||
fmt.Println("Error calculating the expression:", err)
|
||||
}
|
||||
if result, err := expr.EvalString(ctx, source); err == nil {
|
||||
fmt.Printf("%q -> %v [%T]\n", source, result, result)
|
||||
} else {
|
||||
fmt.Println("Error calculating the expression:", err)
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Here is another equivalent version.
|
||||
|
||||
[source,go]
|
||||
----
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
)
|
||||
|
||||
func main() {
|
||||
source := `(3-1)*(10/5) == var`
|
||||
|
||||
if result, err := expr.EvalStringA(source, expr.Arg{"var", 4}); err == nil {
|
||||
fmt.Printf("%q -> %v [%T]\n", source, result, result)
|
||||
} else {
|
||||
fmt.Println("Error calculating the expression:", err)
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Concepts and terminology
|
||||
#TODO#
|
||||
|
||||
=== Data types
|
||||
_Expr_ supports numerical, string, relational, boolean expressions, and mixed-type lists.
|
||||
|
||||
@@ -212,10 +239,39 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
|
||||
====
|
||||
|
||||
==== List
|
||||
#TODO: List operations#
|
||||
_Expr_ supports list of mixed-type values, also specified by normal expressions.
|
||||
|
||||
.List examples
|
||||
[source,go]
|
||||
----
|
||||
[1, 2, 3] // List of integers
|
||||
["one", "two", "three"] // List of strings
|
||||
["one", 2, false, 4.1] // List of mixed-types
|
||||
["one"+1, 2.0*(9-2)] // List of expressions
|
||||
[ [1,"one"], [2,"two"]] // List of lists
|
||||
----
|
||||
|
||||
|
||||
.List operators
|
||||
[cols="^2,^2,5,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
|
||||
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` _[ [1,2,3] ]_
|
||||
|
||||
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` _[ [1,3] ]_
|
||||
|===
|
||||
|
||||
=== Variables
|
||||
#TODO: variables#
|
||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
|
||||
|
||||
.Examples
|
||||
[source,go]
|
||||
----
|
||||
a=1
|
||||
x = 5.2 * (9-3)
|
||||
x = 1; y = 2*x
|
||||
----
|
||||
|
||||
=== Other operations
|
||||
|
||||
@@ -301,12 +357,15 @@ The table below shows all supported operators by decreasing priorities.
|
||||
.2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ "and" _boolean_ -> _boolean_
|
||||
| [blue]`&&` | _Infix_ | _and_ | _boolean_ "&&" _boolean_ -> _boolean_
|
||||
.2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ "or" _boolean_ -> _boolean_
|
||||
| [blue]`||` | _Infix_ | _or_ | _boolean_ "||" _boolean_ -> _boolean_
|
||||
| [blue]`\|\|` | _Infix_ | _or_ | _boolean_ "\|\|" _boolean_ -> _boolean_
|
||||
.1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_
|
||||
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_
|
||||
|===
|
||||
|
||||
=== Functions
|
||||
Functions in _Expr_ are very similar to functions in many programming languages.
|
||||
|
||||
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.
|
||||
|
||||
==== Function calls
|
||||
#TODO: function calls operations#
|
||||
@@ -314,8 +373,13 @@ The table below shows all supported operators by decreasing priorities.
|
||||
==== Function definitions
|
||||
#TODO: function definitions operations#
|
||||
|
||||
==== Builtins
|
||||
=== Builtins
|
||||
#TODO: builtins#
|
||||
|
||||
==== Builtin functions
|
||||
|
||||
==== [blue]_import()_
|
||||
[blue]_import([grey]#<source-file>#)_ loads the multi-expression contained in the specified source and returns its value.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
type Expr interface {
|
||||
Eval(ctx ExprContext) (result any, err error)
|
||||
eval(ctx ExprContext, preset bool) (result any, err error)
|
||||
String() string
|
||||
}
|
||||
|
||||
//-------- ast
|
||||
@@ -118,7 +119,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
|
||||
if self.forest != nil {
|
||||
for _, root := range self.forest {
|
||||
if result, err = root.compute(ctx); err == nil {
|
||||
ctx.SetVar(control_last_result, result)
|
||||
ctx.setVar(ControlLastResult, result)
|
||||
} else {
|
||||
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
|
||||
break
|
||||
|
||||
@@ -33,6 +33,7 @@ type ExprContext interface {
|
||||
Clone() ExprContext
|
||||
GetVar(varName string) (value any, exists bool)
|
||||
SetVar(varName string, value any)
|
||||
setVar(varName string, value any)
|
||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||
GetFuncInfo(name string) ExprFunc
|
||||
|
||||
+5
-5
@@ -5,9 +5,9 @@ import "strings"
|
||||
|
||||
// Preset control variables
|
||||
const (
|
||||
control_last_result = "_last"
|
||||
control_bool_shortcut = "_bool_shortcut"
|
||||
control_import_path = "_import_path"
|
||||
ControlLastResult = "last"
|
||||
ControlBoolShortcut = "_bool_shortcut"
|
||||
ControlImportPath = "_import_path"
|
||||
)
|
||||
|
||||
// Other control variables
|
||||
@@ -21,8 +21,8 @@ const (
|
||||
)
|
||||
|
||||
func initDefaultVars(ctx ExprContext) {
|
||||
ctx.SetVar(control_bool_shortcut, true)
|
||||
ctx.SetVar(control_import_path, init_import_path)
|
||||
ctx.SetVar(ControlBoolShortcut, true)
|
||||
ctx.SetVar(ControlImportPath, init_import_path)
|
||||
}
|
||||
|
||||
func enable(ctx ExprContext, name string) {
|
||||
|
||||
+2
-2
@@ -51,7 +51,7 @@ func addEnvImportDirs(dirList []string) []string {
|
||||
}
|
||||
|
||||
func addPresetImportDirs(ctx ExprContext, dirList []string) []string {
|
||||
if dirSpec, exists := getControlString(ctx, control_import_path); exists {
|
||||
if dirSpec, exists := getControlString(ctx, ControlImportPath); exists {
|
||||
dirs := strings.Split(dirSpec, ":")
|
||||
if dirList == nil {
|
||||
dirList = dirs
|
||||
@@ -133,7 +133,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
return
|
||||
}
|
||||
|
||||
func importImportFunc(ctx ExprContext) {
|
||||
func ImportImportFunc(ctx ExprContext) {
|
||||
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
||||
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func importMathFuncs(ctx ExprContext) {
|
||||
func ImportMathFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
|
||||
ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
|
||||
}
|
||||
|
||||
+18
-18
@@ -19,35 +19,35 @@ func EvalString(ctx ExprContext, source string) (result any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type EvalArg struct {
|
||||
name string
|
||||
value any
|
||||
type Arg struct {
|
||||
Name string
|
||||
Value any
|
||||
}
|
||||
|
||||
func EvalStringA(source string, args ...EvalArg) (result any, err error) {
|
||||
func EvalStringA(source string, args ...Arg) (result any, err error) {
|
||||
return EvalStringV(source, args)
|
||||
}
|
||||
|
||||
func EvalStringV(source string, args []EvalArg) (result any, err error) {
|
||||
func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||
ctx := NewSimpleFuncStore()
|
||||
for _, arg := range args {
|
||||
if isFunc(arg.value) {
|
||||
if f, ok := arg.value.(FuncTemplate); ok {
|
||||
if isFunc(arg.Value) {
|
||||
if f, ok := arg.Value.(FuncTemplate); ok {
|
||||
functor := &simpleFunctor{f: f}
|
||||
ctx.RegisterFunc(arg.name, functor, 0, -1)
|
||||
ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
||||
} else {
|
||||
err = fmt.Errorf("invalid function specification: %q", arg.name)
|
||||
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
||||
}
|
||||
} else if integer, ok := anyInteger(arg.value); ok {
|
||||
ctx.SetVar(arg.name, integer)
|
||||
} else if float, ok := anyFloat(arg.value); ok {
|
||||
ctx.SetVar(arg.name, float)
|
||||
} else if _, ok := arg.value.(string); ok {
|
||||
ctx.SetVar(arg.name, arg.value)
|
||||
} else if _, ok := arg.value.(bool); ok {
|
||||
ctx.SetVar(arg.name, arg.value)
|
||||
} else if integer, ok := anyInteger(arg.Value); ok {
|
||||
ctx.SetVar(arg.Name, integer)
|
||||
} else if float, ok := anyFloat(arg.Value); ok {
|
||||
ctx.SetVar(arg.Name, float)
|
||||
} else if _, ok := arg.Value.(string); ok {
|
||||
ctx.SetVar(arg.Name, arg.Value)
|
||||
} else if _, ok := arg.Value.(bool); ok {
|
||||
ctx.SetVar(arg.Name, arg.Value)
|
||||
} else {
|
||||
err = fmt.Errorf("unsupported type %T specified for item %q", arg.value, arg.name)
|
||||
err = fmt.Errorf("unsupported type %T specified for item %q", arg.Value, arg.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -24,7 +24,7 @@ func subtract(ctx ExprContext, name string, args []any) (result any, err error)
|
||||
func TestEvalStringA(t *testing.T) {
|
||||
|
||||
source := `a + b * subtract(4,2)`
|
||||
args := []EvalArg{
|
||||
args := []Arg{
|
||||
{"a", uint8(1)},
|
||||
{"b", int8(2)},
|
||||
{"subtract", FuncTemplate(subtract)},
|
||||
@@ -45,7 +45,6 @@ func TestEvalStringA(t *testing.T) {
|
||||
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||
t.Errorf("Error: %v", gotErr)
|
||||
}
|
||||
fmt.Println("Hello World!")
|
||||
}
|
||||
|
||||
func TestEvalString(t *testing.T) {
|
||||
@@ -76,5 +75,4 @@ func TestEvalString(t *testing.T) {
|
||||
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||
t.Errorf("Error: %v", gotErr)
|
||||
}
|
||||
fmt.Println("Hello World!")
|
||||
}
|
||||
|
||||
+3
-3
@@ -57,7 +57,7 @@ func exportVar(ctx ExprContext, name string, value any) {
|
||||
if name[0] == '@' {
|
||||
name = name[1:]
|
||||
}
|
||||
ctx.SetVar(name, value)
|
||||
ctx.setVar(name, value)
|
||||
}
|
||||
|
||||
func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
||||
@@ -89,9 +89,9 @@ type funcDefFunctor struct {
|
||||
func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
for i, p := range functor.params {
|
||||
if i < len(args) {
|
||||
ctx.SetVar(p, args[i])
|
||||
ctx.setVar(p, args[i])
|
||||
} else {
|
||||
ctx.SetVar(p, nil)
|
||||
ctx.setVar(p, nil)
|
||||
}
|
||||
}
|
||||
result, err = functor.expr.eval(ctx, false)
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
// operand-selector-case.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -------- selector case term
|
||||
|
||||
@@ -13,6 +16,18 @@ type selectorCase struct {
|
||||
caseExpr Expr
|
||||
}
|
||||
|
||||
func (sc *selectorCase) String() string {
|
||||
var sb strings.Builder
|
||||
if sc.filterList != nil {
|
||||
sc.filterList.toString(&sb)
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
sb.WriteByte('{')
|
||||
sb.WriteString(sc.caseExpr.String())
|
||||
sb.WriteByte('}')
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
|
||||
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||
return &term{
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
||||
if functor, ok := v.(Functor); ok {
|
||||
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||
} else {
|
||||
ctx.SetVar(leftTerm.tk.source, v)
|
||||
ctx.setVar(leftTerm.tk.source, v)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
+2
-2
@@ -48,7 +48,7 @@ func newAndTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
|
||||
func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
if isEnabled(ctx, control_bool_shortcut) {
|
||||
if isEnabled(ctx, ControlBoolShortcut) {
|
||||
v, err = evalAndWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalAndWithoutShortcut(ctx, self)
|
||||
@@ -117,7 +117,7 @@ func newOrTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
|
||||
func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
if isEnabled(ctx, control_bool_shortcut) {
|
||||
if isEnabled(ctx, ControlBoolShortcut) {
|
||||
v, err = evalOrWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalOrWithoutShortcut(ctx, self)
|
||||
|
||||
@@ -75,7 +75,7 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
||||
err = errCoalesceNoFunc(self.children[1])
|
||||
} else {
|
||||
v = rightValue
|
||||
ctx.SetVar(leftTerm.source(), rightValue)
|
||||
ctx.setVar(leftTerm.source(), rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -268,7 +268,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
||||
currentTerm, err = tree.addToken2(tk)
|
||||
}
|
||||
|
||||
if currentTerm != nil && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||
selectorTerm = nil
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -173,8 +173,8 @@ func TestParser(t *testing.T) {
|
||||
ctx := NewSimpleFuncStore()
|
||||
ctx.SetVar("var1", int64(123))
|
||||
ctx.SetVar("var2", "abc")
|
||||
importMathFuncs(ctx)
|
||||
importImportFunc(ctx)
|
||||
ImportMathFuncs(ctx)
|
||||
ImportImportFunc(ctx)
|
||||
parser := NewParser(ctx)
|
||||
|
||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
||||
@@ -204,7 +204,7 @@ func TestParser(t *testing.T) {
|
||||
} else {
|
||||
failed++
|
||||
if i+1 == check_env_expr_path {
|
||||
t.Logf(`NOTICE: Test nr %d requires EXPR_PATH environment variable with value "."`, check_env_expr_path)
|
||||
t.Logf(`NOTICE: Test nr %d requires EXPR_PATH="."`, check_env_expr_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ func TestListParser(t *testing.T) {
|
||||
ctx := NewSimpleFuncStore()
|
||||
ctx.SetVar("var1", int64(123))
|
||||
ctx.SetVar("var2", "abc")
|
||||
importMathFuncs(ctx)
|
||||
ImportMathFuncs(ctx)
|
||||
parser := NewParser(ctx)
|
||||
|
||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
||||
|
||||
+11
-1
@@ -1,6 +1,8 @@
|
||||
// simple-var-store.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
|
||||
type SimpleVarStore struct {
|
||||
varStore map[string]any
|
||||
}
|
||||
@@ -23,10 +25,18 @@ func (ctx *SimpleVarStore) GetVar(varName string) (v any, exists bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ctx *SimpleVarStore) SetVar(varName string, value any) {
|
||||
func (ctx *SimpleVarStore) setVar(varName string, value any) {
|
||||
ctx.varStore[varName] = value
|
||||
}
|
||||
|
||||
func (ctx *SimpleVarStore) SetVar(varName string, value any) {
|
||||
if allowedValue, ok := fromGenericAny(value); ok {
|
||||
ctx.varStore[varName] = allowedValue
|
||||
} else {
|
||||
panic(fmt.Errorf("unsupported type %T of value %v", value, value))
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *SimpleVarStore) EnumVars(acceptor func(name string) (accept bool)) (varNames []string) {
|
||||
varNames = make([]string, 0)
|
||||
for name := range ctx.varStore {
|
||||
|
||||
+3
-4
@@ -5,7 +5,6 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -16,7 +15,7 @@ func TestString(t *testing.T) {
|
||||
|
||||
tree := NewAst()
|
||||
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil {
|
||||
fmt.Println("Tree:", tree)
|
||||
t.Log("Tree:", tree)
|
||||
} else {
|
||||
t.Errorf("err: got <%v>, want <nil>", gotErr)
|
||||
}
|
||||
@@ -27,7 +26,7 @@ func TestGetRoom(t *testing.T) {
|
||||
|
||||
tree := NewAst()
|
||||
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
||||
fmt.Println("Tree-root room:", tree.root.getRoom())
|
||||
t.Log("Tree-root room:", tree.root.getRoom())
|
||||
} else {
|
||||
t.Errorf("err: got <%v>, want <nil>", gotErr)
|
||||
}
|
||||
@@ -37,7 +36,7 @@ func TestGetChildrenCount(t *testing.T) {
|
||||
|
||||
tree := NewAst()
|
||||
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
||||
fmt.Println("Tree-root children count:", tree.root.getChildrenCount())
|
||||
t.Log("Tree-root children count:", tree.root.getChildrenCount())
|
||||
} else {
|
||||
t.Errorf("err: got <%v>, want <nil>", gotErr)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,11 @@ func (tk *Token) DevString() string {
|
||||
|
||||
func (tk *Token) String() string {
|
||||
if tk.Value != nil {
|
||||
return fmt.Sprintf("%#v", tk.Value)
|
||||
if s, ok := tk.Value.(string); ok {
|
||||
return fmt.Sprintf("%q", s)
|
||||
} else {
|
||||
return fmt.Sprintf("%v", tk.Value)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%s", tk.source)
|
||||
}
|
||||
|
||||
+25
-5
@@ -5,14 +5,34 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDevString(t *testing.T) {
|
||||
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||
tk2 := NewToken(0, 0, SymPlus, "+")
|
||||
type inputType struct {
|
||||
source string
|
||||
sym Symbol
|
||||
value any
|
||||
wantResult string
|
||||
}
|
||||
|
||||
fmt.Println("Token '100':", tk1.DevString())
|
||||
fmt.Println("Token '+':", tk2.DevString())
|
||||
inputs := []inputType{
|
||||
/* 1 */ {"100", SymInteger, 100, `[55]"100"{100}`},
|
||||
/* 2 */ {"+", SymPlus, nil, `[6]"+"{}`},
|
||||
}
|
||||
|
||||
for i, input := range inputs {
|
||||
var tk *Token
|
||||
if input.value == nil {
|
||||
tk = NewToken(0, 0, input.sym, input.source)
|
||||
} else {
|
||||
tk = NewValueToken(0, 0, input.sym, input.source, input.value)
|
||||
}
|
||||
|
||||
t.Logf("Test nr %2d: %q --> %q", i+1, input.source, input.wantResult)
|
||||
|
||||
if s := tk.DevString(); s != input.wantResult {
|
||||
t.Errorf("wrong token from symbol '+': expected %q, got %q", input.wantResult, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ func anyInteger(v any) (i int64, ok bool) {
|
||||
i = int64(intval)
|
||||
case uint16:
|
||||
i = int64(intval)
|
||||
case uint64:
|
||||
i = int64(intval)
|
||||
case uint32:
|
||||
i = int64(intval)
|
||||
case int8:
|
||||
@@ -89,6 +91,22 @@ func anyInteger(v any) (i int64, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func fromGenericAny(v any) (exprAny any, ok bool) {
|
||||
if exprAny, ok = v.(bool); ok {
|
||||
return
|
||||
}
|
||||
if exprAny, ok = v.(string); ok {
|
||||
return
|
||||
}
|
||||
if exprAny, ok = anyInteger(v); ok {
|
||||
return
|
||||
}
|
||||
if exprAny, ok = anyFloat(v); ok {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func anyFloat(v any) (float float64, ok bool) {
|
||||
ok = true
|
||||
switch floatval := v.(type) {
|
||||
|
||||
Reference in New Issue
Block a user