Skip to content

User Error or Several Bugs? With Optional Array Format #852

@SJrX

Description

@SJrX

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:

  1. The fact that Add len builtin #3 passes and Print less brackets for binary operators #4 fails is some kind of bug.
  2. I find it odd, that only when I pass in an empty map, does the test case pass.
  3. 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)

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions