small typos, comments, cleanup

This commit is contained in:
Tristan 2025-06-02 22:21:06 -04:00
parent 7e9ed5ead3
commit c39d220259
No known key found for this signature in database
3 changed files with 221 additions and 180 deletions

View File

@ -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<String, FddlValue>,
parent: Option<Box<Environment>>,
}
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<FddlValue, RuntimeError> {
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 {
}
}
}
}
}

View File

@ -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<Statement>;
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<Expression> = 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<Statement> = 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))

8
test.fddl Normal file
View File

@ -0,0 +1,8 @@
let x = 10;
{
let y = 5;
print x + y;
let x = 2;
print x + y;
}
print x;