@@ -10,6 +10,7 @@ import (
1010
1111 "github.com/expr-lang/expr/file"
1212 "github.com/expr-lang/expr/internal/testify/require"
13+ "github.com/expr-lang/expr/vm/runtime"
1314
1415 "github.com/expr-lang/expr"
1516 "github.com/expr-lang/expr/checker"
@@ -267,6 +268,110 @@ func TestRun_InnerMethodWithError_NilSafe(t *testing.T) {
267268 require .Equal (t , nil , out )
268269}
269270
271+ var _ runtime.Proxy = (* proxyNode )(nil )
272+
273+ type proxyNode struct {
274+ parent * proxyNode
275+ values map [any ]any
276+ }
277+
278+ func (n * proxyNode ) GetProperty (key any ) (any , bool ) {
279+ if value , ok := n .values [key ]; ok {
280+ return value , true
281+ }
282+ if n .parent != nil {
283+ return n .parent .GetProperty (key )
284+ }
285+ return nil , false
286+ }
287+
288+ func (n * proxyNode ) SetProperty (key any , value any ) {
289+ n .values [key ] = value
290+ }
291+
292+ func TestRun_Proxy_Read (t * testing.T ) {
293+ cases := []struct {
294+ label string
295+ expr string
296+ env any
297+ expect any
298+ }{
299+ {
300+ label : "proxy env map member" ,
301+ expr : `foo.bar` ,
302+ env : map [string ]any {
303+ "foo" : & proxyNode {
304+ values : map [any ]any {
305+ "bar" : "baz" ,
306+ },
307+ },
308+ },
309+ expect : "baz" ,
310+ },
311+ {
312+ label : "read from root" ,
313+ expr : `value` ,
314+ env : & proxyNode {
315+ values : map [any ]any {
316+ "value" : "hello world" ,
317+ },
318+ },
319+ expect : "hello world" ,
320+ },
321+ {
322+ label : "read from child" ,
323+ expr : `child.value` ,
324+ env : & proxyNode {
325+ values : map [any ]any {
326+ "child" : & proxyNode {
327+ values : map [any ]any {
328+ "value" : "hello world" ,
329+ },
330+ },
331+ },
332+ },
333+ expect : "hello world" ,
334+ },
335+ {
336+ label : "read from grandchild" ,
337+ expr : `grandchild.child.value` ,
338+ env : & proxyNode {
339+ values : map [any ]any {
340+ "grandchild" : & proxyNode {
341+ values : map [any ]any {
342+ "child" : & proxyNode {
343+ values : map [any ]any {
344+ "value" : "hello world" ,
345+ },
346+ },
347+ },
348+ },
349+ },
350+ },
351+ expect : "hello world" ,
352+ },
353+ }
354+
355+ for _ , c := range cases {
356+ t .Run (c .label , func (t * testing.T ) {
357+ tree , err := parser .Parse (c .expr )
358+ require .NoError (t , err )
359+
360+ funcConf := conf .CreateNew ()
361+ _ , err = checker .Check (tree , funcConf )
362+ require .NoError (t , err )
363+
364+ program , err := compiler .Compile (tree , funcConf )
365+ require .NoError (t , err )
366+
367+ out , err := vm .Run (program , c .env )
368+ require .NoError (t , err )
369+
370+ require .Equal (t , c .expect , out )
371+ })
372+ }
373+ }
374+
270375func TestRun_TaggedFieldName (t * testing.T ) {
271376 input := `value`
272377
0 commit comments