Skip to content

Commit 9c67ada

Browse files
committed
Move code duplicate to conf package
1 parent 4fc6235 commit 9c67ada

File tree

4 files changed

+62
-90
lines changed

4 files changed

+62
-90
lines changed

conf/config.go

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,29 @@ import (
99
)
1010

1111
type Config struct {
12-
Env interface{}
13-
MapEnv bool
14-
Types TypesTable
15-
Operators OperatorsTable
16-
Expect reflect.Kind
17-
Optimize bool
18-
Strict bool
19-
DefaultType reflect.Type
20-
ConstExprFns map[string]reflect.Value
21-
Visitors []ast.Visitor
22-
err error
12+
Env interface{}
13+
Types TypesTable
14+
MapEnv bool
15+
DefaultType reflect.Type
16+
Operators OperatorsTable
17+
Expect reflect.Kind
18+
Optimize bool
19+
Strict bool
20+
ConstFns map[string]reflect.Value
21+
Visitors []ast.Visitor
2322
}
2423

2524
func New(env interface{}) *Config {
25+
c := &Config{
26+
Operators: make(map[string][]string),
27+
ConstFns: make(map[string]reflect.Value),
28+
Optimize: true,
29+
}
30+
c.WithEnv(env)
31+
return c
32+
}
33+
34+
func (c *Config) WithEnv(env interface{}) {
2635
var mapEnv bool
2736
var mapValueType reflect.Type
2837
if _, ok := env.(map[string]interface{}); ok {
@@ -33,58 +42,37 @@ func New(env interface{}) *Config {
3342
}
3443
}
3544

36-
return &Config{
37-
Env: env,
38-
MapEnv: mapEnv,
39-
Types: CreateTypesTable(env),
40-
Operators: make(map[string][]string),
41-
Optimize: true,
42-
Strict: true,
43-
DefaultType: mapValueType,
44-
ConstExprFns: make(map[string]reflect.Value),
45-
}
45+
c.Env = env
46+
c.Types = CreateTypesTable(env)
47+
c.MapEnv = mapEnv
48+
c.DefaultType = mapValueType
49+
c.Strict = true
4650
}
4751

