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>"),
|
("Block", "statements: Vec<Stmt>"),
|
||||||
("Print", "expression: Expr"),
|
("Print", "expression: Expr"),
|
||||||
("Var", "name: Token, initializer: Option<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");
|
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_name = field_components.next().unwrap();
|
||||||
let field_raw_type = 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
|
// Filthy hack for box types, but we don't use any other non-referential type right now
|
||||||
let stripped_field_type = field_raw_type
|
let arg_type = if field_raw_type.starts_with("Box<") {
|
||||||
.strip_prefix("Box<")
|
format!(
|
||||||
.and_then(|val| val.strip_suffix('>'))
|
"&{}",
|
||||||
.unwrap_or(field_raw_type);
|
field_raw_type.replacen("Box<", "", 1).replacen('>', "", 1)
|
||||||
let arg_type = format!("&{stripped_field_type}");
|
)
|
||||||
|
} 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 {
|
if i == fields.len() - 1 {
|
||||||
write!(w, "{field_name}: {arg_type}")?;
|
write!(w, "{field_name}: {arg_type}")?;
|
||||||
|
@ -241,6 +250,8 @@ fn write_comma_separated_field_names_with_ref<W: Write>(
|
||||||
// Filthy box hack again
|
// Filthy box hack again
|
||||||
let reference_name = if field_type.starts_with("Box") {
|
let reference_name = if field_type.starts_with("Box") {
|
||||||
format!("{field_name}.as_ref()")
|
format!("{field_name}.as_ref()")
|
||||||
|
} else if field_type.starts_with("Option<Box") {
|
||||||
|
format!("{field_name}.as_deref()")
|
||||||
} else {
|
} else {
|
||||||
format!("&{field_name}")
|
format!("&{field_name}")
|
||||||
};
|
};
|
||||||
|
|
16
src/eval.rs
16
src/eval.rs
|
@ -232,6 +232,22 @@ impl StmtVisitor<Result<(), ScriptError>> for InterpreterRunner<'_> {
|
||||||
|
|
||||||
result
|
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(
|
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>>(
|
fn parse_statement<I: Iterator<Item = Token>>(
|
||||||
iter: &mut Peekable<I>,
|
iter: &mut Peekable<I>,
|
||||||
) -> Result<ParsedStatement, ParseError> {
|
) -> 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(
|
parse_statement_containing_expression(
|
||||||
iter,
|
iter,
|
||||||
|expression| Stmt::Print { expression },
|
|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<
|
fn parse_statement_containing_expression<
|
||||||
I: Iterator<Item = Token>,
|
I: Iterator<Item = Token>,
|
||||||
F: Fn(Expr) -> Stmt,
|
F: Fn(Expr) -> Stmt,
|
||||||
|
|
Loading…
Reference in New Issue