From f43aa36432a74362fbf52ecb9160659c44652ff4 Mon Sep 17 00:00:00 2001 From: Garrit Franke Date: Wed, 9 Dec 2020 18:07:51 +0100 Subject: [PATCH] Add while loop --- README.md | 1 + TODO | 4 ++-- examples/playground.sb | 7 ++++--- src/generator/js.rs | 11 ++++++++++- src/lexer/mod.rs | 4 ++++ src/parser/rules.rs | 9 +++++++++ src/parser/tests.rs | 15 +++++++++++++++ 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b20394f..7018116 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # The Sabre Programming language [![builds.sr.ht status](https://builds.sr.ht/~garritfra/sabre/commits/ci.yml.svg)](https://builds.sr.ht/~garritfra/sabre/commits/ci.yml?) +[![docs](https://img.shields.io/badge/docs-mdBook-blue.svg)](https://garritfra.github.io/sabre/latest) Sabre is a bullshit-free (©) programming language that gets out of your way. It is meant to "just work", without adding unnecessary and bloated language features. diff --git a/TODO b/TODO index 1d8de1d..38afd4a 100644 --- a/TODO +++ b/TODO @@ -2,8 +2,8 @@ # Features -- Add CLI -- Add else/if else branch to conditionals +- Variable reassignment +- Array access syntax - Add while loop - Add for loop - Add type system diff --git a/examples/playground.sb b/examples/playground.sb index 16dd4d9..b10cc29 100644 --- a/examples/playground.sb +++ b/examples/playground.sb @@ -1,5 +1,6 @@ -// There are no nested expressions yet, so we have to hack a little bit - fn main() { - let arr = ["Foo", "Bar", "Baz"] + let x = 5 * 2 + while x > 0 { + return x + } } \ No newline at end of file diff --git a/src/generator/js.rs b/src/generator/js.rs index 964859e..a7f0ab9 100644 --- a/src/generator/js.rs +++ b/src/generator/js.rs @@ -73,7 +73,7 @@ fn generate_statement(statement: Statement) -> String { generate_conditional(expr, *if_state, else_state.map(|x| *x)) } Statement::Block(_) => generate_block(statement), - Statement::While(_, _) => todo!(), + Statement::While(expr, body) => generate_while_loop(expr, *body), } } @@ -89,6 +89,15 @@ fn generate_expression(expr: Expression) -> String { } } +fn generate_while_loop(expr: Expression, body: Statement) -> String { + let mut out_str = String::from("while ("); + + out_str += &generate_expression(expr); + out_str += ") "; + out_str += &generate_block(body); + out_str +} + fn generate_array(elements: Vec) -> String { let mut out_str = String::from("["); diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 4b306dc..3da70c3 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -126,6 +126,9 @@ pub enum Keyword { If, Else, Return, + While, + Break, + Continue, Function, Boolean, Unknown, @@ -323,6 +326,7 @@ impl Cursor<'_> { c if c == "true" || c == "false" => Keyword::Boolean, c if c == "let" => Keyword::Let, c if c == "return" => Keyword::Return, + c if c == "while" => Keyword::While, _ => Keyword::Unknown, } } diff --git a/src/parser/rules.rs b/src/parser/rules.rs index c70f663..455ecc6 100644 --- a/src/parser/rules.rs +++ b/src/parser/rules.rs @@ -94,6 +94,7 @@ impl Parser { TokenKind::Keyword(Keyword::Let) => self.parse_declare(), TokenKind::Keyword(Keyword::Return) => self.parse_return(), TokenKind::Keyword(Keyword::If) => self.parse_conditional_statement(), + TokenKind::Keyword(Keyword::While) => self.parse_while_loop(), TokenKind::Identifier(_) => { let ident = self.match_identifier()?; if let Ok(_) = self.peek_token(TokenKind::BraceOpen) { @@ -217,6 +218,14 @@ impl Parser { Ok(Expression::Array(elements)) } + fn parse_while_loop(&mut self) -> Result { + self.match_keyword(Keyword::While)?; + let expr = self.parse_expression()?; + let body = self.parse_block()?; + + Ok(Statement::While(expr, Box::new(body))) + } + fn parse_conditional_statement(&mut self) -> Result { self.match_keyword(Keyword::If)?; let condition = self.parse_expression()?; diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 9d69581..139f8d6 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -376,3 +376,18 @@ fn test_string_array() { let tree = parse(tokens, Some(raw.to_string())); assert!(tree.is_ok()) } + +#[test] +fn test_basic_while_loop() { + let raw = " + fn main() { + let x = 5 * 2 + while x > 0 { + return x + } + } + "; + let tokens = tokenize(raw); + let tree = parse(tokens, Some(raw.to_string())); + assert!(tree.is_ok()) +}