implement infix expressions

This commit is contained in:
Victor Timofei 2023-09-09 22:30:13 +03:00
parent 36bbc2cba6
commit 9717bb3837
Signed by: vtimofei
GPG Key ID: B790DCEBE281403A
3 changed files with 256 additions and 12 deletions

View File

@ -109,6 +109,69 @@ impl Node for Prefix {}
impl Expression for Prefix {}
#[derive(Debug)]
pub enum InfixOperator {
Plus,
Minus,
Asterisk,
Slash,
Lt,
Gt,
Eq,
NotEq,
}
impl Display for InfixOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use InfixOperator::*;
match self {
Plus => write!(f, "+")?,
Minus => write!(f, "-")?,
Asterisk => write!(f, "*")?,
Slash => write!(f, "/")?,
Lt => write!(f, "<")?,
Gt => write!(f, ">")?,
Eq => write!(f, "==")?,
NotEq => write!(f, "!=")?,
}
Ok(())
}
}
#[derive(Debug)]
pub struct InfixOperatorFromTokenTypeError {
token: TokenType,
}
impl TryFrom<TokenType> for InfixOperator {
type Error = InfixOperatorFromTokenTypeError;
fn try_from(value: TokenType) -> Result<Self, Self::Error> {
use TokenType::*;
match value {
Plus => Ok(Self::Plus),
Minus => Ok(Self::Minus),
Asterisk => Ok(Self::Asterisk),
Slash => Ok(Self::Slash),
Lt => Ok(Self::Lt),
Gt => Ok(Self::Gt),
Eq => Ok(Self::Eq),
NotEq => Ok(Self::NotEq),
token => Err(Self::Error { token }),
}
}
}
#[derive(Debug)]
pub struct Infix {
pub operator: InfixOperator,
pub left: Rc<dyn Expression>,
pub right: Rc<dyn Expression>,
}
impl Node for Infix {}
impl Expression for Infix {}
#[derive(Debug)]
pub struct DummyExpression {}

View File

