Add support for if statements

master
Nick Krichevsky 2024-05-23 11:43:02 -04:00
parent 5b1194ad35
commit 6aa29e25f4
3 changed files with 66 additions and 6 deletions

View File

@ -64,6 +64,10 @@ fn do_ast_codegen<W: Write>(mut output: W) {
("Block", "statements: Vec<Stmt>"),
("Print", "expression: Expr"),
("Var", "name: Token, initializer: Option<Expr>"),
(
"If",
"condition: Expr, then_branch: Box<Stmt>, else_branch: Option<Box<Stmt>>",
),
]);
define_ast(&mut output, "Stmt", &statement_types).expect("failed to generate ast values");
@ -156,11 +160,16 @@ fn define_visitor_trait_method<W: Write>(
let field_name = field_components.next().unwrap();
let field_raw_type = field_components.next().unwrap();
// Filthy hack for box types, but we don't use any other non-referential type right now
let stripped_field_type = field_raw_type
.strip_prefix("Box<")
.and_then(|val| val.strip_suffix('>'))
.unwrap_or(field_raw_type);
let arg_type = format!("&{stripped_field_type}");
let arg_type = if field_raw_type.starts_with("Box<") {
format!(
"&{}",
field_raw_type.replacen("Box<", "", 1).replacen('>', "", 1)
)
} else if field_raw_type.starts_with("Option<Box<") {
field_raw_type.replacen("Box<", "&", 1).replacen('>', "", 1)
} else {
format!("&{}", field_raw_type)
};
if i == fields.len() - 1 {
write!(w, "{field_name}: {arg_type}")?;
@ -241,6 +250,8 @@ fn write_comma_separated_field_names_with_ref<W: Write>(
// Filthy box hack again
let reference_name = if field_type.starts_with("Box") {
format!("{field_name}.as_ref()")
} else if field_type.starts_with("Option<Box") {
format!("{field_name}.as_deref()")
} else {
format!("&{field_name}")
};

View File

@ -232,6 +232,22 @@ impl StmtVisitor<Result<(), ScriptError>> for InterpreterRunner<'_> {
result
}
fn visit_if(
&mut self,
condition: &Expr,
then_branch: &Stmt,
else_branch: Option<&Stmt>,
) -> Result<(), ScriptError> {
let evaluated_cond = self.visit_expr(condition)?;
if evaluated_cond.is_truthy() {
self.visit_stmt(then_branch)
} else if let Some(branch) = else_branch {
self.visit_stmt(branch)
} else {
Ok(())
}
}
}
fn convert_arithmetic_operands(

View File

@ -168,7 +168,9 @@ fn parse_var_declaration<I: Iterator<Item = Token>>(
fn parse_statement<I: Iterator<Item = Token>>(
iter: &mut Peekable<I>,
) -> Result<ParsedStatement, ParseError> {
if match_token_kind!(iter, TokenKind::Print).is_ok() {
if let Ok(token) = match_token_kind!(iter, TokenKind::If) {
parse_if_statement(iter, &token)
} else if match_token_kind!(iter, TokenKind::Print).is_ok() {
parse_statement_containing_expression(
iter,
|expression| Stmt::Print { expression },
@ -186,6 +188,37 @@ fn parse_statement<I: Iterator<Item = Token>>(
}
}
fn parse_if_statement<I: Iterator<Item = Token>>(
iter: &mut Peekable<I>,
if_token: &Token,
) -> Result<ParsedStatement, ParseError> {
match_token_kind!(iter, TokenKind::LeftParen).map_err(|_| ParseError {
message: "Expected '(' after 'if'".to_string(),
line: if_token.line().into(),
})?;
let condition = parse_expression(iter)?;
match_token_kind!(iter, TokenKind::RightParen).map_err(|_| ParseError {
message: "Expected ')' after 'if'".to_string(),
line: if_token.line().into(),
})?;
let then_branch = parse_statement(iter).and_then(ensure_explicit_statement)?;
let else_branch = match match_token_kind!(iter, TokenKind::Else) {
Ok(_) => Some(parse_statement(iter).and_then(ensure_explicit_statement)?),
Err(_) => None,
};
let if_stmt = Stmt::If {
condition,
then_branch: Box::new(then_branch),
else_branch: else_branch.map(Box::new),
};
Ok(ParsedStatement::Stmt { stmt: if_stmt })
}
fn parse_statement_containing_expression<
I: Iterator<Item = Token>,
F: Fn(Expr) -> Stmt,