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 {}
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct DummyExpression {}
|
pub struct DummyExpression {}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ mod tests {
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_next_token() {
|
fn next_token() {
|
||||||
let input = "let five = 5;\
|
let input = "let five = 5;\
|
||||||
let ten = 10;\
|
let ten = 10;\
|
||||||
\
|
\
|
||||||
|
|
203
src/parser.rs
203
src/parser.rs
|
@ -2,11 +2,11 @@ use std::{fmt, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Prefix,
|
DummyExpression, Expression, ExpressionStatement, Identifier, Infix, InfixOperator,
|
||||||
PrefixOperator, Program, Return, Statement,
|
IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement,
|
||||||
},
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
token::{Token, TokenType},
|
token::{self, Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -20,7 +20,8 @@ pub enum Error {
|
||||||
expected: TokenType,
|
expected: TokenType,
|
||||||
actual: Option<TokenType>,
|
actual: Option<TokenType>,
|
||||||
},
|
},
|
||||||
NoPrefixParseFnFound,
|
NoPrefixParseFnFound(TokenType),
|
||||||
|
NoInfixParseFnFound(TokenType),
|
||||||
EmptyExprFollowingPrefixOp(PrefixOperator),
|
EmptyExprFollowingPrefixOp(PrefixOperator),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +36,24 @@ enum Precedence {
|
||||||
Call,
|
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 {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "parser error: ")?;
|
write!(f, "parser error: ")?;
|
||||||
|
@ -48,7 +67,8 @@ impl fmt::Display for Error {
|
||||||
None => "None".to_string(),
|
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}`"),
|
EmptyExprFollowingPrefixOp(op) => write!(f, "empty expression following `{op}`"),
|
||||||
}?;
|
}?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -163,11 +183,26 @@ impl Parser {
|
||||||
let prefix = if let Some(prefix) = self.prefix_parse_fn() {
|
let prefix = if let Some(prefix) = self.prefix_parse_fn() {
|
||||||
prefix
|
prefix
|
||||||
} else {
|
} else {
|
||||||
self.errors.push(Error::NoPrefixParseFnFound);
|
let err = Error::NoPrefixParseFnFound(self.cur_token.clone().unwrap().token_type());
|
||||||
|
self.errors.push(err);
|
||||||
return None;
|
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)
|
Some(left_exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,6 +237,26 @@ impl Parser {
|
||||||
Rc::new(Prefix { operator, right })
|
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 {
|
fn expect_peek(&mut self, token_type: TokenType) -> bool {
|
||||||
let peek_token = if let Some(token) = &self.peek_token {
|
let peek_token = if let Some(token) = &self.peek_token {
|
||||||
token
|
token
|
||||||
|
@ -267,6 +322,32 @@ impl Parser {
|
||||||
cur_token.token_type() == token_type
|
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> {
|
fn prefix_parse_fn(&self) -> Option<PrefixParseFn> {
|
||||||
let token = if let Some(token) = &self.cur_token {
|
let token = if let Some(token) = &self.cur_token {
|
||||||
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::*;
|
use Token::*;
|
||||||
match 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 std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{PrefixOperator, Statement},
|
ast::{InfixOperator, PrefixOperator, Statement},
|
||||||
lexer::{self, Lexer},
|
lexer::Lexer,
|
||||||
token::Token,
|
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) {
|
fn test_let_statement(stmt: Rc<dyn Statement>, name: &str) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{stmt:?}"),
|
format!("{stmt:?}"),
|
||||||
|
@ -515,4 +684,16 @@ mod tests {
|
||||||
format!("ExpressionStatement {{ token: {token:?}, expression: Prefix {{ operator: {operator:?}, right: IntegerLiteral {{ value: {num} }} }} }}"),
|
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