From b89c5a76251f864c22ae30e340e6616c259ab894 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Wed, 18 Oct 2023 23:56:16 +0100 Subject: [PATCH 1/7] cargo fmt --- src/ast.rs | 27 +++++---- src/evaluator.rs | 22 ++++--- src/main.rs | 2 +- src/object.rs | 4 +- src/parser.rs | 146 ++++++++++++++++------------------------------- src/repl.rs | 4 +- 6 files changed, 81 insertions(+), 124 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 5e6b758..40ed89e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -19,7 +19,7 @@ impl std::fmt::Display for Node { pub enum Literal { Integer(Integer), Boolean(Boolean), - String(StringLiteral) + String(StringLiteral), } impl std::fmt::Display for Literal { @@ -65,20 +65,27 @@ impl std::fmt::Display for Expression { alternative, }) => { if let Some(alternative) = alternative { - write!(f, "if {} {{\n{}\n}} else {{\n{}\n}}", condition, consequence, alternative) + write!( + f, + "if {} {{\n{}\n}} else {{\n{}\n}}", + condition, consequence, alternative + ) } else { write!(f, "if {} {{\n{}\n}}", condition, consequence) } - }, + } Expression::Function(FunctionLiteral { token, parameters, body, }) => { - let params = parameters.iter().map(|p| p.to_string()).collect::>(); + let params = parameters + .iter() + .map(|p| p.to_string()) + .collect::>(); write!(f, "fn({}) {{\n{}\n}}", params.join(", "), body) - }, + } Expression::Call(CallExpression { token, function, @@ -109,11 +116,9 @@ pub enum Statement { impl std::fmt::Display for Statement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Statement::Assign(Assignment { - token, - name, - value, - }) => write!(f, "{} {} = {}", token, name, value), + Statement::Assign(Assignment { token, name, value }) => { + write!(f, "{} {} = {}", token, name, value) + } Statement::Expr(expression) => write!(f, "{}", expression), Statement::Return(ReturnStatement { token, @@ -234,4 +239,4 @@ impl std::fmt::Display for BlockStatement { pub struct ReturnStatement { pub token: Token, pub return_value: Expression, -} \ No newline at end of file +} diff --git a/src/evaluator.rs b/src/evaluator.rs index 2b69670..012f143 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,7 +1,10 @@ use anyhow::{Error, Result}; use lazy_static::lazy_static; -use crate::{object::{Object, self}, ast::{Node, Program, Statement, Boolean, Expression, Literal}}; +use crate::{ + ast::{Boolean, Expression, Literal, Node, Program, Statement}, + object::{self, Object}, +}; lazy_static! { static ref TRUE: Object = Object::Boolean(true); @@ -73,16 +76,13 @@ fn native_bool_to_bool_object(input: bool) -> &'static Object { mod tests { use anyhow::Error; - use crate::{object::{Object}, lexer::Lexer, parser::Parser}; + use crate::{lexer::Lexer, object::Object, parser::Parser}; use super::*; #[test] fn test_eval_boolean_literals() -> Result<(), Error> { - let tests = vec![ - ("true", true), - ("false", false), - ]; + let tests = vec![("true", true), ("false", false)]; for (input, expected) in tests { let evaluated = assert_eval(input)?; @@ -94,10 +94,7 @@ mod tests { #[test] fn test_eval_integer_expression() -> Result<(), Error> { - let tests = vec![ - ("5", 5), - ("10", 10), - ]; + let tests = vec![("5", 5), ("10", 10)]; for (input, expected) in tests { let evaluated = assert_eval(input)?; @@ -114,7 +111,8 @@ mod tests { let program = parser.parse_program(); parser.check_errors()?; - let evaluated = eval_statements(&program.statements).ok_or_else(|| anyhow::anyhow!("eval returned None"))?; + let evaluated = eval_statements(&program.statements) + .ok_or_else(|| anyhow::anyhow!("eval returned None"))?; Ok(evaluated) } @@ -138,4 +136,4 @@ mod tests { Ok(()) } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index b6eb0f5..d0ce23f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,11 +6,11 @@ use repl::init_repl; mod ast; mod evaluator; mod lexer; +mod object; mod parser; mod repl; mod string; mod token; -mod object; pub const NAME: &str = env!("CARGO_PKG_NAME"); pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/src/object.rs b/src/object.rs index 0e038fd..ac31e20 100644 --- a/src/object.rs +++ b/src/object.rs @@ -27,9 +27,9 @@ impl std::fmt::Display for Object { } write!(f, "fn({}) {{\n{}\n}}", parameters_string, body) - }, + } Object::Return(value) => write!(f, "{}", value), Object::Null => write!(f, "null"), } } -} \ No newline at end of file +} diff --git a/src/parser.rs b/src/parser.rs index 0026fb0..b5cbb70 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,8 +5,9 @@ use log::trace; use crate::{ ast::{ - Expression, Identifier, InfixExpression, - PrefixExpression, Program, ReturnStatement, Statement, Boolean, IfExpression, BlockStatement, FunctionLiteral, Node, Assignment, CallExpression, Literal, Integer, + Assignment, BlockStatement, Boolean, CallExpression, Expression, FunctionLiteral, + Identifier, IfExpression, InfixExpression, Integer, Literal, Node, PrefixExpression, + Program, ReturnStatement, Statement, }, lexer::Lexer, token::{Token, TokenType}, @@ -115,7 +116,9 @@ impl<'a> Parser<'a> { parser.register_prefix(TokenType::If, |p| Parser::parse_if_expression(p)); parser.register_prefix(TokenType::Bang, |p| Parser::parse_prefix_expression(p)); parser.register_prefix(TokenType::Minus, |p| Parser::parse_prefix_expression(p)); - parser.register_prefix(TokenType::Dollar, |p| Parser::parse_variable_reference_expression(p)); + parser.register_prefix(TokenType::Dollar, |p| { + Parser::parse_variable_reference_expression(p) + }); parser.register_infix(TokenType::LParen, |p, left| { Parser::parse_call_expression(p, left) @@ -197,7 +200,7 @@ impl<'a> Parser<'a> { } }; - Some(Expression::Literal(Literal::Boolean( Boolean { + Some(Expression::Literal(Literal::Boolean(Boolean { token: current_token, value, }))) @@ -397,9 +400,7 @@ impl<'a> Parser<'a> { self.next_token(); - while !self.current_token_is(TokenType::RBrace) - && !self.current_token_is(TokenType::Eof) - { + while !self.current_token_is(TokenType::RBrace) && !self.current_token_is(TokenType::Eof) { let statement = self.parse_statement(); if let Some(statement) = statement { @@ -417,19 +418,19 @@ impl<'a> Parser<'a> { fn parse_function_literal(&mut self) -> Option { let current_token = self.current_token.clone().unwrap(); - + if !self.expect_peek(TokenType::LParen) { return None; } - + let parameters = self.parse_function_parameters(); - + if !self.expect_peek(TokenType::LBrace) { return None; } - + let body = self.parse_block_statement(); - + if let Some(body) = body { Some(Expression::Function(FunctionLiteral { token: current_token, @@ -440,16 +441,15 @@ impl<'a> Parser<'a> { None } } - fn parse_function_parameters(&mut self) -> Vec { let mut identifiers = vec![]; - + if self.peek_token_is(&TokenType::RParen) { self.next_token(); // Consume the RParen and exit return identifiers; } - + // Expecting a Dollar sign and identifier (variable name) if self.expect_peek(TokenType::Dollar) { self.next_token(); // Consume Dollar @@ -459,7 +459,7 @@ impl<'a> Parser<'a> { }; identifiers.push(identifier); } - + // Expecting comma-separated identifiers while self.peek_token_is(&TokenType::Comma) { self.next_token(); // Consume Comma @@ -472,14 +472,14 @@ impl<'a> Parser<'a> { identifiers.push(identifier); } } - + // Expecting RParen to close the parameters list if !self.expect_peek(TokenType::RParen) { return vec![]; } - + identifiers - } + } fn parse_grouped_expression(&mut self) -> Option { self.next_token(); @@ -681,7 +681,9 @@ mod tests { use anyhow::{Error, Result}; - use crate::ast::{InfixExpression, PrefixExpression, Statement, IfExpression, FunctionLiteral, CallExpression}; + use crate::ast::{ + CallExpression, FunctionLiteral, IfExpression, InfixExpression, PrefixExpression, Statement, + }; #[test] fn test_assignment_statements() -> Result<(), Error> { @@ -742,7 +744,9 @@ mod tests { } } Statement::Assign(variable_assignment) => { - if let Expression::Literal(Literal::Boolean(boolean)) = &variable_assignment.value { + if let Expression::Literal(Literal::Boolean(boolean)) = + &variable_assignment.value + { assert_eq!(expected_values[i], boolean.value); } else { assert!(false, "Expected Boolean expression"); @@ -775,24 +779,11 @@ mod tests { assert_eq!(3, call_expression.arguments.len()); - assert_literal_expression( - &call_expression.arguments[0], - "1", - )?; - - assert_infix_expression( - &call_expression.arguments[1], - "2", - "*", - "3", - )?; - - assert_infix_expression( - &call_expression.arguments[2], - "4", - "+", - "5", - )?; + assert_literal_expression(&call_expression.arguments[0], "1")?; + + assert_infix_expression(&call_expression.arguments[1], "2", "*", "3")?; + + assert_infix_expression(&call_expression.arguments[2], "4", "+", "5")?; } else { assert!(false, "Expected CallExpression"); } @@ -820,12 +811,7 @@ mod tests { if let Statement::Expr(expression) = &program.statements[0] { if let Expression::If(if_expression) = &expression { - assert_infix_expression( - &if_expression.condition, - "x", - "<", - "y", - )?; + assert_infix_expression(&if_expression.condition, "x", "<", "y")?; assert_eq!(1, if_expression.consequence.statements.len()); @@ -863,12 +849,7 @@ mod tests { if let Statement::Expr(expression) = &program.statements[0] { if let Expression::If(if_expression) = &expression { - assert_infix_expression( - &if_expression.condition, - "x", - "<", - "y", - )?; + assert_infix_expression(&if_expression.condition, "x", "<", "y")?; assert_eq!(1, if_expression.consequence.statements.len()); @@ -880,7 +861,10 @@ mod tests { assert!(false, "Expected ReturnStatement"); } - assert_eq!(1, if_expression.alternative.as_ref().unwrap().statements.len()); + assert_eq!( + 1, + if_expression.alternative.as_ref().unwrap().statements.len() + ); if let Statement::Return(return_statement) = &if_expression.alternative.as_ref().unwrap().statements[0] @@ -1066,11 +1050,7 @@ mod tests { assert_eq!(1, program.statements.len()); if let Statement::Expr(expression) = &program.statements[0] { - assert_prefix_expression( - &expression, - *_operator, - *value, - )?; + assert_prefix_expression(&expression, *_operator, *value)?; } else { assert!(false, "Expected ExpressionStatement"); } @@ -1134,29 +1114,19 @@ mod tests { fn assert_function_literal(expression: &Expression) -> Result<(), Error> { if let Expression::Function(function_literal) = expression { assert_eq!(2, function_literal.parameters.len()); - + assert_eq!("x", function_literal.parameters[0].value); assert_eq!("y", function_literal.parameters[1].value); - + if let BlockStatement { statements, .. } = &function_literal.body { assert_eq!(1, statements.len()); - + match &statements[0] { Statement::Return(return_statement) => { - assert_infix_expression( - &return_statement.return_value, - "x", - "+", - "y", - )?; + assert_infix_expression(&return_statement.return_value, "x", "+", "y")?; } Statement::Expr(expression) => { - assert_infix_expression( - expression, - "x", - "+", - "y", - )?; + assert_infix_expression(expression, "x", "+", "y")?; } _ => assert!(false, "Expected ReturnStatement or ExpressionStatement"), } @@ -1166,32 +1136,19 @@ mod tests { } else { assert!(false, "Expected FunctionLiteral"); } - + Ok(()) - } + } fn assert_call_expression(expression: &Expression) -> Result<(), Error> { if let Expression::Call(call_expression) = expression { assert_eq!(3, call_expression.arguments.len()); - assert_literal_expression( - &call_expression.arguments[0], - "1", - )?; - - assert_infix_expression( - &call_expression.arguments[1], - "2", - "*", - "3", - )?; - - assert_infix_expression( - &call_expression.arguments[2], - "4", - "+", - "5", - )?; + assert_literal_expression(&call_expression.arguments[0], "1")?; + + assert_infix_expression(&call_expression.arguments[1], "2", "*", "3")?; + + assert_infix_expression(&call_expression.arguments[2], "4", "+", "5")?; } else { assert!(false, "Expected CallExpression"); } @@ -1237,10 +1194,7 @@ mod tests { Ok(()) } - fn assert_literal_expression( - expression: &Expression, - expected: &str, - ) -> Result { + fn assert_literal_expression(expression: &Expression, expected: &str) -> Result { match expression { Expression::Literal(Literal::Integer(integer_literal)) => { assert_eq!(expected, integer_literal.value.to_string()); diff --git a/src/repl.rs b/src/repl.rs index de1bf13..f4eaec6 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -2,7 +2,7 @@ use anyhow::{Error, Result}; use rustyline::error::ReadlineError; -use crate::{lexer::Lexer, parser::Parser, evaluator}; +use crate::{evaluator, lexer::Lexer, parser::Parser}; const PROMPT: &str = ">> "; @@ -55,4 +55,4 @@ pub fn init_repl() -> Result<(), Error> { } Ok(()) -} \ No newline at end of file +} From 7af7a7fad31487f157244ffe13bb649ba7e5c00e Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Wed, 18 Oct 2023 23:57:05 +0100 Subject: [PATCH 2/7] cargo fix --- src/ast.rs | 16 ++++++++-------- src/evaluator.rs | 8 ++++---- src/parser.rs | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 40ed89e..af05b08 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -25,9 +25,9 @@ pub enum Literal { impl std::fmt::Display for Literal { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - Literal::Integer(Integer { token, value }) => write!(f, "{}", value), - Literal::Boolean(Boolean { token, value }) => write!(f, "{}", value), - Literal::String(StringLiteral { token, value }) => write!(f, "{}", value), + Literal::Integer(Integer { token: _, value }) => write!(f, "{}", value), + Literal::Boolean(Boolean { token: _, value }) => write!(f, "{}", value), + Literal::String(StringLiteral { token: _, value }) => write!(f, "{}", value), } } } @@ -48,18 +48,18 @@ impl std::fmt::Display for Expression { Expression::Identifier(identifier) => write!(f, "{}", identifier), Expression::Literal(literal) => write!(f, "{}", literal), Expression::Infix(InfixExpression { - token, + token: _, left, operator, right, }) => write!(f, "({} {} {})", left, operator, right), Expression::Prefix(PrefixExpression { - token, + token: _, operator, right, }) => write!(f, "({}{})", operator, right), Expression::If(IfExpression { - token, + token: _, condition, consequence, alternative, @@ -75,7 +75,7 @@ impl std::fmt::Display for Expression { } } Expression::Function(FunctionLiteral { - token, + token: _, parameters, body, }) => { @@ -87,7 +87,7 @@ impl std::fmt::Display for Expression { write!(f, "fn({}) {{\n{}\n}}", params.join(", "), body) } Expression::Call(CallExpression { - token, + token: _, function, arguments, }) => { diff --git a/src/evaluator.rs b/src/evaluator.rs index 012f143..bceebee 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,9 +1,9 @@ -use anyhow::{Error, Result}; + use lazy_static::lazy_static; use crate::{ - ast::{Boolean, Expression, Literal, Node, Program, Statement}, - object::{self, Object}, + ast::{Boolean, Expression, Literal, Node, Statement}, + object::{Object}, }; lazy_static! { @@ -51,7 +51,7 @@ fn eval_statement(statement: &Statement) -> Option { } } -fn eval_identifier(identifier: String) -> Option { +fn eval_identifier(_identifier: String) -> Option { unimplemented!("eval_identifier"); } diff --git a/src/parser.rs b/src/parser.rs index b5cbb70..42d54f7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,7 +6,7 @@ use log::trace; use crate::{ ast::{ Assignment, BlockStatement, Boolean, CallExpression, Expression, FunctionLiteral, - Identifier, IfExpression, InfixExpression, Integer, Literal, Node, PrefixExpression, + Identifier, IfExpression, InfixExpression, Integer, Literal, PrefixExpression, Program, ReturnStatement, Statement, }, lexer::Lexer, @@ -381,7 +381,7 @@ impl<'a> Parser<'a> { } fn parse_expression_statement(&mut self) -> Option { - let current_token = self.current_token.clone().unwrap(); + let _current_token = self.current_token.clone().unwrap(); let expression = self.parse_expression(Precedence::Lowest); @@ -682,7 +682,7 @@ mod tests { use anyhow::{Error, Result}; use crate::ast::{ - CallExpression, FunctionLiteral, IfExpression, InfixExpression, PrefixExpression, Statement, + Statement, }; #[test] From 6db4dab2ab98be03c0397a5a683756889cae529d Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Thu, 19 Oct 2023 20:27:24 +0100 Subject: [PATCH 3/7] feat: prefix expression evaluation (bang operator) --- src/ast.rs | 16 ++++++++++++++++ src/evaluator.rs | 32 ++++++++++++++++++++++++++++++++ src/object.rs | 1 + 3 files changed, 49 insertions(+) diff --git a/src/ast.rs b/src/ast.rs index af05b08..779e4df 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -16,6 +16,7 @@ impl std::fmt::Display for Node { } } +#[derive(Clone, PartialEq)] pub enum Literal { Integer(Integer), Boolean(Boolean), @@ -32,6 +33,7 @@ impl std::fmt::Display for Literal { } } +#[derive(Clone, PartialEq)] pub enum Expression { Identifier(Identifier), Literal(Literal), @@ -107,6 +109,7 @@ impl std::fmt::Display for Expression { } } +#[derive(Clone, PartialEq)] pub enum Statement { Assign(Assignment), Expr(Expression), @@ -128,6 +131,7 @@ impl std::fmt::Display for Statement { } } +#[derive(Clone, PartialEq)] pub struct Program { pub statements: Vec, } @@ -153,34 +157,40 @@ impl std::fmt::Display for Program { } // LITERALS +#[derive(Clone, PartialEq)] pub struct Boolean { pub token: Token, pub value: bool, } +#[derive(Clone, PartialEq)] pub struct Integer { pub token: Token, pub value: i64, } +#[derive(Clone, PartialEq)] pub struct StringLiteral { pub token: Token, pub value: String, } // EXPRESSIONS +#[derive(Clone, PartialEq)] pub struct FunctionLiteral { pub token: Token, pub parameters: Vec, pub body: BlockStatement, } +#[derive(Clone, PartialEq)] pub struct CallExpression { pub token: Token, pub function: Box, pub arguments: Vec, } +#[derive(Clone, PartialEq)] pub struct Identifier { pub token: Token, pub value: String, @@ -192,6 +202,7 @@ impl std::fmt::Display for Identifier { } } +#[derive(Clone, PartialEq)] pub struct IfExpression { pub token: Token, pub condition: Box, @@ -199,6 +210,7 @@ pub struct IfExpression { pub alternative: Option, } +#[derive(Clone, PartialEq)] pub struct InfixExpression { pub token: Token, pub left: Box, @@ -206,6 +218,7 @@ pub struct InfixExpression { pub right: Box, } +#[derive(Clone, PartialEq)] pub struct PrefixExpression { pub token: Token, pub operator: String, @@ -213,12 +226,14 @@ pub struct PrefixExpression { } // STATEMENTS +#[derive(Clone, PartialEq)] pub struct Assignment { pub token: Token, pub name: Identifier, pub value: Expression, } +#[derive(Clone, PartialEq)] pub struct BlockStatement { pub token: Token, pub statements: Vec, @@ -236,6 +251,7 @@ impl std::fmt::Display for BlockStatement { } } +#[derive(Clone, PartialEq)] pub struct ReturnStatement { pub token: Token, pub return_value: Expression, diff --git a/src/evaluator.rs b/src/evaluator.rs index bceebee..04ca4ef 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -9,6 +9,7 @@ use crate::{ lazy_static! { static ref TRUE: Object = Object::Boolean(true); static ref FALSE: Object = Object::Boolean(false); + static ref NULL: Object = Object::Null; } pub fn eval(node: Node) -> Option { @@ -40,10 +41,29 @@ fn eval_expression(expression: &Expression) -> Option { match expression { Expression::Identifier(identifier) => eval_identifier(identifier.to_string()), Expression::Literal(literal) => eval_literal(&literal), + Expression::Prefix(prefix) => eval_prefix_expression(prefix.operator.to_string(), &prefix.right), _ => None, } } +fn eval_prefix_expression(operator: String, right: &Expression) -> Option { + let right = eval_expression(right)?; + + match operator.as_str() { + "!" => Some(eval_bang_operator_expression(right)), + _ => None, + } +} + +fn eval_bang_operator_expression(right: Object) -> Object { + match right { + val if val == *TRUE => FALSE.clone(), + val if val == *FALSE => TRUE.clone(), + val if val == *NULL => TRUE.clone(), + _ => FALSE.clone(), + } +} + fn eval_statement(statement: &Statement) -> Option { match statement { Statement::Expr(expression) => eval_expression(expression), @@ -104,6 +124,18 @@ mod tests { Ok(()) } + #[test] + fn test_eval_bang_operator() -> Result<(), Error> { + let tests = vec![("!true", false), ("!false", true), ("!5", false), ("!!true", true)]; + + for (input, expected) in tests { + let evaluated = assert_eval(input)?; + assert_boolean_literal_object(evaluated, expected)?; + } + + Ok(()) + } + fn assert_eval(input: &str) -> Result { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); diff --git a/src/object.rs b/src/object.rs index ac31e20..7553310 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,5 +1,6 @@ use crate::ast::BlockStatement; +#[derive(Clone, PartialEq)] pub enum Object { Integer(i64), Boolean(bool), From b1833b2e996448b58f05550cf516b3ae447c405a Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Thu, 19 Oct 2023 20:33:45 +0100 Subject: [PATCH 4/7] feat: boolean, integer and string infix expression evaluation --- src/evaluator.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/src/evaluator.rs b/src/evaluator.rs index 04ca4ef..7730f2c 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -41,7 +41,62 @@ fn eval_expression(expression: &Expression) -> Option { match expression { Expression::Identifier(identifier) => eval_identifier(identifier.to_string()), Expression::Literal(literal) => eval_literal(&literal), - Expression::Prefix(prefix) => eval_prefix_expression(prefix.operator.to_string(), &prefix.right), + Expression::Infix(infix) => eval_infix_expression( + infix.operator.to_string(), + &infix.left, + &infix.right, + ), + Expression::Prefix(prefix) => eval_prefix_expression( + prefix.operator.to_string(), + &prefix.right + ), + _ => None, + } +} + +fn eval_infix_expression(operator: String, left: &Expression, right: &Expression) -> Option { + let left = eval_expression(left)?; + let right = eval_expression(right)?; + + match (left, right) { + (Object::Integer(left), Object::Integer(right)) => { + eval_integer_infix_expression(operator, left, right) + } + (Object::Boolean(left), Object::Boolean(right)) => { + eval_boolean_infix_expression(operator, left, right) + } + (Object::String(left), Object::String(right)) => { + eval_string_infix_expression(operator, left, right) + } + _ => None, + } +} + +fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Option { + match operator.as_str() { + "==" => Some(native_bool_to_bool_object(left == right)), + "!=" => Some(native_bool_to_bool_object(left != right)), + _ => None, + } +} + +fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Option { + match operator.as_str() { + "+" => Some(Object::Integer(left + right)), + "-" => Some(Object::Integer(left - right)), + "*" => Some(Object::Integer(left * right)), + "/" => Some(Object::Integer(left / right)), + "<" => Some(native_bool_to_bool_object(left < right)), + ">" => Some(native_bool_to_bool_object(left > right)), + "==" => Some(native_bool_to_bool_object(left == right)), + "!=" => Some(native_bool_to_bool_object(left != right)), + _ => None, + } +} + +fn eval_string_infix_expression(operator: String, left: String, right: String) -> Option { + match operator.as_str() { + "+" => Some(Object::String(format!("{}{}", left, right))), _ => None, } } @@ -50,6 +105,7 @@ fn eval_prefix_expression(operator: String, right: &Expression) -> Option Some(eval_minus_prefix_operator_expression(right)), "!" => Some(eval_bang_operator_expression(right)), _ => None, } @@ -64,6 +120,13 @@ fn eval_bang_operator_expression(right: Object) -> Object { } } +fn eval_minus_prefix_operator_expression(right: Object) -> Object { + match right { + Object::Integer(integer) => Object::Integer(-integer), + _ => NULL.clone(), + } +} + fn eval_statement(statement: &Statement) -> Option { match statement { Statement::Expr(expression) => eval_expression(expression), @@ -84,11 +147,11 @@ fn eval_literal(literal: &Literal) -> Option { } } -fn native_bool_to_bool_object(input: bool) -> &'static Object { +fn native_bool_to_bool_object(input: bool) -> Object { if input { - &TRUE + TRUE.clone() } else { - &FALSE + FALSE.clone() } } @@ -112,9 +175,57 @@ mod tests { Ok(()) } + #[test] + fn test_eval_boolean_expression() -> Result<(), Error> { + let tests = vec![ + ("true", true), + ("false", false), + ("1 < 2", true), + ("1 > 2", false), + ("1 < 1", false), + ("1 > 1", false), + ("1 == 1", true), + ("1 != 1", false), + ("1 == 2", false), + ("1 != 2", true), + ("true == true", true), + ("false == false", true), + ("true == false", false), + ("true != false", true), + ("false != true", true), + ("(1 < 2) == true", true), + ("(1 < 2) == false", false), + ("(1 > 2) == true", false), + ("(1 > 2) == false", true), + ]; + + for (input, expected) in tests { + let evaluated = assert_eval(input)?; + assert_boolean_literal_object(evaluated, expected)?; + } + + Ok(()) + } + #[test] fn test_eval_integer_expression() -> Result<(), Error> { - let tests = vec![("5", 5), ("10", 10)]; + let tests = vec![ + ("5", 5), + ("10", 10), + ("-5", -5), + ("-10", -10), + ("5 + 5 + 5 + 5 - 10", 10), + ("2 * 2 * 2 * 2 * 2", 32), + ("-50 + 100 + -50", 0), + ("5 * 2 + 10", 20), + ("5 + 2 * 10", 25), + ("20 + 2 * -10", 0), + ("50 / 2 * 2 + 10", 60), + ("2 * (5 + 10)", 30), + ("3 * 3 * 3 + 10", 37), + ("3 * (3 * 3) + 10", 37), + ("(5 + 10 * 2 + 15 / 3) * 2 + -10", 50), + ]; for (input, expected) in tests { let evaluated = assert_eval(input)?; From 60d196a4a218253cc630e3a6e3baf0ad2f046abd Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Fri, 20 Oct 2023 20:17:03 +0100 Subject: [PATCH 5/7] feat: improved error handling and implemented if/else evaluation --- src/ast.rs | 32 ++++----- src/evaluator.rs | 184 +++++++++++++++++++++++++++++++++-------------- src/object.rs | 2 +- src/repl.rs | 2 +- 4 files changed, 147 insertions(+), 73 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 779e4df..7d80541 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -16,7 +16,7 @@ impl std::fmt::Display for Node { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Literal { Integer(Integer), Boolean(Boolean), @@ -33,7 +33,7 @@ impl std::fmt::Display for Literal { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Expression { Identifier(Identifier), Literal(Literal), @@ -109,7 +109,7 @@ impl std::fmt::Display for Expression { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Statement { Assign(Assignment), Expr(Expression), @@ -131,7 +131,7 @@ impl std::fmt::Display for Statement { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Program { pub statements: Vec, } @@ -157,40 +157,40 @@ impl std::fmt::Display for Program { } // LITERALS -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Boolean { pub token: Token, pub value: bool, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Integer { pub token: Token, pub value: i64, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct StringLiteral { pub token: Token, pub value: String, } // EXPRESSIONS -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct FunctionLiteral { pub token: Token, pub parameters: Vec, pub body: BlockStatement, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct CallExpression { pub token: Token, pub function: Box, pub arguments: Vec, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Identifier { pub token: Token, pub value: String, @@ -202,7 +202,7 @@ impl std::fmt::Display for Identifier { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct IfExpression { pub token: Token, pub condition: Box, @@ -210,7 +210,7 @@ pub struct IfExpression { pub alternative: Option, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct InfixExpression { pub token: Token, pub left: Box, @@ -218,7 +218,7 @@ pub struct InfixExpression { pub right: Box, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct PrefixExpression { pub token: Token, pub operator: String, @@ -226,14 +226,14 @@ pub struct PrefixExpression { } // STATEMENTS -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Assignment { pub token: Token, pub name: Identifier, pub value: Expression, } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct BlockStatement { pub token: Token, pub statements: Vec, @@ -251,7 +251,7 @@ impl std::fmt::Display for BlockStatement { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ReturnStatement { pub token: Token, pub return_value: Expression, diff --git a/src/evaluator.rs b/src/evaluator.rs index 7730f2c..456a2c7 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,10 +1,8 @@ +use anyhow::{Result, Ok, Error}; use lazy_static::lazy_static; -use crate::{ - ast::{Boolean, Expression, Literal, Node, Statement}, - object::{Object}, -}; +use crate::{ast::{Boolean, Expression, Literal, Node, Statement, IfExpression}, object::Object}; lazy_static! { static ref TRUE: Object = Object::Boolean(true); @@ -12,49 +10,85 @@ lazy_static! { static ref NULL: Object = Object::Null; } -pub fn eval(node: Node) -> Option { +pub fn eval(node: Node) -> Result { match node { Node::Expression(expression) => eval_expression(&expression), Node::Program(program) => eval_statements(&program.statements), Node::Statement(statement) => eval_statement(&statement), - _ => None, + _ => Err(Error::msg(format!("Unknown node type: {}", node))), } } -pub fn eval_statements(statements: &Vec) -> Option { - let mut result = None; +pub fn eval_statements(statements: &Vec) -> Result { + let mut result: Object = NULL.clone(); for statement in statements { let val = eval_statement(statement); - match val { - Some(Object::Return(value)) => return Some(*value), - Some(_) => result = val, - None => return None, + if let Err(err) = val { + return Err(err); + } + + result = val.unwrap(); + + if let Object::Return(value) = result { + return Ok(*value); } } - result + Ok(result) +} + +fn is_truthy(object: &Object) -> bool { + match object { + Object::Null => false, + Object::Boolean(boolean) => *boolean, + _ => true, + } } -fn eval_expression(expression: &Expression) -> Option { +fn eval_expression(expression: &Expression) -> Result { match expression { - Expression::Identifier(identifier) => eval_identifier(identifier.to_string()), + Expression::Identifier(identifier_expression) => eval_identifier(identifier_expression.to_string()), Expression::Literal(literal) => eval_literal(&literal), - Expression::Infix(infix) => eval_infix_expression( - infix.operator.to_string(), - &infix.left, - &infix.right, + Expression::Infix(infix_expression) => eval_infix_expression( + infix_expression.operator.to_string(), + &infix_expression.left, + &infix_expression.right, ), - Expression::Prefix(prefix) => eval_prefix_expression( - prefix.operator.to_string(), - &prefix.right + Expression::Prefix(prefix_expression) => eval_prefix_expression( + prefix_expression.operator.to_string(), + &prefix_expression.right ), - _ => None, + Expression::If(if_expression) => eval_if_expression(expression), + _ => Err(Error::msg(format!("Unknown expression type: {}", expression))), + } +} + +fn eval_if_expression(expression: &Expression) -> Result { + if let Expression::If(IfExpression { + condition, + consequence, + alternative, + .. + }) = expression + { + let condition = eval_expression(condition)?; + + if is_truthy(&condition) { + eval_statements(&consequence.statements) + } else { + match alternative { + Some(alternative) => eval_statements(&alternative.statements), + None => Ok(NULL.clone()), + } + } + } else { + Err(Error::msg(format!("Unknown expression type: {}", expression))) } } -fn eval_infix_expression(operator: String, left: &Expression, right: &Expression) -> Option { +fn eval_infix_expression(operator: String, left: &Expression, right: &Expression) -> Result { let left = eval_expression(left)?; let right = eval_expression(right)?; @@ -68,46 +102,61 @@ fn eval_infix_expression(operator: String, left: &Expression, right: &Expression (Object::String(left), Object::String(right)) => { eval_string_infix_expression(operator, left, right) } - _ => None, + _ => Err(Error::msg(format!( + "Unknown operator: {}", + operator + ))), } } -fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Option { +fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Result { match operator.as_str() { - "==" => Some(native_bool_to_bool_object(left == right)), - "!=" => Some(native_bool_to_bool_object(left != right)), - _ => None, + "==" => Ok(native_bool_to_bool_object(left == right)), + "!=" => Ok(native_bool_to_bool_object(left != right)), + _ => Err(Error::msg(format!( + "Unknown operator: {} {} {}", + left, operator, right + ))), } } -fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Option { +fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Result { match operator.as_str() { - "+" => Some(Object::Integer(left + right)), - "-" => Some(Object::Integer(left - right)), - "*" => Some(Object::Integer(left * right)), - "/" => Some(Object::Integer(left / right)), - "<" => Some(native_bool_to_bool_object(left < right)), - ">" => Some(native_bool_to_bool_object(left > right)), - "==" => Some(native_bool_to_bool_object(left == right)), - "!=" => Some(native_bool_to_bool_object(left != right)), - _ => None, + "+" => Ok(Object::Integer(left + right)), + "-" => Ok(Object::Integer(left - right)), + "*" => Ok(Object::Integer(left * right)), + "/" => Ok(Object::Integer(left / right)), + "<" => Ok(native_bool_to_bool_object(left < right)), + ">" => Ok(native_bool_to_bool_object(left > right)), + "==" => Ok(native_bool_to_bool_object(left == right)), + "!=" => Ok(native_bool_to_bool_object(left != right)), + _ => Err(Error::msg(format!( + "Unknown operator: {} {} {}", + left, operator, right + ))), } } -fn eval_string_infix_expression(operator: String, left: String, right: String) -> Option { +fn eval_string_infix_expression(operator: String, left: String, right: String) -> Result { match operator.as_str() { - "+" => Some(Object::String(format!("{}{}", left, right))), - _ => None, + "+" => Ok(Object::String(format!("{}{}", left, right))), + _ => Err(Error::msg(format!( + "Unknown operator: {} {} {}", + left, operator, right + ))), } } -fn eval_prefix_expression(operator: String, right: &Expression) -> Option { +fn eval_prefix_expression(operator: String, right: &Expression) -> Result { let right = eval_expression(right)?; match operator.as_str() { - "-" => Some(eval_minus_prefix_operator_expression(right)), - "!" => Some(eval_bang_operator_expression(right)), - _ => None, + "-" => Ok(eval_minus_prefix_operator_expression(right)), + "!" => Ok(eval_bang_operator_expression(right)), + _ => Err(Error::msg(format!( + "Unknown operator: {}{}", + operator, right + ))), } } @@ -127,23 +176,23 @@ fn eval_minus_prefix_operator_expression(right: Object) -> Object { } } -fn eval_statement(statement: &Statement) -> Option { +fn eval_statement(statement: &Statement) -> Result { match statement { Statement::Expr(expression) => eval_expression(expression), - _ => None, + _ => Err(Error::msg(format!("Unknown statement type: {}", statement))), } } -fn eval_identifier(_identifier: String) -> Option { +fn eval_identifier(_identifier: String) -> Result { unimplemented!("eval_identifier"); } -fn eval_literal(literal: &Literal) -> Option { +fn eval_literal(literal: &Literal) -> Result { match literal { - Literal::Integer(integer) => Some(Object::Integer(integer.value)), - Literal::Boolean(Boolean { value, .. }) => Some(Object::Boolean(*value)), - Literal::String(string) => Some(Object::String(string.value.clone())), - _ => None, + Literal::Integer(integer) => Ok(Object::Integer(integer.value)), + Literal::Boolean(Boolean { value, .. }) => Ok(Object::Boolean(*value)), + Literal::String(string) => Ok(Object::String(string.value.clone())), + _ => Err(Error::msg(format!("Unknown literal type: {}", literal))), } } @@ -247,6 +296,31 @@ mod tests { Ok(()) } + #[test] + fn test_eval_if_else_expressions() -> Result<(), Error> { + let tests = vec![ + ("if (true) { 10 }", Some(10)), + ("if (false) { 10 }", None), + ("if (1) { 10 }", Some(10)), + ("if (1 < 2) { 10 }", Some(10)), + ("if (1 > 2) { 10 }", None), + ("if (1 > 2) { 10 } else { 20 }", Some(20)), + ("if (1 < 2) { 10 } else { 20 }", Some(10)), + ]; + + for (input, expected) in tests { + let evaluated = assert_eval(input)?; + + if let Some(expected) = expected { + assert_integer_object(evaluated, expected)?; + } else { + assert_eq!(evaluated, *NULL); + } + } + + Ok(()) + } + fn assert_eval(input: &str) -> Result { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); @@ -255,7 +329,7 @@ mod tests { parser.check_errors()?; let evaluated = eval_statements(&program.statements) - .ok_or_else(|| anyhow::anyhow!("eval returned None"))?; + .or_else(|_| Err(anyhow::anyhow!("eval returned None")))?; Ok(evaluated) } diff --git a/src/object.rs b/src/object.rs index 7553310..d839397 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,6 +1,6 @@ use crate::ast::BlockStatement; -#[derive(Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Object { Integer(i64), Boolean(bool), diff --git a/src/repl.rs b/src/repl.rs index f4eaec6..c5e9b3f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -35,7 +35,7 @@ pub fn init_repl() -> Result<(), Error> { let evaluation = evaluator::eval_statements(&program.statements); - if let Some(evaluated) = evaluation { + if let Ok(evaluated) = evaluation { println!("{}", evaluated); } } From f71ea22dda299077853a0bbc472441c4fca8b23f Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Fri, 20 Oct 2023 22:01:29 +0100 Subject: [PATCH 6/7] feat: environment + variable assignment/reference --- src/evaluator.rs | 259 +++++++++++++++++++------------ src/object/environment.rs | 28 ++++ src/{object.rs => object/mod.rs} | 6 +- src/parser.rs | 8 + src/repl.rs | 8 +- 5 files changed, 204 insertions(+), 105 deletions(-) create mode 100644 src/object/environment.rs rename src/{object.rs => object/mod.rs} (94%) diff --git a/src/evaluator.rs b/src/evaluator.rs index 456a2c7..2caecda 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,8 +1,10 @@ +use std::sync::Arc; + use anyhow::{Result, Ok, Error}; use lazy_static::lazy_static; -use crate::{ast::{Boolean, Expression, Literal, Node, Statement, IfExpression}, object::Object}; +use crate::{ast::{Boolean, Expression, Literal, Node, Statement, IfExpression}, object::{Object, environment::Env}}; lazy_static! { static ref TRUE: Object = Object::Boolean(true); @@ -10,29 +12,24 @@ lazy_static! { static ref NULL: Object = Object::Null; } -pub fn eval(node: Node) -> Result { +pub fn eval(node: Node, env: &Env) -> Result> { match node { - Node::Expression(expression) => eval_expression(&expression), - Node::Program(program) => eval_statements(&program.statements), - Node::Statement(statement) => eval_statement(&statement), + Node::Expression(expression) => eval_expression(&expression, env), + Node::Program(program) => eval_statements(&program.statements, env), + Node::Statement(statement) => eval_statement(&statement, env), _ => Err(Error::msg(format!("Unknown node type: {}", node))), } } -pub fn eval_statements(statements: &Vec) -> Result { - let mut result: Object = NULL.clone(); +pub fn eval_statements(statements: &Vec, env: &Env) -> Result> { + let mut result: Arc = Arc::new(Object::Null); for statement in statements { - let val = eval_statement(statement); - - if let Err(err) = val { - return Err(err); - } + let val = eval_statement(statement, env)?; - result = val.unwrap(); - - if let Object::Return(value) = result { - return Ok(*value); + match *val { + Object::Return(_) => return Ok(val), + _ => result = val, } } @@ -47,25 +44,36 @@ fn is_truthy(object: &Object) -> bool { } } -fn eval_expression(expression: &Expression) -> Result { +fn eval_expression(expression: &Expression, env: &Env) -> Result> { match expression { - Expression::Identifier(identifier_expression) => eval_identifier(identifier_expression.to_string()), + Expression::Identifier(identifier_expression) => eval_identifier(identifier_expression.to_string(), env), Expression::Literal(literal) => eval_literal(&literal), - Expression::Infix(infix_expression) => eval_infix_expression( - infix_expression.operator.to_string(), - &infix_expression.left, - &infix_expression.right, - ), - Expression::Prefix(prefix_expression) => eval_prefix_expression( - prefix_expression.operator.to_string(), - &prefix_expression.right - ), - Expression::If(if_expression) => eval_if_expression(expression), + Expression::Infix(infix_expression) => { + let left = eval_expression(&infix_expression.left, &Arc::clone(env))?; + let right = eval_expression(&infix_expression.right, &Arc::clone(env))?; + + eval_infix_expression( + infix_expression.operator.to_string(), + &left, + &right, + env + ) + }, + Expression::Prefix(prefix_expression) => { + let right = eval_expression(&prefix_expression.right, &Arc::clone(env))?; + + eval_prefix_expression( + prefix_expression.operator.to_string(), + &right, + env + ) + }, + Expression::If(if_expression) => eval_if_expression(expression, env), _ => Err(Error::msg(format!("Unknown expression type: {}", expression))), } } -fn eval_if_expression(expression: &Expression) -> Result { +fn eval_if_expression(expression: &Expression, env: &Env) -> Result> { if let Expression::If(IfExpression { condition, consequence, @@ -73,14 +81,14 @@ fn eval_if_expression(expression: &Expression) -> Result { .. }) = expression { - let condition = eval_expression(condition)?; + let condition = eval_expression(condition, env)?; if is_truthy(&condition) { - eval_statements(&consequence.statements) + eval_statements(&consequence.statements, env) } else { match alternative { - Some(alternative) => eval_statements(&alternative.statements), - None => Ok(NULL.clone()), + Some(alternative) => eval_statements(&alternative.statements, env), + None => Ok(Object::Null.into()), } } } else { @@ -88,19 +96,16 @@ fn eval_if_expression(expression: &Expression) -> Result { } } -fn eval_infix_expression(operator: String, left: &Expression, right: &Expression) -> Result { - let left = eval_expression(left)?; - let right = eval_expression(right)?; - +fn eval_infix_expression(operator: String, left: &Object, right: &Object, env: &Env) -> Result> { match (left, right) { (Object::Integer(left), Object::Integer(right)) => { - eval_integer_infix_expression(operator, left, right) + eval_integer_infix_expression(operator, *left, *right) } (Object::Boolean(left), Object::Boolean(right)) => { - eval_boolean_infix_expression(operator, left, right) + eval_boolean_infix_expression(operator, *left, *right) } (Object::String(left), Object::String(right)) => { - eval_string_infix_expression(operator, left, right) + eval_string_infix_expression(operator, left.to_string(), right.to_string()) } _ => Err(Error::msg(format!( "Unknown operator: {}", @@ -109,91 +114,119 @@ fn eval_infix_expression(operator: String, left: &Expression, right: &Expression } } -fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Result { - match operator.as_str() { - "==" => Ok(native_bool_to_bool_object(left == right)), - "!=" => Ok(native_bool_to_bool_object(left != right)), - _ => Err(Error::msg(format!( +fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Result> { + let result = match operator.as_str() { + "==" => native_bool_to_bool_object(left == right), + "!=" => native_bool_to_bool_object(left != right), + _ => return Err(Error::msg(format!( "Unknown operator: {} {} {}", left, operator, right ))), - } + }; + + Ok(result.into()) } -fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Result { - match operator.as_str() { - "+" => Ok(Object::Integer(left + right)), - "-" => Ok(Object::Integer(left - right)), - "*" => Ok(Object::Integer(left * right)), - "/" => Ok(Object::Integer(left / right)), - "<" => Ok(native_bool_to_bool_object(left < right)), - ">" => Ok(native_bool_to_bool_object(left > right)), - "==" => Ok(native_bool_to_bool_object(left == right)), - "!=" => Ok(native_bool_to_bool_object(left != right)), - _ => Err(Error::msg(format!( +fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Result> { + let result = match operator.as_str() { + "+" => Object::Integer(left + right), + "-" => Object::Integer(left - right), + "*" => Object::Integer(left * right), + "/" => Object::Integer(left / right), + "<" => native_bool_to_bool_object(left < right), + ">" => native_bool_to_bool_object(left > right), + "==" => native_bool_to_bool_object(left == right), + "!=" => native_bool_to_bool_object(left != right), + _ => return Err(Error::msg(format!( "Unknown operator: {} {} {}", left, operator, right ))), - } + }; + + Ok(result.into()) } -fn eval_string_infix_expression(operator: String, left: String, right: String) -> Result { - match operator.as_str() { - "+" => Ok(Object::String(format!("{}{}", left, right))), - _ => Err(Error::msg(format!( +fn eval_string_infix_expression(operator: String, left: String, right: String) -> Result> { + let result = match operator.as_str() { + "+" => Object::String(format!("{}{}", left, right)), + _ => return Err(Error::msg(format!( "Unknown operator: {} {} {}", left, operator, right ))), - } -} + }; -fn eval_prefix_expression(operator: String, right: &Expression) -> Result { - let right = eval_expression(right)?; + Ok(result.into()) +} +fn eval_prefix_expression(operator: String, right: &Object, env: &Env) -> Result> { match operator.as_str() { - "-" => Ok(eval_minus_prefix_operator_expression(right)), - "!" => Ok(eval_bang_operator_expression(right)), - _ => Err(Error::msg(format!( + "-" => eval_minus_prefix_operator_expression(right), + "!" => eval_bang_operator_expression(right), + _ => return Err(Error::msg(format!( "Unknown operator: {}{}", operator, right ))), } } -fn eval_bang_operator_expression(right: Object) -> Object { - match right { - val if val == *TRUE => FALSE.clone(), - val if val == *FALSE => TRUE.clone(), - val if val == *NULL => TRUE.clone(), - _ => FALSE.clone(), - } +fn eval_bang_operator_expression(right: &Object) -> Result> { + let result = match *right { + Object::Null => Arc::new(Object::Boolean(true)), + Object::Boolean(boolean) => Arc::new(Object::Boolean(!boolean)), + _ => Arc::new(Object::Boolean(false)), + }; + + Ok(result.into()) } -fn eval_minus_prefix_operator_expression(right: Object) -> Object { - match right { - Object::Integer(integer) => Object::Integer(-integer), - _ => NULL.clone(), - } +fn eval_minus_prefix_operator_expression(right: &Object) -> Result> { + let result = match *right { + Object::Integer(integer) => Arc::from(Object::Integer(-integer)), + _ => return Err(Error::msg(format!( + "Invalid use of minus operator: -{}", + right + ))), + }; + + Ok(result.into()) } -fn eval_statement(statement: &Statement) -> Result { +fn eval_statement(statement: &Statement, env: &Env) -> Result> { match statement { - Statement::Expr(expression) => eval_expression(expression), + Statement::Expr(expression) => eval_expression(expression, env), + Statement::Assign(assignment) => { + let value = eval_expression(&assignment.value, env)?; + let object = Arc::clone(&value); + + env.borrow_mut().set(assignment.name.to_string(), object); + + Ok(value) + }, + Statement::Return(return_statement) => { + let value = eval_expression(&return_statement.return_value, env)?; + + Ok(Arc::new(Object::Return(value))) + }, _ => Err(Error::msg(format!("Unknown statement type: {}", statement))), } } -fn eval_identifier(_identifier: String) -> Result { - unimplemented!("eval_identifier"); +fn eval_identifier(identifier: String, env: &Env) -> Result> { + match env.borrow().get(&identifier) { + Some(value) => Ok(value), + None => Err(Error::msg(format!("Identifier not found: {}", identifier))), + } } -fn eval_literal(literal: &Literal) -> Result { - match literal { - Literal::Integer(integer) => Ok(Object::Integer(integer.value)), - Literal::Boolean(Boolean { value, .. }) => Ok(Object::Boolean(*value)), - Literal::String(string) => Ok(Object::String(string.value.clone())), - _ => Err(Error::msg(format!("Unknown literal type: {}", literal))), - } +fn eval_literal(literal: &Literal) -> Result> { + let result = match literal { + Literal::Integer(integer) => Object::Integer(integer.value), + Literal::Boolean(Boolean { value, .. }) => Object::Boolean(*value), + Literal::String(string) => Object::String(string.value.clone()), + _ => return Err(Error::msg(format!("Unknown literal type: {}", literal))), + }; + + Ok(result.into()) } fn native_bool_to_bool_object(input: bool) -> Object { @@ -206,9 +239,11 @@ fn native_bool_to_bool_object(input: bool) -> Object { #[cfg(test)] mod tests { + use std::cell::RefCell; + use anyhow::Error; - use crate::{lexer::Lexer, object::Object, parser::Parser}; + use crate::{lexer::Lexer, object::{Object, environment::Environment}, parser::Parser}; use super::*; @@ -314,28 +349,48 @@ mod tests { if let Some(expected) = expected { assert_integer_object(evaluated, expected)?; } else { - assert_eq!(evaluated, *NULL); + assert_eq!(*evaluated, *NULL); } } Ok(()) } - fn assert_eval(input: &str) -> Result { + #[test] + fn test_eval_assertions() -> Result<(), Error> { + let tests = vec![ + ("$a = 5; $a;", 5), + ("$a = 5 * 5; $a;", 25), + ("$a = 5; $a;", 5), + ]; + + for (input, expected) in tests { + let evaluated = assert_eval(input)?; + + dbg!(&evaluated); + + assert_integer_object(evaluated, expected)?; + } + + Ok(()) + } + + fn assert_eval(input: &str) -> Result, Error> { + let env = Arc::new(RefCell::new(Environment::new())); + let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); let program = parser.parse_program(); parser.check_errors()?; - let evaluated = eval_statements(&program.statements) - .or_else(|_| Err(anyhow::anyhow!("eval returned None")))?; + let evaluated = eval(Node::Program(program), &env); - Ok(evaluated) + evaluated } - fn assert_boolean_literal_object(object: Object, expected: bool) -> Result<(), Error> { - if let Object::Boolean(boolean) = object { + fn assert_boolean_literal_object(object: Arc, expected: bool) -> Result<(), Error> { + if let Object::Boolean(boolean) = *object { assert_eq!(boolean, expected); } else { return Err(anyhow::anyhow!("Object is not a Boolean.")); @@ -344,8 +399,8 @@ mod tests { Ok(()) } - fn assert_integer_object(object: Object, expected: i64) -> Result<(), Error> { - if let Object::Integer(integer) = object { + fn assert_integer_object(object: Arc, expected: i64) -> Result<(), Error> { + if let Object::Integer(integer) = *object { assert_eq!(integer, expected); } else { return Err(anyhow::anyhow!("Object is not an Integer.")); diff --git a/src/object/environment.rs b/src/object/environment.rs new file mode 100644 index 0000000..c12b0c2 --- /dev/null +++ b/src/object/environment.rs @@ -0,0 +1,28 @@ +use std::{collections::HashMap, sync::Arc, cell::RefCell}; + +use super::Object; + +pub type Env = Arc>; + +pub struct Environment { + store: HashMap>, +} + +impl Environment { + pub fn new() -> Self { + Self { + store: HashMap::new(), + } + } + + pub fn get(&self, name: &str) -> Option> { + match self.store.get(name) { + Some(value) => Some(Arc::clone(value)), + None => None, + } + } + + pub fn set(&mut self, name: String, value: Arc) -> Option> { + self.store.insert(name, value) + } +} \ No newline at end of file diff --git a/src/object.rs b/src/object/mod.rs similarity index 94% rename from src/object.rs rename to src/object/mod.rs index d839397..7243b05 100644 --- a/src/object.rs +++ b/src/object/mod.rs @@ -1,12 +1,16 @@ +use std::sync::Arc; + use crate::ast::BlockStatement; +pub mod environment; + #[derive(Clone, Debug, PartialEq)] pub enum Object { Integer(i64), Boolean(bool), String(String), Function(Vec, BlockStatement), - Return(Box), + Return(Arc), Null, } diff --git a/src/parser.rs b/src/parser.rs index 42d54f7..bdd2ebd 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -331,6 +331,14 @@ impl<'a> Parser<'a> { None } } + Some(token) if token.token_type == TokenType::Semicolon => { + let identifier = Identifier { + token: name_token.clone(), + value: name_token.literal.clone(), + }; + + Some(Statement::Expr(Expression::Identifier(identifier))) + } Some(token) => { self.errors .push(format!("Expected = or ;, got {:?}", token)); diff --git a/src/repl.rs b/src/repl.rs index c5e9b3f..fec6f53 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,8 +1,10 @@ +use std::{sync::Arc, cell::RefCell}; + use anyhow::{Error, Result}; use rustyline::error::ReadlineError; -use crate::{evaluator, lexer::Lexer, parser::Parser}; +use crate::{evaluator, lexer::Lexer, parser::Parser, object::environment::Environment}; const PROMPT: &str = ">> "; @@ -20,6 +22,8 @@ pub fn init_repl() -> Result<(), Error> { env!("CARGO_PKG_VERSION") ); + let env = Arc::new(RefCell::new(Environment::new())); + loop { let readline = rl.readline(format!("{}", PROMPT).as_str()); @@ -33,7 +37,7 @@ pub fn init_repl() -> Result<(), Error> { let program = parser.parse_program(); parser.check_errors()?; - let evaluation = evaluator::eval_statements(&program.statements); + let evaluation = evaluator::eval_statements(&program.statements, &env); if let Ok(evaluated) = evaluation { println!("{}", evaluated); From 57f178f55bcddc5de3d15bdd3926c89350e66dd4 Mon Sep 17 00:00:00 2001 From: Zak Farmer Date: Sun, 22 Oct 2023 14:23:03 +0100 Subject: [PATCH 7/7] refactor: simplified $ identifier parsing --- src/evaluator.rs | 138 +++++++---- src/lexer.rs | 68 +++--- src/main.rs | 2 +- src/object/environment.rs | 28 ++- src/object/mod.rs | 14 +- src/parser.rs | 473 +++++++++++++++++--------------------- src/repl.rs | 12 +- 7 files changed, 376 insertions(+), 359 deletions(-) diff --git a/src/evaluator.rs b/src/evaluator.rs index 2caecda..5ab94ce 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,18 +1,12 @@ -use std::sync::Arc; +use std::{rc::Rc, cell::RefCell}; use anyhow::{Result, Ok, Error}; use lazy_static::lazy_static; -use crate::{ast::{Boolean, Expression, Literal, Node, Statement, IfExpression}, object::{Object, environment::Env}}; +use crate::{ast::{Boolean, Expression, Literal, Node, Statement, IfExpression, FunctionLiteral, CallExpression}, object::{Object, environment::{Env, Environment}}}; -lazy_static! { - static ref TRUE: Object = Object::Boolean(true); - static ref FALSE: Object = Object::Boolean(false); - static ref NULL: Object = Object::Null; -} - -pub fn eval(node: Node, env: &Env) -> Result> { +pub fn eval(node: Node, env: &Env) -> Result> { match node { Node::Expression(expression) => eval_expression(&expression, env), Node::Program(program) => eval_statements(&program.statements, env), @@ -21,8 +15,8 @@ pub fn eval(node: Node, env: &Env) -> Result> { } } -pub fn eval_statements(statements: &Vec, env: &Env) -> Result> { - let mut result: Arc = Arc::new(Object::Null); +pub fn eval_statements(statements: &Vec, env: &Env) -> Result> { + let mut result: Rc = Rc::new(Object::Null); for statement in statements { let val = eval_statement(statement, env)?; @@ -36,6 +30,22 @@ pub fn eval_statements(statements: &Vec, env: &Env) -> Result, args: &Vec>) -> Result> { + match &**function { + Object::Function(params, body, env) => { + let mut env = Environment::new_enclosed_environment(&env); + + params.iter().enumerate().for_each(|(i, param)| { + env.set(param.value.clone(), args[i].clone()); + }); + + let evaluated = eval_statements(&body.statements, &Rc::new(RefCell::new(env)))?; + return unwrap_return_value(evaluated); + } + f => Err(Error::msg(format!("expected {} to be a function", f))), + } +} + fn is_truthy(object: &Object) -> bool { match object { Object::Null => false, @@ -44,13 +54,20 @@ fn is_truthy(object: &Object) -> bool { } } -fn eval_expression(expression: &Expression, env: &Env) -> Result> { +fn unwrap_return_value(object: Rc) -> Result> { + match &* object { + Object::Return(value) => Ok(Rc::clone(value)), + _ => Ok(object), + } +} + +fn eval_expression(expression: &Expression, env: &Env) -> Result> { match expression { Expression::Identifier(identifier_expression) => eval_identifier(identifier_expression.to_string(), env), Expression::Literal(literal) => eval_literal(&literal), Expression::Infix(infix_expression) => { - let left = eval_expression(&infix_expression.left, &Arc::clone(env))?; - let right = eval_expression(&infix_expression.right, &Arc::clone(env))?; + let left = eval_expression(&infix_expression.left, &Rc::clone(env))?; + let right = eval_expression(&infix_expression.right, &Rc::clone(env))?; eval_infix_expression( infix_expression.operator.to_string(), @@ -60,7 +77,7 @@ fn eval_expression(expression: &Expression, env: &Env) -> Result> { ) }, Expression::Prefix(prefix_expression) => { - let right = eval_expression(&prefix_expression.right, &Arc::clone(env))?; + let right = eval_expression(&prefix_expression.right, &Rc::clone(env))?; eval_prefix_expression( prefix_expression.operator.to_string(), @@ -69,11 +86,34 @@ fn eval_expression(expression: &Expression, env: &Env) -> Result> { ) }, Expression::If(if_expression) => eval_if_expression(expression, env), + Expression::Call(CallExpression { function, token, arguments }) => { + let function = eval_expression(function, env)?; // This should give you a Function object. + let arguments = eval_expressions(arguments, env)?; + + apply_function(&function, &arguments) + }, + Expression::Function(FunctionLiteral { + parameters, + body, + .. + }) => { + Ok(Object::Function(parameters.clone(), body.clone(), Rc::clone(env)).into()) + }, _ => Err(Error::msg(format!("Unknown expression type: {}", expression))), } } -fn eval_if_expression(expression: &Expression, env: &Env) -> Result> { +fn eval_expressions(exprs: &Vec, env: &Env) -> Result>> { + let mut list = Vec::new(); + for expr in exprs { + let val = eval_expression(expr, &Rc::clone(env))?; + list.push(val); + } + + Ok(list) +} + +fn eval_if_expression(expression: &Expression, env: &Env) -> Result> { if let Expression::If(IfExpression { condition, consequence, @@ -96,7 +136,7 @@ fn eval_if_expression(expression: &Expression, env: &Env) -> Result> } } -fn eval_infix_expression(operator: String, left: &Object, right: &Object, env: &Env) -> Result> { +fn eval_infix_expression(operator: String, left: &Object, right: &Object, env: &Env) -> Result> { match (left, right) { (Object::Integer(left), Object::Integer(right)) => { eval_integer_infix_expression(operator, *left, *right) @@ -114,7 +154,7 @@ fn eval_infix_expression(operator: String, left: &Object, right: &Object, env: & } } -fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Result> { +fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> Result> { let result = match operator.as_str() { "==" => native_bool_to_bool_object(left == right), "!=" => native_bool_to_bool_object(left != right), @@ -127,7 +167,7 @@ fn eval_boolean_infix_expression(operator: String, left: bool, right: bool) -> R Ok(result.into()) } -fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Result> { +fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Result> { let result = match operator.as_str() { "+" => Object::Integer(left + right), "-" => Object::Integer(left - right), @@ -146,7 +186,7 @@ fn eval_integer_infix_expression(operator: String, left: i64, right: i64) -> Res Ok(result.into()) } -fn eval_string_infix_expression(operator: String, left: String, right: String) -> Result> { +fn eval_string_infix_expression(operator: String, left: String, right: String) -> Result> { let result = match operator.as_str() { "+" => Object::String(format!("{}{}", left, right)), _ => return Err(Error::msg(format!( @@ -158,7 +198,7 @@ fn eval_string_infix_expression(operator: String, left: String, right: String) - Ok(result.into()) } -fn eval_prefix_expression(operator: String, right: &Object, env: &Env) -> Result> { +fn eval_prefix_expression(operator: String, right: &Object, env: &Env) -> Result> { match operator.as_str() { "-" => eval_minus_prefix_operator_expression(right), "!" => eval_bang_operator_expression(right), @@ -169,19 +209,19 @@ fn eval_prefix_expression(operator: String, right: &Object, env: &Env) -> Result } } -fn eval_bang_operator_expression(right: &Object) -> Result> { +fn eval_bang_operator_expression(right: &Object) -> Result> { let result = match *right { - Object::Null => Arc::new(Object::Boolean(true)), - Object::Boolean(boolean) => Arc::new(Object::Boolean(!boolean)), - _ => Arc::new(Object::Boolean(false)), + Object::Null => Rc::new(Object::Boolean(true)), + Object::Boolean(boolean) => Rc::new(Object::Boolean(!boolean)), + _ => Rc::new(Object::Boolean(false)), }; Ok(result.into()) } -fn eval_minus_prefix_operator_expression(right: &Object) -> Result> { +fn eval_minus_prefix_operator_expression(right: &Object) -> Result> { let result = match *right { - Object::Integer(integer) => Arc::from(Object::Integer(-integer)), + Object::Integer(integer) => Rc::from(Object::Integer(-integer)), _ => return Err(Error::msg(format!( "Invalid use of minus operator: -{}", right @@ -191,12 +231,12 @@ fn eval_minus_prefix_operator_expression(right: &Object) -> Result> Ok(result.into()) } -fn eval_statement(statement: &Statement, env: &Env) -> Result> { +fn eval_statement(statement: &Statement, env: &Env) -> Result> { match statement { Statement::Expr(expression) => eval_expression(expression, env), Statement::Assign(assignment) => { let value = eval_expression(&assignment.value, env)?; - let object = Arc::clone(&value); + let object = Rc::clone(&value); env.borrow_mut().set(assignment.name.to_string(), object); @@ -205,20 +245,20 @@ fn eval_statement(statement: &Statement, env: &Env) -> Result> { Statement::Return(return_statement) => { let value = eval_expression(&return_statement.return_value, env)?; - Ok(Arc::new(Object::Return(value))) + Ok(Rc::new(Object::Return(value))) }, _ => Err(Error::msg(format!("Unknown statement type: {}", statement))), } } -fn eval_identifier(identifier: String, env: &Env) -> Result> { +fn eval_identifier(identifier: String, env: &Env) -> Result> { match env.borrow().get(&identifier) { Some(value) => Ok(value), None => Err(Error::msg(format!("Identifier not found: {}", identifier))), } } -fn eval_literal(literal: &Literal) -> Result> { +fn eval_literal(literal: &Literal) -> Result> { let result = match literal { Literal::Integer(integer) => Object::Integer(integer.value), Literal::Boolean(Boolean { value, .. }) => Object::Boolean(*value), @@ -230,11 +270,7 @@ fn eval_literal(literal: &Literal) -> Result> { } fn native_bool_to_bool_object(input: bool) -> Object { - if input { - TRUE.clone() - } else { - FALSE.clone() - } + Object::Boolean(input) } #[cfg(test)] @@ -349,7 +385,7 @@ mod tests { if let Some(expected) = expected { assert_integer_object(evaluated, expected)?; } else { - assert_eq!(*evaluated, *NULL); + assert_eq!(*evaluated, Object::Null); } } @@ -375,13 +411,29 @@ mod tests { Ok(()) } - fn assert_eval(input: &str) -> Result, Error> { - let env = Arc::new(RefCell::new(Environment::new())); + #[test] + fn test_eval_functions() -> Result<(), Error> { + let tests = vec![ + ("function ($x) { $x + 2; }(2);", 4) + ]; + + for (input, expected) in tests { + let evaluated = assert_eval(input)?; + + dbg!(&evaluated); + assert_integer_object(evaluated, expected)?; + } + + Ok(()) + } + + fn assert_eval(input: &str) -> Result, Error> { + let env = Rc::new(RefCell::new(Environment::new())); let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; let evaluated = eval(Node::Program(program), &env); @@ -389,7 +441,7 @@ mod tests { evaluated } - fn assert_boolean_literal_object(object: Arc, expected: bool) -> Result<(), Error> { + fn assert_boolean_literal_object(object: Rc, expected: bool) -> Result<(), Error> { if let Object::Boolean(boolean) = *object { assert_eq!(boolean, expected); } else { @@ -399,7 +451,7 @@ mod tests { Ok(()) } - fn assert_integer_object(object: Arc, expected: i64) -> Result<(), Error> { + fn assert_integer_object(object: Rc, expected: i64) -> Result<(), Error> { if let Object::Integer(integer) = *object { assert_eq!(integer, expected); } else { diff --git a/src/lexer.rs b/src/lexer.rs index ca89532..1abf93e 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -53,7 +53,25 @@ impl<'a> Lexer<'a> { Some('*') => (TokenType::Asterisk, "*".to_string()), Some('<') => (TokenType::Lt, "<".to_string()), Some('>') => (TokenType::Gt, ">".to_string()), - Some('$') => (TokenType::Dollar, "$".to_string()), + Some('$') => { + self.read_char(); + + if let Some(ch) = self.ch { + if ch.is_alphabetic() || ch == '_' { + let identifier = self.read_identifier(); + let var_name = format!("${}", identifier); + + return Token { + token_type: TokenType::Ident, + literal: var_name, + }; + } else { + (TokenType::Illegal, "$".to_string()) + } + } else { + (TokenType::Illegal, "$".to_string()) + } + } Some(ch) => { if ch.is_alphabetic() { let literal = self.read_identifier(); @@ -152,24 +170,20 @@ mod tests { let input = " $five = 5; - $ten = five + 5; + $ten = $five + 5; - $add = function (x, y) { - return x + y; + $add = function ($x, $y) { + return $x + $y; } - $result = add(five, ten); + $result = $add($five, $ten); " .to_string(); let expected_tokens = [ - Token { - token_type: TokenType::Dollar, - literal: "$".to_string(), - }, Token { token_type: TokenType::Ident, - literal: "five".to_string(), + literal: "$five".to_string(), }, Token { token_type: TokenType::Assign, @@ -183,13 +197,9 @@ mod tests { token_type: TokenType::Semicolon, literal: ";".to_string(), }, - Token { - token_type: TokenType::Dollar, - literal: "$".to_string(), - }, Token { token_type: TokenType::Ident, - literal: "ten".to_string(), + literal: "$ten".to_string(), }, Token { token_type: TokenType::Assign, @@ -197,7 +207,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "five".to_string(), + literal: "$five".to_string(), }, Token { token_type: TokenType::Plus, @@ -211,13 +221,9 @@ mod tests { token_type: TokenType::Semicolon, literal: ";".to_string(), }, - Token { - token_type: TokenType::Dollar, - literal: "$".to_string(), - }, Token { token_type: TokenType::Ident, - literal: "add".to_string(), + literal: "$add".to_string(), }, Token { token_type: TokenType::Assign, @@ -233,7 +239,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "x".to_string(), + literal: "$x".to_string(), }, Token { token_type: TokenType::Comma, @@ -241,7 +247,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "y".to_string(), + literal: "$y".to_string(), }, Token { token_type: TokenType::RParen, @@ -257,7 +263,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "x".to_string(), + literal: "$x".to_string(), }, Token { token_type: TokenType::Plus, @@ -265,7 +271,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "y".to_string(), + literal: "$y".to_string(), }, Token { token_type: TokenType::Semicolon, @@ -275,13 +281,9 @@ mod tests { token_type: TokenType::RBrace, literal: "}".to_string(), }, - Token { - token_type: TokenType::Dollar, - literal: "$".to_string(), - }, Token { token_type: TokenType::Ident, - literal: "result".to_string(), + literal: "$result".to_string(), }, Token { token_type: TokenType::Assign, @@ -289,7 +291,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "add".to_string(), + literal: "$add".to_string(), }, Token { token_type: TokenType::LParen, @@ -297,7 +299,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "five".to_string(), + literal: "$five".to_string(), }, Token { token_type: TokenType::Comma, @@ -305,7 +307,7 @@ mod tests { }, Token { token_type: TokenType::Ident, - literal: "ten".to_string(), + literal: "$ten".to_string(), }, Token { token_type: TokenType::RParen, diff --git a/src/main.rs b/src/main.rs index d0ce23f..bcc89de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ fn main() -> Result<(), Error> { let lexer = lexer::Lexer::new(&file); let mut parser = parser::Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; diff --git a/src/object/environment.rs b/src/object/environment.rs index c12b0c2..36f2825 100644 --- a/src/object/environment.rs +++ b/src/object/environment.rs @@ -1,28 +1,44 @@ -use std::{collections::HashMap, sync::Arc, cell::RefCell}; +use std::{collections::HashMap, rc::Rc, cell::RefCell}; use super::Object; -pub type Env = Arc>; +pub type Env = Rc>; +#[derive(Debug)] pub struct Environment { - store: HashMap>, + store: HashMap>, + outer: Option, +} + +impl PartialEq for Environment { + fn eq(&self, other: &Self) -> bool { + self.store == other.store + } } impl Environment { pub fn new() -> Self { Self { store: HashMap::new(), + outer: None, } } - pub fn get(&self, name: &str) -> Option> { + pub fn new_enclosed_environment(outer: &Env) -> Self { + let mut env = Self::new(); + env.outer = Some(Rc::clone(outer)); + + env + } + + pub fn get(&self, name: &str) -> Option> { match self.store.get(name) { - Some(value) => Some(Arc::clone(value)), + Some(value) => Some(Rc::clone(value)), None => None, } } - pub fn set(&mut self, name: String, value: Arc) -> Option> { + pub fn set(&mut self, name: String, value: Rc) -> Option> { self.store.insert(name, value) } } \ No newline at end of file diff --git a/src/object/mod.rs b/src/object/mod.rs index 7243b05..dc76cb9 100644 --- a/src/object/mod.rs +++ b/src/object/mod.rs @@ -1,6 +1,8 @@ -use std::sync::Arc; +use std::rc::Rc; -use crate::ast::BlockStatement; +use crate::ast::{BlockStatement, Identifier}; + +use self::environment::Env; pub mod environment; @@ -9,8 +11,8 @@ pub enum Object { Integer(i64), Boolean(bool), String(String), - Function(Vec, BlockStatement), - Return(Arc), + Function(Vec, BlockStatement, Env), + Return(Rc), Null, } @@ -20,11 +22,11 @@ impl std::fmt::Display for Object { Object::Integer(integer) => write!(f, "{}", integer), Object::Boolean(boolean) => write!(f, "{}", boolean), Object::String(string) => write!(f, "{}", string), - Object::Function(parameters, body) => { + Object::Function(parameters, body, _env) => { let mut parameters_string = String::new(); for (index, parameter) in parameters.iter().enumerate() { - parameters_string.push_str(parameter); + parameters_string.push_str(¶meter.to_string()); if index < parameters.len() - 1 { parameters_string.push_str(", "); diff --git a/src/parser.rs b/src/parser.rs index bdd2ebd..2cc1519 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use anyhow::Error; -use log::trace; +use anyhow::{Error, Result}; +use log::{trace, info}; use crate::{ ast::{ @@ -13,7 +13,7 @@ use crate::{ token::{Token, TokenType}, }; -type ParseResult = Option; +type ParseResult = Result; type PrefixParseFn = fn(&mut Parser) -> ParseResult; type InfixParseFn = fn(&mut Parser, Expression) -> ParseResult; @@ -169,6 +169,15 @@ impl<'a> Parser<'a> { } } + fn expect_current(&mut self, t: TokenType) -> bool { + if self.current_token.is_some() && self.current_token.as_ref().unwrap().token_type == t { + true + } else { + self.errors.push(format!("Expected {:?}, got {:?}", t, self.current_token)); + false + } + } + pub fn expect_peek(&mut self, token_type: TokenType) -> bool { if self.peek_token_is(&token_type) { self.next_token(); @@ -185,138 +194,118 @@ impl<'a> Parser<'a> { self.peek_token.as_ref().unwrap().token_type == token_type.to_owned() } - fn parse_boolean(&mut self) -> Option { + fn parse_boolean(&mut self) -> Result { let current_token = self.current_token.clone().unwrap(); let value = match self.current_token.as_ref().unwrap().token_type { TokenType::True => true, TokenType::False => false, _ => { - self.errors.push(format!( + return Err(Error::msg(format!( "Expected true or false, got {:?}", self.current_token.as_ref().unwrap().token_type - )); - return None; + ))); } }; - Some(Expression::Literal(Literal::Boolean(Boolean { + Ok(Expression::Literal(Literal::Boolean(Boolean { token: current_token, value, }))) } - pub fn parse_program(&mut self) -> Program { + pub fn parse_program(&mut self) -> Result { let mut program = Program::default(); - while self.current_token.as_ref().unwrap().token_type != TokenType::Eof { - let statement = self.parse_statement(); - - if let Some(statement) = statement { - program.statements.push(statement); + while !self.current_token_is(TokenType::Eof) { + match self.parse_statement() { + Ok(stmt) => program.statements.push(stmt), + Err(e) => self.errors.push(e.to_string()), } - self.next_token(); } - program + if self.errors.is_empty() { + Ok(program) + } else { + Err(Error::msg(format!( + "Parser has errors: {}", + self.errors.join(", ") + ))) + } } - pub fn parse_statement(&mut self) -> Option { + pub fn parse_statement(&mut self) -> Result { if let Some(token) = &self.current_token { - match token.token_type { + match &token.token_type { TokenType::Return => self.parse_return_statement(), - TokenType::Dollar => self.parse_assignment_statement(), + TokenType::Ident if token.literal.starts_with('$') => { + if self.peek_token_is(&TokenType::Assign) { + self.parse_assignment_statement() + } else { + self.parse_expression_statement() + } + }, _ => self.parse_expression_statement(), } } else { - None + Err(Error::msg("Unexpected end of input")) } } - fn parse_variable_reference_expression(&mut self) -> Option { - if let Some(token) = &self.current_token { - if token.token_type != TokenType::Dollar { - self.errors.push(format!("Expected $, got {:?}", token)); - return None; - } - } else { - self.errors.push("Expected $, got None".to_string()); - return None; - } + fn parse_variable_reference_expression(&mut self) -> Result { + info!("parse_variable_reference_expression: Current token: {:?}", self.current_token); - self.next_token(); - - // Ensure the variable name is an identifier. - let name_token = if let Some(token) = &self.current_token { + // Expect the next token to be an identifier + if let Some(token) = &self.current_token { if token.token_type == TokenType::Ident { - token.clone() + let identifier = Identifier { + token: token.clone(), + value: token.literal.clone(), + }; + + self.next_token(); + + return Ok(Expression::Identifier(identifier)); } else { - self.errors - .push(format!("Expected identifier, got {:?}", token)); - return None; + return Err(Error::msg(format!( + "Expected identifier after $, got {:?}", + token + ))); } } else { - self.errors - .push("Expected identifier, got None".to_string()); - return None; - }; - - // Parse as reference - let identifier = Identifier { - token: name_token.clone(), - value: name_token.literal.clone(), - }; - - Some(Expression::Identifier(identifier)) - } - - fn parse_assignment_statement(&mut self) -> Option { - trace!("Current token: {:?}", self.current_token); - // Ensure the current token is a Dollar sign. - if let Some(token) = &self.current_token { - if token.token_type != TokenType::Dollar { - self.errors.push(format!("Expected $, got {:?}", token)); - return None; - } - } else { - self.errors.push("Expected $, got None".to_string()); - return None; + return Err(Error::msg("Unexpected end of input")); } + } - // Move to the variable name. - self.next_token(); + fn parse_assignment_statement(&mut self) -> Result { + info!("parse_assignment_statement: Current token: {:?}", self.current_token); // Ensure the variable name is an identifier. let name_token = if let Some(token) = &self.current_token { if token.token_type == TokenType::Ident { token.clone() } else { - self.errors - .push(format!("Expected identifier, got {:?}", token)); - return None; + self.errors.push(format!("Expected identifier, got {:?}", token)); + return Err(Error::msg(format!("Expected identifier, got {:?}", token))); } } else { - self.errors - .push("Expected identifier, got None".to_string()); - return None; + self.errors.push("Expected identifier, got None".to_string()); + return Err(Error::msg("Expected identifier, got None")); }; - - // Move to the next token and check if it's an assignment or a reference. + + // Move to the next token and check if it's an assignment. self.next_token(); - - match &self.current_token { - Some(token) if token.token_type == TokenType::Assign => { + + if let Some(token) = &self.current_token { + if token.token_type == TokenType::Assign { // Parse as assignment self.next_token(); - let value_expression = self.parse_expression(Precedence::Lowest); - - if let Some(value_expression) = value_expression { - // Ensure the semicolon is present. - if !self.expect_peek(TokenType::Semicolon) { - return None; - } + info!("parse_assignment_statement: Next token: {:?}", self.current_token); + let value_expression = self.parse_expression(Precedence::Lowest); + + if let Ok(value_expression) = value_expression { let variable_assignment = Assignment { token: name_token.clone(), name: Identifier { @@ -326,32 +315,31 @@ impl<'a> Parser<'a> { value: value_expression, }; - Some(Statement::Assign(variable_assignment)) + if self.peek_token_is(&TokenType::Semicolon) { + self.next_token(); + } + + return Ok(Statement::Assign(variable_assignment)); } else { - None + self.errors.push("Expected an expression after =".to_string()); + return Err(Error::msg("Expected an expression after =")); } + } else { + // If it's not an assignment, then it's not an assignment statement. + return Err(Error::msg(format!( + "Expected assignment, got {:?}", + token.token_type + ))); } - Some(token) if token.token_type == TokenType::Semicolon => { - let identifier = Identifier { - token: name_token.clone(), - value: name_token.literal.clone(), - }; - - Some(Statement::Expr(Expression::Identifier(identifier))) - } - Some(token) => { - self.errors - .push(format!("Expected = or ;, got {:?}", token)); - None - } - None => { - self.errors.push("Expected = or ;, got None".to_string()); - None - } + } else { + self.errors.push("Unexpected end of input".to_string()); + return Err(Error::msg("Unexpected end of input")); } - } + } + + fn parse_expression(&mut self, precedence: Precedence) -> Result { + info!("parse_expression: Current token: {:?}", self.current_token); - fn parse_expression(&mut self, precedence: Precedence) -> Option { // Get prefix parse function (if it exists) let prefix_fn = self .prefix_parse_fns @@ -359,12 +347,10 @@ impl<'a> Parser<'a> { .map(|x| *x); if prefix_fn.is_none() { - self.errors.push(format!( + return Err(Error::msg(format!( "No prefix parse function for {:?}", self.current_token.as_ref().unwrap().token_type - )); - - return None; + ))); } // Call the prefix parse function @@ -388,20 +374,19 @@ impl<'a> Parser<'a> { left } - fn parse_expression_statement(&mut self) -> Option { - let _current_token = self.current_token.clone().unwrap(); - - let expression = self.parse_expression(Precedence::Lowest); - - // Move to the next token if it's a semicolon + fn parse_expression_statement(&mut self) -> Result { + info!("parse_expression_statement: Current token: {:?}", self.current_token); + let expr = self.parse_expression(Precedence::Lowest)?; if self.peek_token_is(&TokenType::Semicolon) { self.next_token(); } - Some(Statement::Expr(expression.unwrap())) + Ok(Statement::Expr(expr)) } - fn parse_block_statement(&mut self) -> Option { + fn parse_block_statement(&mut self) -> Result { + info!("parse_block_statement: Current token: {:?}", self.current_token); + let current_token = self.current_token.clone().unwrap(); let mut statements = vec![]; @@ -411,97 +396,103 @@ impl<'a> Parser<'a> { while !self.current_token_is(TokenType::RBrace) && !self.current_token_is(TokenType::Eof) { let statement = self.parse_statement(); - if let Some(statement) = statement { + if let Ok(statement) = statement { statements.push(statement); } self.next_token(); } - Some(BlockStatement { + Ok(BlockStatement { token: current_token, statements, }) } - fn parse_function_literal(&mut self) -> Option { + fn parse_function_literal(&mut self) -> Result { + info!("parse_function_literal: Current token: {:?}", self.current_token); + let current_token = self.current_token.clone().unwrap(); if !self.expect_peek(TokenType::LParen) { - return None; + return Err(Error::msg("Expected LParen")); } - let parameters = self.parse_function_parameters(); + let parameters = self.parse_function_parameters()?; if !self.expect_peek(TokenType::LBrace) { - return None; + return Err(Error::msg("Expected LBrace")); } let body = self.parse_block_statement(); - if let Some(body) = body { - Some(Expression::Function(FunctionLiteral { + if let Ok(body) = body { + Ok(Expression::Function(FunctionLiteral { token: current_token, parameters, body: body, })) } else { - None + Err(Error::msg("Expected a block statement")) } } - fn parse_function_parameters(&mut self) -> Vec { + fn parse_function_parameters(&mut self) -> Result> { + info!("parse_function_parameters: Current token: {:?}", self.current_token); let mut identifiers = vec![]; - + if self.peek_token_is(&TokenType::RParen) { self.next_token(); // Consume the RParen and exit - return identifiers; + return Ok(identifiers); } - // Expecting a Dollar sign and identifier (variable name) - if self.expect_peek(TokenType::Dollar) { - self.next_token(); // Consume Dollar - let identifier = Identifier { - token: self.current_token.clone().unwrap(), - value: self.current_token.as_ref().unwrap().to_string(), - }; - identifiers.push(identifier); - } - - // Expecting comma-separated identifiers - while self.peek_token_is(&TokenType::Comma) { - self.next_token(); // Consume Comma - if self.expect_peek(TokenType::Dollar) { - self.next_token(); // Consume Dollar - let identifier = Identifier { - token: self.current_token.clone().unwrap(), - value: self.current_token.as_ref().unwrap().to_string(), - }; - identifiers.push(identifier); + self.next_token(); + + loop { + if let Some(token) = &self.current_token { + if token.token_type == TokenType::Ident && token.literal.starts_with('$') { + let identifier = Identifier { + token: token.clone(), + value: token.literal.clone(), + }; + identifiers.push(identifier); + } else { + return Err(Error::msg(format!("Expected identifier starting with '$', got {:?}", token))); + } } - } + + if self.peek_token_is(&TokenType::Comma) { + self.next_token(); + self.next_token(); + } else if self.peek_token_is(&TokenType::RParen) { + self.next_token(); - // Expecting RParen to close the parameters list - if !self.expect_peek(TokenType::RParen) { - return vec![]; + break; + } else { + return Err(Error::msg("Expected ',' or ')' after identifier".to_string())); + } } - - identifiers + + Ok(identifiers) } + + + fn parse_grouped_expression(&mut self) -> Result { + info!("parse_grouped_expression: Current token: {:?}", self.current_token); - fn parse_grouped_expression(&mut self) -> Option { self.next_token(); let expression = self.parse_expression(Precedence::Lowest); if !self.expect_peek(TokenType::RParen) { - return None; + return Err(Error::msg("Expected RParen")); } expression } - fn parse_identifier(&mut self) -> Option { + fn parse_identifier(&mut self) -> Result { + info!("parse_identifier: Current token: {:?}", self.current_token); let current_token = self.current_token.clone().unwrap(); let identifier = Identifier { @@ -509,10 +500,12 @@ impl<'a> Parser<'a> { value: self.current_token.as_ref().unwrap().to_string(), }; - Some(Expression::Identifier(identifier)) + Ok(Expression::Identifier(identifier)) } - fn parse_integer_literal(&mut self) -> Option { + fn parse_integer_literal(&mut self) -> Result { + info!("parse_integer_literal: Current token: {:?}", self.current_token); + let current_token = self.current_token.clone().unwrap(); let value = self @@ -523,13 +516,15 @@ impl<'a> Parser<'a> { .parse::() .unwrap(); - Some(Expression::Literal(Literal::Integer(Integer { + Ok(Expression::Literal(Literal::Integer(Integer { token: current_token, value, }))) } fn parse_call_arguments(&mut self) -> Vec { + info!("parse_call_arguments: Current token: {:?}", self.current_token); + let mut arguments = vec![]; if self.peek_token_is(&TokenType::RParen) { @@ -554,64 +549,50 @@ impl<'a> Parser<'a> { arguments } - fn parse_call_expression(&mut self, function: Expression) -> Option { + fn parse_call_expression(&mut self, function: Expression) -> Result { + info!("parse_call_expression: Current token: {:?}", self.current_token); let current_token = self.current_token.clone().unwrap(); let arguments = self.parse_call_arguments(); - Some(Expression::Call(CallExpression { + Ok(Expression::Call(CallExpression { token: current_token, function: Box::new(function), arguments, })) } - fn parse_if_expression(&mut self) -> Option { - let current_token = self.current_token.clone().unwrap(); - - if !self.expect_peek(TokenType::LParen) { - return None; - } + fn parse_if_expression(&mut self) -> Result { + info!("parse_if_expression: Current token: {:?}", self.current_token); + self.expect_peek(TokenType::LParen); self.next_token(); - let condition = self.parse_expression(Precedence::Lowest); + let condition = self.parse_expression(Precedence::Lowest)?; + self.expect_peek(TokenType::RParen); + self.expect_peek(TokenType::LBrace); - if let None = condition { - return None; - } - - if !self.expect_peek(TokenType::RParen) { - return None; - } - - if !self.expect_peek(TokenType::LBrace) { - return None; - } - - let consequence = self.parse_block_statement(); + let consequence = self.parse_block_statement()?; let alternative = if self.peek_token_is(&TokenType::Else) { self.next_token(); - - if !self.expect_peek(TokenType::LBrace) { - return None; - } - - self.parse_block_statement() + self.expect_peek(TokenType::LBrace); + Some(self.parse_block_statement()?) } else { None }; - Some(Expression::If(IfExpression { - token: current_token, - condition: Box::new(condition.unwrap()), - consequence: consequence.unwrap(), - alternative: alternative, - })) + return Ok(Expression::If(IfExpression { + condition: Box::new(condition), + token: self.current_token.clone().unwrap(), + consequence, + alternative, + })); } - fn parse_infix_expression(&mut self, left: Expression) -> Option { + fn parse_infix_expression(&mut self, left: Expression) -> Result { + info!("parse_infix_expression: Current token: {:?}", self.current_token); + let current_token = self.current_token.clone().unwrap(); let operator = self.current_token.as_ref().unwrap().to_string(); @@ -621,21 +602,23 @@ impl<'a> Parser<'a> { self.next_token(); match self.parse_expression(precedence) { - Some(right) => Some(Expression::Infix(InfixExpression { + Ok(right) => Ok(Expression::Infix(InfixExpression { token: current_token, operator, left: Box::new(left), right: Box::new(right), })), - None => { + Err(e) => { self.errors .push("Expected an expression, but found none.".to_string()); - return None; + return Err(Error::msg(e.to_string())); } } } - fn parse_prefix_expression(&mut self) -> Option { + fn parse_prefix_expression(&mut self) -> Result { + info!("parse_prefix_expression: Current token: {:?}", self.current_token); + let current_token = self.current_token.clone().unwrap(); let operator = self.current_token.as_ref().unwrap().to_string(); @@ -644,20 +627,21 @@ impl<'a> Parser<'a> { let right = self.parse_expression(Precedence::Prefix).unwrap(); - Some(Expression::Prefix(PrefixExpression { + Ok(Expression::Prefix(PrefixExpression { token: current_token, operator, right: Box::new(right), })) } - fn parse_return_statement(&mut self) -> Option { + fn parse_return_statement(&mut self) -> Result { let current_token = self.current_token.clone().unwrap(); if !matches!(current_token.token_type, TokenType::Return) { - self.errors - .push(format!("Expected 'return', got {:?}", current_token)); - return None; + return Err(Error::msg(format!( + "Expected 'return', got {:?}", + current_token + ))); } self.next_token(); @@ -665,10 +649,10 @@ impl<'a> Parser<'a> { let return_value = self.parse_expression(Precedence::Lowest); if !self.expect_peek(TokenType::Semicolon) { - return None; + return Err(Error::msg("Expected Semicolon")); } - Some(Statement::Return(ReturnStatement { + Ok(Statement::Return(ReturnStatement { token: current_token, return_value: return_value.unwrap(), })) @@ -704,12 +688,12 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(3, program.statements.len()); - let expected_identifiers = ["x", "y", "foobar"]; + let expected_identifiers = ["$x", "$y", "$foobar"]; for i in 0..expected_identifiers.len() { let statement = &program.statements[i]; @@ -735,7 +719,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(4, program.statements.len()); @@ -776,7 +760,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(1, program.statements.len()); @@ -811,7 +795,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; @@ -819,14 +803,14 @@ mod tests { if let Statement::Expr(expression) = &program.statements[0] { if let Expression::If(if_expression) = &expression { - assert_infix_expression(&if_expression.condition, "x", "<", "y")?; + assert_infix_expression(&if_expression.condition, "$x", "<", "$y")?; assert_eq!(1, if_expression.consequence.statements.len()); if let Statement::Return(return_statement) = &if_expression.consequence.statements[0] { - assert_identifier(&return_statement.return_value, "x")?; + assert_identifier(&return_statement.return_value, "$x")?; } else { assert!(false, "Expected ReturnStatement"); } @@ -849,7 +833,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; @@ -857,14 +841,14 @@ mod tests { if let Statement::Expr(expression) = &program.statements[0] { if let Expression::If(if_expression) = &expression { - assert_infix_expression(&if_expression.condition, "x", "<", "y")?; + assert_infix_expression(&if_expression.condition, "$x", "<", "$y")?; assert_eq!(1, if_expression.consequence.statements.len()); if let Statement::Return(return_statement) = &if_expression.consequence.statements[0] { - assert_identifier(&return_statement.return_value, "x")?; + assert_identifier(&return_statement.return_value, "$x")?; } else { assert!(false, "Expected ReturnStatement"); } @@ -877,7 +861,7 @@ mod tests { if let Statement::Return(return_statement) = &if_expression.alternative.as_ref().unwrap().statements[0] { - assert_identifier(&return_statement.return_value, "y")?; + assert_identifier(&return_statement.return_value, "$y")?; } else { assert!(false, "Expected ReturnStatement"); } @@ -902,7 +886,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(3, program.statements.len()); @@ -921,7 +905,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(1, program.statements.len()); @@ -942,7 +926,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(1, program.statements.len()); @@ -998,7 +982,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(*expected, program.to_string()); @@ -1007,43 +991,6 @@ mod tests { Ok(()) } - // #[test] - // fn test_parsing_infix_expressions() -> Result<(), Error> { - // let infix_tests: [(&str, i64, &str, i64); 8] = [ - // ("5 + 5;", 5, "+", 5), - // ("5 - 5;", 5, "-", 5), - // ("5 * 5;", 5, "*", 5), - // ("5 / 5;", 5, "/", 5), - // ("5 > 5;", 5, ">", 5), - // ("5 < 5;", 5, "<", 5), - // ("5 == 5;", 5, "==", 5), - // ("5 != 5;", 5, "!=", 5), - // ]; - - // for (input, left_value, operator, right_value) in infix_tests.iter() { - // let lexer = Lexer::new(input); - // let mut parser = Parser::new(lexer); - - // let program = parser.parse_program(); - // parser.check_errors()?; - - // assert_eq!(1, program.statements.len()); - - // if let Statement::Expr(expression) = &program.statements[0] { - // assert_infix_expression( - // &expression, - // *left_value, - // *operator, - // *right_value, - // )?; - // } else { - // assert!(false, "Expected ExpressionStatement"); - // } - // } - - // Ok(()) - // } - #[test] fn test_parsing_prefix_expressions() -> Result<(), Error> { let prefix_tests: [(&str, &str, i64); 2] = [("!5;", "!", 5), ("-15;", "-", 15)]; @@ -1052,7 +999,7 @@ mod tests { let lexer = Lexer::new(input); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; assert_eq!(1, program.statements.len()); @@ -1123,18 +1070,18 @@ mod tests { if let Expression::Function(function_literal) = expression { assert_eq!(2, function_literal.parameters.len()); - assert_eq!("x", function_literal.parameters[0].value); - assert_eq!("y", function_literal.parameters[1].value); + assert_eq!("$x", function_literal.parameters[0].value); + assert_eq!("$y", function_literal.parameters[1].value); if let BlockStatement { statements, .. } = &function_literal.body { assert_eq!(1, statements.len()); match &statements[0] { Statement::Return(return_statement) => { - assert_infix_expression(&return_statement.return_value, "x", "+", "y")?; + assert_infix_expression(&return_statement.return_value, "$x", "+", "$y")?; } Statement::Expr(expression) => { - assert_infix_expression(expression, "x", "+", "y")?; + assert_infix_expression(expression, "$x", "+", "$y")?; } _ => assert!(false, "Expected ReturnStatement or ExpressionStatement"), } diff --git a/src/repl.rs b/src/repl.rs index fec6f53..10f0366 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, cell::RefCell}; +use std::{cell::RefCell, rc::Rc}; use anyhow::{Error, Result}; @@ -22,7 +22,7 @@ pub fn init_repl() -> Result<(), Error> { env!("CARGO_PKG_VERSION") ); - let env = Arc::new(RefCell::new(Environment::new())); + let env = Rc::new(RefCell::new(Environment::new())); loop { let readline = rl.readline(format!("{}", PROMPT).as_str()); @@ -34,14 +34,12 @@ pub fn init_repl() -> Result<(), Error> { let lexer = Lexer::new(&line); let mut parser = Parser::new(lexer); - let program = parser.parse_program(); + let program = parser.parse_program()?; parser.check_errors()?; - let evaluation = evaluator::eval_statements(&program.statements, &env); + let evaluated = evaluator::eval_statements(&program.statements, &env)?; - if let Ok(evaluated) = evaluation { - println!("{}", evaluated); - } + println!("{}", evaluated); } Err(ReadlineError::Interrupted) => { println!("CTRL-C");