Skip to content

Commit bd29522

Browse files
authored
Chapter 11: Resolving and Binding (#8)
This implements [chapter 11](http://craftinginterpreters.com/resolving-and-binding.html) to resolve variables. The resolver is probably a little odd for Rust and has two sources for bugs. For one the hash map of the scopes uses the token instead of the expression for locals. If two tokens are on the the same line this might be an issue. For another the `had_error` is still not implemented.
1 parent 0e10d13 commit bd29522

File tree

9 files changed

+348
-6
lines changed

9 files changed

+348
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Each commit corresponds to one chapter in the book:
1212
* [Chapter 7: Evaluating Expressions](https://github.com/jeschkies/lox-rs/commit/fd90ef985c88832c9af6f193e0614e41dd13ef28)
1313
* [Chapter 8: Statements and State](https://github.com/jeschkies/lox-rs/commit/941cbba900acb5876dbe6031b24ef31ff81ca99e)
1414
* [Chapter 9: Control Flow](https://github.com/jeschkies/lox-rs/commit/d1f8d67f65fa4d66e24e654fec7bd8d1529b124d)
15+
* [Chapter 10: Functions](https://github.com/jeschkies/lox-rs/commit/0e10d13944a6cd77d37f9cdf393ed81ba9573172)

examples/inner_outer.lox

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var a = "global";
2+
{
3+
fun showA() {
4+
print a;
5+
}
6+
7+
showA();
8+
var a = "block";
9+
showA();
10+
}

examples/scope.lox

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,3 @@ var c = "global c";
1717
print a;
1818
print b;
1919
print c;
20-

src/env.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,56 @@ impl Environment {
3131
self.values.insert(name, value);
3232
}
3333

34+
fn ancestor(&self, distance: usize) -> Rc<RefCell<Environment>> {
35+
// Get first ancestor
36+
let parent = self
37+
.enclosing
38+
.clone()
39+
.expect(&format!("No enclosing environment at {}", 1));
40+
let mut environment = Rc::clone(&parent);
41+
42+
// Get next ancestors
43+
for i in 1..distance {
44+
let parent = self
45+
.enclosing
46+
.clone()
47+
.expect(&format!("No enclosing environment at {}", i));
48+
environment = Rc::clone(&parent);
49+
}
50+
environment
51+
}
52+
53+
pub fn get_at(&self, distance: usize, name: &Token) -> Result<Object, Error> {
54+
let key = &*name.lexeme;
55+
if distance > 0 {
56+
Ok(self
57+
.ancestor(distance)
58+
.borrow()
59+
.values
60+
.get(key)
61+
.expect(&format!("Undefined variable '{}'", key))
62+
.clone())
63+
} else {
64+
Ok(self
65+
.values
66+
.get(key)
67+
.expect(&format!("Undefined variable '{}'", key))
68+
.clone())
69+
}
70+
}
71+
72+
pub fn assign_at(&mut self, distance: usize, name: &Token, value: Object) -> Result<(), Error> {
73+
if distance > 0 {
74+
self.ancestor(distance)
75+
.borrow_mut()
76+
.values
77+
.insert(name.lexeme.clone(), value);
78+
} else {
79+
self.values.insert(name.lexeme.clone(), value);
80+
}
81+
Ok(())
82+
}
83+
3484
pub fn get(&self, name: &Token) -> Result<Object, Error> {
3585
let key = &*name.lexeme;
3686
if let Some(value) = self.values.get(key) {

src/function.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@ impl Function {
3434
) -> Result<Object, Error> {
3535
match self {
3636
Function::Native { body, .. } => Ok(body(arguments)),
37-
Function::User { params, body, closure, .. } => {
38-
let mut environment =
39-
Rc::new(RefCell::new(Environment::from(closure)));
37+
Function::User {
38+
params,
39+
body,
40+
closure,
41+
..
42+
} => {
43+
let mut environment = Rc::new(RefCell::new(Environment::from(closure)));
4044
for (param, argument) in params.iter().zip(arguments.iter()) {
4145
environment
4246
.borrow_mut()

src/interpreter.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ use crate::syntax::{Expr, LiteralValue, Stmt};
77
use crate::token::{Token, TokenType};
88

99
use std::cell::RefCell;
10+
use std::collections::HashMap;
1011
use std::rc::Rc;
1112
use std::time::{SystemTime, UNIX_EPOCH};
1213

1314
pub struct Interpreter {
1415
pub globals: Rc<RefCell<Environment>>,
1516
environment: Rc<RefCell<Environment>>,
17+
locals: HashMap<Token, usize>, // This might break if two Tokens are on the same line and have the same name.
1618
}
1719

1820
impl Interpreter {
@@ -33,6 +35,7 @@ impl Interpreter {
3335
Interpreter {
3436
globals: Rc::clone(&globals),
3537
environment: Rc::clone(&globals),
38+
locals: HashMap::new(),
3639
}
3740
}
3841

@@ -51,6 +54,10 @@ impl Interpreter {
5154
statement.accept(self)
5255
}
5356

57+
pub fn resolve(&mut self, name: &Token, depth: usize) {
58+
self.locals.insert(name.clone(), depth);
59+
}
60+
5461
pub fn execute_block(
5562
&mut self,
5663
statements: &Vec<Stmt>,
@@ -98,6 +105,14 @@ impl Interpreter {
98105
message: "Operand must be a number.".to_string(),
99106
})
100107
}
108+
109+
fn look_up_variable(&self, name: &Token) -> Result<Object, Error> {
110+
if let Some(distance) = self.locals.get(name) {
111+
self.environment.borrow().get_at(*distance, name)
112+
} else {
113+
self.globals.borrow().get(name)
114+
}
115+
}
101116
}
102117

103118
impl expr::Visitor<Object> for Interpreter {
@@ -254,12 +269,19 @@ impl expr::Visitor<Object> for Interpreter {
254269
}
255270

256271
fn visit_variable_expr(&mut self, name: &Token) -> Result<Object, Error> {
257-
self.environment.borrow().get(name)
272+
self.look_up_variable(name)
258273
}
259274

260275
fn visit_assign_expr(&mut self, name: &Token, value: &Expr) -> Result<Object, Error> {
261276
let v = self.evaluate(value)?;
262-
self.environment.borrow_mut().assign(name, v.clone())?;
277+
278+
if let Some(distance) = self.locals.get(name) {
279+
self.environment
280+
.borrow_mut()
281+
.assign_at(*distance, name, v.clone())?;
282+
} else {
283+
self.environment.borrow_mut().assign(name, v.clone())?;
284+
}
263285
Ok(v)
264286
}
265287
}

src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod function;
44
mod interpreter;
55
mod object;
66
mod parser;
7+
mod resolver;
78
mod scanner;
89
mod syntax;
910
mod token;
@@ -15,6 +16,7 @@ use std::process::exit;
1516
use error::Error;
1617
use interpreter::Interpreter;
1718
use parser::Parser;
19+
use resolver::Resolver;
1820
use scanner::Scanner;
1921
use syntax::AstPrinter;
2022

@@ -49,6 +51,12 @@ impl Lox {
4951

5052
let mut parser = Parser::new(tokens);
5153
let statements = parser.parse()?;
54+
55+
let mut resolver = Resolver::new(&mut self.interpreter);
56+
resolver.resolve_stmts(&statements);
57+
58+
// TODO: check if there was an error and return.
59+
5260
self.interpreter.interpret(&statements)?;
5361
Ok(())
5462
}

0 commit comments

Comments
 (0)