@ -139,7 +139,7 @@ mod tests {
use crate::token::Token;
#[test]
fn test_next_token() {
fn next_token() {
let input = "let five = 5;\
let ten = 10;\
\

View File

@ -2,11 +2,11 @@ use std::{fmt, rc::Rc};
use crate::{
ast::{
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Prefix,
PrefixOperator, Program, Return, Statement,
DummyExpression, Expression, ExpressionStatement, Identifier, Infix, InfixOperator,
IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement,
},
lexer::Lexer,
token::{Token, TokenType},
token::{self, Token, TokenType},
};
pub type Result<T> = std::result::Result<T, Error>;
@ -20,7 +20,8 @@ pub enum Error {
expected: TokenType,
actual: Option<TokenType>,
},
NoPrefixParseFnFound,
NoPrefixParseFnFound(TokenType),
NoInfixParseFnFound(TokenType),
EmptyExprFollowingPrefixOp(PrefixOperator),
}
@ -35,6 +36,24 @@ enum Precedence {
Call,
}
struct PrecedenceFromTokenTypeError {
value: TokenType,
}
impl TryFrom<TokenType> for Precedence {
type Error = PrecedenceFromTokenTypeError;
fn try_from(value: TokenType) -> std::result::Result<Self, Self::Error> {
use TokenType::*;
match value {
Eq | NotEq => Ok(Self::Equals),
Lt | Gt => Ok(Self::LessGreater),
Plus | Minus => Ok(Self::Sum),
Asterisk | Slash => Ok(Self::Product),
value => Err(PrecedenceFromTokenTypeError { value }),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "parser error: ")?;
@ -48,7 +67,8 @@ impl fmt::Display for Error {
None => "None".to_string(),
},
),
NoPrefixParseFnFound => write!(f, "no prefix parse function found"),
NoPrefixParseFnFound(t) => write!(f, "no prefix parse function found for `{t}`"),
NoInfixParseFnFound(t) => write!(f, "no infix parse function found for `{t}`"),
EmptyExprFollowingPrefixOp(op) => write!(f, "empty expression following `{op}`"),
}?;
Ok(())
@ -163,11 +183,26 @@ impl Parser {
let prefix = if let Some(prefix) = self.prefix_parse_fn() {
prefix
} else {
self.errors.push(Error::NoPrefixParseFnFound);
let err = Error::NoPrefixParseFnFound(self.cur_token.clone().unwrap().token_type());
self.errors.push(err);
return None;
};
let left_exp = prefix(self);
let mut left_exp = prefix(self);
while !self.peek_token_is(TokenType::Semicolon) && precedence < self.peek_precedence() {
let infix = if let Some(infix) = self.infix_parse_fn() {
infix
} else {
let err = Error::NoInfixParseFnFound(self.peek_token.clone().unwrap().token_type());
self.errors.push(err);
return None;
};
self.next();
left_exp = infix(self, left_exp);
}
Some(left_exp)
}
@ -202,6 +237,26 @@ impl Parser {
Rc::new(Prefix { operator, right })
}
fn parse_infix_expression(&mut self, left: Rc<dyn Expression>) -> Rc<dyn Expression> {
let token = if let Some(token) = &self.cur_token {
token
} else {
return Rc::new(DummyExpression {});
};
let operator: InfixOperator = token.token_type().try_into().unwrap();
let precedence = self.cur_precedence();
self.next();
let right = self.parse_expression(precedence).unwrap();
Rc::new(Infix {
left,
operator,
right,
})
}
fn expect_peek(&mut self, token_type: TokenType) -> bool {
let peek_token = if let Some(token) = &self.peek_token {
token
@ -267,6 +322,32 @@ impl Parser {
cur_token.token_type() == token_type
}
fn peek_precedence(&self) -> Precedence {
let token = if let Some(token) = &self.peek_token {
token
} else {
return Precedence::Lowest;
};
if let Ok(p) = token.token_type().try_into() {
p
} else {
Precedence::Lowest
}
}
fn cur_precedence(&self) -> Precedence {
let token = if let Some(token) = &self.cur_token {
token
} else {
return Precedence::Lowest;
};
if let Ok(p) = token.token_type().try_into() {
p
} else {
Precedence::Lowest
}
}
fn prefix_parse_fn(&self) -> Option<PrefixParseFn> {
let token = if let Some(token) = &self.cur_token {
token
@ -298,10 +379,19 @@ impl Parser {
}
}
fn infix_parse_fn(token: &Token) -> InfixParseFn {
fn infix_parse_fn(&self) -> Option<InfixParseFn> {
let token = if let Some(token) = &self.peek_token {
token
} else {
return None;
};
use Token::*;
match token {
_ => unimplemented!(),
Plus | Minus | Slash | Asterisk | Eq | NotEq | Lt | Gt => {
Some(Box::new(Self::parse_infix_expression))
}
_ => None,
}
}
}
@ -327,8 +417,8 @@ mod tests {
use std::rc::Rc;
use crate::{
ast::{PrefixOperator, Statement},
lexer::{self, Lexer},
ast::{InfixOperator, PrefixOperator, Statement},
lexer::Lexer,
token::Token,
};
@ -465,6 +555,85 @@ mod tests {
}
}
#[test]
fn infix_expressions() {
struct Test {
input: String,
left_value: i64,
operator: InfixOperator,
right_value: i64,
}
let tests = vec![
Test {
input: "5 + 5".into(),
left_value: 5,
operator: InfixOperator::Plus,
right_value: 5,
},
Test {
input: "5 - 5".into(),
left_value: 5,
operator: InfixOperator::Minus,
right_value: 5,
},
Test {
input: "5 * 5".into(),
left_value: 5,
operator: InfixOperator::Asterisk,
right_value: 5,
},
Test {
input: "5 / 5".into(),
left_value: 5,
operator: InfixOperator::Slash,
right_value: 5,
},
Test {
input: "5 < 5".into(),
left_value: 5,
operator: InfixOperator::Lt,
right_value: 5,
},
Test {
input: "5 > 5".into(),
left_value: 5,
operator: InfixOperator::Gt,
right_value: 5,
},
Test {
input: "5 == 5".into(),
left_value: 5,
operator: InfixOperator::Eq,
right_value: 5,
},
Test {
input: "5 != 5".into(),
left_value: 5,
operator: InfixOperator::NotEq,
right_value: 5,
},
];
for test in tests {
let l = Lexer::new(test.input);
let mut parser = Parser::new(l);
let program = parser.parse().unwrap();
check_parser_errors(parser);
assert_eq!(program.statements.len(), 1);
test_infix_expression(
program.statements.first().unwrap().clone(),
test.left_value,
test.operator,
test.right_value,
);
}
}
fn test_let_statement(stmt: Rc<dyn Statement>, name: &str) {
assert_eq!(
format!("{stmt:?}"),
@ -515,4 +684,16 @@ mod tests {
format!("ExpressionStatement {{ token: {token:?}, expression: Prefix {{ operator: {operator:?}, right: IntegerLiteral {{ value: {num} }} }} }}"),
);
}
fn test_infix_expression(
stmt: Rc<dyn Statement>,
left: i64,
operator: InfixOperator,
right: i64,
) {
assert_eq!(
format!("{stmt:?}"),
format!("ExpressionStatement {{ token: Int({left}), expression: Infix {{ operator: {operator:?}, left: IntegerLiteral {{ value: {left} }}, right: IntegerLiteral {{ value: {right} }} }} }}"),
);
}
}