48-
// Check validates the compiler configuration.
49-
func (c *Config) Check() error {
50-
// Check that all functions that define operator overloading
51-
// exist in environment and have correct signatures.
52-
for op, fns := range c.Operators {
53-
for _, fn := range fns {
54-
fnType, ok := c.Types[fn]
55-
if !ok || fnType.Type.Kind() != reflect.Func {
56-
return fmt.Errorf("function %s for %s operator does not exist in environment", fn, op)
57-
}
58-
requiredNumIn := 2
59-
if fnType.Method {
60-
requiredNumIn = 3 // As first argument of method is receiver.
61-
}
62-
if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 {
63-
return fmt.Errorf("function %s for %s operator does not have a correct signature", fn, op)
64-
}
52+
func (c *Config) Operator(operator string, fns ...string) {
53+
c.Operators[operator] = append(c.Operators[operator], fns...)
54+
for _, fn := range fns {
55+
fnType, ok := c.Types[fn]
56+
if !ok || fnType.Type.Kind() != reflect.Func {
57+
panic(fmt.Errorf("function %s for %s operator does not exist in the environment", fn, operator))
6558
}
66-
}
67-
68-
// Check that all ConstExprFns are functions.
69-
for name, fn := range c.ConstExprFns {
70-
if fn.Kind() != reflect.Func {
71-
return fmt.Errorf("const expression %q must be a function", name)
59+
requiredNumIn := 2
60+
if fnType.Method {
61+
requiredNumIn = 3 // As first argument of method is receiver.
62+
}
63+
if fnType.Type.NumIn() != requiredNumIn || fnType.Type.NumOut() != 1 {
64+
panic(fmt.Errorf("function %s for %s operator does not have a correct signature", fn, operator))
7265
}
7366
}
74-
75-
return c.err
7667
}
7768

7869
func (c *Config) ConstExpr(name string) {
7970
if c.Env == nil {
80-
c.Error(fmt.Errorf("no environment for const expression: %v", name))
81-
return
71+
panic("no environment is specified for ConstExpr()")
8272
}
83-
c.ConstExprFns[name] = reflect.ValueOf(runtime.Fetch(c.Env, name))
84-
}
85-
86-
func (c *Config) Error(err error) {
87-
if c.err == nil {
88-
c.err = err
73+
fn := reflect.ValueOf(runtime.Fetch(c.Env, name))
74+
if fn.Kind() != reflect.Func {
75+
panic(fmt.Errorf("const expression %q must be a function", name))
8976
}
77+
c.ConstFns[name] = fn
9078
}

expr.go

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,12 @@ func Eval(input string, env interface{}) (interface{}, error) {
4848
// Methods defined on this type will be available as functions.
4949
func Env(env interface{}) Option {
5050
return func(c *conf.Config) {
51-
if _, ok := env.(map[string]interface{}); ok {
52-
c.MapEnv = true
53-
} else {
54-
if reflect.ValueOf(env).Kind() == reflect.Map {
55-
c.DefaultType = reflect.TypeOf(env).Elem()
56-
}
57-
}
58-
c.Strict = true
59-
c.Types = conf.CreateTypesTable(env)
60-
c.Env = env
51+
c.WithEnv(env)
6152
}
6253
}
6354

6455
// AllowUndefinedVariables allows to use undefined variables inside expressions.
6556
// This can be used with expr.Env option to partially define a few variables.
66-
// Note what this option is only works in map environment are used, otherwise
67-
// runtime.fetch will panic as there is no way to get missing field zero value.
6857
func AllowUndefinedVariables() Option {
6958
return func(c *conf.Config) {
7059
c.Strict = false
@@ -74,7 +63,7 @@ func AllowUndefinedVariables() Option {
7463
// Operator allows to replace a binary operator with a function.
7564
func Operator(operator string, fn ...string) Option {
7665
return func(c *conf.Config) {
77-
c.Operators[operator] = append(c.Operators[operator], fn...)
66+
c.Operator(operator, fn...)
7867
}
7968
}
8069

@@ -124,9 +113,9 @@ func Patch(visitor ast.Visitor) Option {
124113
// Compile parses and compiles given input expression to bytecode program.
125114
func Compile(input string, ops ...Option) (*vm.Program, error) {
126115
config := &conf.Config{
127-
Operators: make(map[string][]string),
128-
ConstExprFns: make(map[string]reflect.Value),
129-
Optimize: true,
116+
Operators: make(map[string][]string),
117+
ConstFns: make(map[string]reflect.Value),
118+
Optimize: true,
130119
}
131120

132121
for _, op := range ops {
@@ -140,10 +129,6 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
140129
})
141130
}
142131

143-
if err := config.Check(); err != nil {
144-
return nil, err
145-
}
146-
147132
tree, err := parser.Parse(input)
148133
if err != nil {
149134
return nil, err

expr_test.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,23 +1180,22 @@ func TestConstExpr_error_wrong_type(t *testing.T) {
11801180
env := map[string]interface{}{
11811181
"divide": 0,
11821182
}
1183-
1184-
_, err := expr.Compile(
1185-
`1 + divide(1, 0)`,
1186-
expr.Env(env),
1187-
expr.ConstExpr("divide"),
1188-
)
1189-
require.Error(t, err)
1190-
require.Equal(t, "const expression \"divide\" must be a function", err.Error())
1183+
assert.Panics(t, func() {
1184+
_, _ = expr.Compile(
1185+
`1 + divide(1, 0)`,
1186+
expr.Env(env),
1187+
expr.ConstExpr("divide"),
1188+
)
1189+
})
11911190
}
11921191

11931192
func TestConstExpr_error_no_env(t *testing.T) {
1194-
_, err := expr.Compile(
1195-
`1 + divide(1, 0)`,
1196-
expr.ConstExpr("divide"),
1197-
)
1198-
require.Error(t, err)
1199-
require.Equal(t, "no environment for const expression: divide", err.Error())
1193+
assert.Panics(t, func() {
1194+
_, _ = expr.Compile(
1195+
`1 + divide(1, 0)`,
1196+
expr.ConstExpr("divide"),
1197+
)
1198+
})
12001199
}
12011200

12021201
var stringer = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()

optimizer/optimizer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ func Optimize(node *Node, config *conf.Config) error {
1717
break
1818
}
1919
}
20-
if config != nil && len(config.ConstExprFns) > 0 {
20+
if config != nil && len(config.ConstFns) > 0 {
2121
for limit := 100; limit >= 0; limit-- {
2222
constExpr := &constExpr{
23-
fns: config.ConstExprFns,
23+
fns: config.ConstFns,
2424
}
2525
Walk(node, constExpr)
2626
if constExpr.err != nil {

0 commit comments

Comments
 (0)