diff --git a/build.rs b/build.rs index beaf7ac..7f9a7ce 100644 --- a/build.rs +++ b/build.rs @@ -68,6 +68,7 @@ fn do_ast_codegen(mut output: W) { "If", "condition: Expr, then_branch: Box, else_branch: Option>", ), + ("While", "condition: Expr, body: Box"), ]); define_ast(&mut output, "Stmt", &statement_types).expect("failed to generate ast values"); diff --git a/src/eval.rs b/src/eval.rs index a39bfec..d50ecf5 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -269,6 +269,14 @@ impl StmtVisitor> for InterpreterRunner<'_> { Ok(()) } } + + fn visit_while(&mut self, condition: &Expr, body: &Stmt) -> Result<(), ScriptError> { + while self.visit_expr(condition)?.is_truthy() { + self.visit_stmt(body)?; + } + + Ok(()) + } } fn convert_arithmetic_operands( diff --git a/src/parse.rs b/src/parse.rs index b6b443c..5e6dc3d 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -175,6 +175,8 @@ fn parse_statement>( ) -> Result { if let Ok(token) = match_token_kind!(iter, TokenKind::If) { parse_if_statement(iter, &token) + } else if let Ok(token) = match_token_kind!(iter, TokenKind::While) { + parse_while_statement(iter, &token) } else if match_token_kind!(iter, TokenKind::Print).is_ok() { parse_statement_containing_expression( iter, @@ -205,7 +207,7 @@ fn parse_if_statement>( let condition = parse_expression(iter)?; match_token_kind!(iter, TokenKind::RightParen).map_err(|_| ParseError { - message: "Expected ')' after 'if'".to_string(), + message: "Expected ')' after 'if' condition".to_string(), line: if_token.line().into(), })?; @@ -224,6 +226,31 @@ fn parse_if_statement>( Ok(ParsedStatement::Stmt { stmt: if_stmt }) } +fn parse_while_statement>( + iter: &mut Peekable, + while_token: &Token, +) -> Result { + match_token_kind!(iter, TokenKind::LeftParen).map_err(|_| ParseError { + message: "Expected '(' after 'while'".to_string(), + line: while_token.line().into(), + })?; + + let condition = parse_expression(iter)?; + + match_token_kind!(iter, TokenKind::RightParen).map_err(|_| ParseError { + message: "Expected ')' after 'while' condition".to_string(), + line: while_token.line().into(), + })?; + + let body = parse_statement(iter).and_then(ensure_explicit_statement)?; + let while_stmt = Stmt::While { + condition, + body: Box::new(body), + }; + + Ok(ParsedStatement::Stmt { stmt: while_stmt }) +} + fn parse_statement_containing_expression< I: Iterator, F: Fn(Expr) -> Stmt,