parse prefix operators
This commit is contained in:
parent
a16d6eb464
commit
36bbc2cba6
32
src/ast.rs
32
src/ast.rs
|
@ -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 {}
|
||||
|
||||
|
|
107
src/parser.rs
107
src/parser.rs
|
@ -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} }} }} }}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
39
src/token.rs
39
src/token.rs
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue