diff --git a/src/parser.rs b/src/parser.rs index 4113b0a..b2be7dd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,7 +70,7 @@ impl Program { #[derive(Debug, Clone)] pub struct Function { pub name: Identifier, - pub body: Vec, + pub body: Block, } impl Function { @@ -100,36 +100,15 @@ impl Function { )); take_punct(tokens, Punct::CloseParenthesis, output)?; - take_punct(tokens, Punct::OpenBrace, output)?; - let mut body = Vec::new(); - let mut encountered_bad_tokens = false; - loop { - let token_elem = tokens.peek().ok_or(ParseError::EarlyEnd("close brace"))?; - match token_elem.token { - Token::Punct(Punct::CloseBrace) => break, - _ => match BlockItem::parse(tokens, output) { - Ok(block_item) => body.push(block_item), - Err(ParseError::BadTokens) => encountered_bad_tokens = true, - Err(e @ ParseError::EarlyEnd(_)) => return Err(e), - }, - } - } - - if encountered_bad_tokens { - return Err(ParseError::BadTokens); - } - - take_punct(tokens, Punct::CloseBrace, output)?; + let body = Block::parse(tokens, output)?; Ok(Function { name, body }) } fn emit_tacky(self) -> crate::tacky::Function { let mut instructions = Vec::new(); - for block_item in self.body { - block_item.emit_tacky(&mut instructions); - } + self.body.emit_tacky(&mut instructions); // Sometimes C is a very strange language... // A function is allowed to not return a value @@ -149,6 +128,43 @@ impl Function { } } +#[derive(Debug, Clone)] +pub struct Block(pub Vec); + +impl Block { + fn parse(tokens: &mut TokenIter, output: &Output) -> Result { + take_punct(tokens, Punct::OpenBrace, output)?; + + let mut block_items = Vec::new(); + let mut encountered_bad_tokens = false; + loop { + let token_elem = tokens.peek().ok_or(ParseError::EarlyEnd("close brace"))?; + match token_elem.token { + Token::Punct(Punct::CloseBrace) => break, + _ => match BlockItem::parse(tokens, output) { + Ok(block_item) => block_items.push(block_item), + Err(ParseError::BadTokens) => encountered_bad_tokens = true, + Err(e @ ParseError::EarlyEnd(_)) => return Err(e), + }, + } + } + + if encountered_bad_tokens { + return Err(ParseError::BadTokens); + } + + take_punct(tokens, Punct::CloseBrace, output)?; + + Ok(Self(block_items)) + } + + fn emit_tacky(self, instructions: &mut Vec) { + for block_item in self.0 { + block_item.emit_tacky(instructions); + } + } +} + #[derive(Debug, Clone)] pub enum BlockItem { S(Statement), @@ -243,6 +259,7 @@ pub enum Statement { then: Box, else_: Option>, }, + Compound(Block), Null, } @@ -311,6 +328,11 @@ impl Statement { Ok(Self::If { cond, then, else_ }) } + Token::Punct(Punct::OpenBrace) => { + let block = Block::parse(tokens, output)?; + Ok(Self::Compound(block)) + } + _ => { let expr = take_expr(tokens, output)?; take_punct(tokens, Punct::Semicolon, output)?; @@ -353,6 +375,11 @@ impl Statement { instructions.push(crate::tacky::Instruction::Label(end_label)); } + Self::Compound(block) => { + for block_item in block.0 { + block_item.emit_tacky(instructions); + } + } } } } diff --git a/src/validate.rs b/src/validate.rs index b9b01df..8d2a02b 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -1,25 +1,76 @@ -use std::collections::HashMap; - use crate::Identifier; use crate::Output; use crate::parser as p; -/// A map containing unique names for each identifier -type VariableMap = HashMap; +use variable_map::VariableMap; +mod variable_map { + use std::collections::HashMap; + + use super::Identifier; + + /// A map containing unique names for each identifier + pub struct VariableMap<'a> { + current: HashMap, + inner: Option<&'a VariableMap<'a>>, + } + + impl VariableMap<'_> { + pub fn new() -> Self { + Self { + current: HashMap::new(), + inner: None, + } + } + + pub fn is_defined_here(&self, key: &Identifier) -> bool { + self.current.contains_key(key) + } + + pub fn add(&mut self, key: Identifier, value: Identifier) { + self.current.insert(key, value); + } + + pub fn get(&self, key: &Identifier) -> Option { + match self.current.get(key) { + Some(value) => Some(value.clone()), + None => match self.inner { + Some(inner) => inner.get(key).clone(), + None => None, + }, + } + } + } + + impl<'a> VariableMap<'a> { + pub fn wrap(&'a self) -> Self { + Self { + current: HashMap::new(), + inner: Some(&self), + } + } + } +} #[derive(Debug, Clone)] pub struct ValidationError; pub fn main(program: &mut p::Program, output: &Output) -> Result<(), ValidationError> { - let mut vars: VariableMap = HashMap::new(); - + let mut vars = VariableMap::new(); let main: &mut p::Function = &mut program.0; - let errors = main - .body + resolve_block(&mut main.body, &mut vars, output) +} + +fn resolve_block( + block: &mut p::Block, + variable_map: &mut VariableMap, + output: &Output, +) -> Result<(), ValidationError> { + let errors = block + .0 .iter_mut() .map(|block_item| match block_item { - p::BlockItem::D(d) => resolve_decleration(d, &mut vars, output), - p::BlockItem::S(s) => resolve_statement(s, &mut vars, output), + p::BlockItem::D(d) => resolve_decleration(d, variable_map, output), + p::BlockItem::S(s) => resolve_statement(s, variable_map, output), }) .filter_map(Result::err) .collect::>(); @@ -36,7 +87,7 @@ fn resolve_decleration( variable_map: &mut VariableMap, output: &Output, ) -> Result<(), ValidationError> { - if variable_map.contains_key(&decleration.name) { + if variable_map.is_defined_here(&decleration.name) { output.error( decleration.name_span, String::from("duplicate variable decleration"), @@ -49,7 +100,7 @@ fn resolve_decleration( // Retrieve the original name and replace it with the new unique name. let original_name = std::mem::replace(&mut decleration.name, unique_name.clone()); - variable_map.insert(original_name, unique_name); + variable_map.add(original_name, unique_name); if let Some(init) = &mut decleration.init { resolve_expression(init, variable_map, output)?; @@ -76,6 +127,11 @@ fn resolve_statement( Ok(()) } + p::Statement::Compound(block) => { + // Create new map wrapping the current one + let mut variable_map = variable_map.wrap(); + resolve_block(block, &mut variable_map, output) + } p::Statement::Null => Ok(()), // nothing to do } } @@ -100,7 +156,7 @@ fn resolve_expression( } p::Expression::Var(variable) => { - let unique_name = variable_map.get(&variable.name).cloned().ok_or_else(|| { + let unique_name = variable_map.get(&variable.name).ok_or_else(|| { output.error(variable.span, String::from("undeclared variable")); ValidationError })?;