Skip to content

Commit 645ec59

Browse files
committed
feat: implement name resolution for Lox AST
- Add Resolver struct to perform static name resolution - Implement recursive resolution for different AST node types - Support scope tracking, variable declaration, and local variable resolution - Add error handling for variable redeclaration and self-referencing initializers
1 parent 6e24e7d commit 645ec59

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

internal/lox/resolver.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package lox
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
// Locals is the output of the resolve phase
8+
type Locals = map[Expr]int
9+
10+
// Scope represents a Lox scope
11+
type Scope = map[string]bool
12+
13+
// Resolve performs name resolution to the given statements
14+
func Resolve(statements []Stmt) (Locals, error) {
15+
locals := make(Locals)
16+
resolver := &Resolver{scopes: make([]Scope, 0)}
17+
err := resolver.resolveStatements(statements, locals)
18+
return locals, err
19+
}
20+
21+
// Resolver performs variable resolution on an AST
22+
type Resolver struct {
23+
scopes []Scope
24+
}
25+
26+
func (r *Resolver) resolve(node Node, locals Locals) error {
27+
switch n := node.(type) {
28+
case *Block:
29+
r.pushScope()
30+
defer r.popScope()
31+
for _, stmt := range n.Statements {
32+
if err := r.resolve(stmt, locals); err != nil {
33+
return err
34+
}
35+
}
36+
case *Var:
37+
if err := r.declare(n.Name); err != nil {
38+
return nil
39+
}
40+
if n.Initializer != nil {
41+
if err := r.resolve(n.Initializer, locals); err != nil {
42+
return err
43+
}
44+
}
45+
r.define(n.Name)
46+
case *Variable:
47+
if len(r.scopes) != 0 {
48+
if b, ok := r.scopes[len(r.scopes)-1][n.Name.Lexeme]; ok && !b {
49+
return MakeSemanticError("Cannot read local variable in its own initializer.")
50+
}
51+
}
52+
r.resolveLocal(n, n.Name, locals)
53+
case *Assign:
54+
if err := r.resolve(n.Value, locals); err != nil {
55+
return err
56+
}
57+
r.resolveLocal(n, n.Name, locals)
58+
case *Function:
59+
if err := r.declare(n.Name); err != nil {
60+
return err
61+
}
62+
r.define(n.Name)
63+
if err := r.resolveFunction(n, locals); err != nil {
64+
return err
65+
}
66+
case *Expression:
67+
if err := r.resolve(n.Expression, locals); err != nil {
68+
return err
69+
}
70+
case *If:
71+
if err := r.resolve(n.Condition, locals); err != nil {
72+
return err
73+
}
74+
if err := r.resolve(n.ThenBranch, locals); err != nil {
75+
return err
76+
}
77+
if n.ElseBranch != nil {
78+
if err := r.resolve(n.ElseBranch, locals); err != nil {
79+
return err
80+
}
81+
}
82+
case *Print:
83+
if err := r.resolve(n.Expression, locals); err != nil {
84+
return err
85+
}
86+
case *Return:
87+
if n.Value != nil {
88+
if err := r.resolve(n.Value, locals); err != nil {
89+
return err
90+
}
91+
}
92+
case *While:
93+
if err := r.resolve(n.Condition, locals); err != nil {
94+
return err
95+
}
96+
if err := r.resolve(n.Statement, locals); err != nil {
97+
return err
98+
}
99+
case *Binary:
100+
if err := r.resolve(n.Left, locals); err != nil {
101+
return err
102+
}
103+
if err := r.resolve(n.Right, locals); err != nil {
104+
return err
105+
}
106+
case *Call:
107+
if err := r.resolve(n.Callee, locals); err != nil {
108+
return err
109+
}
110+
111+
for _, e := range n.Arguments {
112+
if err := r.resolve(e, locals); err != nil {
113+
return err
114+
}
115+
}
116+
case *Grouping:
117+
if err := r.resolve(n.Expression, locals); err != nil {
118+
return err
119+
}
120+
case *Logical:
121+
if err := r.resolve(n.Left, locals); err != nil {
122+
return err
123+
}
124+
if err := r.resolve(n.Right, locals); err != nil {
125+
return err
126+
}
127+
case *Unary:
128+
if err := r.resolve(n.Right, locals); err != nil {
129+
return err
130+
}
131+
}
132+
return nil
133+
}
134+
135+
func (r *Resolver) resolveStatements(statements []Stmt, locals Locals) error {
136+
for _, stmt := range statements {
137+
if err := r.resolve(stmt, locals); err != nil {
138+
return err
139+
}
140+
}
141+
return nil
142+
}
143+
144+
func (r *Resolver) resolveFunction(function *Function, locals Locals) error {
145+
r.pushScope()
146+
defer r.popScope()
147+
148+
for _, param := range function.Params {
149+
if err := r.declare(param); err != nil {
150+
return err
151+
}
152+
r.define(param)
153+
}
154+
return r.resolveStatements(function.Body, locals)
155+
}
156+
157+
func (r *Resolver) resolveLocal(expr Expr, name Token, locals Locals) {
158+
for i := len(r.scopes) - 1; i >= 0; i-- {
159+
if _, ok := r.scopes[i][name.Lexeme]; ok {
160+
locals[expr] = len(r.scopes) - i - 1
161+
return
162+
}
163+
}
164+
}
165+
166+
func (r *Resolver) pushScope() {
167+
r.scopes = append(r.scopes, make(Scope))
168+
}
169+
170+
func (r *Resolver) popScope() {
171+
r.scopes = r.scopes[:len(r.scopes)-1]
172+
}
173+
174+
func (r *Resolver) declare(name Token) error {
175+
if len(r.scopes) != 0 {
176+
scope := r.scopes[len(r.scopes)-1]
177+
if _, ok := scope[name.Lexeme]; ok {
178+
return MakeSemanticError(
179+
fmt.Sprintf("Variable '%s' already declared in this scope.", name.Lexeme))
180+
}
181+
scope[name.Lexeme] = false
182+
}
183+
return nil
184+
}
185+
186+
func (r *Resolver) define(name Token) {
187+
if len(r.scopes) != 0 {
188+
scope := r.scopes[len(r.scopes)-1]
189+
scope[name.Lexeme] = true
190+
}
191+
}

0 commit comments

Comments
 (0)