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:
parent
6e7b4d8319
commit
155325e78c
58
src/main.rs
58
src/main.rs
|
@ -6,6 +6,7 @@ static LOWEST_PRECEDENCE: usize = 0;
|
||||||
enum Token<'a> {
|
enum Token<'a> {
|
||||||
IntegerLiteral(&'a str),
|
IntegerLiteral(&'a str),
|
||||||
Plus,
|
Plus,
|
||||||
|
Star,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
LeftParen,
|
LeftParen,
|
||||||
RightParen,
|
RightParen,
|
||||||
|
@ -17,13 +18,14 @@ impl Token<'_> {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
return match self {
|
return match self {
|
||||||
Token::IntegerLiteral(s) => s.len(),
|
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 {
|
pub fn precedence(&self) -> usize {
|
||||||
return match self {
|
return match self {
|
||||||
Token::Plus => 1,
|
Token::Plus => 1,
|
||||||
|
Token::Star => 2,
|
||||||
_ => LOWEST_PRECEDENCE,
|
_ => LOWEST_PRECEDENCE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +64,7 @@ impl<'a> Source<'a> {
|
||||||
|
|
||||||
let token = match chars.next()? {
|
let token = match chars.next()? {
|
||||||
'+' => Token::Plus,
|
'+' => Token::Plus,
|
||||||
|
'*' => Token::Star,
|
||||||
';' => Token::Semicolon,
|
';' => Token::Semicolon,
|
||||||
'(' => Token::LeftParen,
|
'(' => Token::LeftParen,
|
||||||
')' => Token::RightParen,
|
')' => Token::RightParen,
|
||||||
|
@ -117,10 +120,18 @@ struct Parser<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Expr<'a> {
|
enum Expr<'a> {
|
||||||
Literal(&'a str),
|
Literal(&'a str),
|
||||||
Paren(Box<Expr<'a>>),
|
// Paren(Box<Expr<'a>>),
|
||||||
Binary(Token<'a>, Box<Expr<'a>>, 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> {
|
impl<'a> Parser<'a> {
|
||||||
pub fn new(source: &'a mut Source<'a>) -> Self {
|
pub fn new(source: &'a mut Source<'a>) -> Self {
|
||||||
return 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)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Token<'a>> {
|
fn next(&mut self) -> Option<Token<'a>> {
|
||||||
return self.source.next();
|
return self.source.next();
|
||||||
|
@ -138,19 +150,26 @@ impl<'a> Parser<'a> {
|
||||||
return self.source.peek();
|
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)]
|
#[inline(always)]
|
||||||
fn bump(&mut self) {
|
fn bump(&mut self) {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_unary_expr(&mut self) -> Option<Expr<'a>> {
|
fn parse_primary_expr(&mut self) -> Option<Expr<'a>> {
|
||||||
return match self.next()? {
|
return match self.next()? {
|
||||||
Token::IntegerLiteral(s) => Some(Expr::Literal(s)),
|
Token::IntegerLiteral(s) => Some(Expr::Literal(s)),
|
||||||
Token::LeftParen => {
|
Token::LeftParen => {
|
||||||
let expr = self.parse_expr(0)?;
|
let expr = self.parse_expr(0)?;
|
||||||
|
|
||||||
return match self.next()? {
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -159,31 +178,28 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_expr(&mut self, min_precedence: usize) -> Option<Expr<'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 {
|
loop {
|
||||||
match self.peek()? {
|
let token = self.peek()?;
|
||||||
token @ Token::Plus => {
|
let prec = token.precedence();
|
||||||
let prec = token.precedence();
|
|
||||||
if prec <= min_precedence {
|
|
||||||
return Some(lhs);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Don't advance the parser before we make sure that the precedence
|
if prec <= min_precedence {
|
||||||
// is correct.
|
return Some(lhs);
|
||||||
self.bump();
|
|
||||||
|
|
||||||
let rhs = self.parse_expr(prec)?;
|
|
||||||
lhs = Expr::Binary(token, box lhs, box rhs);
|
|
||||||
},
|
|
||||||
_ => 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() {
|
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;";
|
||||||
let inline_source = "3 + (5 + (7 + 11));";
|
let inline_source = "3 + (5 + (7 + 11));";
|
||||||
let mut source = Source::new(inline_source);
|
let mut source = Source::new(inline_source);
|
||||||
|
|
Loading…
Reference in a new issue