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
Binary file added resources/succeeds
Binary file not shown.
3 changes: 3 additions & 0 deletions resources/succeeds.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
15 changes: 15 additions & 0 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ pub enum Keyword {
Return,
If,
Else,
Do,
While,
For,
Break,
Continue,
}

impl std::fmt::Display for Keyword {
Expand All @@ -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"),
}
}
}
Expand All @@ -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(()),
}
}
Expand Down
18 changes: 15 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> Self {
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
216 changes: 215 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,31 @@ impl Declaration {
}
}

#[derive(Debug, Clone)]
pub enum ForInit {
D(Declaration),
E(Option<Expression>),
}

impl ForInit {
fn parse(tokens: &mut TokenIter, output: &Output) -> Result<Self, ParseError> {
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),
Expand All @@ -260,6 +285,25 @@ pub enum Statement {
else_: Option<Box<Statement>>,
},
Compound(Block),
Break(Option<Identifier>),
Continue(Option<Identifier>),
While {
cond: Expression,
body: Box<Statement>,
label: Identifier,
},
DoWhile {
body: Box<Statement>,
cond: Expression,
label: Identifier,
},
For {
init: ForInit,
cond: Option<Expression>,
post: Option<Expression>,
body: Box<Statement>,
label: Identifier,
},
Null,
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(),
});
Expand All @@ -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_));
}
}
}
}
Expand Down
Loading