diff --git a/lang/ast/structs.go b/lang/ast/structs.go index 3df8e522..51892b57 100644 --- a/lang/ast/structs.go +++ b/lang/ast/structs.go @@ -8128,6 +8128,9 @@ func (obj *ExprCall) SetScope(scope *interfaces.Scope, sctx map[string]interface } else { f, exists := obj.scope.Variables[obj.Name] if !exists { + if obj.data.Debug || true { // TODO: leave this on permanently? + lambdaScopeFeedback(obj.scope, obj.data.Logf) + } return fmt.Errorf("func `%s` does not exist in this scope", prefixedName) } target = f @@ -8137,6 +8140,9 @@ func (obj *ExprCall) SetScope(scope *interfaces.Scope, sctx map[string]interface prefixedName = obj.Name f, exists := obj.scope.Functions[obj.Name] if !exists { + if obj.data.Debug || true { // TODO: leave this on permanently? + functionScopeFeedback(obj.scope, obj.data.Logf) + } return fmt.Errorf("func `%s` does not exist in this scope", prefixedName) } target = f @@ -8727,6 +8733,9 @@ func (obj *ExprVar) SetScope(scope *interfaces.Scope, sctx map[string]interfaces target, exists := obj.scope.Variables[obj.Name] if !exists { + if obj.data.Debug || true { // TODO: leave this on permanently? + variableScopeFeedback(obj.scope, obj.data.Logf) + } return fmt.Errorf("variable %s not in scope", obj.Name) } diff --git a/lang/ast/util.go b/lang/ast/util.go index 11105b64..4c4e7123 100644 --- a/lang/ast/util.go +++ b/lang/ast/util.go @@ -31,6 +31,7 @@ package ast import ( "fmt" + "sort" "strings" "sync" @@ -340,3 +341,55 @@ func trueCallee(apparentCallee interfaces.Expr) interfaces.Expr { return apparentCallee } } + +// variableScopeFeedback logs some messages about what is actually in scope so +// that the user gets a hint about what's going on. This is useful for catching +// bugs in our programming or in user code! +func variableScopeFeedback(scope *interfaces.Scope, logf func(format string, v ...interface{})) { + logf("variables in scope:") + names := []string{} + for name := range scope.Variables { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + logf("$%s", name) + } +} + +// functionScopeFeedback logs some messages about what is actually in scope so +// that the user gets a hint about what's going on. This is useful for catching +// bugs in our programming or in user code! +func functionScopeFeedback(scope *interfaces.Scope, logf func(format string, v ...interface{})) { + logf("functions in scope:") + names := []string{} + for name := range scope.Functions { + if strings.HasPrefix(name, "_") { // hidden function + continue + } + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + logf("%s(...)", name) + } +} + +// lambdaScopeFeedback logs some messages about what is actually in scope so +// that the user gets a hint about what's going on. This is useful for catching +// bugs in our programming or in user code! +func lambdaScopeFeedback(scope *interfaces.Scope, logf func(format string, v ...interface{})) { + logf("lambdas in scope:") + names := []string{} + for name, val := range scope.Variables { + // XXX: Is this a valid way to filter? + if _, ok := trueCallee(val).(*ExprFunc); !ok { + continue + } + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + logf("$%s(...)", name) + } +}