Fix for overidden operators using interfaces with Patch() on IdentifierNode
This is for the issue I opened here : https://github.com/antonmedv/expr/issues/169
@antonmedv please let me know if you would like me to add additional tests.
Hi, thanks for the fix. I’m on vacation right now. I’ll check you solution as soon as I can.
Change look simple, but really not trivial. I need to recheck all different conditions.
Thanks and no worries. Appreciate all the good work 👍
Basically if there is a way to apply the node patch before the operator method lookup happens, that will work to solve this issue. If it's too much work to test backward compatibility, the other idea could be to introduce maybe something like a expr.PreOperator() config option which can run a node patch call before running the operator patch. If that sounds reasonable, I can push another PR with tests.
Can you create a minimal reproducible example as a failing test? I’ll try to take a look.
I've an issue open here : https://github.com/antonmedv/expr/issues/169. I just simplified the example further to illustrate what I'm running in to.
The test code in there does not run with the current HEAD but with the PR applied the code runs fine. Hopefully that helps.
Is it still actual? Kinda in the refactoring process right now. =)
Looks like it is fixed in the master.
Tested with:
func TestIssue169(t *testing.T) {
env := Iss169Env{}
options := []expr.Option{
expr.Env(&env),
expr.Patch(&env),
expr.Operator("*", "MulIss169InterfaceByFloat64"), // Override `*` with function.
}
code := `m1 * 10.0`
program, err := expr.Compile(code, options...)
require.NoError(t, err)
output, err := expr.Run(program, &env)
require.NoError(t, err)
require.Equal(t, &Iss169Impl{10.0}, output)
}
// Test Issue 169 definitions
type Iss169Interface interface {
Set(v float64)
Get() float64
}
type Iss169Impl struct {
val float64
}
func (s *Iss169Impl) Set(v float64) { s.val = v }
func (s *Iss169Impl) Get() float64 { return s.val }
type Iss169Env struct{}
func (e *Iss169Env) InitSomeImpl(_ string) Iss169Interface {
s := &Iss169Impl{1.0}
return s
}
func (e *Iss169Env) MulIss169InterfaceByFloat64(ss Iss169Interface, v float64) Iss169Interface {
s := ss.(*Iss169Impl)
s.Set(s.Get() * v)
return s
}
func (e *Iss169Env) Visit(node *ast.Node) {
switch n := (*node).(type) {
case *ast.IdentifierNode:
ast.Patch(node, &ast.CallNode{
Callee: &ast.IdentifierNode{Value: "InitSomeImpl"},
Arguments: []ast.Node{&ast.StringNode{Value: n.Value}},
})
}
}