Skip to content

Commit 5bb91b8

Browse files
authored
feat: var resolution (#13)
* fix: unified definition for functions anonymous and named functions follow the same code path now * fix: remove extra clone * wip: resolvers * wip: variable resolution pass * feat: initial resolver pass * feat: some semblance of working resolver * wip: working out the assignment bug via api * fix: logging for statements * feat: working resolution full * fix: tests
1 parent 85acf52 commit 5bb91b8

27 files changed

+883
-168
lines changed

Cargo.lock

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

loxrs_env/src/scope.rs

+27-30
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ type Link<T> = Option<Rc<Scope<T>>>;
1212
pub struct Scope<T> {
1313
values: RefCell<HashMap<String, T>>,
1414
pub parent: Link<T>,
15-
pub globals: Link<T>,
1615
}
1716

1817
impl<T: Display> Scope<T> {
@@ -45,37 +44,20 @@ impl<T: Display + Clone> Scope<T> {
4544
Self {
4645
values: RefCell::new(HashMap::default()),
4746
parent: None,
48-
globals: Self::new_globals(),
4947
}
5048
}
5149

52-
fn new_globals() -> Link<T> {
53-
Some(Rc::new(Self {
54-
values: RefCell::new(HashMap::default()),
55-
parent: None,
56-
globals: None,
57-
}))
58-
}
59-
6050
pub fn from_parent(parent: Rc<Scope<T>>) -> Rc<Scope<T>> {
61-
let globals = parent.as_ref().globals.as_ref().map(Rc::clone);
6251
Rc::new(Self {
6352
values: RefCell::new(HashMap::new()),
6453
parent: Some(parent),
65-
globals,
6654
})
6755
}
6856

6957
pub fn define(&self, name: &str, val: T) {
7058
self.values.borrow_mut().insert(name.to_string(), val);
7159
}
7260

73-
pub fn define_global(&self, name: &str, val: T) {
74-
if let Some(el) = self.globals.as_ref() {
75-
el.define(name, val)
76-
}
77-
}
78-
7961
pub fn assign(&self, name: &str, val: T) -> Result<()> {
8062
trace!("[assign] current env:\n{}", self);
8163
trace!(
@@ -95,28 +77,43 @@ impl<T: Display + Clone> Scope<T> {
9577
}
9678
}
9779

80+
pub fn assign_at(&self, distance: usize, name: &str, val: T) -> Result<()> {
81+
self.ancestor(distance)?
82+
.values
83+
.borrow_mut()
84+
.insert(name.to_owned(), val);
85+
Ok(())
86+
}
87+
9888
pub fn get(&self, name: &str) -> Result<T> {
9989
trace!("[get] looking for: `{}` in:\n{}", name, self);
10090
self.values
10191
.borrow()
10292
.get(name)
10393
.cloned()
104-
.or_else(|| {
105-
trace!("[get] going into parent...");
106-
self.parent
107-
.as_ref()
108-
.and_then(|parent| parent.as_ref().get(name).ok())
109-
})
110-
.or_else(|| {
111-
trace!("[get] going into globals...");
112-
self.globals
113-
.as_ref()
114-
.and_then(|globals| globals.get(name).ok())
115-
})
11694
.ok_or(LoxErr::Undefined {
11795
message: format!("variable undefined: {}", name),
11896
})
11997
}
98+
99+
pub fn get_at(&self, distance: usize, name: &str) -> Result<T> {
100+
self.ancestor(distance)?.get(name)
101+
}
102+
103+
fn ancestor(&self, distance: usize) -> Result<&Scope<T>> {
104+
let mut parent = self;
105+
106+
for _i in 0..distance {
107+
parent = parent
108+
.parent.as_ref()
109+
.ok_or::<LoxErr>(LoxErr::Internal {
110+
message: "Error with ancestor depth! Expected to find some ancestor scope when resolving variable".to_owned(),
111+
})
112+
.map(|el| el.as_ref())?;
113+
}
114+
115+
Ok(parent)
116+
}
120117
}
121118

122119
impl<T: Display> Display for Scope<T> {

loxrs_env/src/scope/test.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,31 @@ fn basics() {
2121
#[test]
2222
fn scope_assignment() {
2323
let scope = Rc::new(Scope::default());
24-
let child = Scope::from_parent(Rc::clone(&scope));
2524

2625
assert!(scope.get("foo").is_err());
2726
scope.define("foo", "bar".to_owned());
2827
assert!(scope.get("foo").is_ok_and(|str| str == "bar"));
29-
assert!(child.get("foo").is_ok_and(|str| str == "bar"));
28+
}
29+
30+
#[test]
31+
fn ancestry() {
32+
let grandparent: Rc<Scope<bool>> = Rc::new(Scope::default());
33+
grandparent.define("grandy", true);
3034

31-
child.define("baz", "fff".to_owned());
32-
assert!(child.get("baz").is_ok_and(|str| str == "fff"));
35+
let parent = Scope::from_parent(Rc::clone(&grandparent));
3336

34-
assert!(scope.get("baz").is_err());
35-
assert!(scope.assign("baz", "bar".to_owned()).is_err());
37+
parent.define("parent", true);
38+
let child = Scope::from_parent(Rc::clone(&parent));
39+
40+
child.define("child", true);
41+
42+
assert!(child.get_at(0, "child").is_ok_and(|el| el));
43+
assert!(child.get_at(1, "parent").is_ok_and(|el| el));
44+
assert!(child.get_at(2, "grandy").is_ok_and(|el| el));
45+
46+
assert!(grandparent.assign_at(33, "foo", true).is_err());
47+
assert!(grandparent.assign_at(1, "bar", true).is_err());
48+
assert!(grandparent.assign_at(0, "works", true).is_ok());
3649

37-
assert!(child.assign("baz", "bar".to_owned()).is_ok());
38-
assert!(child.get("baz").is_ok());
50+
assert!(child.get_at(2, "works").is_ok_and(|el| el));
3951
}

loxrs_interpreter/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2021"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
9+
uuid = { version = "0.8.2", features = ["v4"] }
910
backtrace-on-stack-overflow = "0.3.0"
1011
env_logger = "0.11.3"
1112
log = "0.4.21"
+17-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1+
use std::collections::HashMap;
12
use std::fmt::Debug;
23
use std::rc::Rc;
4+
use std::{cell::RefCell, fmt::Display};
35

4-
use super::Value;
6+
use super::{Expr, Value};
57
use loxrs_env::Scope;
68

79
#[derive(Debug, Clone)]
810
pub struct Interpreter {
911
pub scope: Rc<Scope<Value>>,
12+
pub globals: Rc<Scope<Value>>,
13+
pub locals: RefCell<HashMap<Expr, usize>>,
14+
}
15+
16+
impl Display for Interpreter {
17+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18+
writeln!(f, "\nInterpreter: <locals: [")?;
19+
20+
for (k, v) in self.locals.borrow().iter() {
21+
writeln!(f, " ({}: {}) ", k, v)?;
22+
}
23+
24+
write!(f, "]>")
25+
}
1026
}

loxrs_interpreter/src/lox/entities/expr.rs

+65-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use std::fmt::{self, Display};
2+
use std::hash::Hash;
3+
4+
use uuid::Uuid;
25

36
use super::{stmt::StmtBlock, Literal, Token};
47

@@ -15,7 +18,36 @@ fn parenthesize<T: Display>(name: &str, expressions: Vec<&T>) -> String {
1518
}
1619

1720
#[derive(Debug, Clone, PartialEq)]
18-
pub enum Expr {
21+
pub struct Expr {
22+
id: Uuid,
23+
pub kind: ExprKind,
24+
}
25+
26+
impl Eq for Expr {}
27+
28+
impl Expr {
29+
pub fn new(kind: ExprKind) -> Self {
30+
Self {
31+
id: Uuid::new_v4(),
32+
kind,
33+
}
34+
}
35+
}
36+
37+
impl Hash for Expr {
38+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
39+
self.id.hash(state);
40+
}
41+
}
42+
43+
impl fmt::Display for Expr {
44+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45+
write!(f, "Expr<[id: {}], [kind: {}]>", self.id, self.kind)
46+
}
47+
}
48+
49+
#[derive(Debug, Clone, PartialEq)]
50+
pub enum ExprKind {
1951
Unary(Box<ExprUnary>),
2052
Binary(Box<ExprBinary>),
2153
Call(Box<ExprCall>),
@@ -24,7 +56,7 @@ pub enum Expr {
2456
Grouping(Box<ExprGrouping>),
2557
Function(Box<ExprFunction>),
2658
Var(Token),
27-
Assign(Token, Box<ExprAssign>),
59+
Assign(Box<ExprAssign>),
2860
}
2961

3062
#[derive(Debug, Clone, PartialEq)]
@@ -33,6 +65,28 @@ pub struct ExprFunction {
3365
pub body: StmtBlock,
3466
}
3567

68+
impl Display for ExprFunction {
69+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70+
f.debug_struct("[ExprFunction]")
71+
.field("params", &self.params)
72+
.field("body", &self.body)
73+
.finish()
74+
75+
// let params: Vec<&str> = self
76+
// .params
77+
// .iter()
78+
// .map(|el| el.extract_identifier_str().unwrap())
79+
// .collect();
80+
//
81+
// write!(f, "Expr[Function] ")?;
82+
// if !params.is_empty() {
83+
// write!(f, "params: [{:?}] ", params)?;
84+
// }
85+
//
86+
// write!(f, "body:\n {}", self.body)
87+
}
88+
}
89+
3690
#[derive(Debug, Clone, PartialEq)]
3791
pub struct ExprCall {
3892
pub callee: Expr,
@@ -42,6 +96,7 @@ pub struct ExprCall {
4296

4397
#[derive(Debug, Clone, PartialEq)]
4498
pub struct ExprAssign {
99+
pub name: Token,
45100
pub expression: Expr,
46101
}
47102

@@ -70,7 +125,7 @@ pub struct ExprLogical {
70125
pub operator: Token,
71126
}
72127

73-
impl fmt::Display for Expr {
128+
impl fmt::Display for ExprKind {
74129
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75130
write!(
76131
f,
@@ -96,11 +151,13 @@ impl fmt::Display for Expr {
96151
vec![&logical.left, &logical.right]
97152
),
98153
Self::Literal(value) => value.to_string(),
99-
Self::Var(var) => var
100-
.literal
101-
.clone()
102-
.map_or("None".to_string(), |t| t.to_string()),
103-
Self::Assign(token, expr) => format!("token: {}, expr: {}", token, expr.expression),
154+
Self::Var(var) => var.literal.clone().map_or("None".to_string(), |t| format!(
155+
"line: {}, col: {}, {}",
156+
var.line, var.column, t
157+
)
158+
.to_string()),
159+
Self::Assign(assign) =>
160+
format!("target: {}, expr: {}", assign.name, assign.expression),
104161
}
105162
)
106163
}

loxrs_interpreter/src/lox/entities/func.rs

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ use std::fmt::Result as fmt_result;
77
use std::fmt::{Debug, Formatter};
88
use std::rc::Rc;
99

10+
#[derive(Clone, PartialEq, Debug)]
11+
pub enum FuncType {
12+
Function,
13+
None,
14+
}
15+
1016
#[derive(Clone, PartialEq, Debug)]
1117
pub enum Func {
1218
Lox(Function),

0 commit comments

Comments
 (0)