From 2a014fd2107e7566f9b5d7b5abc96b53e3ea960e Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 28 Jan 2022 19:34:31 +0100 Subject: [PATCH] Implement while loop --- src/interpreter.rs | 14 +++++++++++++- src/lexer.rs | 12 ++++++++++++ src/parser.rs | 39 ++++++++++++++++++++++++++++++++++----- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/interpreter.rs b/src/interpreter.rs index 4ca6132..d426f7a 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -29,6 +29,18 @@ impl Interpreter { let result = self.resolve_expr(rhs); self.vartable.insert(name, result); }, + Stmt::While(condition, body) => { + loop { + // Check condition + match self.resolve_expr(condition.clone()) { + Value::I64(val) if val == 0 => break, + Value::I64(_) => (), + } + + // Execute loop body + self.run(body.clone()); + } + } } } } @@ -59,7 +71,7 @@ impl Interpreter { _ => panic!("Runtime error: Left hand side of assignment must be an identifier"), } } - + let lhs = self.resolve_expr(lhs); let rhs = self.resolve_expr(rhs); diff --git a/src/lexer.rs b/src/lexer.rs index e281f9a..90d7a10 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -13,12 +13,21 @@ pub enum Token { /// Right parentheses (')') RParen, + /// Left brace ({) + LBrace, + + /// Right brace (}) + RBrace, + /// Identifier (variable / function / ... name) Ident(String), /// Let identifier (let) Let, + /// While (while) + While, + /// Assignment (single equal) (=) Assign, @@ -158,6 +167,8 @@ impl<'a> Lexer<'a> { '>' => tokens.push(Token::Gt), '=' => tokens.push(Token::Assign), ';' => tokens.push(Token::Semicolon), + '{' => tokens.push(Token::LBrace), + '}' => tokens.push(Token::RBrace), 'a'..='z' | 'A'..='Z' | '_' => { let mut ident = String::from(ch); @@ -172,6 +183,7 @@ impl<'a> Lexer<'a> { "true" => tokens.push(Token::I64(1)), "false" => tokens.push(Token::I64(0)), "let" => tokens.push(Token::Let), + "while" => tokens.push(Token::While), _ => tokens.push(Token::Ident(ident)), } } diff --git a/src/parser.rs b/src/parser.rs index 87a03fe..020f60d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -73,6 +73,7 @@ pub struct Ast { pub enum Stmt { Expr(Expr), Let(String, Expr), + While(Expr, Ast), } #[derive(Debug, PartialEq, Eq, Clone)] @@ -104,7 +105,8 @@ expr = expr_bor ## Statements stmt_expr = expr stmt_let = "let" IDENT "=" expr -stmt = stmt_expr | stmt_let (";")* +stmt_while = "while" expr "{" (stmt)* "}" +stmt = stmt_expr | stmt_let | stmt_while */ struct Parser> { @@ -128,17 +130,39 @@ impl> Parser { continue; } Token::EoF => break, + Token::RBrace => break, Token::Let => self.parse_let_stmt(), + Token::While => self.parse_while(), // By default try to parse an expression _ => Stmt::Expr(self.parse_expr()), }; prog.push(stmt); } - + Ast { prog } } + fn parse_while(&mut self) -> Stmt { + if !matches!(self.next(), Token::While) { + panic!("Error parsing while: Expected while token"); + } + + let condition = self.parse_expr(); + + if !matches!(self.next(), Token::LBrace) { + panic!("Error parsing while: Expected '{{' token"); + } + + let body = self.parse(); + + if !matches!(self.next(), Token::RBrace) { + panic!("Error parsing while: Expected '}}' token"); + } + + Stmt::While(condition, body) + } + fn parse_let_stmt(&mut self) -> Stmt { if !matches!(self.next(), Token::Let) { panic!("Error parsing let: Expected let token"); @@ -257,8 +281,11 @@ impl BinOpType { #[cfg(test)] mod tests { - use super::{parse, Expr, BinOpType}; - use crate::{lexer::Token, parser::{Stmt, Ast}}; + use super::{parse, BinOpType, Expr}; + use crate::{ + lexer::Token, + parser::{Ast, Stmt}, + }; #[test] fn test_parser() { @@ -285,7 +312,9 @@ mod tests { Expr::I64(4).into(), ); - let expected = Ast { prog: vec![Stmt::Expr(expected)] }; + let expected = Ast { + prog: vec![Stmt::Expr(expected)], + }; let actual = parse(tokens); assert_eq!(expected, actual);