parse prefix operators

This commit is contained in:
Victor Timofei 2023-09-08 23:27:28 +03:00
parent a16d6eb464
commit 36bbc2cba6
Signed by: vtimofei
GPG Key ID: B790DCEBE281403A
3 changed files with 169 additions and 9 deletions

View File

@ -1,4 +1,7 @@
use std::{fmt::Debug, rc::Rc};
use std::{
fmt::{Debug, Display},
rc::Rc,
};
use crate::token::{Token, TokenType};
@ -79,6 +82,33 @@ impl Node for IntegerLiteral {}
impl Expression for IntegerLiteral {}
#[derive(Debug)]
pub enum PrefixOperator {
Minus,
Bang,
}
impl Display for PrefixOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use PrefixOperator::*;
match self {
Bang => write!(f, "!")?,
Minus => write!(f, "-")?,
}
Ok(())
}
}
#[derive(Debug)]
pub struct Prefix {
pub operator: PrefixOperator,
pub right: Rc<dyn Expression>,
}
impl Node for Prefix {}
impl Expression for Prefix {}
#[derive(Debug)]
pub struct DummyExpression {}

View File

@ -2,8 +2,8 @@ use std::{fmt, rc::Rc};
use crate::{
ast::{
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Program,
Return, Statement,
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Prefix,
PrefixOperator, Program, Return, Statement,
},
lexer::Lexer,
token::{Token, TokenType},
@ -20,6 +20,8 @@ pub enum Error {
expected: TokenType,
actual: Option<TokenType>,
},
NoPrefixParseFnFound,
EmptyExprFollowingPrefixOp(PrefixOperator),
}
#[derive(PartialEq, PartialOrd)]
@ -40,10 +42,15 @@ impl fmt::Display for Error {
match self {
UnexpectedTokenType { expected, actual } => write!(
f,
"expected token `{:?}`, actual token: `{:?}`",
expected, actual
)?,
}
"expected token `{expected}`, actual token: `{}`",
match actual {
Some(token) => format!("{token}"),
None => "None".to_string(),
},
),
NoPrefixParseFnFound => write!(f, "no prefix parse function found"),
EmptyExprFollowingPrefixOp(op) => write!(f, "empty expression following `{op}`"),
}?;
Ok(())
}
}
@ -153,7 +160,12 @@ impl Parser {
}
fn parse_expression(&mut self, precedence: Precedence) -> Option<Rc<dyn Expression>> {
let prefix = self.prefix_parse_fn()?;
let prefix = if let Some(prefix) = self.prefix_parse_fn() {
prefix
} else {
self.errors.push(Error::NoPrefixParseFnFound);
return None;
};
let left_exp = prefix(self);
Some(left_exp)
@ -176,6 +188,20 @@ impl Parser {
Rc::new(IntegerLiteral { value })
}
fn parse_prefix_expression(&mut self, operator: PrefixOperator) -> Rc<dyn Expression> {
self.next();
let right = if let Some(expr) = self.parse_expression(Precedence::Prefix) {
expr
} else {
self.errors
.push(Error::EmptyExprFollowingPrefixOp(operator));
return Rc::new(DummyExpression {});
};
Rc::new(Prefix { operator, right })
}
fn expect_peek(&mut self, token_type: TokenType) -> bool {
let peek_token = if let Some(token) = &self.peek_token {
token
@ -262,6 +288,12 @@ impl Parser {
Self::parse_integer_literal(parser, value)
}))
}
Minus => Some(Box::new(|parser| {
Self::parse_prefix_expression(parser, PrefixOperator::Minus)
})),
Bang => Some(Box::new(|parser| {
Self::parse_prefix_expression(parser, PrefixOperator::Bang)
})),
_ => None,
}
}
@ -294,7 +326,11 @@ impl Iterator for Parser {
mod tests {
use std::rc::Rc;
use crate::{ast::Statement, lexer::Lexer};
use crate::{
ast::{PrefixOperator, Statement},
lexer::{self, Lexer},
token::Token,
};
use super::Parser;
@ -386,6 +422,49 @@ mod tests {
}
}
#[test]
fn prefix_expressions() {
struct Test {
input: String,
token: Token,
operator: PrefixOperator,
value: i64,
}
let tests = vec![
Test {
input: "!15".to_owned(),
token: Token::Bang,
operator: PrefixOperator::Bang,
value: 15,
},
Test {
input: "-15".to_owned(),
token: Token::Minus,
operator: PrefixOperator::Minus,
value: 15,
},
];
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_prefix_expression(
program.statements.first().unwrap().clone(),
test.token,
test.operator,
test.value,
);
}
}
fn test_let_statement(stmt: Rc<dyn Statement>, name: &str) {
assert_eq!(
format!("{stmt:?}"),
@ -424,4 +503,16 @@ mod tests {
),
);
}
fn test_prefix_expression(
stmt: Rc<dyn Statement>,
token: Token,
operator: PrefixOperator,
num: i64,
) {
assert_eq!(
format!("{stmt:?}"),
format!("ExpressionStatement {{ token: {token:?}, expression: Prefix {{ operator: {operator:?}, right: IntegerLiteral {{ value: {num} }} }} }}"),
);
}
}

View File

@ -1,3 +1,5 @@
use std::fmt::Display;
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
Illegal,
@ -72,6 +74,43 @@ pub enum TokenType {
Return,
}
impl Display for TokenType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use TokenType::*;
let name = match self {
Illegal => "Illegal",
EOF => "EOF",
Ident => "Ident",
Int => "Int",
Assign => "=",
Plus => "+",
Minus => "-",
Bang => "!",
Asterisk => "*",
Slash => "/",
Lt => "<",
Gt => ">",
Eq => "==",
NotEq => "!=",
Comma => ",",
Semicolon => ";",
Lparen => "(",
Rparen => ")",
Lbrace => "{",
Rbrace => "}",
Function => "fn",
Let => "let",
True => "true",
False => "false",
If => "if",
Else => "else",
Return => "return",
};
write!(f, "{name}")?;
Ok(())
}
}
impl Token {
pub fn token_type(&self) -> TokenType {
match self {