mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Add option to allow using undefined variables
This commit is contained in:
@@ -28,6 +28,7 @@ func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
|
||||
v.types = config.Types
|
||||
v.operators = config.Operators
|
||||
v.expect = config.Expect
|
||||
v.undefVars = config.AllowUndefinedVariables
|
||||
}
|
||||
|
||||
t = v.visit(tree.Node)
|
||||
@@ -55,6 +56,7 @@ type visitor struct {
|
||||
operators conf.OperatorsTable
|
||||
expect reflect.Kind
|
||||
collections []reflect.Type
|
||||
undefVars bool
|
||||
}
|
||||
|
||||
func (v *visitor) visit(node ast.Node) reflect.Type {
|
||||
@@ -127,6 +129,9 @@ func (v *visitor) IdentifierNode(node *ast.IdentifierNode) reflect.Type {
|
||||
if t, ok := v.types[node.Value]; ok {
|
||||
return t.Type
|
||||
}
|
||||
if v.undefVars {
|
||||
return interfaceType
|
||||
}
|
||||
panic(v.error(node, "unknown name %v", node.Value))
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,22 @@ func Env(i interface{}) Option {
|
||||
if _, ok := i.(map[string]interface{}); ok {
|
||||
c.MapEnv = true
|
||||
}
|
||||
c.CheckTypes = true
|
||||
c.Types = conf.CreateTypesTable(i)
|
||||
}
|
||||
}
|
||||
|
||||
// AllowUndefinedVariables allows to use undefined variables inside expressions.
|
||||
// This can be used with expr.Env option to partially define a few variables.
|
||||
// Note what this option is only works in map environment are used, otherwise
|
||||
// runtime.fetch will panic as there is no way to get missing field zero value.
|
||||
func AllowUndefinedVariables() Option {
|
||||
return func(c *conf.Config) {
|
||||
c.CheckTypes = true
|
||||
c.AllowUndefinedVariables = true
|
||||
}
|
||||
}
|
||||
|
||||
// Operator allows to override binary operator with function.
|
||||
func Operator(operator string, fn ...string) Option {
|
||||
return func(c *conf.Config) {
|
||||
@@ -108,7 +120,7 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.Types != nil {
|
||||
if config.CheckTypes {
|
||||
_, err = checker.Check(tree, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+27
-38
@@ -1,14 +1,12 @@
|
||||
package expr_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/antonmedv/expr"
|
||||
"github.com/antonmedv/expr/vm"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -209,6 +207,33 @@ func ExampleEnv_with_undefined_variables() {
|
||||
// Output: 5
|
||||
}
|
||||
|
||||
func ExampleEnv_allow_undefined_variables() {
|
||||
env := map[string]string{
|
||||
"greet": "",
|
||||
}
|
||||
|
||||
program, err := expr.Compile(`greet + name`, expr.Env(env), expr.AllowUndefinedVariables())
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"greet": "hello, ",
|
||||
"name": "world",
|
||||
}
|
||||
|
||||
output, err := expr.Run(program, params)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%v", output)
|
||||
|
||||
// Output: hello, world
|
||||
}
|
||||
|
||||
func ExampleAsBool() {
|
||||
env := map[string]int{
|
||||
"foo": 0,
|
||||
@@ -359,42 +384,6 @@ func ExampleOperator_time() {
|
||||
// Output: true
|
||||
}
|
||||
|
||||
func ExampleEval_marshal() {
|
||||
env := map[string]int{
|
||||
"foo": 1,
|
||||
"bar": 2,
|
||||
}
|
||||
|
||||
program, err := expr.Compile("(foo + bar) in [1, 2, 3]", expr.Env(env))
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(program)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
unmarshaledProgram := &vm.Program{}
|
||||
err = json.Unmarshal(b, unmarshaledProgram)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
output, err := expr.Run(unmarshaledProgram, env)
|
||||
if err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%v", output)
|
||||
|
||||
// Output: true
|
||||
}
|
||||
|
||||
func TestOperator_struct(t *testing.T) {
|
||||
env := &mockEnv{
|
||||
BirthDay: time.Date(2017, time.October, 23, 18, 30, 0, 0, time.UTC),
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
MapEnv bool
|
||||
Types TypesTable
|
||||
Operators OperatorsTable
|
||||
Expect reflect.Kind
|
||||
Optimize bool
|
||||
MapEnv bool
|
||||
Types TypesTable
|
||||
CheckTypes bool
|
||||
Operators OperatorsTable
|
||||
Expect reflect.Kind
|
||||
Optimize bool
|
||||
AllowUndefinedVariables bool
|
||||
}
|
||||
|
||||
func New(i interface{}) *Config {
|
||||
|
||||
Reference in New Issue
Block a user