Add support for if statements
parent
5b1194ad35
commit
6aa29e25f4
21
build.rs
21
build.rs
|
@ -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}")
|
||||
};
|
||||
|
|
16
src/eval.rs
16
src/eval.rs
|
@ -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(
|
||||
|
|
35
src/parse.rs
35
src/parse.rs
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue