Compare commits

...

4 Commits

10 changed files with 119 additions and 45 deletions

View File

@ -63,35 +63,37 @@ package main
import ( import (
"fmt" "fmt"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr" "git.portale-stac.it/go-pkg/expr"
) )
func main() { func main() {
ctx := expr.NewSimpleVarStore() ctx := expr.NewSimpleVarStore()
ctx.SetValue("var", int64(4)) ctx.SetVar("var", 4)
source := `(3-1)*(10/5) == var` source := `(3-1)*(10/5) == var`
r := strings.NewReader(source) r := strings.NewReader(source)
scanner := expr.NewScanner(r, DefaultTranslations()) scanner := expr.NewScanner(r, expr.DefaultTranslations())
parser := expr.NewParser(ctx) parser := expr.NewParser(ctx)
if ast, err := parser.Parse(scanner); err == nil { if ast, err := parser.Parse(scanner); err == nil {
if result, err := ast.Eval(ctx); err == nil { if result, err := ast.Eval(ctx); err == nil {
fmt.Printf("%q -> %v [%T]\n", source, result, result) fmt.Printf("%q -> %v [%T]\n", source, result, result)
} else { } else {
fmt.Println("Error calculating the expression:", err) fmt.Println("Error calculating the expression:", err)
} }
} else { } else {
fmt.Println("Error parsing the expression:", err) fmt.Println("Error parsing the expression:", err)
} }
} }
---- ----
The above program is equivalent to the following one. The above program is equivalent to the following one.
[source,go] [source,go]
---- ----
package main
import ( import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr" "git.portale-stac.it/go-pkg/expr"
@ -99,15 +101,37 @@ import (
func main() { func main() {
ctx := expr.NewSimpleVarStore() ctx := expr.NewSimpleVarStore()
ctx.SetValue("var", int64(4)) ctx.SetVar("var", 4)
source := `(3-1)*(10/5) == var` source := `(3-1)*(10/5) == var`
if result, err := expr.EvalString(ctx, source); err == nil { if result, err := expr.EvalString(ctx, source); err == nil {
fmt.Printf("%q -> %v [%T]\n", source, result, result) fmt.Printf("%q -> %v [%T]\n", source, result, result)
} else { } else {
fmt.Println("Error calculating the expression:", err) 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)
}
} }
---- ----
@ -212,7 +236,28 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
==== ====
==== List ==== 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 === Variables
#TODO: variables# #TODO: variables#

2
ast.go
View File

@ -118,7 +118,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
if self.forest != nil { if self.forest != nil {
for _, root := range self.forest { for _, root := range self.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
ctx.SetVar(control_last_result, result) ctx.setVar(control_last_result, result)
} else { } else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err) //err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break break

View File

@ -33,6 +33,7 @@ type ExprContext interface {
Clone() ExprContext Clone() ExprContext
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
SetVar(varName string, value any) SetVar(varName string, value any)
setVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string) EnumVars(func(name string) (accept bool)) (varNames []string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string) EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) ExprFunc GetFuncInfo(name string) ExprFunc

View File

@ -21,23 +21,23 @@ const (
) )
func initDefaultVars(ctx ExprContext) { func initDefaultVars(ctx ExprContext) {
ctx.SetVar(control_bool_shortcut, true) ctx.setVar(control_bool_shortcut, true)
ctx.SetVar(control_import_path, init_import_path) ctx.setVar(control_import_path, init_import_path)
} }
func enable(ctx ExprContext, name string) { func enable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") { if strings.HasPrefix(name, "_") {
ctx.SetVar(name, true) ctx.setVar(name, true)
} else { } else {
ctx.SetVar("_"+name, true) ctx.setVar("_"+name, true)
} }
} }
func disable(ctx ExprContext, name string) { func disable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") { if strings.HasPrefix(name, "_") {
ctx.SetVar(name, false) ctx.setVar(name, false)
} else { } else {
ctx.SetVar("_"+name, false) ctx.setVar("_"+name, false)
} }
} }

View File

