From f048048593f2854366b6e3c3835a6b3ab7756997 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Tue, 25 Mar 2025 20:37:14 +0100 Subject: [PATCH 1/5] Ch. 8: Lexer --- src/lexer.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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(()), } } From a22df9ff12946cd6100cec6088010820c41e60b9 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Tue, 25 Mar 2025 22:06:09 +0100 Subject: [PATCH 2/5] Ch. 8: Parser --- resources/succeeds.c | 3 ++ src/parser.rs | 119 +++++++++++++++++++++++++++++++++++++++++++ src/validate.rs | 1 + 3 files changed, 123 insertions(+) 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/parser.rs b/src/parser.rs index b2be7dd..d3212c7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -250,6 +250,31 @@ impl Declaration { } } +#[derive(Debug, Clone)] +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,22 @@ pub enum Statement { else_: Option>, }, Compound(Block), + Break, + Continue, + While { + cond: Expression, + body: Box, + }, + DoWhile { + body: Box, + cond: Expression, + }, + For { + init: ForInit, + cond: Option, + post: Option, + body: Box, + }, Null, } @@ -305,6 +346,83 @@ 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) + } + + Token::Keyword(Keyword::Continue) => { + let _ = tokens.next().expect("token must be continue keyword"); + take_punct(tokens, Punct::Semicolon, output)?; + Ok(Self::Continue) + } + + 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)?); + + Ok(Self::While { cond, body }) + } + + 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)?; + + Ok(Self::DoWhile { body, cond }) + } + + 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)?); + + Ok(Self::For { + init, + cond, + post, + body, + }) + } + Token::Punct(Punct::Semicolon) => { let _ = tokens.next().expect("token must be semicolon"); Ok(Self::Null) @@ -380,6 +498,7 @@ impl Statement { block_item.emit_tacky(instructions); } } + _ => todo!("emit tacky for loops"), } } } diff --git a/src/validate.rs b/src/validate.rs index 8d2a02b..390b670 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -133,6 +133,7 @@ fn resolve_statement( resolve_block(block, &mut variable_map, output) } p::Statement::Null => Ok(()), // nothing to do + _ => unimplemented!(), } } From 8c0226b900df11363a53bf98fb12c1b29f54b3af Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Thu, 27 Mar 2025 23:00:05 +0100 Subject: [PATCH 3/5] Ch. 8: Validation --- src/lib.rs | 14 +++++-- src/parser.rs | 24 ++++++++---- src/validate.rs | 101 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 792483f..d1b3f70 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,16 @@ 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}")) + } } impl std::fmt::Display for Identifier { @@ -225,7 +232,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 d3212c7..281f321 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -251,7 +251,7 @@ impl Declaration { } #[derive(Debug, Clone)] -enum ForInit { +pub enum ForInit { D(Declaration), E(Option), } @@ -285,21 +285,24 @@ pub enum Statement { else_: Option>, }, Compound(Block), - Break, - Continue, + 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, } @@ -349,13 +352,13 @@ impl Statement { Token::Keyword(Keyword::Break) => { let _ = tokens.next().expect("token must be break keyword"); take_punct(tokens, Punct::Semicolon, output)?; - Ok(Self::Break) + 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) + Ok(Self::Continue(None)) } Token::Keyword(Keyword::While) => { @@ -367,7 +370,9 @@ impl Statement { let body = Box::new(Statement::parse(tokens, output)?); - Ok(Self::While { cond, body }) + let label = Identifier::new_loop(); + + Ok(Self::While { cond, body, label }) } Token::Keyword(Keyword::Do) => { @@ -387,7 +392,9 @@ impl Statement { take_punct(tokens, Punct::Semicolon, output)?; - Ok(Self::DoWhile { body, cond }) + let label = Identifier::new_loop(); + + Ok(Self::DoWhile { body, cond, label }) } Token::Keyword(Keyword::For) => { @@ -415,11 +422,14 @@ impl Statement { let body = Box::new(Statement::parse(tokens, output)?); + let label = Identifier::new_loop(); + Ok(Self::For { init, cond, post, body, + label, }) } diff --git a/src/validate.rs b/src/validate.rs index 390b670..42d3007 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -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,13 +128,52 @@ 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 - _ => unimplemented!(), } } @@ -187,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 + } +} From 220a3c2ac0532fe14f32b580d9bc49180b1e07f7 Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Thu, 27 Mar 2025 23:30:56 +0100 Subject: [PATCH 4/5] Ch. 8: Tacky --- resources/succeeds | Bin 0 -> 4272 bytes src/lib.rs | 4 +++ src/parser.rs | 87 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100755 resources/succeeds diff --git a/resources/succeeds b/resources/succeeds new file mode 100755 index 0000000000000000000000000000000000000000..3332370dd547d124d33e959d378f929d4645c4e3 GIT binary patch literal 4272 zcmeHKL2DCH5PsW4Vzo8ZgD8sZNvwyq7!UPU?LL~KXwfbdB|KWQl3)|1-C%nN6qX)B zF8%=TwkJL2P%vO%dkC0A4*_%NAwnT31c@X_5Wm^om~1Q_J?+5k%r|e|>^HM}nJ+(& ze+3wme2lRnXb@V4v(039j9rF?p+YR&o#RjW<3|*Ai{yqUBC2x)6=IPuExJ>z`(y4H zbMPsINr@YY5W2RZ+rEw6^-|~13=TpLDZ(!Cv#owCL@N0vo{Wlk^2IA04D#E-K&aG` zbBS=5OMjiN-<#Ki`G@zpGT-eL{kZyc`upfyq%()I6}ipWmX(6XG$J7fPXkW@6NgLC z*TCezg_U8?LKmR@Lg)YD+@Cw>)A(fQ2y`;FzBaiUUzv<$^!n8WWN^|*k~ zlg)KqOHACG0I&E85+T>2R6mBtY1nZG)4V+KCg4rLn}9a~Zvx%~ya{*{@Fw6*;6F}4 z4QIZEje6MR9oh$!Y^1|qewCC=q@!fjj*?N^CU5$!BektaY7^lm!VM$Qkl;7q*G*0f zB{R}>rVpFEP3uMNnvrf>uu9uPEnxDh{R*{;J4Yrzq%@OP$QbF$8IzacvEVyiHqvFL zhfw|ysRv+npaefeDxd{`Kn4d{6<949yhJ=o@GG^P$&2*1R-}kn5wZQ2x(~Mvw}3hY zs#8ZD032l0JZu4d Self { + Self(format!("{prefix}{}", self.0)) + } } impl std::fmt::Display for Identifier { diff --git a/src/parser.rs b/src/parser.rs index 281f321..9665b3f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -508,7 +508,92 @@ impl Statement { block_item.emit_tacky(instructions); } } - _ => todo!("emit tacky for loops"), + 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 start = label.clone().with_prefix("continue_"); + let end = label.with_prefix("break_"); + + instructions.push(crate::tacky::Instruction::Label(start.clone())); + + let condition = cond.emit_tacky(instructions); + instructions.push(crate::tacky::Instruction::JumpIfZero { + condition, + target: end.clone(), + }); + + body.emit_tacky(instructions); + + instructions.push(crate::tacky::Instruction::Jump { target: start }); + instructions.push(crate::tacky::Instruction::Label(end)); + } + + 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_)); + } } } } From 31667eff257f9b45184bb6fac8457b436e98293e Mon Sep 17 00:00:00 2001 From: Herman Skogseth Date: Thu, 27 Mar 2025 23:34:49 +0100 Subject: [PATCH 5/5] Some minor cleanup --- src/parser.rs | 14 +++++++------- src/validate.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 9665b3f..7b2e962 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -490,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(), }); @@ -517,21 +517,21 @@ impl Statement { instructions.push(crate::tacky::Instruction::Jump { target }); } Self::While { cond, body, label } => { - let start = label.clone().with_prefix("continue_"); - let end = label.with_prefix("break_"); + let continue_ = label.clone().with_prefix("continue_"); + let break_ = label.with_prefix("break_"); - instructions.push(crate::tacky::Instruction::Label(start.clone())); + instructions.push(crate::tacky::Instruction::Label(continue_.clone())); let condition = cond.emit_tacky(instructions); instructions.push(crate::tacky::Instruction::JumpIfZero { condition, - target: end.clone(), + target: break_.clone(), }); body.emit_tacky(instructions); - instructions.push(crate::tacky::Instruction::Jump { target: start }); - instructions.push(crate::tacky::Instruction::Label(end)); + instructions.push(crate::tacky::Instruction::Jump { target: continue_ }); + instructions.push(crate::tacky::Instruction::Label(break_)); } Self::DoWhile { body, cond, label } => { diff --git a/src/validate.rs b/src/validate.rs index 42d3007..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), } } }