Skip to content

Commit 2c99373

Browse files
authored
Feat: unused vars + e2e tests (#14)
* feat: adding tests for e2e lox blocks * chore: docs * fix: run all tests * fix: unused code * feat: working detecting unassigned vars * test: add benchmarks
1 parent 5bb91b8 commit 2c99373

File tree

287 files changed

+6744
-107
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

287 files changed

+6744
-107
lines changed

.github/workflows/pre-commit-checks.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ jobs:
1414
- name: Run rustfmt
1515
run: cargo fmt -- --check
1616
- name: Run tests
17-
run: cargo test -p loxrs_env
17+
run: cargo test

loxrs_interpreter/src/lox/entities/expr.rs

+22-32
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,6 @@ impl Display for ExprFunction {
7171
.field("params", &self.params)
7272
.field("body", &self.body)
7373
.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)
8774
}
8875
}
8976

@@ -131,33 +118,36 @@ impl fmt::Display for ExprKind {
131118
f,
132119
"{}",
133120
match self {
134-
Self::Call(call) => {
121+
Self::Call(call) => format!(
122+
"[<call> {}]",
135123
parenthesize(&call.callee.to_string(), call.args.iter().collect())
136-
}
137-
Self::Grouping(grouping) => {
138-
parenthesize("grouping", vec![&grouping.expression])
139-
}
140-
Self::Function(func) => {
141-
parenthesize("<function>", func.params.iter().collect())
142-
}
143-
Self::Unary(unary) =>
144-
parenthesize(&unary.operator.token_type.to_string(), vec![&unary.right]),
145-
Self::Binary(binary) => parenthesize(
146-
&binary.operator.token_type.to_string(),
147-
vec![&binary.left, &binary.right]
148124
),
149-
Self::Logical(logical) => parenthesize(
125+
Self::Grouping(grouping) => parenthesize("<grouping>", vec![&grouping.expression]),
126+
Self::Function(func) => parenthesize("<function>", func.params.iter().collect()),
127+
Self::Unary(unary) => format!(
128+
"[<unary> token: {}, expr: {}]",
129+
&unary.operator.token_type, &unary.right
130+
),
131+
Self::Binary(binary) => format!(
132+
"[<binary> operator: {}, left: {} right: {}]",
133+
&binary.operator.token_type, &binary.left, &binary.right
134+
),
135+
Self::Logical(logical) => format!(
136+
"[<logical> {} {} {}]",
137+
&logical.left,
150138
&logical.operator.token_type.to_string(),
151-
vec![&logical.left, &logical.right]
139+
&logical.right
152140
),
153-
Self::Literal(value) => value.to_string(),
141+
Self::Literal(value) => format!("[<logical> {}]", value),
154142
Self::Var(var) => var.literal.clone().map_or("None".to_string(), |t| format!(
155-
"line: {}, col: {}, {}",
143+
"[<var> line: {}, col: {}, name: {}]",
156144
var.line, var.column, t
157145
)
158146
.to_string()),
159-
Self::Assign(assign) =>
160-
format!("target: {}, expr: {}", assign.name, assign.expression),
147+
Self::Assign(assign) => format!(
148+
"[<assign> target: {}, expr: {}]",
149+
assign.name, assign.expression
150+
),
161151
}
162152
)
163153
}

loxrs_interpreter/src/lox/interpreter.rs

+3
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ mod resolver;
77
mod scanner;
88
mod visitor;
99

10+
#[cfg(test)]
11+
mod test;
12+
1013
pub(crate) use input::read_input as start;
1114
pub(super) use scanner::scan_parse;

loxrs_interpreter/src/lox/interpreter/eval.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ impl ExprVisitor<Value> for Interpreter {
205205
self.eval(&expression.expression)
206206
}
207207

208-
fn var(&self, expression: &Expr) -> Result<Value> {
208+
fn var(&mut self, expression: &Expr) -> Result<Value> {
209209
if let ExprKind::Var(var) = &expression.kind {
210210
let str = var.extract_identifier_str()?;
211211

loxrs_interpreter/src/lox/interpreter/parser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ impl Parser {
621621
});
622622
}
623623
params.push(
624-
self.consume(&TokenType::Identifier, "expected parameer name")?
624+
self.consume(&TokenType::Identifier, "expected parameter name")?
625625
.clone(),
626626
);
627627

loxrs_interpreter/src/lox/interpreter/reader.rs

+20-14
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ pub fn run_file(filename: &String) {
1616
match fs::read_to_string(filename) {
1717
Ok(str) => {
1818
let interpreter = Rc::new(RefCell::new(Interpreter::new()));
19-
repl(interpreter, &str);
19+
let _ = repl(interpreter, &str).inspect_err(|errs| {
20+
for e in errs {
21+
error!("{:?}", e);
22+
}
23+
});
2024
}
2125
Err(e) => {
2226
error!("Error reading file: {e}");
@@ -45,16 +49,23 @@ pub fn run_prompt() {
4549
continue;
4650
}
4751
};
48-
repl(Rc::clone(&interpreter), &statement);
52+
let _ = repl(Rc::clone(&interpreter), &statement).inspect_err(|errs| {
53+
for e in errs {
54+
error!("{:?}", e);
55+
}
56+
});
4957
}
5058
}
5159

52-
fn repl(interpreter: Rc<RefCell<Interpreter>>, str: &str) {
53-
let mut resolver = Resolver::new(Rc::clone(&interpreter));
54-
55-
let _ = scan_parse(str)
56-
.and_then(|stmts| {
57-
resolver.resolve(&stmts).map_err(|e| vec![e]).and_then(|_| {
60+
pub fn repl(
61+
interpreter: Rc<RefCell<Interpreter>>,
62+
str: &str,
63+
) -> Result<(), Vec<loxrs_types::LoxErr>> {
64+
scan_parse(str).and_then(|stmts| {
65+
Resolver::new(Rc::clone(&interpreter))
66+
.resolve(&stmts)
67+
.map_err(|e| vec![e])
68+
.and_then(|_| {
5869
trace!(
5970
"post resolver Interpreter: {}",
6071
interpreter.as_ref().borrow()
@@ -66,10 +77,5 @@ fn repl(interpreter: Rc<RefCell<Interpreter>>, str: &str) {
6677
.interpret(&stmts[..])
6778
.map_err(|e| vec![e])
6879
})
69-
})
70-
.inspect_err(|errs| {
71-
for e in errs {
72-
error!("{:?}", e);
73-
}
74-
});
80+
})
7581
}

loxrs_interpreter/src/lox/interpreter/resolver.rs

+94-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::cell::RefCell;
2-
use std::fmt::Display;
2+
use std::fmt::{Display, Formatter};
33
use std::{collections::HashMap, rc::Rc};
44

55
use crate::lox::entities::expr::{ExprFunction, ExprKind};
@@ -19,10 +19,36 @@ use log::trace;
1919
use loxrs_env::Scope;
2020
use loxrs_types::{LoxErr, Result};
2121

22+
#[derive(Default, Debug, Clone, PartialEq)]
23+
enum VarStatus {
24+
#[default]
25+
Declared,
26+
Defined,
27+
Assigned,
28+
}
29+
30+
impl Display for VarStatus {
31+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32+
write!(
33+
f,
34+
"{}",
35+
match self {
36+
Self::Declared => "declared",
37+
Self::Defined => "defined",
38+
Self::Assigned => "assigned",
39+
}
40+
)
41+
}
42+
}
43+
2244
#[derive(Debug)]
2345
pub struct Resolver {
2446
interpreter: Rc<RefCell<Interpreter>>,
25-
stack: Vec<HashMap<String, bool>>,
47+
// TODO here this needs to be changed into an enum
48+
// and on `assign` it needs to be set to assigned
49+
// when we pop a scope, any unassigned vars should be
50+
// an error
51+
stack: Vec<HashMap<String, VarStatus>>,
2652
curr_function: FuncType,
2753
}
2854

@@ -65,10 +91,11 @@ impl Resolver {
6591
for param in &stmt.def.params {
6692
self.declare(param)?;
6793
self.define(param)?;
94+
self.assign(param)?;
6895
}
6996
self.resolve_stmt(&Stmt::Block(stmt.def.body.to_owned()))?;
7097

71-
self.end_scope();
98+
self.end_scope()?;
7299

73100
self.curr_function = prev_function_type;
74101
Ok(None)
@@ -86,15 +113,47 @@ impl Resolver {
86113
});
87114
}
88115

89-
last.insert(name.extract_identifier_str()?.to_owned(), false);
116+
last.insert(
117+
name.extract_identifier_str()?.to_owned(),
118+
VarStatus::default(),
119+
);
90120
}
91121

92122
Ok(None)
93123
}
94124

95125
fn define(&mut self, name: &Token) -> Result<Option<Value>> {
126+
let var_name = name.extract_identifier_str()?;
96127
if let Some(last) = self.stack.last_mut() {
97-
last.insert(name.extract_identifier_str()?.to_owned(), true);
128+
if !last.contains_key(var_name) {
129+
return Err(LoxErr::Resolve {
130+
message: format!("Can't define local variable {} before it is declared\n at line: {}, col: {}",
131+
var_name, name.line,
132+
name.column),
133+
});
134+
}
135+
last.insert(
136+
name.extract_identifier_str()?.to_owned(),
137+
VarStatus::Defined,
138+
);
139+
}
140+
141+
Ok(None)
142+
}
143+
144+
fn assign(&mut self, name: &Token) -> Result<Option<Value>> {
145+
let var_name = name.extract_identifier_str()?;
146+
if let Some(last) = self.stack.last_mut() {
147+
if !last
148+
.get(var_name)
149+
.is_some_and(|el| *el == VarStatus::Defined)
150+
{
151+
return Err(LoxErr::Resolve {
152+
message: format!("Can't assign local variable {} before it is defined\n at line: {}, col: {}", var_name, name.line,
153+
name.column),
154+
});
155+
}
156+
last.insert(var_name.to_owned(), VarStatus::Assigned);
98157
}
99158

100159
Ok(None)
@@ -104,8 +163,17 @@ impl Resolver {
104163
self.stack.push(HashMap::new());
105164
}
106165

107-
fn end_scope(&mut self) {
108-
self.stack.pop();
166+
fn end_scope(&mut self) -> Result<Option<Value>> {
167+
if let Some(stack) = self.stack.pop() {
168+
for (k, v) in stack {
169+
if v != VarStatus::Assigned {
170+
return Err(LoxErr::Resolve {
171+
message: format!("Variable `{}` not assigned", k),
172+
});
173+
}
174+
}
175+
}
176+
Ok(None)
109177
}
110178

111179
pub fn resolve(&mut self, stmts: &Vec<Stmt>) -> Result<Option<Value>> {
@@ -125,9 +193,9 @@ impl Resolver {
125193
Ok(None)
126194
}
127195

128-
fn resolve_local(&self, expr: &Expr, name: &str) -> Result<Option<Value>> {
196+
fn resolve_local(&mut self, expr: &Expr, name: &str, assign: bool) -> Result<Option<Value>> {
129197
trace!("resolving to locals: {} with stack: {}", expr, self,);
130-
for (idx, scope) in self.stack.iter().rev().enumerate() {
198+
for (idx, scope) in self.stack.iter_mut().rev().enumerate() {
131199
trace!(
132200
"searching for {} within stack no: {} and curr stack: {:?}",
133201
expr,
@@ -137,6 +205,10 @@ impl Resolver {
137205
if scope.contains_key(name) {
138206
trace!("found! resolving {} within stack no.: {}", expr, idx,);
139207
self.interpreter.as_ref().borrow_mut().resolve(expr, idx);
208+
if assign {
209+
trace!("Also setting {} to assigned", name);
210+
scope.insert(name.to_owned(), VarStatus::Assigned);
211+
}
140212
return Ok(None);
141213
}
142214
}
@@ -189,6 +261,10 @@ impl StmtVisitor for Resolver {
189261
self.declare(&var.token)?;
190262
if let Some(expr) = &var.expr {
191263
self.resolve_expr(expr)?;
264+
self.define(&var.token)?;
265+
self.assign(&var.token)?;
266+
267+
return Ok(None);
192268
}
193269

194270
self.define(&var.token)?;
@@ -198,6 +274,7 @@ impl StmtVisitor for Resolver {
198274
fn fun_stmt(&mut self, stmt: &StmtFun) -> Result<Option<Value>> {
199275
self.declare(&stmt.name)?;
200276
self.define(&stmt.name)?;
277+
self.assign(&stmt.name)?;
201278
self.resolve_fun_stmt(stmt)
202279
}
203280

@@ -217,7 +294,7 @@ impl StmtVisitor for Resolver {
217294
self.resolve(&block.stmts)?;
218295

219296
trace!("done traversing block! ejecting scope... {}", &self);
220-
self.end_scope();
297+
self.end_scope()?;
221298

222299
Ok(None)
223300
}
@@ -248,13 +325,14 @@ impl ExprVisitor<Option<Value>> for Resolver {
248325
for param in &def.params {
249326
self.declare(param)?;
250327
self.define(param)?;
328+
self.assign(param)?;
251329
}
252330

253331
self.resolve_stmt(&Stmt::Block(StmtBlock {
254332
stmts: def.body.stmts.to_owned(),
255333
}))?;
256334

257-
self.end_scope();
335+
self.end_scope()?;
258336

259337
self.curr_function = prev_function_type;
260338
Ok(None)
@@ -280,14 +358,14 @@ impl ExprVisitor<Option<Value>> for Resolver {
280358
self.resolve_expr(&expression.expression)
281359
}
282360

283-
fn var(&self, expression: &Expr) -> Result<Option<Value>> {
361+
fn var(&mut self, expression: &Expr) -> Result<Option<Value>> {
284362
if let ExprKind::Var(var) = &expression.kind {
285363
trace!("var expr: {}", var);
286364
let name = var.extract_identifier_str()?;
287365
if self
288366
.stack
289367
.last()
290-
.is_some_and(|last| last.get(name).is_some_and(|el| !*el))
368+
.is_some_and(|last| last.get(name).is_some_and(|el| *el == VarStatus::Declared))
291369
{
292370
return Err(LoxErr::Resolve {
293371
message: format!("Can't read local variable {} from its own initalizer\n at line: {}, col: {}", name, var.line,
@@ -296,7 +374,7 @@ impl ExprVisitor<Option<Value>> for Resolver {
296374
}
297375

298376
trace!("resolving to locals from var expr: {}", var);
299-
self.resolve_local(expression, name)?;
377+
self.resolve_local(expression, name, false)?;
300378
return Ok(None);
301379
}
302380

@@ -334,7 +412,8 @@ impl ExprVisitor<Option<Value>> for Resolver {
334412
expr_assign.name,
335413
expr
336414
);
337-
return self.resolve_local(expr, expr_assign.name.extract_identifier_str()?);
415+
// TODO need to set assigned here too
416+
return self.resolve_local(expr, expr_assign.name.extract_identifier_str()?, true);
338417
}
339418

340419
Err(LoxErr::Internal {

0 commit comments

Comments
 (0)