From 1742cf647d9eb4e5ce64d0f9adecf3b117da2a91 Mon Sep 17 00:00:00 2001 From: Tristan Date: Thu, 29 May 2025 22:34:57 -0400 Subject: [PATCH] variables are being stored --- src/interpreter/evaluator.rs | 104 +++++++++++++++++++----- src/main.rs | 57 +++++++++++--- tests/parser_tests.rs | 149 +++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+), 31 deletions(-) diff --git a/src/interpreter/evaluator.rs b/src/interpreter/evaluator.rs index b2a9941..2309fde 100644 --- a/src/interpreter/evaluator.rs +++ b/src/interpreter/evaluator.rs @@ -1,4 +1,5 @@ use crate::parser::ast::{Expression, Statement, Literal, Operator}; +use std::collections::HashMap; #[derive(Debug, Clone, PartialEq)] pub enum FddlValue { @@ -23,13 +24,56 @@ impl std::fmt::Display for FddlValue { pub enum RuntimeError { TypeMismatch(String), UndefinedVariable(String), + DivisionByZero, } -pub struct Evaluator; +pub struct Environment { + values: HashMap, +} + +impl Environment { + pub fn new() -> Self { + Environment { + values: HashMap::new(), + } + } + + pub fn define(&mut self, name: String, value: FddlValue) { + self.values.insert(name, value); + } + + pub fn get(&self, name: &str) -> Result { + match self.values.get(name) { + Some(value) => Ok(value.clone()), // Clone to return an owned value + None => Err(RuntimeError::UndefinedVariable(format!( + "Undefined variable '{}'.", + name + ))), + } + } + + pub fn assign(&mut self, name: &str, value: FddlValue) -> Result<(), RuntimeError> { + if self.values.contains_key(name) { + self.values.insert(name.to_string(), value); + Ok(()) + } else { + Err(RuntimeError::UndefinedVariable(format!( + "Cannot assign to undefined variable '{}'.", + name + ))) + } + } +} + +pub struct Evaluator { + environment: Environment, +} impl Evaluator { pub fn new() -> Self { - Evaluator + Evaluator { + environment: Environment::new(), + } } pub fn evaluate_program(&mut self, statements: Vec) -> Result<(), RuntimeError> { @@ -50,6 +94,27 @@ impl Evaluator { self.evaluate_expression(expr)?; } + Statement::VariableDeclaration(name, initializer) => { + let value = match initializer { + Some(init_expr) => self.evaluate_expression(init_expr)?, + None => FddlValue::Nil, + }; + self.environment.define(name.clone(), value); + } + + Statement::Assignment { target_name, value } => { + let val_to_assign = self.evaluate_expression(value)?; + self.environment.assign(target_name, val_to_assign)?; + } + + Statement::Block(statements) => { + for stmt_in_block in statements { + self.evaluate_statement(stmt_in_block)?; + } + } + + // TODO: IfStatement, WhileStatement, ForStatement, FunctionDeclaration, ReturnStatement + _ => { println!("Interpreter: Skipping unimplemented statement: {:?}", statement); } @@ -66,9 +131,13 @@ impl Evaluator { Literal::String(s) => Ok(FddlValue::String(s.clone())), Literal::Nil => Ok(FddlValue::Nil), } - } + }, - Expression::Unary(op, right_expr) => { + Expression::Variable(name) => { + self.environment.get(name) + }, + + Expression::Unary(op, right_expr) => { let right_val = self.evaluate_expression(right_expr)?; match op { Operator::Minus => { @@ -80,7 +149,7 @@ impl Evaluator { )) } } - Operator::Not => { + Operator::Not => { if let FddlValue::Boolean(b) = right_val { Ok(FddlValue::Boolean(!b)) } else { @@ -89,7 +158,7 @@ impl Evaluator { )) } } - Operator::Some => { + Operator::Some => { Ok(right_val) } Operator::Almost => { @@ -106,26 +175,23 @@ impl Evaluator { op ))), } - } + }, - Expression::Binary(left_expr, op, right_expr) => { // Add this new arm + Expression::Binary(left_expr, op, right_expr) => { let left_val = self.evaluate_expression(left_expr)?; let right_val = self.evaluate_expression(right_expr)?; - // Now, perform the operation based on 'op' and the types of left_val and right_val match op { Operator::Plus => { - // Example for addition (assuming numbers for now) if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Number(l + r)) } else { - // Later, you might allow string concatenation here Err(RuntimeError::TypeMismatch( format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val) )) } } - Operator::Minus => { // Binary Minus + Operator::Minus => { if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Number(l - r)) } else { @@ -146,7 +212,7 @@ impl Evaluator { Operator::Divide => { if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { if *r == 0.0 { - Err(RuntimeError::TypeMismatch("Division by zero.".to_string())) // Or a specific DivisionByZero error + Err(RuntimeError::DivisionByZero) } else { Ok(FddlValue::Number(l / r)) } @@ -156,10 +222,10 @@ impl Evaluator { )) } } - Operator::Modulus => { // Assuming you have Operator::Modulus from earlier + Operator::Modulus => { if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { if *r == 0.0 { - Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string())) + Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string())) // Or DivisionByZero } else { Ok(FddlValue::Number(l % r)) } @@ -169,18 +235,16 @@ impl Evaluator { )) } } - // TODO: Add cases for Operator::Greater, Less, EqualEqual, NotEqual, And, Or etc. - // These will typically operate on numbers or booleans and produce FddlValue::Boolean. _ => Err(RuntimeError::TypeMismatch(format!( "Unsupported binary operator {:?}.", op ))), } - } + }, - Expression::Grouping(inner_expr) => { + Expression::Grouping(inner_expr) => { self.evaluate_expression(inner_expr) - } + }, _ => { println!("Interpreter: Unimplemented expression: {:?}", expression); diff --git a/src/main.rs b/src/main.rs index d9e7bde..3560b29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use std::io::{self, Write}; use fddl::lexer::Lexer; use fddl::parser::Parser; use fddl::interpreter::evaluator::Evaluator; +use fddl::parser::ast::{Statement, Expression}; fn main() { let args: Vec = env::args().collect(); @@ -21,29 +22,63 @@ fn main() { } } -// basic REPL fn run_repl() { - println!("FDDL REPL"); + println!("fddl REPL"); println!("---------"); - loop { - print!("fddl % "); - io::stdout().flush().unwrap(); + let mut evaluator = Evaluator::new(); - let mut buffer = String::new(); - io::stdin().read_line(&mut buffer).unwrap(); + loop { + print!("fddl % "); + std::io::stdout().flush().unwrap(); + + let mut buffer = String::new(); + if std::io::stdin().read_line(&mut buffer).is_err() || buffer.trim() == "exit" { + break; + } if buffer.trim().is_empty() { continue; } - run(buffer.clone()); + run_line(buffer, &mut evaluator); } } -// runs file +fn run_line(source: String, evaluator: &mut Evaluator) { + println!("Source: {}", source.trim()); + + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + + let mut parser = Parser::new(tokens); + let program_ast: Vec = parser.parse_program(); + + if !program_ast.is_empty() { + + println!("Output:"); + match evaluator.evaluate_program(program_ast) { + Ok(()) => { /* Statement executed successfully */ } + Err(e) => { + eprintln!("Runtime Error: {:?}", e); + } + } + } else { + println!("No AST generated or parsing failed for this line."); + } + println!("---"); +} + fn run_file(path: &str) { - let source = fs::read_to_string(path).expect("Failed to read source file"); - run(source); + println!("Running file: {}", path); + match std::fs::read_to_string(path) { + Ok(source) => { + let mut file_evaluator = Evaluator::new(); + run_line(source, &mut file_evaluator); + } + Err(e) => { + eprintln!("Error reading file '{}': {}", path, e); + } + } } fn run(source: String) { diff --git a/tests/parser_tests.rs b/tests/parser_tests.rs index e69de29..9986228 100644 --- a/tests/parser_tests.rs +++ b/tests/parser_tests.rs @@ -0,0 +1,149 @@ +use fddl::lexer::Lexer; +use fddl::parser::Parser; +use fddl::parser::ast::{Statement, Expression, Literal, Operator}; + +#[test] +fn test_simple_print_statement_number() { + let source = String::from("print 123;"); + + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::PrintStatement( + Expression::Literal(Literal::Number(123.0)) + ) + ]; + + assert_eq!(program_ast, expected_ast, "AST for 'print 123;' did not match."); +} + +#[test] +fn test_variable_declaration_with_initializer() { + let source = String::from("let x = 10;"); + + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::VariableDeclaration( + "x".to_string(), + Some(Expression::Literal(Literal::Number(10.0))) + ) + ]; + assert_eq!(program_ast, expected_ast, "AST for 'let x = 10;' did not match."); +} + +#[test] +fn test_unary_not_expression() { + let source = String::from("print not true;"); + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::PrintStatement( + Expression::Unary( + Operator::Not, // Assuming Operator::Not exists from previous steps + Box::new(Expression::Literal(Literal::Boolean(true))) + ) + ) + ]; + assert_eq!(program_ast, expected_ast, "AST for 'print not true;' did not match."); +} + +#[test] +fn test_binary_precedence() { + let source = String::from("print 1 + 2 * 3;"); + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::PrintStatement( + Expression::Binary( + Box::new(Expression::Literal(Literal::Number(1.0))), + Operator::Plus, + Box::new(Expression::Binary( + Box::new(Expression::Literal(Literal::Number(2.0))), + Operator::Multiply, + Box::new(Expression::Literal(Literal::Number(3.0))) + )) + ) + ) + ]; + assert_eq!(program_ast, expected_ast, "AST for 'print 1 + 2 * 3;' did not match."); +} + +#[test] +fn test_simple_function_call_statement() { + let source = String::from("my_func();"); // As an expression statement + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::ExpressionStatement( + Expression::FunctionCall( + Box::new(Expression::Variable("my_func".to_string())), + Vec::new() // No arguments + ) + ) + ]; + assert_eq!(program_ast, expected_ast, "AST for 'my_func();' did not match."); +} + +#[test] +fn test_assignment_statement() { + let source = String::from("count = count + 1;"); + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::Assignment { // Assuming you added this variant to Statement + target_name: "count".to_string(), + value: Expression::Binary( + Box::new(Expression::Variable("count".to_string())), + Operator::Plus, + Box::new(Expression::Literal(Literal::Number(1.0))) + ) + } + ]; + assert_eq!(program_ast, expected_ast, "AST for 'count = count + 1;' did not match."); +} + +#[test] +fn test_if_else_statement_with_blocks() { + let source = String::from("if (x < 10) { print \"small\"; } else { print \"large\"; }"); + let mut lexer = Lexer::new(source); + let tokens = lexer.scan_tokens(); + let mut parser = Parser::new(tokens); + let program_ast = parser.parse_program(); + + let expected_ast = vec![ + Statement::IfStatement( + Expression::Binary( // Condition: x < 10 + Box::new(Expression::Variable("x".to_string())), + Operator::Less, + Box::new(Expression::Literal(Literal::Number(10.0))) + ), + Box::new(Statement::Block(vec![ // Then branch + Statement::PrintStatement(Expression::Literal(Literal::String("small".to_string()))) + ])), + Some(Box::new(Statement::Block(vec![ // Else branch + Statement::PrintStatement(Expression::Literal(Literal::String("large".to_string()))) + ]))) + ) + ]; + assert_eq!(program_ast, expected_ast, "AST for if-else statement did not match."); +} \ No newline at end of file