diff options
| -rw-r--r-- | crates/sloth/src/parser/expr.rs | 2 | ||||
| -rw-r--r-- | crates/sloth/src/parser/stmt.rs | 109 | ||||
| -rw-r--r-- | examples/guessing.sloth | 20 |
3 files changed, 110 insertions, 21 deletions
diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 4095f98..866663a 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -21,7 +21,7 @@ impl<'a> AstParser<'a> { TokenType::Bang => UnaryOp::Not, TokenType::Tilde => UnaryOp::BWComp, TokenType::Minus => UnaryOp::Neg, - _ => panic!(), // TODO: Idk how to not have this shit + _ => panic!(), }; let rhs = self.unary(); diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index c35c4e3..94ddec0 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -1,6 +1,7 @@ -use super::ast::{FuncArgs, Stmt}; +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> { @@ -42,13 +43,56 @@ impl<'a> AstParser<'a> { 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!("uh oh {:?}", 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::DefineVariable { + name: (ident), + value: (value), + typ: (None), + }; + } 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'"); + panic!("Identifier expected after 'var', not {:?}", self.peek()); }; self.advance(); // Advancing from the identifier TODO: Check for type @@ -346,9 +390,12 @@ mod tests { } #[test] fn basic_statement_e() { - let lexer = Lexer::new("if a+5 > 10 {\nprint(a);\n}\nif a+5 < 10 {\nprint(10);\n}"); + 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:?}"); + // println!("{tokens:?}"); let expected_ast = vec![ Stmt::If { @@ -379,12 +426,40 @@ mod tests { rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), }), body: (vec![Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + 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::DefineVariable { + name: ("a".to_string()), + value: (Expr::Literal(Literal::Bool(true))), + typ: (None), + }, + ]), + + else_if: (Vec::new()), + els: (None), + }, ]; let mut parser = AstParser::new(tokens); @@ -395,4 +470,28 @@ mod tests { 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::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); + } } diff --git a/examples/guessing.sloth b/examples/guessing.sloth index 01f0796..830519b 100644 --- a/examples/guessing.sloth +++ b/examples/guessing.sloth @@ -1,26 +1,16 @@ val computer = random(1, 10); - var tries = 0; var correct = false; - while !correct { print("\nPick a number between 1 and 10: "); val human = parse_int(readln()); - - if human == computer { - println("You guessed the same number as me!"); - var correct = true; - } - - if human > computer { - println("Your guess was too high."); - } - - if human < computer { - println("Your guess was too low."); + if human == computer {println("You guessed the same number as me!"); + correct = true; } + if human > computer {println("Your guess was too high.");} + if human < computer {println("Your guess was too low.");} - var tries = tries + 1; + tries = tries + 1; } println("\nIt took you ", tries, " tries to guess correctly!"); |
