Skip to content

Commit 2218889

Browse files
committed
Variable resolution for multiple scopes
1 parent 11e4c20 commit 2218889

2 files changed

Lines changed: 95 additions & 12 deletions

File tree

src/pretty_print.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,15 @@ fn print_stmt(stmt: &ast::Stmt, indent: usize) -> String {
9898
true_stmt,
9999
false_stmt,
100100
} => print_if(cond, true_stmt, false_stmt, indent),
101-
ast::Stmt::Compound(block) => todo!(),
101+
ast::Stmt::Compound(block) => {
102+
let mut res: String = print_line("Block (", indent);
103+
104+
block.items.iter().for_each(|block_item| {
105+
res += &print_block(block_item, indent + INDENT_INCREMENT);
106+
});
107+
res += &print_line(")", indent);
108+
res
109+
}
102110
}
103111
}
104112

src/semantics.rs

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ fn check_lvalue(expr: &ast::Expr) -> Result<(), Error> {
2121
}
2222
}
2323

24-
type VarTable = HashMap<String, String>;
24+
#[derive(Clone)]
25+
struct VarEntry {
26+
name: String,
27+
in_current_scope: bool,
28+
}
29+
30+
type VarTable = HashMap<String, VarEntry>;
2531
struct VariableResolution {
2632
var_table: VarTable,
2733
var_index: i32,
@@ -36,23 +42,30 @@ impl VariableResolution {
3642
}
3743

3844
/// Resolve a variable name that has already been mangled
39-
fn resolve_var_name(&self, name: &Identifier) -> Result<&String, Error> {
40-
let new_name = self.var_table.get(name.val());
41-
new_name.ok_or(format!("Could not find identifier {}", name.val()))
45+
fn resolve_var_name(&self, name: &Identifier) -> Result<&VarEntry, Error> {
46+
let new_entry = self.var_table.get(name.val());
47+
new_entry.ok_or(format!("Could not find identifier {}", name.val()))
4248
}
4349

4450
/// Return a globally unique variable name to avoid collisions in ASM
4551
fn record_var_name(&mut self, name: &Identifier) -> Result<Identifier, Error> {
4652
let orig_name = name.val();
4753
let new_name = self.var_table.get(orig_name);
48-
if let Some(name) = new_name {
49-
return Err(format!("Variable {} already defined", name));
54+
// If we find the variable in this scope, fail for a redeclaration
55+
if let Some(name) = new_name
56+
&& name.in_current_scope
57+
{
58+
return Err(format!("Variable {} already defined", name.name));
5059
}
5160
let new_name = format!("{}.{}", orig_name, self.var_index);
5261
self.var_index += 1;
53-
self
54-
.var_table
55-
.insert(orig_name.to_string(), new_name.clone());
62+
self.var_table.insert(
63+
orig_name.to_string(),
64+
VarEntry {
65+
name: new_name.clone(),
66+
in_current_scope: true,
67+
},
68+
);
5669
Ok(Identifier::new(&new_name))
5770
}
5871

@@ -71,7 +84,7 @@ impl VariableResolution {
7184
}
7285
ast::Expr::Var(identifier) => {
7386
let new_name = self.resolve_var_name(&identifier)?;
74-
*identifier = Identifier::new(new_name);
87+
*identifier = Identifier::new(&new_name.name);
7588
Ok(())
7689
}
7790
ast::Expr::Conditional {
@@ -101,9 +114,25 @@ impl VariableResolution {
101114
}
102115
Ok(())
103116
}
104-
ast::Stmt::Compound(block) => todo!(),
117+
ast::Stmt::Compound(block) => {
118+
// We're entering a new block so adjust name resolution by backing up
119+
// the current var table and using a new one with in_current_scope set to false
120+
let old_table = self.var_table.clone();
121+
122+
// Note that the vars are not in the current scope
123+
self.var_table.iter_mut().for_each(|(_, v)| {
124+
v.in_current_scope = false;
125+
});
126+
for block_item in &mut block.items {
127+
self.resolve_in_block(block_item)?;
128+
}
129+
// Restore the old table as we leave the scope
130+
self.var_table = old_table;
131+
Ok(())
132+
}
105133
}
106134
}
135+
107136
fn resolve_in_block(&mut self, block: &mut ast::BlockItem) -> Result<(), Error> {
108137
match block {
109138
ast::BlockItem::Declaration(identifier, expr) => {
@@ -290,4 +319,50 @@ Program(
290319

291320
Ok(())
292321
}
322+
323+
#[test]
324+
fn resolve_compound() {
325+
let main = r#"
326+
int main(void) {
327+
int x = 0;
328+
int outer = 0;
329+
{
330+
int y = x + 1;
331+
int x = y + 2;
332+
{
333+
int z = x + 1;
334+
int x = z + 2;
335+
outer = x;
336+
}
337+
}
338+
{
339+
int x = outer;
340+
return x;
341+
}
342+
}
343+
"#;
344+
345+
let expected = r#"
346+
Program(
347+
Function main() {
348+
Declaration(x.0, $0);
349+
Declaration(outer.1, $0);
350+
Block (
351+
Declaration(y.2, Plus($x.0, $1));
352+
Declaration(x.3, Plus($y.2, $2));
353+
Block (
354+
Declaration(z.4, Plus($x.3, $1));
355+
Declaration(x.5, Plus($z.4, $2));
356+
=($outer.1, $x.5);
357+
)
358+
)
359+
Block (
360+
Declaration(x.6, $outer.1);
361+
return $x.6;
362+
)
363+
}
364+
)
365+
"#;
366+
assert_has_pretty_print_after_semantics(main, expected);
367+
}
293368
}

0 commit comments

Comments
 (0)