Closed
Description
I'm adding support for "github.com/shopspring/decimal" to my project that makes use of "expr/patcher/value" and ran into an issue where cpu usage spikes to 100% and expr gets stuck compiling the expression when I try to introduce operator support.
From the trace it seems like it is getting stuck in the checker for some reason.
Here is an example program that can recreate the problem.
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/patcher/value"
"github.com/expr-lang/expr/vm"
"github.com/shopspring/decimal"
)
type myInt struct {
Int int
}
func (v *myInt) AsInt() int {
return v.Int
}
func (v *myInt) AsAny() any {
return v.Int
}
func toDecimal(val any) (decimal.Decimal, error) {
switch v := val.(type) {
case int:
return decimal.NewFromInt(int64(v)), nil
case int32:
return decimal.NewFromInt32(v), nil
case int64:
return decimal.NewFromInt(v), nil
case float32:
return decimal.NewFromFloat32(v), nil
case float64:
return decimal.NewFromFloat(v), nil
case string:
d, err := decimal.NewFromString(v)
if err != nil {
return decimal.Decimal{}, err
}
return d, nil
case decimal.Decimal:
return v, nil
default:
return decimal.Decimal{}, fmt.Errorf("Unhandled type for rounding (type: %T)", v)
}
}
func ExampleAnyValuer() {
env := make(map[string]any)
//using a plain int here also works fine
//env["ValueOne"] = 1
//But value types are broken
env["ValueOne"] = &myInt{1}
env["ValueTwo"] = &myInt{2}
fmt.Println("Compiling")
//this is fine
//program, err := expr.Compile("decimal(1) * decimal(2)",
//these are broken
//program, err := expr.Compile("decimal(ValueOne) * decimal(ValueTwo)",
program, err := expr.Compile("decimal(ValueOne) * decimal(2)",
expr.Env(env),
value.ValueGetter,
expr.Function(
"decimal",
func(params ...any) (any, error) {
return toDecimal(params[0])
},
new(func(int) decimal.Decimal),
new(func(int32) decimal.Decimal),
new(func(int64) decimal.Decimal),
new(func(float32) decimal.Decimal),
new(func(float64) decimal.Decimal),
new(func(string) decimal.Decimal),
),
expr.Function(
"_decimalMul",
func(params ...any) (any, error) {
return params[0].(decimal.Decimal).Mul(params[1].(decimal.Decimal)), nil
},
new(func(decimal.Decimal, decimal.Decimal) decimal.Decimal),
),
expr.Operator("*", "_decimalMul"),
)
if err != nil {
panic(err)
}
fmt.Println("Running")
out, err := vm.Run(program, env)
if err != nil {
panic(err)
}
fmt.Println(out)
}
func main() {
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
ExampleAnyValuer()
}
If there is any additional information that would help just let me know.