diff --git a/src/lexer.rs b/src/lexer.rs index 2fcb4bb4..8474e8cb 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -94,6 +94,12 @@ pub enum Token { #[token("loop")] Loop, + #[token("break")] + Break, + + #[token("hopback")] + HopBack, + // Literals /// True, False #[regex("true|false", get_bool)] @@ -115,6 +121,9 @@ pub enum Token { #[regex(r"[a-zA-Z_][a-zA-Z_0-9]*", get_iden)] Identifier(String), + #[regex("nul")] + Nul, + #[token("(")] LeftParenthesis, diff --git a/src/parser/item.rs b/src/parser/item.rs index e9e8a84d..2e229680 100644 --- a/src/parser/item.rs +++ b/src/parser/item.rs @@ -3,7 +3,7 @@ use crate::variables::Value; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Iden(pub String); -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Item { Expr(Expr), Stmt(Stmt), @@ -15,13 +15,19 @@ impl From for Item { } } +impl From for Item { + fn from(i: Iden) -> Self { + Item::Expr(Expr::Identifier(i)) + } +} + impl From for Item { fn from(s: Stmt) -> Self { Item::Stmt(s) } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Expr { Add { left: Box, right: Box }, Subtract { left: Box, right: Box }, @@ -43,7 +49,7 @@ impl From for Expr { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Stmt { VariableDeclaration { iden: Iden, @@ -66,6 +72,11 @@ pub enum Stmt { iden: Iden, args: Vec, }, + Loop { + body: Vec, + }, + Break, + HopBack, Print(Expr), Melo(Iden), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d58ed000..a3393df3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -67,10 +67,21 @@ impl<'a> Parser<'a> { | Token::Boolean(_) | Token::Integer(_) | Token::String(_) + | Token::Nul | Token::LogNot => self.parse_ops(token), // Control flow Token::If => self.if_cond(), + Token::Loop => self.loop_block(), + + Token::HopBack => { + self.require(Token::Semicolon)?; + Ok(Stmt::HopBack.into()) + } + Token::Break => { + self.require(Token::Semicolon)?; + Ok(Stmt::Break.into()) + } // Declarations Token::Variable => self.variable_declaration(), @@ -194,6 +205,14 @@ impl<'a> Parser<'a> { .into()) } + /// Parse loop + pub fn loop_block(&mut self) -> ParseResult { + self.require(Token::LeftBrace)?; + let body = self.parse_body()?; + + Ok(Stmt::Loop { body }.into()) + } + /// T-Dark block parsing pub fn tdark_block(&mut self) -> Result, Error> { self.require(Token::LeftBrace)?; @@ -221,3 +240,62 @@ impl<'a> Parser<'a> { Ok(body) } } + +#[cfg(test)] +mod tests { + use super::*; + use Expr::*; + use Stmt::*; + + #[test] + fn control_flow() { + let code = r#"loop { var a = 3 + 2; if (a == 5) { break; } }"#; + + let expected: &[Item] = &[Item::Stmt(Loop { + body: vec![ + VariableDeclaration { + iden: Iden("a".to_owned()), + init: Some(Box::new( + Add { + left: Box::new(Literal(Value::Int(3))), + right: Box::new(Literal(Value::Int(2))), + } + .into(), + )), + } + .into(), + If { + cond: Box::new( + Eq { + left: Box::new(Iden("a".to_owned()).into()), + right: Box::new(Literal(Value::Int(5)).into()), + } + .into(), + ), + body: vec![Break.into()], + } + .into(), + ], + })]; + let ast = Parser::new(code).init().unwrap(); + + assert_eq!(ast, expected) + } + + #[test] + fn tdark() { + let code = r#"T-Dark { var lang = nul; lang print; }"#; + let expected: &[Item] = &[ + VariableDeclaration { + iden: Iden("script".to_owned()), + init: Some(Box::new(Literal(Value::Nul).into())), + } + .into(), + Print(Iden("script".to_owned()).into()).into(), + ]; + + let ast = Parser::new(code).init().unwrap(); + + assert_eq!(ast, expected) + } +} diff --git a/src/parser/ops.rs b/src/parser/ops.rs index c400925c..a8104ca6 100644 --- a/src/parser/ops.rs +++ b/src/parser/ops.rs @@ -83,6 +83,7 @@ impl<'a> Parser<'a> { } else { i }))), + Token::Nul => Ok(Expr::Literal(Value::Nul)), Token::LogNot => { let next = self.lexer.next(); Ok(Expr::Not(Box::new(self.parse_expr(next)?))) diff --git a/src/variables.rs b/src/variables.rs index 10927c14..8c94fba6 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -17,7 +17,7 @@ impl From for bool { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Value { Str(String), Int(i32),