diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/sloth/src/lexer.rs | 8 | ||||
| -rw-r--r-- | crates/sloth/src/main.rs | 14 | ||||
| -rw-r--r-- | crates/sloth/src/parser/ast.rs | 81 | ||||
| -rw-r--r-- | crates/sloth/src/parser/expr.rs | 267 | ||||
| -rw-r--r-- | crates/sloth/src/parser/mod.rs | 56 | ||||
| -rw-r--r-- | crates/sloth/src/parser/stmt.rs | 631 |
6 files changed, 1048 insertions, 9 deletions
diff --git a/crates/sloth/src/lexer.rs b/crates/sloth/src/lexer.rs index 62b1a8d..0afaf1c 100644 --- a/crates/sloth/src/lexer.rs +++ b/crates/sloth/src/lexer.rs @@ -86,6 +86,7 @@ pub enum TokenType { Var, Fn, + Return, If, Else, @@ -292,12 +293,14 @@ impl<'a> Iterator for Lexer<'a> { let tt = match self.window { ['#', '#', ..] => { self.advance_while(|it| it[0] != '\n'); - TokenType::DocComment + // TODO: TokenType::DocComment + return self.next(); } ['#', ..] => { self.advance_while(|it| it[0] != '\n'); - TokenType::Comment + // TODO: okenType::Comment + return self.next(); } // Blocks @@ -382,6 +385,7 @@ impl<'a> Iterator for Lexer<'a> { "val" => TokenType::Val, "var" => TokenType::Var, "fn" => TokenType::Fn, + "return" => TokenType::Return, "if" => TokenType::If, "else" => TokenType::Else, "while" => TokenType::While, diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index 6502f19..0d33e91 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -13,6 +13,7 @@ use std::{env, fs}; use itertools::Itertools; use lexer::Lexer; +use parser::AstParser; fn main() { let args = env::args().collect_vec(); @@ -29,10 +30,13 @@ fn main() { return; }; - let lexer = Lexer::new(&source); - for token in lexer { - println!("{token:?}"); - } + let tokens = Lexer::new(&source).collect_vec(); + // for t in &tokens{ + // println!("{:#?}", t); + // } + let mut parser = AstParser::new(tokens); + // println!("{:#?}", parser); + let parsed = &parser.parse(); - // TODO: + println!("{:?}", parsed); } diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 85309a5..3c8cdeb 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,3 +1,4 @@ +#[derive(Debug, PartialEq)] pub enum BinaryOp { Add, Con, @@ -5,22 +6,44 @@ pub enum BinaryOp { Mul, Pow, Div, + Mod, BWSftRight, BWSftLeft, BWAnd, BWOr, BWXor, -} + Lt, + Gt, + LtEq, + GtEq, + EqEq, + NotEq, + LogAnd, + LogOr, + Range, +} +#[derive(Debug, PartialEq)] pub enum UnaryOp { Not, Neg, BWComp, } - +#[derive(Debug, PartialEq)] +pub enum Literal { + Integer(i128), + Float(f64), + Bool(bool), + Char(char), + String(String), + Regex(String), + List(Vec<Expr>), +} +#[derive(Debug, PartialEq)] pub enum Expr { + Grouping(Box<Expr>), BinaryOp { op: BinaryOp, lhs: Box<Expr>, @@ -30,4 +53,58 @@ pub enum Expr { op: UnaryOp, value: Box<Expr>, }, + Call { + ident: Box<Expr>, + args: Vec<Expr>, + }, + Variable(String), + Literal(Literal), + Lambda, // TODO: Lambda +} +#[derive(PartialEq, Debug)] +pub struct FuncArgs { + pub name: String, + pub typ: Option<String>, +} +#[derive(PartialEq, Debug)] +pub enum Stmt { + ExprStmt(Expr), + DefineFunction { + ident: String, + args: Option<Vec<FuncArgs>>, + body: Vec<Stmt>, + return_type: Option<String>, + }, + DefineVariable { + name: String, + value: Expr, + typ: Option<String>, + }, + DefineValue { + name: String, + value: Expr, + typ: Option<String>, + }, + AssignVariable { + name: String, + value: Expr, + }, + If { + expr: Expr, + body: Vec<Stmt>, + else_if: Vec<(Expr, Stmt)>, + els: Option<Box<Stmt>>, + }, + For { + name: String, + iter: Expr, + body: Vec<Stmt>, + }, + While { + condition: Expr, + body: Vec<Stmt>, + }, + Return { + value: Expr, + }, } diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs new file mode 100644 index 0000000..ad35d20 --- /dev/null +++ b/crates/sloth/src/parser/expr.rs @@ -0,0 +1,267 @@ +use super::ast::{BinaryOp, Expr, Literal, UnaryOp}; +use super::AstParser; +use crate::lexer::TokenType; + +/// Implementation containing parsers internal components related to expressions +impl<'a> AstParser<'a> { + // FIXME: Should probably avoid cloning token types + + pub fn expression(&mut self) -> Expr { + self.logical_or() + } + + fn unary(&mut self) -> Expr { + if !self.eof() + && matches!( + self.peek().tt, + TokenType::Bang | TokenType::Plus | TokenType::Minus + ) + { + let operator = match self.advance().unwrap().tt.clone() { + TokenType::Bang => UnaryOp::Not, + TokenType::Tilde => UnaryOp::BWComp, + TokenType::Minus => UnaryOp::Neg, + _ => panic!(), + }; + + let rhs = self.unary(); + return Expr::UnaryOp { + op: (operator), + value: (Box::new(rhs)), + }; + } + + self.call() + } + + fn call(&mut self) -> Expr { + let mut expr = self.primary(); + + if self.advance_if_eq(&TokenType::OpeningParen) { + let mut arguments = Vec::<Expr>::new(); + + if self.peek().tt != TokenType::ClosingParen { + loop { + arguments.push(self.expression()); + if !self.advance_if_eq(&TokenType::Comma) { + break; + } + } + } + + self.consume( + TokenType::ClosingParen, + "Expected ')' to close off function call", + ); + + // let Expr::Variable(_ident) = expr else { panic!("uh oh spaghettio"); }; + + expr = Expr::Call { + ident: (Box::new(expr)), + args: (arguments), + } + } + + expr + } + + fn primary(&mut self) -> Expr { + match self.advance().unwrap().tt.clone() { + TokenType::Integer(literal) => Expr::Literal(Literal::Integer(literal)), + TokenType::Float(literal) => Expr::Literal(Literal::Float(literal)), + TokenType::Boolean(literal) => Expr::Literal(Literal::Bool(literal)), + TokenType::Character(literal) => Expr::Literal(Literal::Char(literal)), + TokenType::String(literal) => Expr::Literal(Literal::String(literal)), + TokenType::Regex(literal) => Expr::Literal(Literal::Regex(literal)), + TokenType::Identifier(ident) => { + if self.peek().tt.clone() != TokenType::OpeningParen { + Expr::Variable(ident) + } else { + Expr::Literal(Literal::String(ident)) + } + } + TokenType::OpeningParen => { + let expr = self.expression(); + self.consume(TokenType::ClosingParen, "Must end expression with ')'"); + Expr::Grouping(Box::new(expr)) + } + TokenType::OpeningBracket => { + let mut expr: Vec<Expr> = Vec::new(); + + while !self.eof() && self.peek().tt != TokenType::ClosingBracket { + let exp = self.expression(); + expr.push(exp); + + self.advance_if_eq(&TokenType::Comma); + } + self.consume(TokenType::ClosingBracket, "Expected ']' at end of list"); + Expr::Literal(Literal::List(expr)) + } + _ => unimplemented!("{:?}", self.peek()), + } + } +} + +// Macro to generate repetitive binary expressions. Things like addition, +// multiplication, exc. +macro_rules! binary_expr { + ($name:ident, $parent:ident, $pattern:pat) => { + fn $name(&mut self) -> Expr { + let mut expr = self.$parent(); + + while !self.eof() && matches!(self.peek().tt, $pattern) { + let operator = match self.advance().unwrap().tt.clone() { + TokenType::Plus => BinaryOp::Add, + TokenType::PlusPlus => BinaryOp::Con, + TokenType::Minus => BinaryOp::Sub, + TokenType::Star => BinaryOp::Mul, + TokenType::StarStar => BinaryOp::Pow, + TokenType::Slash => BinaryOp::Div, + TokenType::Perc => BinaryOp::Mod, + TokenType::DotDot => BinaryOp::Range, + + TokenType::LtLt => BinaryOp::BWSftRight, + TokenType::GtGt => BinaryOp::BWSftLeft, + TokenType::Amp => BinaryOp::BWAnd, + TokenType::Pipe => BinaryOp::BWOr, + TokenType::Caret => BinaryOp::BWXor, + + TokenType::Lt => BinaryOp::Lt, + TokenType::Gt => BinaryOp::Gt, + TokenType::LtEq => BinaryOp::LtEq, + TokenType::GtEq => BinaryOp::GtEq, + TokenType::EqEq => BinaryOp::EqEq, + TokenType::BangEq => BinaryOp::NotEq, + TokenType::AmpAmp => BinaryOp::LogAnd, + TokenType::PipePipe => BinaryOp::LogOr, + _ => panic!("uh oh spagghetio"), + }; + + let rhs = self.$parent(); + expr = Expr::BinaryOp { + op: (operator), + lhs: (Box::new(expr)), + rhs: (Box::new(rhs)), + } + } + + expr + } + }; +} + +#[rustfmt::skip] +#[allow(unused_parens)] +impl<'a> AstParser<'a> { + // Binary expressions in order of precedence from lowest to highest. + binary_expr!(logical_or , logical_and , (TokenType::PipePipe)); + binary_expr!(logical_and , range , (TokenType::AmpAmp)); + binary_expr!(range , equality , (TokenType::DotDot)); + binary_expr!(equality , comparison , (TokenType::BangEq | TokenType::EqEq)); + binary_expr!(comparison , bitwise_shifting, (TokenType::Lt | TokenType::Gt | TokenType::LtEq | TokenType::GtEq)); + binary_expr!(bitwise_shifting, additive , (TokenType::LtLt | TokenType::GtGt)); + binary_expr!(additive , multiplicative , (TokenType::Plus | TokenType::Minus)); + binary_expr!(multiplicative , unary , (TokenType::Star | TokenType::Slash | TokenType::Perc)); +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + + use super::{AstParser, BinaryOp, Expr, Literal}; + use crate::lexer::Lexer; + use crate::parser::ast::UnaryOp; + + #[test] + fn basic_expression_a() { + let lexer = Lexer::new("3 + 5 * 4"); + let tokens = lexer.collect_vec(); + + let expected_ast = Expr::BinaryOp { + op: BinaryOp::Add, + lhs: Box::new(Expr::Literal(Literal::Integer(3))), + rhs: Box::new(Expr::BinaryOp { + op: BinaryOp::Mul, + lhs: Box::new(Expr::Literal(Literal::Integer(5))), + rhs: Box::new(Expr::Literal(Literal::Integer(4))), + }), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.expression(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + + #[test] + fn basic_expression_b() { + let lexer = Lexer::new("17 - (-5 + 5) / 6"); + let tokens = lexer.collect_vec(); + + let expected_ast = Expr::BinaryOp { + op: BinaryOp::Sub, + lhs: Box::new(Expr::Literal(Literal::Integer(17))), + rhs: Box::new(Expr::BinaryOp { + op: BinaryOp::Div, + lhs: Box::new(Expr::Grouping(Box::new(Expr::BinaryOp { + op: BinaryOp::Add, + lhs: Box::new(Expr::UnaryOp { + op: UnaryOp::Neg, + value: Box::new(Expr::Literal(Literal::Integer(5))), + }), + rhs: Box::new(Expr::Literal(Literal::Integer(5))), + }))), + rhs: Box::new(Expr::Literal(Literal::Integer(6))), + }), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.expression(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + #[test] + fn basic_expression_c() { + let lexer = Lexer::new("[1, 2, 3]"); + let tokens = lexer.collect_vec(); + + let expected_ast = Expr::Literal(Literal::List(vec![ + Expr::Literal(Literal::Integer(1)), + Expr::Literal(Literal::Integer(2)), + Expr::Literal(Literal::Integer(3)), + ])); + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.expression(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + #[test] + fn basic_expression_d() { + let lexer = Lexer::new("1 .. 17"); + let tokens = lexer.collect_vec(); + + let expected_ast = Expr::BinaryOp { + op: (BinaryOp::Range), + lhs: (Box::new(Expr::Literal(Literal::Integer(1)))), + rhs: (Box::new(Expr::Literal(Literal::Integer(17)))), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.expression(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } +} diff --git a/crates/sloth/src/parser/mod.rs b/crates/sloth/src/parser/mod.rs index 851c0bc..9d77acc 100644 --- a/crates/sloth/src/parser/mod.rs +++ b/crates/sloth/src/parser/mod.rs @@ -1 +1,57 @@ pub mod ast; +pub mod expr; +pub mod stmt; + +use crate::lexer::{Token, TokenType}; +#[derive(Debug)] +pub struct AstParser<'a> { + tokens: Vec<Token<'a>>, + index: usize, +} + +/// Implementation containing utilities used by the parsers internal components +impl<'a> AstParser<'a> { + pub fn new(tokens: Vec<Token<'a>>) -> Self { + Self { tokens, index: 0 } + } + pub fn peek(&self) -> &Token { + &self.tokens[self.index] + } + + pub fn advance(&mut self) -> Option<&Token> { + if self.eof() { + return None; + } + + self.index += 1; + Some(&self.tokens[self.index - 1]) + } + + pub fn advance_if(&mut self, next: impl FnOnce(&Token) -> bool) -> bool { + if self.eof() { + return false; + } + + if next(self.peek()) { + self.advance(); + return true; + } + + false + } + + pub fn advance_if_eq(&mut self, next: &TokenType) -> bool { + self.advance_if(|it| it.tt == *next) + } + + pub fn consume(&mut self, next: TokenType, error: &str) { + if std::mem::discriminant(&self.peek().tt) != std::mem::discriminant(&next) { + panic!("{error} at index {:?}", self.index); + } + self.advance(); + } + + pub fn eof(&self) -> bool { + self.index >= self.tokens.len() + } +} diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs new file mode 100644 index 0000000..2c48da8 --- /dev/null +++ b/crates/sloth/src/parser/stmt.rs @@ -0,0 +1,631 @@ +use super::ast::{Expr, FuncArgs, Stmt}; +use super::AstParser; +use crate::lexer::TokenType; +use crate::parser::ast::Literal; + +impl<'a> AstParser<'a> { + pub fn parse(&mut self) -> Vec<Stmt> { + let mut statements = Vec::new(); + + while !self.eof() { + statements.push(self.statement()); + } + + statements + } + + fn statement(&mut self) -> Stmt { + if self.advance_if_eq(&TokenType::Var) { + return self.var_statement(); + } + + if self.advance_if_eq(&TokenType::Val) { + return self.val_statement(); + } + + if self.advance_if_eq(&TokenType::If) { + return self.if_statement(); + } + + if self.advance_if_eq(&TokenType::For) { + return self.for_statement(); + } + + if self.advance_if_eq(&TokenType::While) { + return self.while_statement(); + } + + if self.advance_if_eq(&TokenType::Fn) { + return self.function_statement(); + } + + if self.advance_if_eq(&TokenType::Return) { + return self.return_statement(); + } + + self.mut_statement() + + // If we couldn't parse a statement return an expression statement + // self.expression_statement() + } + + fn mut_statement(&mut self) -> Stmt { + let TokenType::Identifier(ident) = self.peek().tt.clone() else { + panic!("Identifier error {:?}", self.peek()); + }; + + self.advance(); + let next = self.advance().unwrap().tt.clone(); + if next == TokenType::Eq { + let value = self.expression(); + self.consume(TokenType::SemiColon, "No semi colon for me i guess"); + return Stmt::AssignVariable { + name: (ident), + value: (value), + }; + } else if next == TokenType::OpeningParen { + let mut arguments = Vec::<Expr>::new(); + + if self.peek().tt != TokenType::ClosingParen { + loop { + arguments.push(self.expression()); + if !self.advance_if_eq(&TokenType::Comma) { + break; + } + } + } + + self.consume( + TokenType::ClosingParen, + "Expected ')' to close off function call", + ); + + self.consume(TokenType::SemiColon, "No semi colon for me i guess"); + return Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String(ident)))), + args: (arguments), + }); + } + self.expression_statement() + } + + fn var_statement(&mut self) -> Stmt { + let TokenType::Identifier(ident) = self.peek().tt.clone() else { + panic!("Identifier expected after 'var', not {:?}", self.peek()); + }; + + self.advance(); + + let mut typ: Option<String> = None; + if self.peek().tt.clone() == TokenType::Colon { + self.consume(TokenType::Colon, "How did you even get this error?"); + let TokenType::Identifier(name) = self.peek().tt.clone() else { + panic!("Type expected after identifier, not {:?}", self.peek()); + }; + self.advance(); + typ = Some(name); + } + + self.consume(TokenType::Eq, "Expected '=' after identifier at "); + + let value = self.expression(); + + self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + + Stmt::DefineVariable { + name: (ident), + value: (value), + typ: (typ), + } + } + + fn val_statement(&mut self) -> Stmt { + let TokenType::Identifier(ident) = self.peek().tt.clone() else { + panic!("Identifier expected after 'val'"); + }; + + self.advance(); // Advancing from the identifier + + let mut typ: Option<String> = None; + if self.peek().tt.clone() == TokenType::Colon { + self.consume(TokenType::Colon, "How did you even get this error?"); + let TokenType::Identifier(name) = self.peek().tt.clone() else { + panic!("Type expected after identifier, not {:?}", self.peek()); + }; + self.advance(); + typ = Some(name); + } + + self.consume(TokenType::Eq, "Expected '=' after identifier"); + + let value = self.expression(); + + self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + + Stmt::DefineValue { + name: (ident), + value: (value), + typ: (typ), + } + } + + fn if_statement(&mut self) -> Stmt { + let condition = self.expression(); + + self.consume( + TokenType::OpeningBrace, + "Expected '{' at beggining of block", + ); + let mut body = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + body.push(self.statement()); + } + self.advance(); + Stmt::If { + expr: (condition), + body: (body), + else_if: (Vec::new()), + els: (None), + } // TODO: implement else if and else + } + + fn for_statement(&mut self) -> Stmt { + let binding = self.expression(); + let Expr::Variable(binding) = binding else { + panic!("Left side of for statement must be identifier"); + }; + + self.consume( + TokenType::In, + "Expected 'in' in between identifier and range", + ); + + // let range_start = self.expression(); + // self.consume( + // TokenType::DotDot, + // "Expected '..' denoting min and max of range", + // ); + // let range_end = self.expression(); + + let expr = self.expression(); + + self.consume(TokenType::OpeningBrace, "Expected '{' after iterator"); + + let mut body = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + body.push(self.statement()); + } + self.advance(); + + Stmt::For { + name: (binding), + iter: (expr), + body: (body), + } + } // TODO: Fix this garbage + + fn while_statement(&mut self) -> Stmt { + let condition = self.expression(); + + self.consume( + TokenType::OpeningBrace, + "Expected '{' at beggining of block", + ); + let mut body = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + body.push(self.statement()); + } + self.advance(); + Stmt::While { condition, body } + } + + fn expression_statement(&mut self) -> Stmt { + let expr = self.expression(); + + // FIXME: Move assignment handling + // if self.advance_if_eq(&TokenType::Eq) { + // if let Expr::Literal(_ident) = &expr { + // let value = self.expression(); + + // self.consume(TokenType::SemiColon, "Expected ';' at end of + // statement"); // return Stmt::DefineVariable { + // // name: (ident.clone()), + // // value: (value), + // // typ: (None), + // // }; + // return Stmt::ExprStmt(expr); + // } + // } + + self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + Stmt::ExprStmt(expr) + } + + fn function_statement(&mut self) -> Stmt { + let TokenType::Identifier(ident) = self.advance().unwrap().tt.clone() else { + panic!("Identifier expected after 'fn'"); + }; + + self.consume(TokenType::OpeningParen, "Expected '(' after identifier"); + let mut args: Vec<FuncArgs> = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingParen { + let TokenType::Identifier(name) = self.advance().unwrap().tt.clone() else { + panic!("parameter expected after '('"); + }; + + let mut typ: Option<String> = None; + + if self.peek().tt.clone() == TokenType::Colon { + self.consume(TokenType::Colon, "How did you even get this error?"); + let TokenType::Identifier(name) = self.peek().tt.clone() else { + panic!("Type expected after ':', not {:?}", self.peek()); + }; + self.advance(); + typ = Some(name); + } + + self.advance_if_eq(&TokenType::Comma); + + let arg = FuncArgs { + name: (name), + typ: (typ), + }; + args.push(arg); + } + self.advance(); + let mut typ: Option<String> = None; + if self.peek().tt.clone() == TokenType::Arrow { + self.advance(); + let TokenType::Identifier(name) = self.peek().tt.clone() else { + panic!("Type expected after ':', not {:?}", self.peek()); + }; + typ = Some(name); + self.advance(); + } + self.consume(TokenType::OpeningBrace, "Expected '{' after parameters"); + let mut body = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + body.push(self.statement()); + } + + Stmt::DefineFunction { + ident: (ident), + args: (Some(args)), + body: (body), + return_type: (typ), + } + } + + fn return_statement(&mut self) -> Stmt { + let expr = self.expression(); + self.consume(TokenType::SemiColon, "Expected ';' after return statement"); + Stmt::Return { value: (expr) } + } +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + + use super::{AstParser, Stmt}; + use crate::lexer::Lexer; + use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal, UnaryOp}; + + #[test] + fn basic_statement_a() { + let lexer = Lexer::new("var test_a = 5 + 3;"); + let tokens = lexer.collect_vec(); + + let expected_ast = Stmt::DefineVariable { + name: ("test_a".to_string()), + value: (Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + rhs: (Box::new(Expr::Literal(Literal::Integer(3)))), + }), + typ: (None), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + + #[test] + fn basic_statement_b() { + let lexer = Lexer::new("val test_b = \"Hello World\";"); + let tokens = lexer.collect_vec(); + + let expected_ast = Stmt::DefineValue { + name: ("test_b".to_string()), + value: (Expr::Literal(Literal::String("Hello World".to_string()))), + typ: (None), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + + #[test] + fn basic_statement_c() { + let lexer = Lexer::new( + "\ + fn test_c (a, b, c) {\nreturn (a + b * c);\n}", + ); + let tokens = lexer.collect_vec(); + println!("{tokens:?}"); + + let expected_ast = Stmt::DefineFunction { + ident: ("test_c".to_string()), + args: Some(vec![ + FuncArgs { + name: ("a".to_string()), + typ: None, + }, + FuncArgs { + name: ("b".to_string()), + typ: None, + }, + FuncArgs { + name: ("c".to_string()), + typ: None, + }, + ]), + body: (vec![Stmt::Return { + value: (Expr::Grouping(Box::new(Expr::BinaryOp { + op: BinaryOp::Add, + lhs: Box::new(Expr::Variable("a".to_string())), + rhs: Box::new(Expr::BinaryOp { + op: BinaryOp::Mul, + lhs: Box::new(Expr::Variable("b".to_string())), + rhs: Box::new(Expr::Variable("c".to_string())), + }), + }))), + }]), + return_type: (None), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + #[test] + fn basic_statement_d() { + let lexer = Lexer::new( + "\ + while true {\nprint(\"Hello World\");\nprintln(5 + 7/-3);\n}", + ); + let tokens = lexer.collect_vec(); + println!("{tokens:?}"); + + let expected_ast = Stmt::While { + condition: (Expr::Literal(Literal::Bool(true))), + body: (vec![ + Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + args: (vec![Expr::Literal(Literal::String("Hello World".to_string()))]), + }), + Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String("println".to_string())))), + args: (vec![Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + rhs: (Box::new(Expr::BinaryOp { + op: (BinaryOp::Div), + lhs: (Box::new(Expr::Literal(Literal::Integer(7)))), + rhs: (Box::new(Expr::UnaryOp { + op: (UnaryOp::Neg), + value: (Box::new(Expr::Literal(Literal::Integer(3)))), + })), + })), + }]), + }), + ]), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + #[test] + fn basic_statement_e() { + let lexer = Lexer::new( + "\ + if a+5 > 10 {\nprint(a);\n}\nif a+5 < 10 {\nprintln(10);\n}\nif a+5 == 10 \ + {\nprint(toString(10));\na = true;\n}", + ); + let tokens = lexer.collect_vec(); + // println!("{tokens:?}"); + + let expected_ast = vec![ + Stmt::If { + expr: (Expr::BinaryOp { + op: (BinaryOp::Gt), + lhs: (Box::new(Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Variable("a".to_string()))), + rhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + })), + rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), + }), + body: (vec![Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + args: (vec![Expr::Variable("a".to_string())]), + })]), + else_if: (Vec::new()), + els: (None), + }, + Stmt::If { + expr: (Expr::BinaryOp { + op: (BinaryOp::Lt), + lhs: (Box::new(Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Variable("a".to_string()))), + rhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + })), + rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), + }), + body: (vec![Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String("println".to_string())))), + args: (vec![Expr::Literal(Literal::Integer(10))]), + })]), + else_if: (Vec::new()), + els: (None), + }, + Stmt::If { + expr: (Expr::BinaryOp { + op: (BinaryOp::EqEq), + lhs: (Box::new(Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Variable("a".to_string()))), + rhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + })), + rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), + }), + body: (vec![ + Stmt::ExprStmt(Expr::Call { + ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + args: (vec![Expr::Call { + ident: Box::new(Expr::Literal(Literal::String("toString".to_string()))), + args: vec![Expr::Literal(Literal::Integer(10))], + }]), + }), + Stmt::AssignVariable { + name: ("a".to_string()), + value: (Expr::Literal(Literal::Bool(true))), + }, + ]), + + else_if: (Vec::new()), + els: (None), + }, + ]; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.parse(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + + #[test] + fn basic_statement_f() { + let lexer = Lexer::new("test_a = 5 + 3;"); + let tokens = lexer.collect_vec(); + + let expected_ast = Stmt::AssignVariable { + name: ("test_a".to_string()), + value: (Expr::BinaryOp { + op: (BinaryOp::Add), + lhs: (Box::new(Expr::Literal(Literal::Integer(5)))), + rhs: (Box::new(Expr::Literal(Literal::Integer(3)))), + }), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + #[test] + fn basic_statement_g() { + let lexer = Lexer::new( + "\ + fn times_two(x: int) -> int {\nval y: int = x*2;\nreturn y;\n}", + ); + let tokens = lexer.collect_vec(); + + let expected_ast = Stmt::DefineFunction { + ident: ("times_two".to_string()), + args: (Some(vec![FuncArgs { + name: ("x".to_string()), + typ: (Some("int".to_string())), + }])), + body: (vec![ + Stmt::DefineValue { + name: "y".to_string(), + value: (Expr::BinaryOp { + op: (BinaryOp::Mul), + lhs: (Box::new(Expr::Variable("x".to_string()))), + rhs: (Box::new(Expr::Literal(Literal::Integer(2)))), + }), + typ: Some("int".to_string()), + }, + Stmt::Return { + value: (Expr::Variable("y".to_string())), + }, + ]), + + return_type: Some("int".to_string()), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } + + #[test] + fn basic_statement_h() { + let lexer = Lexer::new("for i in 1 .. 3 {\nfor j in [1, 2, 3] {\nprint(j*i);}}"); + let tokens = lexer.collect_vec(); + + let expected_ast = Stmt::For { + name: ("i".to_string()), + iter: (Expr::BinaryOp { + op: (BinaryOp::Range), + lhs: (Box::new(Expr::Literal(Literal::Integer(1)))), + rhs: (Box::new(Expr::Literal(Literal::Integer(3)))), + }), + body: (vec![Stmt::For { + name: ("j".to_string()), + iter: (Expr::Literal(Literal::List(vec![ + Expr::Literal(Literal::Integer(1)), + Expr::Literal(Literal::Integer(2)), + Expr::Literal(Literal::Integer(3)), + ]))), + body: (vec![Stmt::ExprStmt(Expr::Call { + ident: Box::new(Expr::Literal(Literal::String("print".to_string()))), + args: (vec![Expr::BinaryOp { + op: (BinaryOp::Mul), + lhs: (Box::new(Expr::Variable("j".to_string()))), + rhs: (Box::new(Expr::Variable("i".to_string()))), + }]), + })]), + }]), + }; + + let mut parser = AstParser::new(tokens); + let generated_ast = parser.statement(); + + println!("Expected AST:\n{expected_ast:#?}\n\n"); + println!("Generated AST:\n{generated_ast:#?}\n\n"); + + assert_eq!(expected_ast, generated_ast); + } +} |
