From a93c98402a431917d1f908f7d0e3b9d3bf1cb719 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Wed, 7 Aug 2024 17:17:15 -0400 Subject: [PATCH] lang: ast: Add better logging about scope issues This may help out programmers who aren't sure what's going on. --- lang/ast/structs.go | 9 ++++++++ lang/ast/util.go | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) 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) + } +}