aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-04-14 00:19:26 -0500
committerGitHub <noreply@github.com>2023-04-14 00:19:26 -0500
commit6b25c191a4522610877898506856bd00cd1fc4d5 (patch)
tree032725940ffbd28907abee8b6b1099b5590fe520
parentc458e9f46afcd04445dcb35d1fd9e2a85a451937 (diff)
parenta97d9f4a7f8c1e8c1e9d2921b40b34aad3643481 (diff)
downloadsloth-6b25c191a4522610877898506856bd00cd1fc4d5.tar.gz
Merge pull request #3 from slothlang/recursive-descent-parser
Recursive descent parser
-rw-r--r--crates/sloth/src/lexer.rs8
-rw-r--r--crates/sloth/src/main.rs14
-rw-r--r--crates/sloth/src/parser/ast.rs81
-rw-r--r--crates/sloth/src/parser/expr.rs267
-rw-r--r--crates/sloth/src/parser/mod.rs56
-rw-r--r--crates/sloth/src/parser/stmt.rs631
-rw-r--r--documentation/order.txt1
-rw-r--r--examples/guessing.sloth8
-rw-r--r--examples/hello.sloth2
9 files changed, 1054 insertions, 14 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);
+ }
+}
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 |
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!");
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!");
}