implement infix expressions
This commit is contained in:
parent
36bbc2cba6
commit
9717bb3837
63
src/ast.rs
63
src/ast.rs
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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;\
|
||||
\
|
||||
|
|
203
src/parser.rs
203
src/parser.rs
|
@ -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} }} }} }}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue