Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 51 additions & 24 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl Program {
#[derive(Debug, Clone)]
pub struct Function {
pub name: Identifier,
pub body: Vec<BlockItem>,
pub body: Block,
}

impl Function {
Expand Down Expand Up @@ -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
Expand All @@ -149,6 +128,43 @@ impl Function {
}
}

#[derive(Debug, Clone)]
pub struct Block(pub Vec<BlockItem>);

impl Block {
fn parse(tokens: &mut TokenIter, output: &Output) -> Result<Self, ParseError> {
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<crate::tacky::Instruction>) {
for block_item in self.0 {
block_item.emit_tacky(instructions);
}
}
}

#[derive(Debug, Clone)]
pub enum BlockItem {
S(Statement),
Expand Down Expand Up @@ -243,6 +259,7 @@ pub enum Statement {
then: Box<Statement>,
else_: Option<Box<Statement>>,
},
Compound(Block),
Null,
}

Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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);
}
}
}
}
}
Expand Down
82 changes: 69 additions & 13 deletions src/validate.rs
Original file line number Diff line number Diff line change
@@ -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<Identifier, Identifier>;
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<Identifier, Identifier>,
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<Identifier> {
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::<Vec<ValidationError>>();
Expand All @@ -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"),
Expand All @@ -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)?;
Expand All @@ -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
}
}
Expand All @@ -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
})?;
Expand Down