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};
|
use crate::token::{Token, TokenType};
|
||||||
|
|
||||||
|
@ -79,6 +82,33 @@ impl Node for IntegerLiteral {}
|
||||||
|
|
||||||
impl Expression 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)]
|
#[derive(Debug)]
|
||||||
pub struct DummyExpression {}
|
pub struct DummyExpression {}
|
||||||
|
|
||||||
|
|
107
src/parser.rs
107
src/parser.rs
|
@ -2,8 +2,8 @@ use std::{fmt, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Program,
|
DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Prefix,
|
||||||
Return, Statement,
|
PrefixOperator, Program, Return, Statement,
|
||||||
},
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
token::{Token, TokenType},
|
token::{Token, TokenType},
|
||||||
|
@ -20,6 +20,8 @@ pub enum Error {
|
||||||
expected: TokenType,
|
expected: TokenType,
|
||||||
actual: Option<TokenType>,
|
actual: Option<TokenType>,
|
||||||
},
|
},
|
||||||
|
NoPrefixParseFnFound,
|
||||||
|
EmptyExprFollowingPrefixOp(PrefixOperator),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd)]
|
#[derive(PartialEq, PartialOrd)]
|
||||||
|
@ -40,10 +42,15 @@ impl fmt::Display for Error {
|
||||||
match self {
|
match self {
|
||||||
UnexpectedTokenType { expected, actual } => write!(
|
UnexpectedTokenType { expected, actual } => write!(
|
||||||
f,
|
f,
|
||||||
"expected token `{:?}`, actual token: `{:?}`",
|
"expected token `{expected}`, actual token: `{}`",
|
||||||
expected, actual
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +160,12 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression(&mut self, precedence: Precedence) -> Option<Rc<dyn Expression>> {
|
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);
|
let left_exp = prefix(self);
|
||||||
Some(left_exp)
|
Some(left_exp)
|
||||||
|
@ -176,6 +188,20 @@ impl Parser {
|
||||||
Rc::new(IntegerLiteral { value })
|
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 {
|
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
|
||||||
|
@ -262,6 +288,12 @@ impl Parser {
|
||||||
Self::parse_integer_literal(parser, value)
|
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,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,7 +326,11 @@ impl Iterator for Parser {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{ast::Statement, lexer::Lexer};
|
use crate::{
|
||||||
|
ast::{PrefixOperator, Statement},
|
||||||
|
lexer::{self, Lexer},
|
||||||
|
token::Token,
|
||||||
|
};
|
||||||
|
|
||||||
use super::Parser;
|
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) {
|
fn test_let_statement(stmt: Rc<dyn Statement>, name: &str) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{stmt:?}"),
|
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)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Illegal,
|
Illegal,
|
||||||
|
@ -72,6 +74,43 @@ pub enum TokenType {
|
||||||
Return,
|
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 {
|
impl Token {
|
||||||
pub fn token_type(&self) -> TokenType {
|
pub fn token_type(&self) -> TokenType {
|
||||||
match self {
|
match self {
|
||||||
|
|
Loading…
Reference in New Issue