-
-
Couldn't load subscription status.
- Fork 463
Description
So I didn't see it documented, but found things like #547 that suggest that the syntax foo?.[0] can be used to allow optional array indexing.
I guess to step back, the problem I'm trying to solve is in my use case, I actually don't care about undefined parameters, and it would be nice, if I could just have everything be treated as nil, if not defined. That could be a distinct feature request, I suppose.
To get around this, I've decided to Patch the AST, I've been using this.
type walker struct {
}
func (a *walker) Visit(node *ast.Node) {
if n, ok := (*node).(*ast.MemberNode); ok {
v := *n
v.Optional = true
ast.Patch(node, &v)
}
}
I've also tried on a few test programs, but they all error with some variation of:
cannot fetch foo from struct {} (1:1)
| foo[0]
| ^
Here is the output from my test program:
1. Optional Chained Field With Map: ` foo?.bar` ==> ` foo?.bar` => v: <nil>, err: false
2. Patched Optional Fi Chained With Map: ` foo.bar` ==> ` foo?.bar` => v: <nil>, err: false
3. Optional Chained With Map: ` foo?.[0]` ==> ` foo?.[0]` => v: <nil>, err: false
4. Patched Optional Chained With Map: ` foo[0]` ==> ` foo?.[0]` => v: <nil>, err: true
5. Optional Chained With Nil: ` foo?.[0]` ==> ` foo?.[0]` => v: <nil>, err: true
6. Patched Optional Chained With Nil: ` foo[0]` ==> ` foo?.[0]` => v: <nil>, err: true
7. Optional Chained With Struct: ` foo?.[0]` ==> ` foo?.[0]` => v: <nil>, err: true
8. Patched Optional Chained With Struct: ` foo[0]` ==> ` foo?.[0]` => v: <nil>, err: true
I believe it supports the following conclusions:
Case #2, suggests that the rewriting works correctly, as it was able to rewrite it (although maybe actually it didn't and the AllowUnedifiedVariables() is what is doing it).
Case #2,#4,#6,#8 suggest that I'm rewriting ... something, because I'm printing the output of the program .Node().String().
Case #1,#2 and #3, #4, suggest the rewritten syntax is correct (that is the patched AST has the same Node().String().
So the possible bugs I would like to suggest are:
- The fact that Add len builtin #3 passes and Print less brackets for binary operators #4 fails is some kind of bug.
- I find it odd, that only when I pass in an empty map, does the test case pass.
- Maybe AllowUndefinedVariables is suppose to just allow this to work without an AST rewriting.
Do let me know if I'm just doing something wrong.
func TestExprHandling(t *testing.T) {
w := walker{}
oProg := "foo?.bar"
optionalChainedFieldMap, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny())
v, err := expr.Run(optionalChainedFieldMap, map[string]interface{}{})
fmt.Printf("1. Optional Chained Field With Map: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedFieldMap.Node().String(), v, err != nil)
oProg = "foo.bar"
patchedToOptionalChainedFieldWithMap, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny(), expr.Patch(&w))
v, err = expr.Run(optionalChainedFieldMap, map[string]interface{}{})
fmt.Printf("2. Patched Optional Fi Chained With Map: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, patchedToOptionalChainedFieldWithMap.Node().String(), v, err != nil)
oProg = "foo?.[0]"
optionalChainedWithMap, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny())
v, err = expr.Run(optionalChainedWithMap, map[string]interface{}{})
fmt.Printf("3. Optional Chained With Map: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
oProg = "foo[0]"
patchedToOptionalChainWithMap, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny(), expr.Patch(&w))
v, err = expr.Run(patchedToOptionalChainWithMap, map[string]interface{}{})
fmt.Printf("4. Patched Optional Chained With Map: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
oProg = "foo?.[0]"
optionalChainedWithNil, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny())
v, err = expr.Run(optionalChainedWithNil, nil)
fmt.Printf("5. Optional Chained With Nil: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
oProg = "foo[0]"
patchedToOptionalChainWithNil, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny(), expr.Patch(&w))
v, err = expr.Run(patchedToOptionalChainWithNil, nil)
fmt.Printf("6. Patched Optional Chained With Nil: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
oProg = "foo?.[0]"
optionalChainedWithStruct, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny())
v, err = expr.Run(optionalChainedWithStruct, struct{}{})
fmt.Printf("7. Optional Chained With Struct: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
oProg = "foo[0]"
patchedToOptionalChainWithStruct, err := expr.Compile(oProg, expr.AllowUndefinedVariables(), expr.AsAny(), expr.Patch(&w))
v, err = expr.Run(patchedToOptionalChainWithStruct, struct{}{})
fmt.Printf("8. Patched Optional Chained With Struct: `%10v` ==> `%10v` => v: %v, err: %v\n", oProg, optionalChainedWithMap.Node().String(), v, err != nil)
//fmt.Println(err)
//fmt.Printf("\nP1 Source: %s ==> %s \nP2 Source: %s ==> %s\n", optionalChainedWithMap.Node().String(), ocwmOutput, patchedToOptionalChainWithMap.Node().String(), pocwmOutput)
}