From 5869d8acc26b32a2bd8d41aa6469953acf95255e Mon Sep 17 00:00:00 2001 From: Yann Ahlgrim Date: Sat, 11 Jul 2026 20:25:22 +0200 Subject: [PATCH] evaluator --- src/ast/mod.rs | 57 +++++++++++++++++++++++++++++++++ src/evaluator/mod.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ src/object/mod.rs | 50 +++++++++++++++++++++++++++++ src/repl/mod.rs | 8 +++-- 5 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 src/evaluator/mod.rs create mode 100644 src/object/mod.rs diff --git a/src/ast/mod.rs b/src/ast/mod.rs index cc168eb..b52cb42 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3,6 +3,7 @@ use crate::token::Token; pub trait NodeTrait { fn token_literal(&self) -> String; fn string(&self) -> String; + fn as_any(&self) -> &dyn std::any::Any; } #[allow(dead_code)] @@ -47,6 +48,10 @@ impl NodeTrait for Expression { Expression::CallExpression(i) => i.string(), } } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct Program { @@ -69,6 +74,10 @@ impl NodeTrait for Program { } out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct LetStatement { @@ -102,6 +111,10 @@ impl NodeTrait for LetStatement { out.push_str(";"); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl StatementTrait for LetStatement { @@ -121,6 +134,10 @@ impl NodeTrait for ReturnStatement { out.push_str(";"); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl StatementTrait for ReturnStatement { @@ -135,6 +152,10 @@ impl NodeTrait for ExpressionStatement { fn string(&self) -> String { self.expression.string() } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } impl StatementTrait for ExpressionStatement { @@ -155,6 +176,10 @@ impl NodeTrait for Identifier { fn string(&self) -> String { self.value.clone() } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } #[derive(Debug)] @@ -171,6 +196,10 @@ impl NodeTrait for IntegerLiteral { fn string(&self) -> String { self.value.to_string() } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct PrefixExpression { @@ -190,6 +219,10 @@ impl NodeTrait for PrefixExpression { out.push_str(&self.right.string()); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct InfixExpression { @@ -215,6 +248,10 @@ impl NodeTrait for InfixExpression { out.push_str(")"); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct Boolean { @@ -230,6 +267,10 @@ impl NodeTrait for Boolean { fn string(&self) -> String { self.value.to_string() } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct IfExpression { @@ -256,6 +297,10 @@ impl NodeTrait for IfExpression { } out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct BlockStatement { @@ -275,6 +320,10 @@ impl NodeTrait for BlockStatement { } out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct FunctionLiteral { @@ -298,6 +347,10 @@ impl NodeTrait for FunctionLiteral { out.push_str(&self.body.string()); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } pub struct CallExpression { @@ -320,4 +373,8 @@ impl NodeTrait for CallExpression { out.push_str(")"); out } + + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs new file mode 100644 index 0000000..58953d0 --- /dev/null +++ b/src/evaluator/mod.rs @@ -0,0 +1,75 @@ +use crate::ast::Program; +use crate::ast::{self}; +use crate::object::{self, Null, Object}; + +const NULL: Null = Null; +const BOOL_TRUE: object::Boolean = object::Boolean { value: true }; +const BOOL_FALSE: object::Boolean = object::Boolean { value: false }; + +fn eval(node: Box) -> Option> { + let node = node.as_ref(); + if let Some(stmt) = node.as_any().downcast_ref::() { + return eval_expression(&stmt.expression); + } + if let Some(stmt) = node.as_any().downcast_ref::() { + return None; + } + if let Some(stmt) = node.as_any().downcast_ref::() { + return eval_expression(&stmt.return_value); + } + if let Some(expr) = node.as_any().downcast_ref::() { + return eval_expression(expr); + } + if let Some(expr) = node.as_any().downcast_ref::() { + let right = &eval(expr.right); + return eval_prefix_expression(expr.operator, right); + } + None +} + +pub fn eval_program(program: Program) -> Option> { + let mut res: Option> = None; + for stmt in program.statements { + res = eval(stmt) + } + res +} + +fn eval_expression(expression: &ast::Expression) -> Option> { + match expression { + ast::Expression::IntegerLiteral(int) => { + Some(Box::new(object::Integer { value: int.value })) + } + ast::Expression::Boolean(b) => Some(Box::new(native_bool_to_bool_object(b.value))), + _ => None, + } +} + +fn eval_prefix_expression( + operator: String, + right: &Option>, +) -> Option> { + match operator.as_str() { + "!" => eval_bang_operator_expression(right), + _ => Some(Box::new(NULL)), + } +} + +fn eval_bang_operator_expression( + right: &Option>, +) -> Option> { + let bool_true = Box::new(BOOL_TRUE); + let bool_false = Box::new(BOOL_FALSE); + let null = Box::new(NULL); + + match right..unwrap() { + bool_true => Some(bool_false), + bool_false => Some(bool_true), + null => Some(bool_true), + _ => Some(bool_false), + } +} + +fn native_bool_to_bool_object(input: bool) -> object::Boolean { + if input { BOOL_TRUE } else { BOOL_FALSE } +} diff --git a/src/main.rs b/src/main.rs index 0079669..ea233ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ mod ast; +mod evaluator; mod lexer; +mod object; mod parser; mod repl; mod token; diff --git a/src/object/mod.rs b/src/object/mod.rs new file mode 100644 index 0000000..cec9bd5 --- /dev/null +++ b/src/object/mod.rs @@ -0,0 +1,50 @@ +pub enum ObjectType { + INTEGER, + BOOLEAN, + NULL, +} + +pub trait Object { + fn object_type(&self) -> ObjectType; + fn inspect(&self) -> String; +} + +pub struct Integer { + pub value: i64, +} + +impl Object for Integer { + fn object_type(&self) -> ObjectType { + ObjectType::INTEGER + } + + fn inspect(&self) -> String { + self.value.to_string() + } +} + +pub struct Boolean { + pub value: bool, +} + +impl Object for Boolean { + fn object_type(&self) -> ObjectType { + ObjectType::BOOLEAN + } + + fn inspect(&self) -> String { + self.value.to_string() + } +} + +pub struct Null; + +impl Object for Null { + fn object_type(&self) -> ObjectType { + ObjectType::NULL + } + + fn inspect(&self) -> String { + "null".to_string() + } +} diff --git a/src/repl/mod.rs b/src/repl/mod.rs index e3eb801..cb783ce 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -1,4 +1,4 @@ -use crate::ast::NodeTrait; +use crate::evaluator; use crate::lexer::new; use crate::parser::Parser; use crate::parser::new_parser; @@ -20,11 +20,13 @@ pub fn start_repl() { let lexer = new(input.to_string()); let mut parser: Parser = new_parser(lexer); let program = parser.parse_program(); + let evaluated = evaluator::eval_program(program); for err in parser.errors() { eprintln!("ERROR: {}", err); } - - println!("{}", program.string()); + if !evaluated.is_none() { + println!("{}", evaluated.unwrap().inspect()); + } } }