diff --git a/resources/succeeds b/resources/succeeds new file mode 100755 index 0000000..3332370 Binary files /dev/null and b/resources/succeeds differ diff --git a/resources/succeeds.c b/resources/succeeds.c index 60622bd..3868409 100644 --- a/resources/succeeds.c +++ b/resources/succeeds.c @@ -10,5 +10,8 @@ int main(void) { else d = a ? 2 : 3; + int j = 1; + for(int i = 1; i < 10; i = i + 1) j = j * i; + return ~(-2) + a * b - (c = 1); } diff --git a/src/lexer.rs b/src/lexer.rs index e0b2634..b22c33f 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -238,6 +238,11 @@ pub enum Keyword { Return, If, Else, + Do, + While, + For, + Break, + Continue, } impl std::fmt::Display for Keyword { @@ -248,6 +253,11 @@ impl std::fmt::Display for Keyword { Self::Return => f.write_str("return"), Self::If => f.write_str("if"), Self::Else => f.write_str("else"), + Self::Do => f.write_str("do"), + Self::While => f.write_str("while"), + Self::For => f.write_str("for"), + Self::Break => f.write_str("break"), + Self::Continue => f.write_str("continue"), } } } @@ -261,6 +271,11 @@ impl FromStr for Keyword { "return" => Ok(Self::Return), "if" => Ok(Self::If), "else" => Ok(Self::Else), + "do" => Ok(Self::Do), + "while" => Ok(Self::While), + "for" => Ok(Self::For), + "break" => Ok(Self::Break), + "continue" => Ok(Self::Continue), _ => Err(()), } } diff --git a/src/lib.rs b/src/lib.rs index 792483f..4b1806e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ pub struct Options { } #[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Identifier(pub String); +pub struct Identifier(String); impl Identifier { pub fn new(s: impl Into) -> Self { @@ -51,9 +51,20 @@ impl Identifier { pub fn new_label(prefix: &str) -> Self { static COUNTER: AtomicUsize = AtomicUsize::new(0); let value = COUNTER.fetch_add(1, Ordering::Relaxed); - assert_ne!(value, usize::MAX, "max number of temp values exceeded"); + assert_ne!(value, usize::MAX, "max number of labels exceeded"); Self(format!("{prefix}.{value}")) } + + pub fn new_loop() -> Self { + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let value = COUNTER.fetch_add(1, Ordering::Relaxed); + assert_ne!(value, usize::MAX, "max number of loops exceeded"); + Self(format!("loop.{value}")) + } + + pub fn with_prefix(self, prefix: &str) -> Self { + Self(format!("{prefix}{}", self.0)) + } } impl std::fmt::Display for Identifier { @@ -225,7 +236,8 @@ pub fn compiler( return Ok(false); } - validate::main(&mut program, &output).map_err(|_| None)?; + validate::resolve_variables(&mut program, &output).map_err(|_| None)?; + validate::resolve_loops(&mut program, &output).map_err(|_| None)?; if options.validate { println!("validation succeeded, new program:\n{program:#?}"); return Ok(false); diff --git a/src/parser.rs b/src/parser.rs index b2be7dd..7b2e962 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -250,6 +250,31 @@ impl Declaration { } } +#[derive(Debug, Clone)] +pub enum ForInit { + D(Declaration), + E(Option), +} + +impl ForInit { + fn parse(tokens: &mut TokenIter, output: &Output) -> Result { + let token_elem = tokens.peek().ok_or(ParseError::EarlyEnd("block item"))?; + + match token_elem.token { + Token::Keyword(Keyword::Int) => Declaration::parse(tokens, output).map(ForInit::D), + Token::Punct(Punct::Semicolon) => { + let _ = tokens.next().expect("token must be semicolon"); + Ok(Self::E(None)) + } + _ => { + let expr = take_expr(tokens, output)?; + take_punct(tokens, Punct::Semicolon, output)?; + Ok(Self::E(Some(expr))) + } + } + } +} + #[derive(Debug, Clone)] pub enum Statement { Return(Expression), @@ -260,6 +285,25 @@ pub enum Statement { else_: Option>, }, Compound(Block), + Break(Option), + Continue(Option), + While { + cond: Expression, + body: Box, + label: Identifier, + }, + DoWhile { + body: Box, + cond: Expression, + label: Identifier, + }, + For { + init: ForInit, + cond: Option, + post: Option, + body: Box, + label: Identifier, + }, Null, } @@ -305,6 +349,90 @@ impl Statement { Ok(Self::Return(expr)) } + Token::Keyword(Keyword::Break) => { + let _ = tokens.next().expect("token must be break keyword"); + take_punct(tokens, Punct::Semicolon, output)?; + Ok(Self::Break(None)) + } + + Token::Keyword(Keyword::Continue) => { + let _ = tokens.next().expect("token must be continue keyword"); + take_punct(tokens, Punct::Semicolon, output)?; + Ok(Self::Continue(None)) + } + + Token::Keyword(Keyword::While) => { + let _ = tokens.next().expect("token must be while keyword"); + + take_punct(tokens, Punct::OpenParenthesis, output)?; + let cond = take_expr(tokens, output)?; + take_punct(tokens, Punct::CloseParenthesis, output)?; + + let body = Box::new(Statement::parse(tokens, output)?); + + let label = Identifier::new_loop(); + + Ok(Self::While { cond, body, label }) + } + + Token::Keyword(Keyword::Do) => { + let _ = tokens.next().expect("token must be do keyword"); + + let body = Box::new(Statement::parse(tokens, output)?); + + let elem = tokens.next().expect("token must be while keyword"); + if !matches!(elem.token, Token::Keyword(Keyword::While)) { + output.error(elem.span, String::from("expected `while`")); + return Err(ParseError::BadTokens); + } + + take_punct(tokens, Punct::OpenParenthesis, output)?; + let cond = take_expr(tokens, output)?; + take_punct(tokens, Punct::CloseParenthesis, output)?; + + take_punct(tokens, Punct::Semicolon, output)?; + + let label = Identifier::new_loop(); + + Ok(Self::DoWhile { body, cond, label }) + } + + Token::Keyword(Keyword::For) => { + let _ = tokens.next().expect("token must be for keyword"); + + take_punct(tokens, Punct::OpenParenthesis, output)?; + + let init = ForInit::parse(tokens, output)?; + + let peeked = tokens.peek().ok_or(ParseError::EarlyEnd("condition"))?; + let cond = match peeked.token { + Token::Punct(Punct::Semicolon) => None, + _ => Some(take_expr(tokens, output)?), + }; + + take_punct(tokens, Punct::Semicolon, output)?; + + let peeked = tokens.peek().ok_or(ParseError::EarlyEnd("post expr"))?; + let post = match peeked.token { + Token::Punct(Punct::CloseParenthesis) => None, + _ => Some(Expression::parse(tokens, output)?), + }; + + take_punct(tokens, Punct::CloseParenthesis, output)?; + + let body = Box::new(Statement::parse(tokens, output)?); + + let label = Identifier::new_loop(); + + Ok(Self::For { + init, + cond, + post, + body, + label, + }) + } + Token::Punct(Punct::Semicolon) => { let _ = tokens.next().expect("token must be semicolon"); Ok(Self::Null) @@ -362,7 +490,7 @@ impl Statement { target: else_label.clone(), }); - let _ = then.emit_tacky(instructions); + then.emit_tacky(instructions); instructions.push(crate::tacky::Instruction::Jump { target: end_label.clone(), }); @@ -380,6 +508,92 @@ impl Statement { block_item.emit_tacky(instructions); } } + Self::Break(label) => { + let target = label.unwrap().with_prefix("break_"); + instructions.push(crate::tacky::Instruction::Jump { target }); + } + Self::Continue(label) => { + let target = label.unwrap().with_prefix("continue_"); + instructions.push(crate::tacky::Instruction::Jump { target }); + } + Self::While { cond, body, label } => { + let continue_ = label.clone().with_prefix("continue_"); + let break_ = label.with_prefix("break_"); + + instructions.push(crate::tacky::Instruction::Label(continue_.clone())); + + let condition = cond.emit_tacky(instructions); + instructions.push(crate::tacky::Instruction::JumpIfZero { + condition, + target: break_.clone(), + }); + + body.emit_tacky(instructions); + + instructions.push(crate::tacky::Instruction::Jump { target: continue_ }); + instructions.push(crate::tacky::Instruction::Label(break_)); + } + + Self::DoWhile { body, cond, label } => { + let start = Identifier::new_label("start"); + instructions.push(crate::tacky::Instruction::Label(start.clone())); + + body.emit_tacky(instructions); + + let continue_ = label.clone().with_prefix("continue_"); + instructions.push(crate::tacky::Instruction::Label(continue_)); + + let condition = cond.emit_tacky(instructions); + instructions.push(crate::tacky::Instruction::JumpIfNotZero { + condition, + target: start, + }); + + let break_ = label.with_prefix("break_"); + instructions.push(crate::tacky::Instruction::Label(break_)); + } + + Self::For { + init, + cond, + post, + body, + label, + } => { + let start = Identifier::new_label("start"); + let continue_ = label.clone().with_prefix("continue_"); + let break_ = label.with_prefix("break_"); + + match init { + ForInit::D(decleration) => decleration.emit_tacky(instructions), + ForInit::E(maybe_expr) => { + if let Some(expr) = maybe_expr { + expr.emit_tacky(instructions); + } + } + } + + instructions.push(crate::tacky::Instruction::Label(start.clone())); + + if let Some(cond) = cond { + let condition = cond.emit_tacky(instructions); + instructions.push(crate::tacky::Instruction::JumpIfZero { + condition, + target: break_.clone(), + }); + } + + body.emit_tacky(instructions); + + instructions.push(crate::tacky::Instruction::Label(continue_.clone())); + + if let Some(post) = post { + post.emit_tacky(instructions); + } + + instructions.push(crate::tacky::Instruction::Jump { target: start }); + instructions.push(crate::tacky::Instruction::Label(break_)); + } } } } diff --git a/src/validate.rs b/src/validate.rs index 8d2a02b..2fc8b02 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -45,7 +45,7 @@ mod variable_map { pub fn wrap(&'a self) -> Self { Self { current: HashMap::new(), - inner: Some(&self), + inner: Some(self), } } } @@ -54,7 +54,7 @@ mod variable_map { #[derive(Debug, Clone)] pub struct ValidationError; -pub fn main(program: &mut p::Program, output: &Output) -> Result<(), ValidationError> { +pub fn resolve_variables(program: &mut p::Program, output: &Output) -> Result<(), ValidationError> { let mut vars = VariableMap::new(); let main: &mut p::Function = &mut program.0; resolve_block(&mut main.body, &mut vars, output) @@ -117,6 +117,7 @@ fn resolve_statement( match statement { p::Statement::Return(expr) => resolve_expression(expr, variable_map, output), p::Statement::Expression(expr) => resolve_expression(expr, variable_map, output), + p::Statement::If { cond, then, else_ } => { resolve_expression(cond, variable_map, output)?; resolve_statement(then, variable_map, output)?; @@ -127,11 +128,51 @@ 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::Break(_) | p::Statement::Continue(_) => Ok(()), // nothing to do + + p::Statement::While { cond, body, .. } | p::Statement::DoWhile { body, cond, .. } => { + resolve_expression(cond, variable_map, output)?; + resolve_statement(body, variable_map, output)?; + Ok(()) + } + + p::Statement::For { + init, + cond, + post, + body, + .. + } => { + // Create new map wrapping the current one + let mut variable_map = variable_map.wrap(); + + // Resolve ForInit + match init { + p::ForInit::D(decl) => resolve_decleration(decl, &mut variable_map, output)?, + p::ForInit::E(Some(expr)) => resolve_expression(expr, &mut variable_map, output)?, + p::ForInit::E(None) => {} // nothing to do + } + + if let Some(cond) = cond { + resolve_expression(cond, &mut variable_map, output)?; + } + + if let Some(post) = post { + resolve_expression(post, &mut variable_map, output)?; + } + + resolve_statement(body, &mut variable_map, output)?; + + Ok(()) + } + p::Statement::Null => Ok(()), // nothing to do } } @@ -186,3 +227,60 @@ fn resolve_expression( } } } + +pub fn resolve_loops(program: &mut p::Program, output: &Output) -> Result<(), ValidationError> { + let main: &mut p::Function = &mut program.0; + resolve_loops_block(&mut main.body, None, output) +} + +fn resolve_loops_block( + block: &mut p::Block, + current_loop: Option, + output: &Output, +) -> Result<(), ValidationError> { + for block_item in &mut block.0 { + match block_item { + p::BlockItem::S(s) => resolve_loops_statement(s, current_loop.clone(), output)?, + p::BlockItem::D(_) => {} // nothing to do + } + } + + Ok(()) +} + +fn resolve_loops_statement( + statement: &mut p::Statement, + current_loop: Option, + output: &Output, +) -> Result<(), ValidationError> { + match statement { + p::Statement::Break(label) | p::Statement::Continue(label) => { + // TODO: Return proper errors here + *label = Some(current_loop.unwrap()); + Ok(()) + } + + #[rustfmt::skip] + p::Statement::While { cond: _, body, label } + | p::Statement::DoWhile { body, cond: _, label } + | p::Statement::For { init: _, cond: _, post: _, body, label } => { + // We here pass in the label of the loop! + resolve_loops_statement(body, Some(label.clone()), output) + } + + #[rustfmt::skip] + p::Statement::If { cond: _, then, else_ } => { + resolve_loops_statement(then, current_loop.clone(), output)?; + + if let Some(else_) = else_ { + resolve_loops_statement(else_, current_loop, output)?; + } + + Ok(()) + } + + p::Statement::Compound(block) => resolve_loops_block(block, current_loop, output), + + p::Statement::Return(_) | p::Statement::Expression(_) | p::Statement::Null => Ok(()), // nothing to do + } +}