code: func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) {
  	var result []InterpolatedVariable
  	var resultErr error
  
  	// Visitor callback
  	fn := func(n ast.Node) ast.Node {
  		if resultErr != nil {
  			return n
  		}
  
  		switch vn := n.(type) {
  		case *ast.VariableAccess:
  			v, err := NewInterpolatedVariable(vn.Name)
  			if err != nil {
  				resultErr = err
  				return n
  			}
  			result = append(result, v)
  		case *ast.Index:
  			if va, ok := vn.Target.(*ast.VariableAccess); ok {
  				v, err := NewInterpolatedVariable(va.Name)
  				if err != nil {
  					resultErr = err
  					return n
  				}
  				result = append(result, v)
  			}
  			if va, ok := vn.Key.(*ast.VariableAccess); ok {
  				v, err := NewInterpolatedVariable(va.Name)
  				if err != nil {
  					resultErr = err
  					return n
  				}
  				result = append(result, v)
  			}
  		default:
  			return n
  		}
  
  		return n
  	}
  
  	// Visitor pattern
  	root.Accept(fn)
  
  	if resultErr != nil {
  		return nil, resultErr
  	}
  
  	return result, nil
  }
docs: // DetectVariables takes an AST root and returns all the interpolated
  // variables that are detected in the AST tree.