diff --git a/Cargo.lock b/Cargo.lock index ba411ac..a237607 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,6 +150,7 @@ dependencies = [ "color-eyre", "dotenv", "from_variants", + "itertools", "match_any", "tracing", "tracing-subscriber", @@ -196,6 +197,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "eyre" version = "0.6.7" @@ -282,6 +289,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index aa6fbc8..e6ff1a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ clap = { version = "3.1.9", features = ["derive"] } color-eyre = "0.6.1" dotenv = "0.15.0" from_variants = "1.0.0" +itertools = "0.10.3" match_any = "1.0.1" tracing = "0.1.32" tracing-subscriber = { version = "0.3.9", features = ["env-filter"] } diff --git a/src/ast/ast_parser_iter.rs b/src/ast/ast_parser_iter.rs new file mode 100644 index 0000000..820b4c0 --- /dev/null +++ b/src/ast/ast_parser_iter.rs @@ -0,0 +1,37 @@ +use itertools::{peek_nth, PeekNth, PeekingNext}; + +pub struct ParserIter(PeekNth); + +impl ParserIter { + pub fn new(iter: T) -> ParserIter { + ParserIter(peek_nth(iter)) + } + + pub fn next(&mut self) -> T::Item { + self.0.next().unwrap() + } + + pub fn peek_nth(&mut self, n: usize) -> &T::Item { + self.0.peek_nth(n).unwrap() + } + pub fn peek(&mut self) -> &T::Item { + self.peek_nth(0) + } +} + +impl Iterator for ParserIter { + type Item = T::Item; + + fn next(&mut self) -> Option { + Some(self.next()) + } +} + +impl PeekingNext for ParserIter { + fn peeking_next(&mut self, accept: F) -> Option + where + F: FnOnce(&Self::Item) -> bool, + { + self.0.peeking_next(accept) + } +} diff --git a/src/ast/expression/expression_node.rs b/src/ast/expression/expression_node.rs index 99aedae..41d20d0 100644 --- a/src/ast/expression/expression_node.rs +++ b/src/ast/expression/expression_node.rs @@ -120,7 +120,7 @@ pub enum Literal { Int(i32), Float(f32), Bool(bool), - Nil + Nil, } pub struct BinaryExpr { diff --git a/src/ast/expression/expression_parser.rs b/src/ast/expression/expression_parser.rs index 754af3a..3059434 100644 --- a/src/ast/expression/expression_parser.rs +++ b/src/ast/expression/expression_parser.rs @@ -1,3 +1,5 @@ +use itertools::PeekingNext; + use crate::lexer::token::{self, TokenType}; use super::super::parser::{InnerASTParsingError, Parser, Result}; @@ -12,7 +14,7 @@ impl<'a, T: Iterator>> Parser<'a, T> { let mut node = self.comparison()?; while let Some(o) = self .token_iter - .next_if(|t| matches!(t.token_type, TokenType::EqualEqual | TokenType::BangEqual)) + .peeking_next(|t| matches!(t.token_type, TokenType::EqualEqual | TokenType::BangEqual)) { node = BinaryExpr::new( Box::new(node), @@ -27,7 +29,7 @@ impl<'a, T: Iterator>> Parser<'a, T> { fn comparison(&mut self) -> Result { let mut node = self.term()?; - while let Some(o) = self.token_iter.next_if(|t| { + while let Some(o) = self.token_iter.peeking_next(|t| { matches!( t.token_type, TokenType::Greater @@ -50,7 +52,7 @@ impl<'a, T: Iterator>> Parser<'a, T> { while let Some(o) = self .token_iter - .next_if(|t| matches!(t.token_type, TokenType::Minus | TokenType::Plus)) + .peeking_next(|t| matches!(t.token_type, TokenType::Minus | TokenType::Plus)) { node = BinaryExpr::new( Box::new(node), @@ -67,7 +69,7 @@ impl<'a, T: Iterator>> Parser<'a, T> { while let Some(o) = self .token_iter - .next_if(|t| matches!(t.token_type, TokenType::Star | TokenType::Slash)) + .peeking_next(|t| matches!(t.token_type, TokenType::Star | TokenType::Slash)) { node = BinaryExpr::new( Box::new(node), @@ -82,7 +84,7 @@ impl<'a, T: Iterator>> Parser<'a, T> { fn unary(&mut self) -> Result { if let Some(op) = self .token_iter - .next_if(|t| matches!(t.token_type, TokenType::Bang | TokenType::Minus)) + .peeking_next(|t| matches!(t.token_type, TokenType::Bang | TokenType::Minus)) { let right = Box::new(self.unary()?); Ok(ExpressionNode::UnaryExpr(UnaryExpr::new( @@ -95,30 +97,26 @@ impl<'a, T: Iterator>> Parser<'a, T> { } fn primary(&mut self) -> Result { - let node = match self.token_iter.next() { - Some(token) => match token.token_type { - TokenType::False => ExpressionNode::Literal(Literal::Bool(false)), - TokenType::True => ExpressionNode::Literal(Literal::Bool(true)), - TokenType::Int(i) => ExpressionNode::Literal(Literal::Int(i)), - TokenType::String(i) => ExpressionNode::Literal(Literal::String(i)), - TokenType::Float(f) => ExpressionNode::Literal(Literal::Float(f)), - TokenType::Nil => ExpressionNode::Literal(Literal::Nil), - TokenType::LeftParen => { - let expr = self.expression()?; - let group = GroupingExpr::new(Box::new(expr)); - match self - .token_iter - .next_if(|v| matches!(v.token_type, TokenType::RightParen)) - { - Some(_) => return Ok(group.into()), - None => { - return Err(token.location.wrap(InnerASTParsingError::UnmatchedBrace)) - } - } + let token = self.token_iter.next(); + let node = match token.token_type { + TokenType::False => ExpressionNode::Literal(Literal::Bool(false)), + TokenType::True => ExpressionNode::Literal(Literal::Bool(true)), + TokenType::Int(i) => ExpressionNode::Literal(Literal::Int(i)), + TokenType::String(i) => ExpressionNode::Literal(Literal::String(i)), + TokenType::Float(f) => ExpressionNode::Literal(Literal::Float(f)), + TokenType::Nil => ExpressionNode::Literal(Literal::Nil), + TokenType::LeftParen => { + let expr = self.expression()?; + let group = GroupingExpr::new(Box::new(expr)); + match self + .token_iter + .peeking_next(|v| matches!(v.token_type, TokenType::RightParen)) + { + Some(_) => return Ok(group.into()), + None => return Err(token.location.wrap(InnerASTParsingError::UnmatchedBrace)), } - a => return Err(token.location.wrap(InnerASTParsingError::IncorrectToken(a))), - }, - None => todo!(), + } + a => return Err(token.location.wrap(InnerASTParsingError::IncorrectToken(a))), }; Ok(node) } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 38850b6..484dde7 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1,3 +1,4 @@ +mod ast_parser_iter; pub mod expression; -pub mod statement; pub mod parser; +pub mod statement; diff --git a/src/ast/parser.rs b/src/ast/parser.rs index 4e0337b..0d39e41 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -1,15 +1,14 @@ -use super::expression::expression_node; +use super::ast_parser_iter::ParserIter; use super::statement::statement_node; use crate::error::ErrorLocationWrapper; use crate::lexer::{token, token::TokenType}; - -use std::iter; use std::result::Result as StdResult; #[derive(Debug)] pub enum InnerASTParsingError { IncorrectToken(TokenType), UnmatchedBrace, + ExpectedSemi, } impl std::fmt::Display for InnerASTParsingError { @@ -17,6 +16,7 @@ impl std::fmt::Display for InnerASTParsingError { match *self { Self::UnmatchedBrace => write!(f, "Unmatched brace"), Self::IncorrectToken(ref token) => write!(f, "Incorrect token {:?}", token), + Self::ExpectedSemi => write!(f, "Expected semicolon"), } } } @@ -26,7 +26,7 @@ pub type ASTParsingError = ErrorLocationWrapper; pub(super) type Result = StdResult; pub struct Parser<'a, T: Iterator>> { - pub(super) token_iter: iter::Peekable, + pub(super) token_iter: super::ast_parser_iter::ParserIter, } pub type ParseAllResult = StdResult, Vec>; @@ -34,13 +34,13 @@ pub type ParseAllResult = StdResult, Vec>> Parser<'a, T> { pub fn new(iter: T) -> Parser<'a, T> { Parser { - token_iter: iter.peekable(), + token_iter: ParserIter::new(iter), } } pub fn parse_all(&mut self) -> ParseAllResult { let mut res = Ok(Vec::new()); - while !matches!(self.token_iter.peek().unwrap().token_type, token::TokenType::Eof) { + while !matches!(self.token_iter.peek().token_type, token::TokenType::Eof) { match self.statement() { Ok(s) => { if let Ok(ref mut v) = res { diff --git a/src/ast/statement/statement_node.rs b/src/ast/statement/statement_node.rs index 2ae168f..c99127c 100644 --- a/src/ast/statement/statement_node.rs +++ b/src/ast/statement/statement_node.rs @@ -5,6 +5,7 @@ use from_variants::FromVariants; pub enum Statement { Expression(ExpressionStatement), Print(PrintStatement), + VariableAssignment(VariableAssignmentStatement), } macro_rules! all_variants { @@ -12,7 +13,7 @@ macro_rules! all_variants { { use match_any::match_any; use $crate::ast::statement::statement_node::*; - match_any!($expr, Statement::Expression($val_name) | Statement::Print($val_name) => $expr_arm) + match_any!($expr, Statement::Expression($val_name) | Statement::Print($val_name) | Statement::VariableAssignment($val_name) => $expr_arm) } }; } @@ -33,3 +34,15 @@ impl PrintStatement { Self(expr) } } + +#[derive(Debug)] +pub struct VariableAssignmentStatement { + pub var_name: String, + pub node: ExpressionNode, +} + +impl VariableAssignmentStatement { + pub fn new(var_name: String, node: ExpressionNode) -> Self { + Self { var_name, node } + } +} diff --git a/src/ast/statement/statement_parser.rs b/src/ast/statement/statement_parser.rs index cbacce9..905a36e 100644 --- a/src/ast/statement/statement_parser.rs +++ b/src/ast/statement/statement_parser.rs @@ -1,37 +1,70 @@ -use super::statement_node::{ExpressionStatement, Statement, PrintStatement}; +use itertools::PeekingNext; + +use super::statement_node::{ExpressionStatement, PrintStatement, Statement}; use crate::{ - ast::parser::{ASTParsingError, Parser, Result}, - lexer::token, + ast::{ + parser::{InnerASTParsingError, Parser, Result}, + statement::statement_node::VariableAssignmentStatement, + }, + lexer::token::{self, Token, TokenType}, }; impl<'a, T: Iterator>> Parser<'a, T> { pub fn statement(&mut self) -> Result { - if let Some(_) = self + self.print_statement() + } + + fn print_statement(&mut self) -> Result { + if let Some(Token { location: loc, .. }) = self .token_iter - .next_if(|t| matches!(t.token_type, token::TokenType::Print)) + .peeking_next(|t| matches!(t.token_type, token::TokenType::Print)) { - self.print_statement() + let expr = self.expression()?; + if let token::TokenType::Semicolon = self.token_iter.peek().token_type { + self.token_iter.next(); + Ok(PrintStatement::new(expr).into()) + } else { + Err(loc.wrap(InnerASTParsingError::ExpectedSemi)) + } } else { - self.expression_statement() + self.variable_assignment_statement() } } - fn print_statement(&mut self) -> Result { - let expr = self.expression()?; - if let token::TokenType::Semicolon = self.token_iter.peek().unwrap().token_type { + + fn variable_assignment_statement(&mut self) -> Result { + if matches!( + self.token_iter.peek_nth(0).token_type, + TokenType::Identifier(_) + ) && matches!(self.token_iter.peek_nth(1).token_type, TokenType::Equal) + { + let ident = if let TokenType::Identifier(ident) = self.token_iter.next().token_type { + ident + } else { + unreachable!() + }; self.token_iter.next(); - Ok(PrintStatement::new(expr).into()) + let expr = self.expression()?; + + let token = self.token_iter.peek(); + if let token::TokenType::Semicolon = token.token_type { + self.token_iter.next(); + Ok(VariableAssignmentStatement::new(ident, expr).into()) + } else { + Err(token.location.wrap(InnerASTParsingError::ExpectedSemi)) + } } else { - panic!(); + self.expression_statement() } } fn expression_statement(&mut self) -> Result { let expr = self.expression()?; - if let token::TokenType::Semicolon = self.token_iter.peek().unwrap().token_type { + let token = self.token_iter.peek(); + if let TokenType::Semicolon = token.token_type { self.token_iter.next(); Ok(ExpressionStatement::new(expr).into()) } else { - panic!(); + Err(token.location.wrap(InnerASTParsingError::ExpectedSemi)) } } } diff --git a/src/interpreter/ast_walker/expression_interpreter.rs b/src/interpreter/ast_walker/expression_interpreter.rs index cbc4193..e3a0892 100644 --- a/src/interpreter/ast_walker/expression_interpreter.rs +++ b/src/interpreter/ast_walker/expression_interpreter.rs @@ -1,9 +1,7 @@ -use super::{RuntimeError, types::Value}; use super::Interpret; +use super::{types::Value, RuntimeError}; use crate::ast::expression::expression_node; - - impl Interpret for expression_node::ExpressionNode { fn interpret(&self) -> Result { expression_node::all_variants!(self, n => n.interpret()) diff --git a/src/interpreter/ast_walker/mod.rs b/src/interpreter/ast_walker/mod.rs index cca6bb4..912b791 100644 --- a/src/interpreter/ast_walker/mod.rs +++ b/src/interpreter/ast_walker/mod.rs @@ -1,7 +1,7 @@ mod expression_interpreter; mod statement_interpreter; -pub use super::{error::RuntimeError, types}; use super::types::Value; +pub use super::{error::RuntimeError, types}; pub trait Interpret { fn interpret(&self) -> Result; diff --git a/src/interpreter/ast_walker/statement_interpreter.rs b/src/interpreter/ast_walker/statement_interpreter.rs index f0c3a6a..777003b 100644 --- a/src/interpreter/ast_walker/statement_interpreter.rs +++ b/src/interpreter/ast_walker/statement_interpreter.rs @@ -1,26 +1,30 @@ -use super::{RuntimeError, types::Value}; use super::Interpret; +use super::{types::Value, RuntimeError}; use crate::ast::statement::statement_node; - - impl Interpret for statement_node::Statement { fn interpret(&self) -> Result { statement_node::all_variants!(self, n => n.interpret()) } } - impl Interpret for statement_node::PrintStatement { - fn interpret(&self) -> Result { + fn interpret(&self) -> Result { let res = self.0.interpret()?; println!("{:?}", res); Ok(res) - } + } } impl Interpret for statement_node::ExpressionStatement { - fn interpret(&self) -> Result { + fn interpret(&self) -> Result { self.0.interpret() - } + } +} + +impl Interpret for statement_node::VariableAssignmentStatement { + fn interpret(&self) -> Result { + let expr_val = self.node.interpret()?; + Ok(expr_val) + } } diff --git a/src/interpreter/environment.rs b/src/interpreter/environment.rs deleted file mode 100644 index f5a191a..0000000 --- a/src/interpreter/environment.rs +++ /dev/null @@ -1,3 +0,0 @@ -struct Environment { - -} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f33f8b5..d2e9cfb 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,4 +1,3 @@ pub mod ast_walker; -pub mod types; pub mod error; -mod environment; +pub mod types; diff --git a/src/lexer/lexer_iter.rs b/src/lexer/lexer_iter.rs index 23a9ca0..575d0c6 100644 --- a/src/lexer/lexer_iter.rs +++ b/src/lexer/lexer_iter.rs @@ -42,17 +42,24 @@ impl<'a> LexerIter<'a> { pub fn as_str_while bool>(&mut self, mut predicate: F) -> &'a str { let str = self.inner.as_str(); - let mut end_indice = 0; - for (i, c) in str.char_indices() { - end_indice = i; - if !predicate(c) { - break; + + let mut iter = str.char_indices(); + let end_indice = loop { + match iter.next() { + Some((i, c)) => { + if !predicate(c) { + break i; + } + } + None => { + break iter.offset(); + } } - } + }; unsafe { self.inner = str.get_unchecked(end_indice..).chars(); - let res = str.get_unchecked(0..end_indice); - res + + str.get_unchecked(0..end_indice) as _ } } diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 757b499..c066cf2 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -100,7 +100,7 @@ impl<'a, 'b> Lexer<'a, 'b> { .map(|v| self.get_token(token::TokenType::Int(v))) .map_err(|_| LexingErrorKind::IntPrimitiveTooBig) }; - return res.map(|v| Some(v)).map_err(|e| self.get_error(e)) + return res.map(Some).map_err(|e| self.get_error(e)); /* Err(IntErrorKind::PosOverflow) | Err(IntErrorKind::NegOverflow) => return Err(self.get_error(LexingErrorKind::IntPrimitiveTooBig)), @@ -191,7 +191,7 @@ mod tests { #[test] fn test_int_literal_too_large() { - let mut lexer = Lexer::new("2222222222222222222", None); + let mut lexer = Lexer::new("2222222222222222222223", None); let errors = lexer.scan_tokens().unwrap_err(); assert_eq!(errors.len(), 1); assert!(matches!( diff --git a/src/lexer/token.rs b/src/lexer/token.rs index ca834d2..970c339 100644 --- a/src/lexer/token.rs +++ b/src/lexer/token.rs @@ -6,6 +6,12 @@ pub struct Token<'a> { pub location: Location<'a>, } +impl<'a> Token<'a> { + pub fn as_tuple<'b>(&'b self) -> (&'b TokenType, Location<'a>) { + (&self.token_type, self.location) + } +} + #[derive(Debug)] pub enum TokenType { LeftParen, diff --git a/src/lib.rs b/src/lib.rs index 275d2b4..d29df90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(char_indices_offset)] pub mod ast; pub mod error; pub mod interpreter; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 511a24f..dec1f59 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -2,12 +2,12 @@ use crftng_intrprtrs::run; #[test] fn test_one_equality() { - run_check_result_eq_bool("1 == 1", true); - run_check_result_eq_bool("1 >= 1", true); - run_check_result_eq_bool("1 <= 1", true); - run_check_result_eq_bool("1 != 1", false); - run_check_result_eq_bool("1 > 1", false); - run_check_result_eq_bool("1 < 1", false); + run_check_result_eq_bool("1 == 1;", true); + run_check_result_eq_bool("1 >= 1;", true); + run_check_result_eq_bool("1 <= 1;", true); + run_check_result_eq_bool("1 != 1;", false); + run_check_result_eq_bool("1 > 1;", false); + run_check_result_eq_bool("1 < 1;", false); } fn run_check_result_eq_bool(code: &str, value: bool) {