Add multiplication operator

Can now parse a multiplication operator with the correct precedence.
Parentheses are no longer included in the AST output.

Binary operator parser has been simplified to avoid redundancy, and to
avoid having to manually provide every token that can be a binary
operator.
This commit is contained in:
Aodhnait Étaín 2021-05-27 12:24:25 +00:00
parent 6e7b4d8319
commit 155325e78c
Signed by: aodhneine
GPG key ID: ECE91C73AD45F245

View file

@ -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<Expr<'a>>),
// Paren(Box<Expr<'a>>),
Binary(Token<'a>, Box<Expr<'a>>, Box<Expr<'a>>),
}
// 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<Token<'a>> {
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<Expr<'a>> {
fn parse_primary_expr(&mut self) -> Option<Expr<'a>> {
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<Expr<'a>> {
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);