@ -20,8 +20,8 @@ func EvalString(ctx ExprContext, source string) (result any, err error) {
} }
type Arg struct { type Arg struct {
name string Name string
value any Value any
} }
func EvalStringA(source string, args ...Arg) (result any, err error) { func EvalStringA(source string, args ...Arg) (result any, err error) {
@ -31,23 +31,23 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
func EvalStringV(source string, args []Arg) (result any, err error) { func EvalStringV(source string, args []Arg) (result any, err error) {
ctx := NewSimpleFuncStore() ctx := NewSimpleFuncStore()
for _, arg := range args { for _, arg := range args {
if isFunc(arg.value) { if isFunc(arg.Value) {
if f, ok := arg.value.(FuncTemplate); ok { if f, ok := arg.Value.(FuncTemplate); ok {
functor := &simpleFunctor{f: f} functor := &simpleFunctor{f: f}
ctx.RegisterFunc(arg.name, functor, 0, -1) ctx.RegisterFunc(arg.Name, functor, 0, -1)
} else { } 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 { } else if integer, ok := anyInteger(arg.Value); ok {
ctx.SetVar(arg.name, integer) ctx.SetVar(arg.Name, integer)
} else if float, ok := anyFloat(arg.value); ok { } else if float, ok := anyFloat(arg.Value); ok {
ctx.SetVar(arg.name, float) ctx.SetVar(arg.Name, float)
} else if _, ok := arg.value.(string); ok { } else if _, ok := arg.Value.(string); ok {
ctx.SetVar(arg.name, arg.value) ctx.SetVar(arg.Name, arg.Value)
} else if _, ok := arg.value.(bool); ok { } else if _, ok := arg.Value.(bool); ok {
ctx.SetVar(arg.name, arg.value) ctx.SetVar(arg.Name, arg.Value)
} else { } 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)
} }
} }

View File

@ -57,7 +57,7 @@ func exportVar(ctx ExprContext, name string, value any) {
if name[0] == '@' { if name[0] == '@' {
name = name[1:] name = name[1:]
} }
ctx.SetVar(name, value) ctx.setVar(name, value)
} }
func exportFunc(ctx ExprContext, name string, info ExprFunc) { 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) { func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
for i, p := range functor.params { for i, p := range functor.params {
if i < len(args) { if i < len(args) {
ctx.SetVar(p, args[i]) ctx.setVar(p, args[i])
} else { } else {
ctx.SetVar(p, nil) ctx.setVar(p, nil)
} }
} }
result, err = functor.expr.eval(ctx, false) result, err = functor.expr.eval(ctx, false)

View File

@ -31,7 +31,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
if functor, ok := v.(Functor); ok { if functor, ok := v.(Functor); ok {
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
} else { } else {
ctx.SetVar(leftTerm.tk.source, v) ctx.setVar(leftTerm.tk.source, v)
} }
} }
return return

View File

@ -75,7 +75,7 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
err = errCoalesceNoFunc(self.children[1]) err = errCoalesceNoFunc(self.children[1])
} else { } else {
v = rightValue v = rightValue
ctx.SetVar(leftTerm.source(), rightValue) ctx.setVar(leftTerm.source(), rightValue)
} }
} }
return return

View File

@ -1,6 +1,8 @@
// simple-var-store.go // simple-var-store.go
package expr package expr
import "fmt"
type SimpleVarStore struct { type SimpleVarStore struct {
varStore map[string]any varStore map[string]any
} }
@ -23,10 +25,18 @@ func (ctx *SimpleVarStore) GetVar(varName string) (v any, exists bool) {
return return
} }
func (ctx *SimpleVarStore) SetVar(varName string, value any) { func (ctx *SimpleVarStore) setVar(varName string, value any) {
ctx.varStore[varName] = value 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) { func (ctx *SimpleVarStore) EnumVars(acceptor func(name string) (accept bool)) (varNames []string) {
varNames = make([]string, 0) varNames = make([]string, 0)
for name := range ctx.varStore { for name := range ctx.varStore {

View File

@ -73,6 +73,8 @@ func anyInteger(v any) (i int64, ok bool) {
i = int64(intval) i = int64(intval)
case uint16: case uint16:
i = int64(intval) i = int64(intval)
case uint64:
i = int64(intval)
case uint32: case uint32:
i = int64(intval) i = int64(intval)
case int8: case int8:
@ -89,6 +91,22 @@ func anyInteger(v any) (i int64, ok bool) {
return 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) { func anyFloat(v any) (float float64, ok bool) {
ok = true ok = true
switch floatval := v.(type) { switch floatval := v.(type) {