From 0d468f0b4808167fb507f10d71552657872e7018 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 18:34:26 -0500 Subject: some token stuff ig --- crates/sloth/src/main.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index 6502f19..1bffa73 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -29,10 +29,8 @@ fn main() { return; }; - let lexer = Lexer::new(&source); - for token in lexer { - println!("{token:?}"); - } + let tokens = Lexer::new(&source).collect_vec(); - // TODO: + // TODO: Write a parser } + -- cgit v1.2.3 From fdfbad1309cc46b3e9d2dcb83983fec004b94e61 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 18:59:37 -0500 Subject: clippy bullshit --- crates/sloth/src/main.rs | 1 - crates/sloth/src/parser/expr.rs | 1 + crates/sloth/src/parser/mod.rs | 2 ++ crates/sloth/src/parser/stmt.rs | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 crates/sloth/src/parser/expr.rs create mode 100644 crates/sloth/src/parser/stmt.rs diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index 1bffa73..46621eb 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -33,4 +33,3 @@ fn main() { // TODO: Write a parser } - diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/sloth/src/parser/expr.rs @@ -0,0 +1 @@ + diff --git a/crates/sloth/src/parser/mod.rs b/crates/sloth/src/parser/mod.rs index 851c0bc..f74a3d7 100644 --- a/crates/sloth/src/parser/mod.rs +++ b/crates/sloth/src/parser/mod.rs @@ -1 +1,3 @@ pub mod ast; +pub mod expr; +pub mod stmt; diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/crates/sloth/src/parser/stmt.rs @@ -0,0 +1 @@ + -- cgit v1.2.3 From 949acb3de2d1166a8c521af974e099a47dfd739f Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 19:01:14 -0500 Subject: clippy bullshit --- crates/sloth/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index 46621eb..7821097 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -29,7 +29,7 @@ fn main() { return; }; - let tokens = Lexer::new(&source).collect_vec(); + let _tokens = Lexer::new(&source).collect_vec(); // TODO: Write a parser } -- cgit v1.2.3 From f0840b54cd995450b85c18ce1274f5732ae97bcf Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 20:38:28 -0500 Subject: ast thingy idk im new here --- crates/sloth/src/parser/ast.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 85309a5..3e710d0 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,3 +1,5 @@ +use super::stmt; + pub enum BinaryOp { Add, Con, @@ -5,12 +7,22 @@ pub enum BinaryOp { Mul, Pow, Div, + Mod, BWSftRight, BWSftLeft, BWAnd, BWOr, BWXor, + + Lt, + Gt, + LtEq, + GtEq, + EqEq, + NotEq, + LogAnd, + LogOr, } pub enum UnaryOp { @@ -20,6 +32,16 @@ pub enum UnaryOp { BWComp, } +pub enum Literal { + Integer(i128), + Float(f64), + Bool(bool), + Char(char), + String(String), + Regex(String), + List(Vec), // TODO: holy shit we forgor empty listys +} + pub enum Expr { BinaryOp { op: BinaryOp, @@ -30,4 +52,51 @@ pub enum Expr { op: UnaryOp, value: Box, }, + Call { + ident: Box, + args: Vec, + }, + Variable(String), + Literal(Literal), + Lambda, // TODO: Lambda bitch +} + +pub struct FuncArgs { + pub name: String, + pub typ: Option, } + +pub enum Stmt { + ExprStmt(Expr), + DefineFunction { + ident: String, + args: Vec, + body: Vec, + return_type: Option, + }, + DefineVariable { + name: String, + value: Expr, + typ: Option, + }, + DefineValue { + name: String, + value: Expr, + typ: Option, + }, + If { + expr: Vec, + body: Vec, + else_if: Vec<(Expr, Stmt)>, + els: Option>, + }, + For { + name: Expr, + iter: Expr, + body: Vec, + }, + While { + condition: Expr, + body: Vec, + }, +} \ No newline at end of file -- cgit v1.2.3 From a72f47465c34a8812993e764c4444a228eaf061e Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 20:38:59 -0500 Subject: ast thingy idk im new here --- crates/sloth/src/parser/ast.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 3e710d0..45c4d8f 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,5 +1,3 @@ -use super::stmt; - pub enum BinaryOp { Add, Con, -- cgit v1.2.3 From 047ce431153c3b39b22e18eac3c149d2e12a9fd6 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 23:12:20 -0500 Subject: Stole the old code and refactored it for the new shit, gonna go and change some shit up in the future --- crates/sloth/src/parser/ast.rs | 79 +++++++++++++-- crates/sloth/src/parser/expr.rs | 206 ++++++++++++++++++++++++++++++++++++++++ crates/sloth/src/parser/stmt.rs | 158 ++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+), 10 deletions(-) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 45c4d8f..d472269 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,3 +1,6 @@ +use crate::lexer::{Token, TokenType}; +#[derive(Debug)] +#[derive(PartialEq)] pub enum BinaryOp { Add, Con, @@ -22,28 +25,32 @@ pub enum BinaryOp { LogAnd, LogOr, } - +#[derive(Debug)] +#[derive(PartialEq)] pub enum UnaryOp { Not, Neg, BWComp, } - +#[derive(Debug)] +#[derive(PartialEq)] pub enum Literal { Integer(i128), Float(f64), Bool(bool), Char(char), String(String), - Regex(String), - List(Vec), // TODO: holy shit we forgor empty listys + Regex(String), + List(Vec), // TODO: holy shit we forgor listys } - +#[derive(Debug)] +#[derive(PartialEq)] pub enum Expr { + Grouping(Box), BinaryOp { op: BinaryOp, - lhs: Box, + lhs: Box, rhs: Box, }, UnaryOp { @@ -64,7 +71,7 @@ pub struct FuncArgs { pub typ: Option, } -pub enum Stmt { +pub enum Stmt { ExprStmt(Expr), DefineFunction { ident: String, @@ -83,13 +90,13 @@ pub enum Stmt { typ: Option, }, If { - expr: Vec, + expr: Expr, body: Vec, else_if: Vec<(Expr, Stmt)>, els: Option>, }, For { - name: Expr, + name: String, iter: Expr, body: Vec, }, @@ -97,4 +104,56 @@ pub enum Stmt { condition: Expr, body: Vec, }, -} \ No newline at end of file +} + +pub struct AstParser<'a> { + tokens: Vec>, + index: usize, +} + +/// Implementation containing utilities used by the parsers internal components +impl<'a> AstParser<'a> { + pub fn new(tokens: Vec>) -> 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}"); + } + self.advance(); + } + + pub fn eof(&self) -> bool { + self.index >= self.tokens.len() + } +} diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 8b13789..84aab31 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -1 +1,207 @@ +use super::ast::{AstParser, BinaryOp, Expr, Literal, UnaryOp}; +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, + _ => UnaryOp::Neg, // TODO: Idk how to not have this shit + }; + + 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::::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) => Expr::Variable(ident), + TokenType::OpeningParen => { + let expr = self.expression(); + self.consume(TokenType::ClosingParen, "Must end expression with ')'"); + Expr::Grouping(Box::new(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::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, + _ => BinaryOp::Add, // TODO: Idk how to not have this shit + }; + + 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 , equality , (TokenType::AmpAmp)); + 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}, 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); + } +} \ No newline at end of file diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index 8b13789..c5c0c6a 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -1 +1,159 @@ +use super::ast::{AstParser, Expr, Stmt}; +use crate::lexer::TokenType; +impl<'a> AstParser<'a> { + pub fn parse(&mut self) -> Vec { + 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 we couldn't parse a statement return an expression statement + self.expression_statement() + } + + fn var_statement(&mut self) -> Stmt { + let TokenType::Identifier(ident) = self.peek().tt.clone() else { + panic!("Identifier expected after 'var'"); + }; + + self.advance(); // Advancing from the identifier TODO: Check for type + self.consume(TokenType::Eq, "Expected '=' after identifier"); + + let value = self.expression(); + + self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + + Stmt::DefineVariable { + name: (ident), + value: (value), + typ: (None), + } + } + + 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 + 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: (None), + } + } + + 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()); + } + + 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 mut body = Vec::new(); + // while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + // body.push(self.statement()); + // } + + // Stmt::For { name: (binding), iter: (), 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()); + } + + 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::Variable(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), + }; + } + } + + self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + Stmt::ExprStmt(expr) + } +} -- cgit v1.2.3 From b5befbb1e4b96013421458769fa95c5b53fbc03e Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Mon, 10 Apr 2023 23:15:02 -0500 Subject: Stole the old code and refactored it for the new shit, gonna go and change some shit up in the future --- crates/sloth/src/parser/ast.rs | 14 +++++--------- crates/sloth/src/parser/expr.rs | 7 ++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index d472269..c008905 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,6 +1,5 @@ use crate::lexer::{Token, TokenType}; -#[derive(Debug)] -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub enum BinaryOp { Add, Con, @@ -25,16 +24,14 @@ pub enum BinaryOp { LogAnd, LogOr, } -#[derive(Debug)] -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub enum UnaryOp { Not, Neg, BWComp, } -#[derive(Debug)] -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub enum Literal { Integer(i128), Float(f64), @@ -44,13 +41,12 @@ pub enum Literal { Regex(String), List(Vec), // TODO: holy shit we forgor listys } -#[derive(Debug)] -#[derive(PartialEq)] +#[derive(Debug, PartialEq)] pub enum Expr { Grouping(Box), BinaryOp { op: BinaryOp, - lhs: Box, + lhs: Box, rhs: Box, }, UnaryOp { diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 84aab31..7cce919 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -146,9 +146,10 @@ impl<'a> AstParser<'a> { #[cfg(test)] mod tests { use itertools::Itertools; - use super::{AstParser, BinaryOp, Expr, Literal}; - use crate::{lexer::{Lexer}, parser::ast::UnaryOp}; + use super::{AstParser, BinaryOp, Expr, Literal}; + use crate::lexer::Lexer; + use crate::parser::ast::UnaryOp; #[test] fn basic_expression_a() { @@ -204,4 +205,4 @@ mod tests { assert_eq!(expected_ast, generated_ast); } -} \ No newline at end of file +} -- cgit v1.2.3 From 5a556ee16afe1c4065152adf089a33b4d9d2fba2 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 17:31:20 -0500 Subject: Fucky wucky and some testy westies --- crates/sloth/src/lexer.rs | 2 + crates/sloth/src/main.rs | 6 +- crates/sloth/src/parser/ast.rs | 62 ++----------- crates/sloth/src/parser/expr.rs | 11 ++- crates/sloth/src/parser/mod.rs | 54 +++++++++++ crates/sloth/src/parser/stmt.rs | 198 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 256 insertions(+), 77 deletions(-) diff --git a/crates/sloth/src/lexer.rs b/crates/sloth/src/lexer.rs index 62b1a8d..cc17b3d 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, @@ -382,6 +383,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 7821097..fd4a1eb 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,7 +30,6 @@ fn main() { return; }; - let _tokens = Lexer::new(&source).collect_vec(); - - // TODO: Write a parser + let tokens = Lexer::new(&source).collect_vec(); + let _parser = AstParser::new(tokens); } diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index c008905..67bc9da 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,4 +1,3 @@ -use crate::lexer::{Token, TokenType}; #[derive(Debug, PartialEq)] pub enum BinaryOp { Add, @@ -61,17 +60,17 @@ pub enum Expr { Literal(Literal), Lambda, // TODO: Lambda bitch } - +#[derive(PartialEq, Debug)] pub struct FuncArgs { pub name: String, pub typ: Option, } - +#[derive(PartialEq, Debug)] pub enum Stmt { ExprStmt(Expr), DefineFunction { ident: String, - args: Vec, + args: Option>, body: Vec, return_type: Option, }, @@ -100,56 +99,7 @@ pub enum Stmt { condition: Expr, body: Vec, }, -} - -pub struct AstParser<'a> { - tokens: Vec>, - index: usize, -} - -/// Implementation containing utilities used by the parsers internal components -impl<'a> AstParser<'a> { - pub fn new(tokens: Vec>) -> 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}"); - } - self.advance(); - } - - pub fn eof(&self) -> bool { - self.index >= self.tokens.len() - } + Return { + value: Expr, + }, } diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 7cce919..a23359d 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -1,4 +1,5 @@ -use super::ast::{AstParser, BinaryOp, Expr, Literal, UnaryOp}; +use super::ast::{BinaryOp, Expr, Literal, UnaryOp}; +use super::AstParser; use crate::lexer::TokenType; /// Implementation containing parsers internal components related to expressions @@ -20,7 +21,7 @@ impl<'a> AstParser<'a> { TokenType::Bang => UnaryOp::Not, TokenType::Tilde => UnaryOp::BWComp, TokenType::Minus => UnaryOp::Neg, - _ => UnaryOp::Neg, // TODO: Idk how to not have this shit + _ => panic!(), // TODO: Idk how to not have this shit }; let rhs = self.unary(); @@ -72,7 +73,9 @@ impl<'a> AstParser<'a> { 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) => Expr::Variable(ident), + TokenType::Identifier(ident) => Expr::Variable(ident), /* TODO: make this shit check + * if it is calling a + * function dumbass */ TokenType::OpeningParen => { let expr = self.expression(); self.consume(TokenType::ClosingParen, "Must end expression with ')'"); @@ -114,7 +117,7 @@ macro_rules! binary_expr { TokenType::BangEq => BinaryOp::NotEq, TokenType::AmpAmp => BinaryOp::LogAnd, TokenType::PipePipe => BinaryOp::LogOr, - _ => BinaryOp::Add, // TODO: Idk how to not have this shit + _ => panic!("fuck"), // TODO: Idk how to not have this shit }; let rhs = self.$parent(); diff --git a/crates/sloth/src/parser/mod.rs b/crates/sloth/src/parser/mod.rs index f74a3d7..c900168 100644 --- a/crates/sloth/src/parser/mod.rs +++ b/crates/sloth/src/parser/mod.rs @@ -1,3 +1,57 @@ pub mod ast; pub mod expr; pub mod stmt; + +use crate::lexer::{Token, TokenType}; + +pub struct AstParser<'a> { + tokens: Vec>, + index: usize, +} + +/// Implementation containing utilities used by the parsers internal components +impl<'a> AstParser<'a> { + pub fn new(tokens: Vec>) -> 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}"); + } + 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 index c5c0c6a..bd7b4b2 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -1,4 +1,5 @@ -use super::ast::{AstParser, Expr, Stmt}; +use super::ast::{Expr, FuncArgs, Stmt}; +use super::AstParser; use crate::lexer::TokenType; impl<'a> AstParser<'a> { @@ -33,6 +34,14 @@ impl<'a> AstParser<'a> { 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(); + } + // If we couldn't parse a statement return an expression statement self.expression_statement() } @@ -119,7 +128,7 @@ impl<'a> AstParser<'a> { // } // Stmt::For { name: (binding), iter: (), body: (body) } - // } TODO: Fix this garbage + // } // TODO: Fix this garbage fn while_statement(&mut self) -> Stmt { let condition = self.expression(); @@ -140,20 +149,181 @@ impl<'a> AstParser<'a> { let expr = self.expression(); // FIXME: Move assignment handling - if self.advance_if_eq(&TokenType::Eq) { - if let Expr::Variable(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), - }; - } - } + // 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 = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingParen { + let TokenType::Identifier(name) = self.advance().unwrap().tt.clone() else { + panic!("Identifier expected after '('"); + }; + + self.advance_if_eq(&TokenType::Comma); + + let arg = FuncArgs { + name: (name), + typ: (None), + }; + args.push(arg); + } + 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: (None), + } + } + + fn return_statement(&mut self) -> Stmt { + let expr = self.expression(); + Stmt::Return { value: (expr) } + } +} + +#[cfg(test)] +mod tests { + use itertools::Itertools; + + use super::{AstParser, Expr, Stmt}; + use crate::lexer::Lexer; + use crate::parser::ast::{BinaryOp, FuncArgs, Literal}; + + #[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 (b + a * 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::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\");}"); + 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()))]), + })]), + }; + + 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); + } } -- cgit v1.2.3 From 02f9abad0419e84cf62497a31657a632c1ace5eb Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 18:16:31 -0500 Subject: Functions work? --- crates/sloth/src/parser/expr.rs | 10 +++++++--- crates/sloth/src/parser/stmt.rs | 13 +++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index a23359d..4095f98 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -73,9 +73,13 @@ impl<'a> AstParser<'a> { 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) => Expr::Variable(ident), /* TODO: make this shit check - * if it is calling a - * function dumbass */ + 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 ')'"); diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index bd7b4b2..abb90fc 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -1,4 +1,4 @@ -use super::ast::{Expr, FuncArgs, Stmt}; +use super::ast::{FuncArgs, Stmt}; use super::AstParser; use crate::lexer::TokenType; @@ -204,6 +204,7 @@ impl<'a> AstParser<'a> { fn return_statement(&mut self) -> Stmt { let expr = self.expression(); + self.consume(TokenType::SemiColon, "Expected ';' after return statement"); Stmt::Return { value: (expr) } } } @@ -212,9 +213,9 @@ impl<'a> AstParser<'a> { mod tests { use itertools::Itertools; - use super::{AstParser, Expr, Stmt}; + use super::{AstParser, Stmt}; use crate::lexer::Lexer; - use crate::parser::ast::{BinaryOp, FuncArgs, Literal}; + use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal}; #[test] fn basic_statement_a() { @@ -262,7 +263,7 @@ mod tests { #[test] fn basic_statement_c() { - let lexer = Lexer::new("fn test_c (a, b, c) {\nreturn (b + a * c);\n}"); + let lexer = Lexer::new("fn test_c (a, b, c) {\nreturn (a + b * c);\n}"); let tokens = lexer.collect_vec(); println!("{tokens:?}"); @@ -283,7 +284,7 @@ mod tests { }, ]), body: (vec![Stmt::Return { - value: (Expr::BinaryOp { + value: (Expr::Grouping(Box::new(Expr::BinaryOp { op: BinaryOp::Add, lhs: Box::new(Expr::Variable("a".to_string())), rhs: Box::new(Expr::BinaryOp { @@ -291,7 +292,7 @@ mod tests { lhs: Box::new(Expr::Variable("b".to_string())), rhs: Box::new(Expr::Variable("c".to_string())), }), - }), + }))), }]), return_type: (None), }; -- cgit v1.2.3 From 6bc1c9adfca016cdf2e1dd39425ca97a5ebd24b6 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 19:08:25 -0500 Subject: Tests for statements done, everything works great --- crates/sloth/src/parser/stmt.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index abb90fc..9049f6c 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -325,6 +325,38 @@ mod tests { 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}"); + let tokens = lexer.collect_vec(); + println!("{tokens:?}"); + + let expected_ast = 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), + }; + + 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); } } -- cgit v1.2.3 From a875d1626dd74a6dee31ef056f3ffe874256a8e2 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 20:30:43 -0500 Subject: Fixed if statements and while loops --- crates/sloth/src/main.rs | 9 +++- crates/sloth/src/parser/mod.rs | 4 +- crates/sloth/src/parser/stmt.rs | 92 ++++++++++++++++++++++++++++------------- examples/guessing.sloth | 4 +- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index fd4a1eb..0d33e91 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -31,5 +31,12 @@ fn main() { }; let tokens = Lexer::new(&source).collect_vec(); - let _parser = AstParser::new(tokens); + // for t in &tokens{ + // println!("{:#?}", t); + // } + let mut parser = AstParser::new(tokens); + // println!("{:#?}", parser); + let parsed = &parser.parse(); + + println!("{:?}", parsed); } diff --git a/crates/sloth/src/parser/mod.rs b/crates/sloth/src/parser/mod.rs index c900168..9d77acc 100644 --- a/crates/sloth/src/parser/mod.rs +++ b/crates/sloth/src/parser/mod.rs @@ -3,7 +3,7 @@ pub mod expr; pub mod stmt; use crate::lexer::{Token, TokenType}; - +#[derive(Debug)] pub struct AstParser<'a> { tokens: Vec>, index: usize, @@ -46,7 +46,7 @@ impl<'a> AstParser<'a> { pub fn consume(&mut self, next: TokenType, error: &str) { if std::mem::discriminant(&self.peek().tt) != std::mem::discriminant(&next) { - panic!("{error}"); + panic!("{error} at index {:?}", self.index); } self.advance(); } diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index 9049f6c..c35c4e3 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -52,7 +52,7 @@ impl<'a> AstParser<'a> { }; self.advance(); // Advancing from the identifier TODO: Check for type - self.consume(TokenType::Eq, "Expected '=' after identifier"); + self.consume(TokenType::Eq, "Expected '=' after identifier at "); let value = self.expression(); @@ -95,7 +95,7 @@ impl<'a> AstParser<'a> { while !self.eof() && self.peek().tt != TokenType::ClosingBrace { body.push(self.statement()); } - + self.advance(); Stmt::If { expr: (condition), body: (body), @@ -141,7 +141,7 @@ impl<'a> AstParser<'a> { while !self.eof() && self.peek().tt != TokenType::ClosingBrace { body.push(self.statement()); } - + self.advance(); Stmt::While { condition, body } } @@ -215,7 +215,7 @@ mod tests { use super::{AstParser, Stmt}; use crate::lexer::Lexer; - use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal}; + use crate::parser::ast::{BinaryOp, Expr, FuncArgs, Literal, UnaryOp}; #[test] fn basic_statement_a() { @@ -307,16 +307,33 @@ mod tests { } #[test] fn basic_statement_d() { - let lexer = Lexer::new("while true {\nprint(\"Hello World\");}"); + let lexer = Lexer::new("while true {\nprint(\"Hello World\");\n\nprintln(5 + 7/-3);}"); 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()))]), - })]), + 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); @@ -329,30 +346,49 @@ mod tests { } #[test] fn basic_statement_e() { - let lexer = Lexer::new("if a+5 > 10 {\nprint(a);\n}"); + let lexer = Lexer::new("if a+5 > 10 {\nprint(a);\n}\nif a+5 < 10 {\nprint(10);\n}"); let tokens = lexer.collect_vec(); println!("{tokens:?}"); - let expected_ast = 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), - }; + 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("print".to_string())))), + args: (vec![Expr::Literal(Literal::Integer(10))]), + })]), + else_if: (Vec::new()), + els: (None), + }, + ]; let mut parser = AstParser::new(tokens); - let generated_ast = parser.statement(); + let generated_ast = parser.parse(); println!("Expected AST:\n{expected_ast:#?}\n\n"); println!("Generated AST:\n{generated_ast:#?}\n\n"); diff --git a/examples/guessing.sloth b/examples/guessing.sloth index 1938269..01f0796 100644 --- a/examples/guessing.sloth +++ b/examples/guessing.sloth @@ -9,7 +9,7 @@ while !correct { if human == computer { println("You guessed the same number as me!"); - correct = true; + var correct = true; } if human > computer { @@ -20,7 +20,7 @@ while !correct { println("Your guess was too low."); } - tries = tries + 1; + var tries = tries + 1; } println("\nIt took you ", tries, " tries to guess correctly!"); -- cgit v1.2.3 From 81c56242feb2951cece9b4876edb2e298548a5c1 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 22:34:24 -0500 Subject: Mutability should work now (very jank) --- crates/sloth/src/parser/expr.rs | 2 +- crates/sloth/src/parser/stmt.rs | 109 ++++++++++++++++++++++++++++++++++++++-- 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 { @@ -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::::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!"); -- cgit v1.2.3 From a6dc77aa4db8aec2593743f058d91d4fe572ab60 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Tue, 11 Apr 2023 22:35:37 -0500 Subject: Reformatted guessing.sloth --- examples/guessing.sloth | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/guessing.sloth b/examples/guessing.sloth index 830519b..1938269 100644 --- a/examples/guessing.sloth +++ b/examples/guessing.sloth @@ -1,14 +1,24 @@ 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!"); - correct = true; + + 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."); } - if human > computer {println("Your guess was too high.");} - if human < computer {println("Your guess was too low.");} tries = tries + 1; } -- cgit v1.2.3 From 6449ff2c0582bec9f8289ef643ce867311a18218 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Wed, 12 Apr 2023 20:56:17 -0500 Subject: types are done --- crates/sloth/src/parser/ast.rs | 4 ++ crates/sloth/src/parser/stmt.rs | 114 +++++++++++++++++++++++++++++++++++----- 2 files changed, 104 insertions(+), 14 deletions(-) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 67bc9da..fb03634 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -84,6 +84,10 @@ pub enum Stmt { value: Expr, typ: Option, }, + AssignVariable { + name: String, + value: Expr, + }, If { expr: Expr, body: Vec, diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index 94ddec0..6c02f35 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -59,10 +59,9 @@ impl<'a> AstParser<'a> { if next == TokenType::Eq { let value = self.expression(); self.consume(TokenType::SemiColon, "No semi colon for me i guess"); - return Stmt::DefineVariable { + return Stmt::AssignVariable { name: (ident), value: (value), - typ: (None), }; } else if next == TokenType::OpeningParen { let mut arguments = Vec::::new(); @@ -95,7 +94,18 @@ impl<'a> AstParser<'a> { panic!("Identifier expected after 'var', not {:?}", self.peek()); }; - self.advance(); // Advancing from the identifier TODO: Check for type + self.advance(); + + let mut typ: Option = 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(); @@ -105,7 +115,7 @@ impl<'a> AstParser<'a> { Stmt::DefineVariable { name: (ident), value: (value), - typ: (None), + typ: (typ), } } @@ -115,6 +125,17 @@ impl<'a> AstParser<'a> { }; self.advance(); // Advancing from the identifier + + let mut typ: Option = 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(); @@ -124,7 +145,7 @@ impl<'a> AstParser<'a> { Stmt::DefineValue { name: (ident), value: (value), - typ: (None), + typ: (typ), } } @@ -223,15 +244,35 @@ impl<'a> AstParser<'a> { panic!("Identifier expected after '('"); }; + let mut typ: Option = 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: (None), + typ: (typ), }; args.push(arg); } self.advance(); + let mut typ: Option = 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 { @@ -242,7 +283,7 @@ impl<'a> AstParser<'a> { ident: (ident), args: (Some(args)), body: (body), - return_type: (None), + return_type: (typ), } } @@ -307,7 +348,10 @@ mod tests { #[test] fn basic_statement_c() { - let lexer = Lexer::new("fn test_c (a, b, c) {\nreturn (a + b * c);\n}"); + let lexer = Lexer::new( + "\ + fn test_c (a, b, c) {\nreturn (a + b * c);\n}", + ); let tokens = lexer.collect_vec(); println!("{tokens:?}"); @@ -351,7 +395,10 @@ mod tests { } #[test] fn basic_statement_d() { - let lexer = Lexer::new("while true {\nprint(\"Hello World\");\n\nprintln(5 + 7/-3);}"); + let lexer = Lexer::new( + "\ + while true {\nprint(\"Hello World\");\nprintln(5 + 7/-3);\n}", + ); let tokens = lexer.collect_vec(); println!("{tokens:?}"); @@ -391,7 +438,8 @@ mod tests { #[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 \ + "\ + 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(); @@ -450,10 +498,9 @@ mod tests { args: vec![Expr::Literal(Literal::Integer(10))], }]), }), - Stmt::DefineVariable { + Stmt::AssignVariable { name: ("a".to_string()), value: (Expr::Literal(Literal::Bool(true))), - typ: (None), }, ]), @@ -476,14 +523,53 @@ mod tests { let lexer = Lexer::new("test_a = 5 + 3;"); let tokens = lexer.collect_vec(); - let expected_ast = Stmt::DefineVariable { + 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)))), }), - 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_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); -- cgit v1.2.3 From 93b2afeda3a0f413ec7df2b411a53973f2d7a8a6 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Wed, 12 Apr 2023 21:17:18 -0500 Subject: Guessing game has explicit types now --- examples/guessing.sloth | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/guessing.sloth b/examples/guessing.sloth index 1938269..05c25db 100644 --- a/examples/guessing.sloth +++ b/examples/guessing.sloth @@ -1,11 +1,11 @@ -val computer = random(1, 10); +val computer: int = random(1, 10); -var tries = 0; -var correct = false; +var tries: int = 0; +var correct: bool = false; while !correct { print("\nPick a number between 1 and 10: "); - val human = parse_int(readln()); + val human: int = parse_int(readln()); if human == computer { println("You guessed the same number as me!"); -- cgit v1.2.3 From 8d548081d53781bd918dccf0714200f0fc995d8b Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Thu, 13 Apr 2023 23:16:00 -0500 Subject: For loops and lists --- crates/sloth/src/parser/ast.rs | 1 + crates/sloth/src/parser/expr.rs | 56 +++++++++++++++++++++++- crates/sloth/src/parser/stmt.rs | 97 ++++++++++++++++++++++++++++++----------- documentation/order.txt | 1 + 4 files changed, 128 insertions(+), 27 deletions(-) diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index fb03634..99bbf52 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -22,6 +22,7 @@ pub enum BinaryOp { NotEq, LogAnd, LogOr, + Range, } #[derive(Debug, PartialEq)] pub enum UnaryOp { diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 866663a..0cc3a93 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -85,6 +85,18 @@ impl<'a> AstParser<'a> { self.consume(TokenType::ClosingParen, "Must end expression with ')'"); Expr::Grouping(Box::new(expr)) } + TokenType::OpeningBracket => { + let mut expr: Vec = 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()), } } @@ -106,6 +118,7 @@ macro_rules! binary_expr { TokenType::StarStar => BinaryOp::Pow, TokenType::Slash => BinaryOp::Div, TokenType::Perc => BinaryOp::Mod, + TokenType::DotDot => BinaryOp::Range, TokenType::LtLt => BinaryOp::BWSftRight, TokenType::GtGt => BinaryOp::BWSftLeft, @@ -121,7 +134,7 @@ macro_rules! binary_expr { TokenType::BangEq => BinaryOp::NotEq, TokenType::AmpAmp => BinaryOp::LogAnd, TokenType::PipePipe => BinaryOp::LogOr, - _ => panic!("fuck"), // TODO: Idk how to not have this shit + _ => panic!("uh oh spagghetio"), // TODO: Idk how to not have this shit }; let rhs = self.$parent(); @@ -142,7 +155,8 @@ macro_rules! binary_expr { 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 , equality , (TokenType::AmpAmp)); + 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)); @@ -210,6 +224,44 @@ mod tests { 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/stmt.rs b/crates/sloth/src/parser/stmt.rs index 6c02f35..0d28131 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -27,9 +27,9 @@ impl<'a> AstParser<'a> { return self.if_statement(); } - // if self.advance_if_eq(&TokenType::For) { - // return self.for_statement(); - // } + if self.advance_if_eq(&TokenType::For) { + return self.for_statement(); + } if self.advance_if_eq(&TokenType::While) { return self.while_statement(); @@ -169,31 +169,39 @@ impl<'a> AstParser<'a> { } // 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"); - // }; + 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", - // ); + 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 range_start = self.expression(); + // self.consume( + // TokenType::DotDot, + // "Expected '..' denoting min and max of range", + // ); + // let range_end = self.expression(); - // let mut body = Vec::new(); - // while !self.eof() && self.peek().tt != TokenType::ClosingBrace { - // body.push(self.statement()); - // } + 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()); + } - // Stmt::For { name: (binding), iter: (), body: (body) } - // } // TODO: Fix this garbage + Stmt::For { + name: (binding), + iter: (expr), + body: (body), + } + } // TODO: Fix this garbage fn while_statement(&mut self) -> Stmt { let condition = self.expression(); @@ -241,7 +249,7 @@ impl<'a> AstParser<'a> { let mut args: Vec = Vec::new(); while !self.eof() && self.peek().tt != TokenType::ClosingParen { let TokenType::Identifier(name) = self.advance().unwrap().tt.clone() else { - panic!("Identifier expected after '('"); + panic!("parameter expected after '('"); }; let mut typ: Option = None; @@ -580,4 +588,43 @@ mod tests { 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); + } } diff --git a/documentation/order.txt b/documentation/order.txt index ba22100..3c06ce0 100644 --- a/documentation/order.txt +++ b/documentation/order.txt @@ -13,6 +13,7 @@ | bitwise or | | | Left | | comparison | < > <= >= | Left | | equality | == != | Left | +| range | .. | Left | | logical and | && | Left | | logical or | || | Left | -- cgit v1.2.3 From a97d9f4a7f8c1e8c1e9d2921b40b34aad3643481 Mon Sep 17 00:00:00 2001 From: nic-gaffney Date: Thu, 13 Apr 2023 23:47:25 -0500 Subject: Fixed for loops --- crates/sloth/src/lexer.rs | 6 ++++-- crates/sloth/src/parser/ast.rs | 4 ++-- crates/sloth/src/parser/expr.rs | 2 +- crates/sloth/src/parser/stmt.rs | 3 ++- examples/hello.sloth | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/sloth/src/lexer.rs b/crates/sloth/src/lexer.rs index cc17b3d..0afaf1c 100644 --- a/crates/sloth/src/lexer.rs +++ b/crates/sloth/src/lexer.rs @@ -293,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 diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 99bbf52..3c8cdeb 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -39,7 +39,7 @@ pub enum Literal { Char(char), String(String), Regex(String), - List(Vec), // TODO: holy shit we forgor listys + List(Vec), } #[derive(Debug, PartialEq)] pub enum Expr { @@ -59,7 +59,7 @@ pub enum Expr { }, Variable(String), Literal(Literal), - Lambda, // TODO: Lambda bitch + Lambda, // TODO: Lambda } #[derive(PartialEq, Debug)] pub struct FuncArgs { diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index 0cc3a93..ad35d20 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -134,7 +134,7 @@ macro_rules! binary_expr { TokenType::BangEq => BinaryOp::NotEq, TokenType::AmpAmp => BinaryOp::LogAnd, TokenType::PipePipe => BinaryOp::LogOr, - _ => panic!("uh oh spagghetio"), // TODO: Idk how to not have this shit + _ => panic!("uh oh spagghetio"), }; let rhs = self.$parent(); diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index 0d28131..2c48da8 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -51,7 +51,7 @@ impl<'a> AstParser<'a> { fn mut_statement(&mut self) -> Stmt { let TokenType::Identifier(ident) = self.peek().tt.clone() else { - panic!("uh oh {:?}", self.peek()); + panic!("Identifier error {:?}", self.peek()); }; self.advance(); @@ -195,6 +195,7 @@ impl<'a> AstParser<'a> { while !self.eof() && self.peek().tt != TokenType::ClosingBrace { body.push(self.statement()); } + self.advance(); Stmt::For { name: (binding), diff --git a/examples/hello.sloth b/examples/hello.sloth index 3dbc685..8201dae 100644 --- a/examples/hello.sloth +++ b/examples/hello.sloth @@ -1,6 +1,6 @@ print("Hello World!"); -## A basic for loop greeting the user in multiple languages +# Comment for greeting in ["Hello", "Hola", "你好"] { print(greeting + " World!"); } -- cgit v1.2.3