Skip to content

Commit

Permalink
Add option to allow using undefined variables
Browse files Browse the repository at this point in the history
  • Loading branch information
antonmedv committed Nov 7, 2019
1 parent 1042ff4 commit c9f3ed9
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 44 deletions.
5 changes: 5 additions & 0 deletions checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}

Expand Down
14 changes: 13 additions & 1 deletion expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
65 changes: 27 additions & 38 deletions expr_test.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
12 changes: 7 additions & 5 deletions internal/conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit c9f3ed9

Please sign in to comment.