parse booleans and heavy refactoring
This commit is contained in:
parent
9717bb3837
commit
d84878c323
|
@ -2,7 +2,3 @@
|
||||||
name = "monkeyrs"
|
name = "monkeyrs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
229
src/ast.rs
229
src/ast.rs
|
@ -3,19 +3,122 @@ use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::token::{Token, TokenType};
|
use crate::token::TokenType;
|
||||||
|
|
||||||
pub trait Node: Debug {}
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum NodeType {
|
||||||
|
Program,
|
||||||
|
LetStatement,
|
||||||
|
ReturnStatement,
|
||||||
|
ExpressionStatement,
|
||||||
|
IdentifierExpression,
|
||||||
|
IntegerLiteralExpression,
|
||||||
|
PrefixExpression,
|
||||||
|
InfixExpression,
|
||||||
|
BooleanExpression,
|
||||||
|
DummyExpression,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! node_type_eq {
|
||||||
|
( $x:expr, $y:expr ) => {{
|
||||||
|
if $x.node_type() != $y.node_type() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match $x.node_type() {
|
||||||
|
NodeType::Program => unreachable!(),
|
||||||
|
NodeType::DummyExpression => true,
|
||||||
|
NodeType::BooleanExpression => {
|
||||||
|
let __x = unsafe { $x.downcast::<Boolean>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Boolean>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::ExpressionStatement => {
|
||||||
|
let __x = unsafe { $x.downcast::<ExpressionStatement>() };
|
||||||
|
let __y = unsafe { $y.downcast::<ExpressionStatement>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::LetStatement => {
|
||||||
|
let __x = unsafe { $x.downcast::<Let>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Let>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::ReturnStatement => {
|
||||||
|
let __x = unsafe { $x.downcast::<Return>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Return>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::InfixExpression => {
|
||||||
|
let __x = unsafe { $x.downcast::<Infix>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Infix>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::PrefixExpression => {
|
||||||
|
let __x = unsafe { $x.downcast::<Prefix>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Prefix>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::IdentifierExpression => {
|
||||||
|
let __x = unsafe { $x.downcast::<Identifier>() };
|
||||||
|
let __y = unsafe { $y.downcast::<Identifier>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
NodeType::IntegerLiteralExpression => {
|
||||||
|
let __x = unsafe { $x.downcast::<IntegerLiteral>() };
|
||||||
|
let __y = unsafe { $y.downcast::<IntegerLiteral>() };
|
||||||
|
__x == __y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Node: Debug {
|
||||||
|
fn node_type(&self) -> NodeType;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Statement: Node {}
|
pub trait Statement: Node {}
|
||||||
|
|
||||||
|
impl dyn Statement {
|
||||||
|
unsafe fn downcast<T>(&self) -> &T {
|
||||||
|
&*(self as *const dyn Statement as *const T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Expression: Node {}
|
pub trait Expression: Node {}
|
||||||
|
|
||||||
|
impl dyn Expression {
|
||||||
|
unsafe fn downcast<T>(&self) -> &T {
|
||||||
|
&*(self as *const dyn Expression as *const T)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub statements: Vec<Rc<dyn Statement>>,
|
pub statements: Vec<Rc<dyn Statement>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Program {}
|
impl PartialEq for Program {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.statements.len() != other.statements.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut other_iter = other.statements.iter();
|
||||||
|
for stmt in self.statements.iter() {
|
||||||
|
let other = other_iter.next().unwrap();
|
||||||
|
|
||||||
|
if !node_type_eq!(stmt, other) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Program {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::Program
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for Program {
|
impl Debug for Program {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -33,56 +136,97 @@ impl Debug for Program {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Let {
|
pub struct Let {
|
||||||
pub token: Token,
|
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub value: Rc<dyn Expression>,
|
pub value: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Let {}
|
impl PartialEq for Let {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.name != other.name {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_type_eq!(self.value, other.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Let {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::LetStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Statement for Let {}
|
impl Statement for Let {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Return {
|
pub struct Return {
|
||||||
pub token: Token,
|
|
||||||
pub value: Rc<dyn Expression>,
|
pub value: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Return {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
node_type_eq!(self.value, other.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Return {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::ReturnStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Statement for Return {}
|
impl Statement for Return {}
|
||||||
|
|
||||||
impl Node for Return {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ExpressionStatement {
|
pub struct ExpressionStatement {
|
||||||
// TODO: probably not needed
|
|
||||||
pub token: Token,
|
|
||||||
pub expression: Rc<dyn Expression>,
|
pub expression: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ExpressionStatement {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
let expr = &self.expression;
|
||||||
|
let other = &other.expression;
|
||||||
|
|
||||||
|
node_type_eq!(expr, other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for ExpressionStatement {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::ExpressionStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Statement for ExpressionStatement {}
|
impl Statement for ExpressionStatement {}
|
||||||
|
|
||||||
impl Node for ExpressionStatement {}
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Identifier {
|
pub struct Identifier {
|
||||||
pub token_type: TokenType,
|
pub token_type: TokenType,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Identifier {}
|
impl Node for Identifier {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::IdentifierExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expression for Identifier {}
|
impl Expression for Identifier {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct IntegerLiteral {
|
pub struct IntegerLiteral {
|
||||||
pub value: i64,
|
pub value: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for IntegerLiteral {}
|
impl Node for IntegerLiteral {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::IntegerLiteralExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expression for IntegerLiteral {}
|
impl Expression for IntegerLiteral {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum PrefixOperator {
|
pub enum PrefixOperator {
|
||||||
Minus,
|
Minus,
|
||||||
Bang,
|
Bang,
|
||||||
|
@ -105,11 +249,25 @@ pub struct Prefix {
|
||||||
pub right: Rc<dyn Expression>,
|
pub right: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Prefix {}
|
impl PartialEq for Prefix {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.operator != other.operator {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
println!("Prefix PartialEq");
|
||||||
|
node_type_eq!(self.right, other.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Prefix {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::PrefixExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expression for Prefix {}
|
impl Expression for Prefix {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum InfixOperator {
|
pub enum InfixOperator {
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
|
@ -168,13 +326,42 @@ pub struct Infix {
|
||||||
pub right: Rc<dyn Expression>,
|
pub right: Rc<dyn Expression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for Infix {}
|
impl PartialEq for Infix {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.operator == other.operator
|
||||||
|
&& node_type_eq!(self.left, other.left)
|
||||||
|
&& node_type_eq!(self.right, other.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Infix {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::InfixExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expression for Infix {}
|
impl Expression for Infix {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Boolean {
|
||||||
|
pub value: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for Boolean {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::BooleanExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression for Boolean {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DummyExpression {}
|
pub struct DummyExpression {}
|
||||||
|
|
||||||
impl Node for DummyExpression {}
|
impl Node for DummyExpression {
|
||||||
|
fn node_type(&self) -> NodeType {
|
||||||
|
NodeType::DummyExpression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expression for DummyExpression {}
|
impl Expression for DummyExpression {}
|
||||||
|
|
24
src/lexer.rs
24
src/lexer.rs
|
@ -158,45 +158,45 @@ mod tests {
|
||||||
10 == 10;
|
10 == 10;
|
||||||
10 != 9;
|
10 != 9;
|
||||||
"
|
"
|
||||||
.to_string();
|
.into();
|
||||||
|
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
let tests = vec![
|
let tests = vec![
|
||||||
Let,
|
Let,
|
||||||
Ident("five".to_string()),
|
Ident("five".into()),
|
||||||
Assign,
|
Assign,
|
||||||
Int(5),
|
Int(5),
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Let,
|
Let,
|
||||||
Ident("ten".to_string()),
|
Ident("ten".into()),
|
||||||
Assign,
|
Assign,
|
||||||
Int(10),
|
Int(10),
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Let,
|
Let,
|
||||||
Ident("add".to_string()),
|
Ident("add".into()),
|
||||||
Assign,
|
Assign,
|
||||||
Function,
|
Function,
|
||||||
Lparen,
|
Lparen,
|
||||||
Ident("x".to_string()),
|
Ident("x".into()),
|
||||||
Comma,
|
Comma,
|
||||||
Ident("y".to_string()),
|
Ident("y".into()),
|
||||||
Rparen,
|
Rparen,
|
||||||
Lbrace,
|
Lbrace,
|
||||||
Ident("x".to_string()),
|
Ident("x".into()),
|
||||||
Plus,
|
Plus,
|
||||||
Ident("y".to_string()),
|
Ident("y".into()),
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Rbrace,
|
Rbrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Let,
|
Let,
|
||||||
Ident("result".to_string()),
|
Ident("result".into()),
|
||||||
Assign,
|
Assign,
|
||||||
Ident("add".to_string()),
|
Ident("add".into()),
|
||||||
Lparen,
|
Lparen,
|
||||||
Ident("five".to_string()),
|
Ident("five".into()),
|
||||||
Comma,
|
Comma,
|
||||||
Ident("ten".to_string()),
|
Ident("ten".into()),
|
||||||
Rparen,
|
Rparen,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Bang,
|
Bang,
|
||||||
|
|
269
src/parser.rs
269
src/parser.rs
|
@ -2,8 +2,8 @@ use std::{fmt, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
DummyExpression, Expression, ExpressionStatement, Identifier, Infix, InfixOperator,
|
Boolean, DummyExpression, Expression, ExpressionStatement, Identifier, Infix,
|
||||||
IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement,
|
InfixOperator, IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement,
|
||||||
},
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
token::{self, Token, TokenType},
|
token::{self, Token, TokenType},
|
||||||
|
@ -64,7 +64,7 @@ impl fmt::Display for Error {
|
||||||
"expected token `{expected}`, actual token: `{}`",
|
"expected token `{expected}`, actual token: `{}`",
|
||||||
match actual {
|
match actual {
|
||||||
Some(token) => format!("{token}"),
|
Some(token) => format!("{token}"),
|
||||||
None => "None".to_string(),
|
None => "None".into(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
NoPrefixParseFnFound(t) => write!(f, "no prefix parse function found for `{t}`"),
|
NoPrefixParseFnFound(t) => write!(f, "no prefix parse function found for `{t}`"),
|
||||||
|
@ -131,12 +131,6 @@ impl Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_let_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
fn parse_let_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
||||||
let token = if let Some(token) = &self.cur_token {
|
|
||||||
token.clone()
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let value = self.expect_peek_ident()?;
|
let value = self.expect_peek_ident()?;
|
||||||
|
|
||||||
let name = Identifier {
|
let name = Identifier {
|
||||||
|
@ -148,35 +142,33 @@ impl Parser {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
while !self.cur_token_is(TokenType::Semicolon) {
|
self.next();
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
let value = Rc::new(DummyExpression {});
|
|
||||||
|
|
||||||
Some(Rc::new(Let { token, name, value }))
|
// TODO: maybe not unwrap
|
||||||
|
let value = self.parse_expression(Precedence::Lowest).unwrap();
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
Some(Rc::new(Let { name, value }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_return_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
fn parse_return_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
||||||
let token = self.cur_token.clone().unwrap();
|
self.next();
|
||||||
|
|
||||||
while !self.cur_token_is(TokenType::Semicolon) {
|
// TODO: maybe not unwrap
|
||||||
self.next();
|
let value = self.parse_expression(Precedence::Lowest).unwrap();
|
||||||
}
|
self.next();
|
||||||
let value = Rc::new(DummyExpression {});
|
|
||||||
|
|
||||||
Some(Rc::new(Return { token, value }))
|
Some(Rc::new(Return { value }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
fn parse_expression_statement(&mut self) -> Option<Rc<dyn Statement>> {
|
||||||
let token = self.cur_token.clone()?;
|
|
||||||
|
|
||||||
let expression = self.parse_expression(Precedence::Lowest)?;
|
let expression = self.parse_expression(Precedence::Lowest)?;
|
||||||
|
|
||||||
if self.peek_token_is(TokenType::Semicolon) {
|
if self.peek_token_is(TokenType::Semicolon) {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Rc::new(ExpressionStatement { token, expression }))
|
Some(Rc::new(ExpressionStatement { expression }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression(&mut self, precedence: Precedence) -> Option<Rc<dyn Expression>> {
|
fn parse_expression(&mut self, precedence: Precedence) -> Option<Rc<dyn Expression>> {
|
||||||
|
@ -237,6 +229,12 @@ impl Parser {
|
||||||
Rc::new(Prefix { operator, right })
|
Rc::new(Prefix { operator, right })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_boolean(&mut self) -> Rc<dyn Expression> {
|
||||||
|
Rc::new(Boolean {
|
||||||
|
value: self.cur_token_is(TokenType::True),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_infix_expression(&mut self, left: Rc<dyn Expression>) -> Rc<dyn Expression> {
|
fn parse_infix_expression(&mut self, left: Rc<dyn Expression>) -> Rc<dyn Expression> {
|
||||||
let token = if let Some(token) = &self.cur_token {
|
let token = if let Some(token) = &self.cur_token {
|
||||||
token
|
token
|
||||||
|
@ -375,6 +373,8 @@ impl Parser {
|
||||||
Bang => Some(Box::new(|parser| {
|
Bang => Some(Box::new(|parser| {
|
||||||
Self::parse_prefix_expression(parser, PrefixOperator::Bang)
|
Self::parse_prefix_expression(parser, PrefixOperator::Bang)
|
||||||
})),
|
})),
|
||||||
|
True => Some(Box::new(Self::parse_boolean)),
|
||||||
|
False => Some(Box::new(Self::parse_boolean)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,9 +417,12 @@ mod tests {
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{InfixOperator, PrefixOperator, Statement},
|
ast::{
|
||||||
|
Boolean, ExpressionStatement, Identifier, Infix, InfixOperator, IntegerLiteral, Let,
|
||||||
|
Prefix, PrefixOperator, Program, Return, Statement,
|
||||||
|
},
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
token::Token,
|
token::{Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Parser;
|
use super::Parser;
|
||||||
|
@ -430,7 +433,7 @@ mod tests {
|
||||||
let y = 10;\
|
let y = 10;\
|
||||||
let foobar = 838383;\
|
let foobar = 838383;\
|
||||||
"
|
"
|
||||||
.to_string();
|
.into();
|
||||||
|
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
|
@ -439,14 +442,34 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
assert_eq!(program.statements.len(), 3);
|
assert_eq!(
|
||||||
|
program,
|
||||||
let expected_identifiers = vec!["x", "y", "foobar"];
|
Program {
|
||||||
let mut statements_iter = program.statements.iter();
|
statements: vec![
|
||||||
for tt in expected_identifiers {
|
Rc::new(Let {
|
||||||
let statement = statements_iter.next().unwrap();
|
name: Identifier {
|
||||||
test_let_statement(statement.clone(), tt);
|
token_type: TokenType::Let,
|
||||||
}
|
value: "x".into()
|
||||||
|
},
|
||||||
|
value: Rc::new(IntegerLiteral { value: 5 })
|
||||||
|
}),
|
||||||
|
Rc::new(Let {
|
||||||
|
name: Identifier {
|
||||||
|
token_type: TokenType::Let,
|
||||||
|
value: "y".into()
|
||||||
|
},
|
||||||
|
value: Rc::new(IntegerLiteral { value: 10 })
|
||||||
|
}),
|
||||||
|
Rc::new(Let {
|
||||||
|
name: Identifier {
|
||||||
|
token_type: TokenType::Let,
|
||||||
|
value: "foobar".into()
|
||||||
|
},
|
||||||
|
value: Rc::new(IntegerLiteral { value: 838383 })
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -455,7 +478,7 @@ mod tests {
|
||||||
return 10;\
|
return 10;\
|
||||||
return 838383;\
|
return 838383;\
|
||||||
"
|
"
|
||||||
.to_string();
|
.into();
|
||||||
|
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
|
@ -464,19 +487,27 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
assert_eq!(program.statements.len(), 3);
|
assert_eq!(
|
||||||
|
program,
|
||||||
for stmt in program.statements {
|
Program {
|
||||||
assert_eq!(
|
statements: vec![
|
||||||
format!("{stmt:?}"),
|
Rc::new(Return {
|
||||||
"Return { token: Return, value: DummyExpression }"
|
value: Rc::new(IntegerLiteral { value: 5 })
|
||||||
)
|
}),
|
||||||
}
|
Rc::new(Return {
|
||||||
|
value: Rc::new(IntegerLiteral { value: 10 })
|
||||||
|
}),
|
||||||
|
Rc::new(Return {
|
||||||
|
value: Rc::new(IntegerLiteral { value: 838383 })
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn identifier_expression() {
|
fn identifier_expression() {
|
||||||
let source = "foobar;".to_owned();
|
let source = "foobar;".into();
|
||||||
|
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
|
@ -485,17 +516,42 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
let expected_identifiers = vec!["foobar"];
|
assert_eq!(
|
||||||
let mut statements_iter = program.statements.iter();
|
program,
|
||||||
for tt in expected_identifiers {
|
Program {
|
||||||
let statement = statements_iter.next().unwrap();
|
statements: vec![Rc::new(ExpressionStatement {
|
||||||
test_identifier_expression(statement.clone(), tt);
|
expression: Rc::new(Identifier {
|
||||||
}
|
token_type: TokenType::Ident,
|
||||||
|
value: "foobar".into()
|
||||||
|
})
|
||||||
|
})]
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn integer_literal_expression() {
|
fn integer_literal_expression() {
|
||||||
let source = "6;".to_owned();
|
let source = "6;".into();
|
||||||
|
|
||||||
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
|
let mut parser = Parser::new(lexer);
|
||||||
|
|
||||||
|
let program = parser.parse().unwrap();
|
||||||
|
check_parser_errors(parser);
|
||||||
|
assert_eq!(
|
||||||
|
program,
|
||||||
|
Program {
|
||||||
|
statements: vec![Rc::new(ExpressionStatement {
|
||||||
|
expression: Rc::new(IntegerLiteral { value: 6 })
|
||||||
|
})]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bool_expression() {
|
||||||
|
let source = "true; false".into();
|
||||||
|
|
||||||
let lexer = Lexer::new(source);
|
let lexer = Lexer::new(source);
|
||||||
|
|
||||||
|
@ -504,12 +560,19 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
let expected_integers = vec![6];
|
assert_eq!(
|
||||||
let mut statements_iter = program.statements.iter();
|
program,
|
||||||
for tt in expected_integers {
|
Program {
|
||||||
let statement = statements_iter.next().unwrap();
|
statements: vec![
|
||||||
test_integer_literal_expression(statement.clone(), tt);
|
Rc::new(ExpressionStatement {
|
||||||
}
|
expression: Rc::new(Boolean { value: true })
|
||||||
|
}),
|
||||||
|
Rc::new(ExpressionStatement {
|
||||||
|
expression: Rc::new(Boolean { value: false })
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -523,13 +586,13 @@ mod tests {
|
||||||
|
|
||||||
let tests = vec![
|
let tests = vec![
|
||||||
Test {
|
Test {
|
||||||
input: "!15".to_owned(),
|
input: "!15".into(),
|
||||||
token: Token::Bang,
|
token: Token::Bang,
|
||||||
operator: PrefixOperator::Bang,
|
operator: PrefixOperator::Bang,
|
||||||
value: 15,
|
value: 15,
|
||||||
},
|
},
|
||||||
Test {
|
Test {
|
||||||
input: "-15".to_owned(),
|
input: "-15".into(),
|
||||||
token: Token::Minus,
|
token: Token::Minus,
|
||||||
operator: PrefixOperator::Minus,
|
operator: PrefixOperator::Minus,
|
||||||
value: 15,
|
value: 15,
|
||||||
|
@ -544,13 +607,16 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
assert_eq!(program.statements.len(), 1);
|
assert_eq!(
|
||||||
|
program,
|
||||||
test_prefix_expression(
|
Program {
|
||||||
program.statements.first().unwrap().clone(),
|
statements: vec![Rc::new(ExpressionStatement {
|
||||||
test.token,
|
expression: Rc::new(Prefix {
|
||||||
test.operator,
|
operator: test.operator,
|
||||||
test.value,
|
right: Rc::new(IntegerLiteral { value: test.value })
|
||||||
|
})
|
||||||
|
})]
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -623,24 +689,25 @@ mod tests {
|
||||||
let program = parser.parse().unwrap();
|
let program = parser.parse().unwrap();
|
||||||
check_parser_errors(parser);
|
check_parser_errors(parser);
|
||||||
|
|
||||||
assert_eq!(program.statements.len(), 1);
|
assert_eq!(
|
||||||
|
program,
|
||||||
test_infix_expression(
|
Program {
|
||||||
program.statements.first().unwrap().clone(),
|
statements: vec![Rc::new(ExpressionStatement {
|
||||||
test.left_value,
|
expression: Rc::new(Infix {
|
||||||
test.operator,
|
operator: test.operator,
|
||||||
test.right_value,
|
left: Rc::new(IntegerLiteral {
|
||||||
|
value: test.left_value
|
||||||
|
}),
|
||||||
|
right: Rc::new(IntegerLiteral {
|
||||||
|
value: test.right_value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})]
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_let_statement(stmt: Rc<dyn Statement>, name: &str) {
|
|
||||||
assert_eq!(
|
|
||||||
format!("{stmt:?}"),
|
|
||||||
format!("Let {{ token: Let, name: Identifier {{ token_type: Let, value: \"{name}\" }}, value: DummyExpression }}"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_parser_errors(parser: Parser) {
|
fn check_parser_errors(parser: Parser) {
|
||||||
if parser.errors().len() == 0 {
|
if parser.errors().len() == 0 {
|
||||||
return;
|
return;
|
||||||
|
@ -654,46 +721,4 @@ mod tests {
|
||||||
|
|
||||||
panic!("{err}");
|
panic!("{err}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_identifier_expression(stmt: Rc<dyn Statement>, name: &str) {
|
|
||||||
assert_eq!(
|
|
||||||
format!("{stmt:?}"),
|
|
||||||
format!(
|
|
||||||
"ExpressionStatement {{ token: Ident(\"{name}\"), expression: Identifier {{ token_type: Ident, value: \"{name}\" }} }}"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_integer_literal_expression(stmt: Rc<dyn Statement>, num: i64) {
|
|
||||||
assert_eq!(
|
|
||||||
format!("{stmt:?}"),
|
|
||||||
format!(
|
|
||||||
"ExpressionStatement {{ token: Int({num}), expression: IntegerLiteral {{ value: {num} }} }}"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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} }} }} }}"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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} }} }} }}"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Illegal,
|
Illegal,
|
||||||
EOF,
|
EOF,
|
||||||
|
@ -154,7 +154,7 @@ impl Token {
|
||||||
"if" => If,
|
"if" => If,
|
||||||
"else" => Else,
|
"else" => Else,
|
||||||
"return" => Return,
|
"return" => Return,
|
||||||
ident => Ident(ident.to_string()),
|
ident => Ident(ident.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue