diff --git a/src/interpreter/evaluator.rs b/src/interpreter/evaluator.rs index 3f60450..fc6e190 100644 --- a/src/interpreter/evaluator.rs +++ b/src/interpreter/evaluator.rs @@ -1,12 +1,14 @@ use crate::parser::ast::{Expression, Statement, Literal, Operator}; use std::collections::HashMap; +// --- Runtime Values --- #[derive(Debug, Clone, PartialEq)] pub enum FddlValue { Number(f64), Boolean(bool), String(String), Nil, + // Later, you might add: Function, Array, Object/Struct, etc. } impl std::fmt::Display for FddlValue { @@ -20,71 +22,108 @@ impl std::fmt::Display for FddlValue { } } +// --- Runtime Errors --- #[derive(Debug)] pub enum RuntimeError { TypeMismatch(String), UndefinedVariable(String), DivisionByZero, + // You could add more specific errors, e.g., IncorrectArgumentCount, etc. } +// --- Environment for Variables --- pub struct Environment { values: HashMap, parent: Option>, } -impl Environment { - pub fn new() -> Self { +impl Default for Environment { + fn default() -> Self { Environment { values: HashMap::new(), parent: None, } } +} - pub fn with_parent(parent: Environment) -> Self { - Environment{ +impl Environment { + // Creates a new global/base environment + pub fn new() -> Self { + Environment::default() + } + + // Creates a new environment that encloses a parent environment (for new scopes) + pub fn new_enclosed(parent_environment: Environment) -> Self { + Environment { values: HashMap::new(), - parent: Some(Box::new(parent)), + parent: Some(Box::new(parent_environment)), } } + // Defines a new variable in the current scope. Allows shadowing. pub fn define(&mut self, name: String, value: FddlValue) { self.values.insert(name, value); } + // Gets a variable's value, looking up through parent scopes if necessary. pub fn get(&self, name: &str) -> Result { match self.values.get(name) { - Some(value) => Ok(value.clone()), - None => match &self.parent { - Some(parent_env) => parent_env.get(name), - None => Err(RuntimeError::UndefinedVariable(format!( - "Variable '{}' is not defined.", - name - ))), - }, + Some(value) => Ok(value.clone()), // Clone to return an owned value + None => { // Not found in current scope, try parent + if let Some(parent_env_box) = &self.parent { + parent_env_box.get(name) // Recursive call + } else { + Err(RuntimeError::UndefinedVariable(format!( + "Undefined variable '{}'.", + name + ))) + } + } } } + // Assigns a new value to an existing variable. + // It must exist in the current or an enclosing scope. pub fn assign(&mut self, name: &str, value: FddlValue) -> Result<(), RuntimeError> { if self.values.contains_key(name) { + // Variable exists in the current scope, assign here. self.values.insert(name.to_string(), value); Ok(()) } else { - Err(RuntimeError::UndefinedVariable(format!( - "Cannot assign to undefined variable '{}'.", - name - ))) + // Not in current scope, try to assign in parent scope. + if let Some(parent_env_box) = &mut self.parent { + parent_env_box.assign(name, value) // Recursive call + } else { + Err(RuntimeError::UndefinedVariable(format!( + "Cannot assign to undefined variable '{}' (not found in any scope).", + name + ))) + } } } } +// --- Evaluator --- pub struct Evaluator { environment: Environment, } +// Helper for truthiness (nil and false are falsey, everything else is truthy) +// Defined as an associated function because it doesn't need `self`. +impl Evaluator { + fn is_truthy(value: &FddlValue) -> bool { + match value { + FddlValue::Boolean(false) => false, + FddlValue::Nil => false, + _ => true, + } + } +} + impl Evaluator { pub fn new() -> Self { Evaluator { - environment: Environment::new(), + environment: Environment::new(), // Start with a global environment } } @@ -95,46 +134,52 @@ impl Evaluator { Ok(()) } - fn is_truthy(value: &FddlValue) -> bool { - match value { - FddlValue::Boolean(false) => false, - FddlValue::Nil => false, - _ => true, - } - } - fn evaluate_statement(&mut self, statement: &Statement) -> Result<(), RuntimeError> { match statement { Statement::PrintStatement(expr) => { let value = self.evaluate_expression(expr)?; println!("{}", value); } - Statement::ExpressionStatement(expr) => { - self.evaluate_expression(expr)?; + self.evaluate_expression(expr)?; // Evaluate for side effects, discard result } - Statement::VariableDeclaration(name, initializer) => { let value = match initializer { Some(init_expr) => self.evaluate_expression(init_expr)?, - None => FddlValue::Nil, + None => FddlValue::Nil, // Default to nil if no initializer }; 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) => { + // Create a new scope for the block + let outer_environment = std::mem::take(&mut self.environment); // Takes current, leaves Default in self.env + self.environment = Environment::new_enclosed(outer_environment); // New current env, old one is parent + + let mut block_execution_result: Result<(), RuntimeError> = Ok(()); for stmt_in_block in statements { - self.evaluate_statement(stmt_in_block)?; + block_execution_result = self.evaluate_statement(stmt_in_block); + if block_execution_result.is_err() { + break; // Stop executing statements in block if one errors + } } + + // Restore the outer environment + if let Some(parent_env_box) = self.environment.parent.take() { // .take() to get ownership + self.environment = *parent_env_box; // Move parent back to be the current environment + } else { + // This should ideally not happen if scopes are managed correctly. + eprintln!("Warning: Exited a block scope that had no parent environment. Resetting to global."); + self.environment = Environment::new(); // Fallback + } + return block_execution_result; // Return the result from the block (e.g., if an error occurred) } - - // TODO: IfStatement, WhileStatement, ForStatement, FunctionDeclaration, ReturnStatement - + // TODO: Implement IfStatement, WhileStatement, ForStatement evaluation + // TODO: Implement FunctionDeclaration (store function object in environment) + // TODO: Implement ReturnStatement (special handling for unwinding and returning value) _ => { println!("Interpreter: Skipping unimplemented statement: {:?}", statement); } @@ -151,13 +196,13 @@ impl Evaluator { Literal::String(s) => Ok(FddlValue::String(s.clone())), Literal::Nil => Ok(FddlValue::Nil), } - }, + }, Expression::Variable(name) => { self.environment.get(name) - }, + }, - Expression::Unary(op, right_expr) => { + Expression::Unary(op, right_expr) => { let right_val = self.evaluate_expression(right_expr)?; match op { Operator::Minus => { @@ -169,149 +214,143 @@ impl Evaluator { )) } } - - Operator::Not => { - if let FddlValue::Boolean(b) = right_val { - Ok(FddlValue::Boolean(!b)) - } else { - Err(RuntimeError::TypeMismatch( - "Operand for 'not' must be a boolean.".to_string(), - )) - } - } - - Operator::Some => { - Ok(FddlValue::Boolean(!matches!(right_val, FddlValue::Nil))) - } - - Operator::Almost => { - if let FddlValue::Number(n) = right_val { - Ok(FddlValue::Number(n.floor())) - } else { - Err(RuntimeError::TypeMismatch( - "Operand for unary '~' (Almost) must be a number for this example.".to_string(), - )) + Operator::Not => { + Ok(FddlValue::Boolean(!Self::is_truthy(&right_val))) + } + Operator::Some => { + Ok(FddlValue::Boolean(!matches!(right_val, FddlValue::Nil))) + } + Operator::Almost => { // '~' operator + match right_val { + FddlValue::Number(n) => { + if n == 0.0 { + Ok(FddlValue::Number(0.1337)) // Arbitrary small chaotic number + } else { + let bits = n.to_bits(); + let offset_seed = (bits >> 16) & 0xFFF; + let scale_seed = bits & 0xFFF; + let chaotic_offset = (offset_seed as f64 / 4095.0 - 0.5) * n.abs() * 0.2; + let chaotic_scale = 1.0 + (scale_seed as f64 / 4095.0 - 0.5) * 0.1; + Ok(FddlValue::Number((n + chaotic_offset) * chaotic_scale)) + } + } + FddlValue::Boolean(b) => Ok(FddlValue::Boolean(!b)), + FddlValue::String(s) => { + if s.is_empty() { + Ok(FddlValue::String("?!~".to_string())) + } else { + let mut new_s: String = s.chars().rev().collect(); + new_s.push('~'); + Ok(FddlValue::String(new_s)) + } + } + FddlValue::Nil => Ok(FddlValue::String("almost nil?".to_string())), } } + // Add other unary operators if you have them in your Operator enum _ => Err(RuntimeError::TypeMismatch(format!( "Unsupported unary operator {:?}.", op ))), } - }, + }, Expression::Binary(left_expr, op, right_expr) => { - let left_val = self.evaluate_expression(left_expr)?; - let right_val = self.evaluate_expression(right_expr)?; - + // Handle logical AND and OR first for short-circuiting match op { - Operator::EqualEqual => { // For == - Ok(FddlValue::Boolean(left_val == right_val)) - } - Operator::NotEqual => { // For != - Ok(FddlValue::Boolean(left_val != right_val)) - } - Operator::Plus => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Number(l + r)) - } else { - Err(RuntimeError::TypeMismatch( - format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val) - )) + Operator::And => { + let left_val = self.evaluate_expression(left_expr)?; + if !Self::is_truthy(&left_val) { + return Ok(FddlValue::Boolean(false)); // Short-circuit } + let right_val = self.evaluate_expression(right_expr)?; + return Ok(FddlValue::Boolean(Self::is_truthy(&right_val))); } - Operator::Minus => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Number(l - r)) - } else { - Err(RuntimeError::TypeMismatch( - format!("Operands for '-' must be numbers. Got {:?} and {:?}", left_val, right_val) - )) + Operator::Or => { + let left_val = self.evaluate_expression(left_expr)?; + if Self::is_truthy(&left_val) { + return Ok(FddlValue::Boolean(true)); // Short-circuit } + let right_val = self.evaluate_expression(right_expr)?; + return Ok(FddlValue::Boolean(Self::is_truthy(&right_val))); } - Operator::Multiply => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Number(l * r)) - } else { - Err(RuntimeError::TypeMismatch( - format!("Operands for '*' must be numbers. Got {:?} and {:?}", left_val, right_val) - )) - } - } - Operator::Divide => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - if *r == 0.0 { - Err(RuntimeError::DivisionByZero) - } else { - Ok(FddlValue::Number(l / r)) + _ => { // For all other binary operators, evaluate both operands first + let left_val = self.evaluate_expression(left_expr)?; + let right_val = self.evaluate_expression(right_expr)?; + + match op { + // Arithmetic + Operator::Plus => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { + Ok(FddlValue::Number(l + r)) + } else { // TODO: String concatenation? + Err(RuntimeError::TypeMismatch(format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val))) + } } - } else { - Err(RuntimeError::TypeMismatch( - format!("Operands for '/' must be numbers. Got {:?} and {:?}", left_val, right_val) - )) - } - } - 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())) // Or DivisionByZero - } else { - Ok(FddlValue::Number(l % r)) + Operator::Minus => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { + Ok(FddlValue::Number(l - r)) + } else { + Err(RuntimeError::TypeMismatch(format!("Operands for '-' must be numbers. Got {:?} and {:?}", left_val, right_val))) + } } - } else { - Err(RuntimeError::TypeMismatch( - format!("Operands for '%' must be numbers. Got {:?} and {:?}", left_val, right_val) - )) + Operator::Multiply => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { + Ok(FddlValue::Number(l * r)) + } else { + Err(RuntimeError::TypeMismatch(format!("Operands for '*' must be numbers. Got {:?} and {:?}", left_val, right_val))) + } + } + Operator::Divide => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { + if *r == 0.0 { Err(RuntimeError::DivisionByZero) } else { Ok(FddlValue::Number(l / r)) } + } else { + Err(RuntimeError::TypeMismatch(format!("Operands for '/' must be numbers. Got {:?} and {:?}", left_val, right_val))) + } + } + 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())) } else { Ok(FddlValue::Number(l % r)) } + } else { + Err(RuntimeError::TypeMismatch(format!("Operands for '%' must be numbers. Got {:?} and {:?}", left_val, right_val))) + } + } + + // Comparison + Operator::Greater => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Boolean(l > r)) } else { Err(RuntimeError::TypeMismatch("Operands for '>' must be numbers.".to_string())) } + } + Operator::GreaterEqual => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Boolean(l >= r)) } else { Err(RuntimeError::TypeMismatch("Operands for '>=' must be numbers.".to_string())) } + } + Operator::Less => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Boolean(l < r)) } else { Err(RuntimeError::TypeMismatch("Operands for '<' must be numbers.".to_string())) } + } + Operator::LessEqual => { + if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { Ok(FddlValue::Boolean(l <= r)) } else { Err(RuntimeError::TypeMismatch("Operands for '<=' must be numbers.".to_string())) } + } + + // Equality + Operator::EqualEqual => Ok(FddlValue::Boolean(left_val == right_val)), + Operator::NotEqual => Ok(FddlValue::Boolean(left_val != right_val)), + + // And & Or are handled above due to short-circuiting. + // This _ should catch any other Operator variants not explicitly handled here. + _ => Err(RuntimeError::TypeMismatch(format!( + "Unsupported binary operator '{:?}' after operand evaluation.", op + ))), } } - Operator::Greater => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Boolean(l > r)) - } else { - Err(RuntimeError::TypeMismatch( - "Operands for '>' must be numbers.".to_string() - )) - } - } - Operator::GreaterEqual => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Boolean(l >= r)) - } else { - Err(RuntimeError::TypeMismatch( - "Operands for '>=' must be numbers.".to_string() - )) - } - } - Operator::Less => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Boolean(l < r)) - } else { - Err(RuntimeError::TypeMismatch( - "Operands for '<' must be numbers.".to_string() - )) - } - } - Operator::LessEqual => { - if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) { - Ok(FddlValue::Boolean(l <= r)) - } else { - Err(RuntimeError::TypeMismatch( - "Operands for '<=' must be numbers.".to_string() - )) - } - } - _ => Err(RuntimeError::TypeMismatch(format!( - "Unsupported binary operator {:?}.", - op - ))), } }, - Expression::Grouping(inner_expr) => { + Expression::Grouping(inner_expr) => { self.evaluate_expression(inner_expr) }, - _ => { + // TODO: Implement Expression::FunctionCall evaluation + + _ => { // Fallback for unimplemented expression types println!("Interpreter: Unimplemented expression: {:?}", expression); Err(RuntimeError::TypeMismatch(format!( "Unimplemented expression type: {:?}", @@ -320,5 +359,4 @@ impl Evaluator { } } } - -} \ No newline at end of file +} diff --git a/src/parser/parser.rs b/src/parser/parser.rs index c012ecf..2a49ee2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -480,11 +480,10 @@ impl Parser { return None; } - // 1. Initializer Statement - self.skip_comments(); // Skip comments before initializer part + self.skip_comments(); let initializer: Box; if self.check(&Token::Let) { - self.advance(); // Consume 'let' + self.advance(); let var_name = match self.peek_and_advance() { Some(Token::Identifier(name_str)) => name_str, _ => { @@ -493,47 +492,45 @@ impl Parser { } }; let var_initializer_expr: Option = if self.match_token(Token::Equal) { - self.skip_comments(); // Skip comments before the expression value + self.skip_comments(); self.parse_expression() } else { None }; initializer = Box::new(Statement::VariableDeclaration(var_name, var_initializer_expr)); - self.skip_comments(); // Skip comments before the semicolon separator + self.skip_comments(); if !self.match_token(Token::Semicolon) { eprintln!("Error: Expected ';' after 'let' declaration in for-loop initializer."); return None; } } else if self.check(&Token::Semicolon) { // Check for ';' for empty initializer - self.advance(); // Consume the ';' + self.advance(); initializer = Box::new(Statement::ExpressionStatement(Expression::Literal(Literal::Nil))); - } else { // Expression initializer + } else { let init_expr = self.parse_expression()?; initializer = Box::new(Statement::ExpressionStatement(init_expr)); - self.skip_comments(); // Skip comments before the semicolon separator + self.skip_comments(); if !self.match_token(Token::Semicolon) { eprintln!("Error: Expected ';' after for-loop initializer expression."); return None; } } - // 2. Condition Expression - self.skip_comments(); // Skip comments before condition part + self.skip_comments(); let condition: Expression; - if self.check(&Token::Semicolon) { // Check for ';' for empty condition - self.advance(); // Consume the ';' - condition = Expression::Literal(Literal::Boolean(true)); // Default to true + if self.check(&Token::Semicolon) { + self.advance(); + condition = Expression::Literal(Literal::Boolean(true)); } else { condition = self.parse_expression()?; - self.skip_comments(); // Skip comments before the semicolon separator + self.skip_comments(); if !self.match_token(Token::Semicolon) { eprintln!("Error: Expected ';' after for-loop condition."); return None; } } - // 3. Increment Statement - self.skip_comments(); // <--- CRUCIAL FIX: Skip comments before increment part + self.skip_comments(); let increment: Box = if self.check(&Token::RightParen) { Box::new(Statement::ExpressionStatement(Expression::Literal(Literal::Nil))) } else { @@ -541,19 +538,17 @@ impl Parser { Box::new(Statement::ExpressionStatement(incr_expr)) }; - self.skip_comments(); // Skip comments before ')' + self.skip_comments(); if !self.match_token(Token::RightParen) { eprintln!("Error: Expected ')' after for-loop clauses."); return None; } - // 4. Body Statement (must be a block) - self.skip_comments(); // Skip comments before '{' + self.skip_comments(); if !self.check(&Token::LeftBrace) { eprintln!("Error: Expected '{{' for for-loop body."); return None; } - // parse_statement() already calls skip_comments() at its beginning let body = Box::new(self.parse_statement()?); Some(Statement::ForStatement(initializer, condition, increment, body)) diff --git a/test.fddl b/test.fddl new file mode 100644 index 0000000..55f95a9 --- /dev/null +++ b/test.fddl @@ -0,0 +1,8 @@ +let x = 10; +{ + let y = 5; + print x + y; + let x = 2; + print x + y; +} +print x;