diff --git a/src/main.rs b/src/main.rs index 8008898..ad8c9e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ static LOWEST_PRECEDENCE: usize = 0; enum Token<'a> { IntegerLiteral(&'a str), Plus, + Star, Semicolon, LeftParen, RightParen, @@ -17,13 +18,14 @@ impl Token<'_> { pub fn len(&self) -> usize { return match self { Token::IntegerLiteral(s) => s.len(), - Token::Plus | Token::Semicolon | Token::LeftParen | Token::RightParen => 1, + Token::Plus | Token::Semicolon | Token::LeftParen | Token::RightParen | Token::Star => 1, }; } pub fn precedence(&self) -> usize { return match self { Token::Plus => 1, + Token::Star => 2, _ => LOWEST_PRECEDENCE, } } @@ -62,6 +64,7 @@ impl<'a> Source<'a> { let token = match chars.next()? { '+' => Token::Plus, + '*' => Token::Star, ';' => Token::Semicolon, '(' => Token::LeftParen, ')' => Token::RightParen, @@ -117,10 +120,18 @@ struct Parser<'a> { #[derive(Debug)] enum Expr<'a> { Literal(&'a str), - Paren(Box>), + // Paren(Box>), Binary(Token<'a>, Box>, Box>), } +// TODO: Add expect method, which checks for whether two tokens are of the same +// kind, without checking for the equality of their (possible) inner values. +// The issue is if it should advance the parser (the more obvious way), or make +// it up to caller to do it (less obvious, but maybe useful?). The other issue +// is how should the parser behave on failed call to expect — should it panic, +// or maybe we have to introduce more sophisticated error handling? And then +// we also would want to use self.peek()? inside its implementation, is it even +// possible? So many questions. >_< impl<'a> Parser<'a> { pub fn new(source: &'a mut Source<'a>) -> Self { return Self { @@ -128,6 +139,7 @@ impl<'a> Parser<'a> { }; } + #[must_use = "You should always use the return value of the next method, as it mutates the parser."] #[inline(always)] fn next(&mut self) -> Option> { return self.source.next(); @@ -138,19 +150,26 @@ impl<'a> Parser<'a> { return self.source.peek(); } + // Just a wrapper around self.next which disables unused_must_use lint, so it + // can be more useful in some cases (such as when we already know what's the + // following token, for example from the previous call to self.peek, but we + // want to advance the parser now, and don't care about the result). + #[allow(unused_must_use)] #[inline(always)] fn bump(&mut self) { self.next(); } - fn parse_unary_expr(&mut self) -> Option> { + fn parse_primary_expr(&mut self) -> Option> { return match self.next()? { Token::IntegerLiteral(s) => Some(Expr::Literal(s)), Token::LeftParen => { let expr = self.parse_expr(0)?; return match self.next()? { - Token::RightParen => Some(Expr::Paren(box expr)), + // Should we bother keeping the parentheses information in the AST? + // Token::RightParen => Some(Expr::Paren(box expr)), + Token::RightParen => Some(expr), _ => None, }; }, @@ -159,31 +178,28 @@ impl<'a> Parser<'a> { } pub fn parse_expr(&mut self, min_precedence: usize) -> Option> { - let mut lhs = self.parse_unary_expr()?; + let mut lhs = self.parse_primary_expr()?; loop { - match self.peek()? { - token @ Token::Plus => { - let prec = token.precedence(); - if prec <= min_precedence { - return Some(lhs); - }; + let token = self.peek()?; + let prec = token.precedence(); - // Don't advance the parser before we make sure that the precedence - // is correct. - self.bump(); - - let rhs = self.parse_expr(prec)?; - lhs = Expr::Binary(token, box lhs, box rhs); - }, - _ => return Some(lhs), + if prec <= min_precedence { + return Some(lhs); }; - } + + // NOTE: Don't advance the parser before we make sure that the + // precedence is correct. + self.bump(); + + let rhs = self.parse_expr(prec)?; + lhs = Expr::Binary(token, box lhs, box rhs); + }; } } fn main() { - // let inline_source = "3 + 5 + 7;"; + // let inline_source = "3 + 5 * 7;"; // let inline_source = "(3 + 5) + 7;"; let inline_source = "3 + (5 + (7 + 11));"; let mut source = Source::new(inline_source);