expr icon indicating copy to clipboard operation
expr copied to clipboard

Fix for overidden operators using interfaces with Patch() on IdentifierNode

Open rajvikram opened this issue 4 years ago • 6 comments

This is for the issue I opened here : https://github.com/antonmedv/expr/issues/169

rajvikram avatar Mar 04 '21 07:03 rajvikram

@antonmedv please let me know if you would like me to add additional tests.

rajvikram avatar Mar 04 '21 20:03 rajvikram

Hi, thanks for the fix. I’m on vacation right now. I’ll check you solution as soon as I can.

antonmedv avatar Mar 06 '21 15:03 antonmedv

Change look simple, but really not trivial. I need to recheck all different conditions.

antonmedv avatar Mar 06 '21 15:03 antonmedv

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.

rajvikram avatar Mar 08 '21 06:03 rajvikram

Can you create a minimal reproducible example as a failing test? I’ll try to take a look.

antonmedv avatar Mar 08 '21 07:03 antonmedv

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.

rajvikram avatar Mar 08 '21 18:03 rajvikram

Is it still actual? Kinda in the refactoring process right now. =)

antonmedv avatar Oct 16 '22 20:10 antonmedv

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}},
		})
	}
}

antonmedv avatar Nov 05 '22 18:11 antonmedv