mirror of
https://github.com/urinalcaketopper/fddl.git
synced 2025-06-07 05:34:47 +00:00
small typos, comments, cleanup
This commit is contained in:
parent
7e9ed5ead3
commit
c39d220259
@ -1,12 +1,14 @@
|
|||||||
use crate::parser::ast::{Expression, Statement, Literal, Operator};
|
use crate::parser::ast::{Expression, Statement, Literal, Operator};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
// --- Runtime Values ---
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum FddlValue {
|
pub enum FddlValue {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
String(String),
|
String(String),
|
||||||
Nil,
|
Nil,
|
||||||
|
// Later, you might add: Function, Array, Object/Struct, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for FddlValue {
|
impl std::fmt::Display for FddlValue {
|
||||||
@ -20,71 +22,108 @@ impl std::fmt::Display for FddlValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Runtime Errors ---
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
TypeMismatch(String),
|
TypeMismatch(String),
|
||||||
UndefinedVariable(String),
|
UndefinedVariable(String),
|
||||||
DivisionByZero,
|
DivisionByZero,
|
||||||
|
// You could add more specific errors, e.g., IncorrectArgumentCount, etc.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Environment for Variables ---
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
values: HashMap<String, FddlValue>,
|
values: HashMap<String, FddlValue>,
|
||||||
parent: Option<Box<Environment>>,
|
parent: Option<Box<Environment>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Environment {
|
impl Default for Environment {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
Environment {
|
Environment {
|
||||||
values: HashMap::new(),
|
values: HashMap::new(),
|
||||||
parent: None,
|
parent: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_parent(parent: Environment) -> Self {
|
impl Environment {
|
||||||
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(),
|
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) {
|
pub fn define(&mut self, name: String, value: FddlValue) {
|
||||||
self.values.insert(name, value);
|
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> {
|
pub fn get(&self, name: &str) -> Result<FddlValue, RuntimeError> {
|
||||||
match self.values.get(name) {
|
match self.values.get(name) {
|
||||||
Some(value) => Ok(value.clone()),
|
Some(value) => Ok(value.clone()), // Clone to return an owned value
|
||||||
None => match &self.parent {
|
None => { // Not found in current scope, try parent
|
||||||
Some(parent_env) => parent_env.get(name),
|
if let Some(parent_env_box) = &self.parent {
|
||||||
None => Err(RuntimeError::UndefinedVariable(format!(
|
parent_env_box.get(name) // Recursive call
|
||||||
"Variable '{}' is not defined.",
|
} else {
|
||||||
name
|
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> {
|
pub fn assign(&mut self, name: &str, value: FddlValue) -> Result<(), RuntimeError> {
|
||||||
if self.values.contains_key(name) {
|
if self.values.contains_key(name) {
|
||||||
|
// Variable exists in the current scope, assign here.
|
||||||
self.values.insert(name.to_string(), value);
|
self.values.insert(name.to_string(), value);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(RuntimeError::UndefinedVariable(format!(
|
// Not in current scope, try to assign in parent scope.
|
||||||
"Cannot assign to undefined variable '{}'.",
|
if let Some(parent_env_box) = &mut self.parent {
|
||||||
name
|
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 {
|
pub struct Evaluator {
|
||||||
environment: Environment,
|
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 {
|
impl Evaluator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Evaluator {
|
Evaluator {
|
||||||
environment: Environment::new(),
|
environment: Environment::new(), // Start with a global environment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,46 +134,52 @@ impl Evaluator {
|
|||||||
Ok(())
|
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> {
|
fn evaluate_statement(&mut self, statement: &Statement) -> Result<(), RuntimeError> {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::PrintStatement(expr) => {
|
Statement::PrintStatement(expr) => {
|
||||||
let value = self.evaluate_expression(expr)?;
|
let value = self.evaluate_expression(expr)?;
|
||||||
println!("{}", value);
|
println!("{}", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::ExpressionStatement(expr) => {
|
Statement::ExpressionStatement(expr) => {
|
||||||
self.evaluate_expression(expr)?;
|
self.evaluate_expression(expr)?; // Evaluate for side effects, discard result
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::VariableDeclaration(name, initializer) => {
|
Statement::VariableDeclaration(name, initializer) => {
|
||||||
let value = match initializer {
|
let value = match initializer {
|
||||||
Some(init_expr) => self.evaluate_expression(init_expr)?,
|
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);
|
self.environment.define(name.clone(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::Assignment { target_name, value } => {
|
Statement::Assignment { target_name, value } => {
|
||||||
let val_to_assign = self.evaluate_expression(value)?;
|
let val_to_assign = self.evaluate_expression(value)?;
|
||||||
self.environment.assign(target_name, val_to_assign)?;
|
self.environment.assign(target_name, val_to_assign)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::Block(statements) => {
|
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 {
|
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: Implement IfStatement, WhileStatement, ForStatement evaluation
|
||||||
// TODO: IfStatement, WhileStatement, ForStatement, FunctionDeclaration, ReturnStatement
|
// TODO: Implement FunctionDeclaration (store function object in environment)
|
||||||
|
// TODO: Implement ReturnStatement (special handling for unwinding and returning value)
|
||||||
_ => {
|
_ => {
|
||||||
println!("Interpreter: Skipping unimplemented statement: {:?}", statement);
|
println!("Interpreter: Skipping unimplemented statement: {:?}", statement);
|
||||||
}
|
}
|
||||||
@ -151,13 +196,13 @@ impl Evaluator {
|
|||||||
Literal::String(s) => Ok(FddlValue::String(s.clone())),
|
Literal::String(s) => Ok(FddlValue::String(s.clone())),
|
||||||
Literal::Nil => Ok(FddlValue::Nil),
|
Literal::Nil => Ok(FddlValue::Nil),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Variable(name) => {
|
Expression::Variable(name) => {
|
||||||
self.environment.get(name)
|
self.environment.get(name)
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Unary(op, right_expr) => {
|
Expression::Unary(op, right_expr) => {
|
||||||
let right_val = self.evaluate_expression(right_expr)?;
|
let right_val = self.evaluate_expression(right_expr)?;
|
||||||
match op {
|
match op {
|
||||||
Operator::Minus => {
|
Operator::Minus => {
|
||||||
@ -169,149 +214,143 @@ impl Evaluator {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Operator::Not => {
|
||||||
Operator::Not => {
|
Ok(FddlValue::Boolean(!Self::is_truthy(&right_val)))
|
||||||
if let FddlValue::Boolean(b) = right_val {
|
}
|
||||||
Ok(FddlValue::Boolean(!b))
|
Operator::Some => {
|
||||||
} else {
|
Ok(FddlValue::Boolean(!matches!(right_val, FddlValue::Nil)))
|
||||||
Err(RuntimeError::TypeMismatch(
|
}
|
||||||
"Operand for 'not' must be a boolean.".to_string(),
|
Operator::Almost => { // '~' operator
|
||||||
))
|
match right_val {
|
||||||
}
|
FddlValue::Number(n) => {
|
||||||
}
|
if n == 0.0 {
|
||||||
|
Ok(FddlValue::Number(0.1337)) // Arbitrary small chaotic number
|
||||||
Operator::Some => {
|
} else {
|
||||||
Ok(FddlValue::Boolean(!matches!(right_val, FddlValue::Nil)))
|
let bits = n.to_bits();
|
||||||
}
|
let offset_seed = (bits >> 16) & 0xFFF;
|
||||||
|
let scale_seed = bits & 0xFFF;
|
||||||
Operator::Almost => {
|
let chaotic_offset = (offset_seed as f64 / 4095.0 - 0.5) * n.abs() * 0.2;
|
||||||
if let FddlValue::Number(n) = right_val {
|
let chaotic_scale = 1.0 + (scale_seed as f64 / 4095.0 - 0.5) * 0.1;
|
||||||
Ok(FddlValue::Number(n.floor()))
|
Ok(FddlValue::Number((n + chaotic_offset) * chaotic_scale))
|
||||||
} else {
|
}
|
||||||
Err(RuntimeError::TypeMismatch(
|
}
|
||||||
"Operand for unary '~' (Almost) must be a number for this example.".to_string(),
|
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!(
|
_ => Err(RuntimeError::TypeMismatch(format!(
|
||||||
"Unsupported unary operator {:?}.",
|
"Unsupported unary operator {:?}.",
|
||||||
op
|
op
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Binary(left_expr, op, right_expr) => {
|
Expression::Binary(left_expr, op, right_expr) => {
|
||||||
let left_val = self.evaluate_expression(left_expr)?;
|
// Handle logical AND and OR first for short-circuiting
|
||||||
let right_val = self.evaluate_expression(right_expr)?;
|
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Operator::EqualEqual => { // For ==
|
Operator::And => {
|
||||||
Ok(FddlValue::Boolean(left_val == right_val))
|
let left_val = self.evaluate_expression(left_expr)?;
|
||||||
}
|
if !Self::is_truthy(&left_val) {
|
||||||
Operator::NotEqual => { // For !=
|
return Ok(FddlValue::Boolean(false)); // Short-circuit
|
||||||
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)
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
let right_val = self.evaluate_expression(right_expr)?;
|
||||||
|
return Ok(FddlValue::Boolean(Self::is_truthy(&right_val)));
|
||||||
}
|
}
|
||||||
Operator::Minus => {
|
Operator::Or => {
|
||||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
let left_val = self.evaluate_expression(left_expr)?;
|
||||||
Ok(FddlValue::Number(l - r))
|
if Self::is_truthy(&left_val) {
|
||||||
} else {
|
return Ok(FddlValue::Boolean(true)); // Short-circuit
|
||||||
Err(RuntimeError::TypeMismatch(
|
|
||||||
format!("Operands for '-' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
let right_val = self.evaluate_expression(right_expr)?;
|
||||||
|
return Ok(FddlValue::Boolean(Self::is_truthy(&right_val)));
|
||||||
}
|
}
|
||||||
Operator::Multiply => {
|
_ => { // For all other binary operators, evaluate both operands first
|
||||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
let left_val = self.evaluate_expression(left_expr)?;
|
||||||
Ok(FddlValue::Number(l * r))
|
let right_val = self.evaluate_expression(right_expr)?;
|
||||||
} else {
|
|
||||||
Err(RuntimeError::TypeMismatch(
|
match op {
|
||||||
format!("Operands for '*' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
// Arithmetic
|
||||||
))
|
Operator::Plus => {
|
||||||
}
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
}
|
Ok(FddlValue::Number(l + r))
|
||||||
Operator::Divide => {
|
} else { // TODO: String concatenation?
|
||||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
Err(RuntimeError::TypeMismatch(format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val)))
|
||||||
if *r == 0.0 {
|
}
|
||||||
Err(RuntimeError::DivisionByZero)
|
|
||||||
} else {
|
|
||||||
Ok(FddlValue::Number(l / r))
|
|
||||||
}
|
}
|
||||||
} else {
|
Operator::Minus => {
|
||||||
Err(RuntimeError::TypeMismatch(
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
format!("Operands for '/' must be numbers. Got {:?} and {:?}", 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::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))
|
|
||||||
}
|
}
|
||||||
} else {
|
Operator::Multiply => {
|
||||||
Err(RuntimeError::TypeMismatch(
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
format!("Operands for '%' must be numbers. Got {:?} and {:?}", 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)
|
self.evaluate_expression(inner_expr)
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
// TODO: Implement Expression::FunctionCall evaluation
|
||||||
|
|
||||||
|
_ => { // Fallback for unimplemented expression types
|
||||||
println!("Interpreter: Unimplemented expression: {:?}", expression);
|
println!("Interpreter: Unimplemented expression: {:?}", expression);
|
||||||
Err(RuntimeError::TypeMismatch(format!(
|
Err(RuntimeError::TypeMismatch(format!(
|
||||||
"Unimplemented expression type: {:?}",
|
"Unimplemented expression type: {:?}",
|
||||||
@ -320,5 +359,4 @@ impl Evaluator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -480,11 +480,10 @@ impl Parser {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Initializer Statement
|
self.skip_comments();
|
||||||
self.skip_comments(); // Skip comments before initializer part
|
|
||||||
let initializer: Box<Statement>;
|
let initializer: Box<Statement>;
|
||||||
if self.check(&Token::Let) {
|
if self.check(&Token::Let) {
|
||||||
self.advance(); // Consume 'let'
|
self.advance();
|
||||||
let var_name = match self.peek_and_advance() {
|
let var_name = match self.peek_and_advance() {
|
||||||
Some(Token::Identifier(name_str)) => name_str,
|
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) {
|
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()
|
self.parse_expression()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
initializer = Box::new(Statement::VariableDeclaration(var_name, var_initializer_expr));
|
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) {
|
if !self.match_token(Token::Semicolon) {
|
||||||
eprintln!("Error: Expected ';' after 'let' declaration in for-loop initializer.");
|
eprintln!("Error: Expected ';' after 'let' declaration in for-loop initializer.");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
} else if self.check(&Token::Semicolon) { // Check for ';' for empty initializer
|
} 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)));
|
initializer = Box::new(Statement::ExpressionStatement(Expression::Literal(Literal::Nil)));
|
||||||
} else { // Expression initializer
|
} else {
|
||||||
let init_expr = self.parse_expression()?;
|
let init_expr = self.parse_expression()?;
|
||||||
initializer = Box::new(Statement::ExpressionStatement(init_expr));
|
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) {
|
if !self.match_token(Token::Semicolon) {
|
||||||
eprintln!("Error: Expected ';' after for-loop initializer expression.");
|
eprintln!("Error: Expected ';' after for-loop initializer expression.");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Condition Expression
|
self.skip_comments();
|
||||||
self.skip_comments(); // Skip comments before condition part
|
|
||||||
let condition: Expression;
|
let condition: Expression;
|
||||||
if self.check(&Token::Semicolon) { // Check for ';' for empty condition
|
if self.check(&Token::Semicolon) {
|
||||||
self.advance(); // Consume the ';'
|
self.advance();
|
||||||
condition = Expression::Literal(Literal::Boolean(true)); // Default to true
|
condition = Expression::Literal(Literal::Boolean(true));
|
||||||
} else {
|
} else {
|
||||||
condition = self.parse_expression()?;
|
condition = self.parse_expression()?;
|
||||||
self.skip_comments(); // Skip comments before the semicolon separator
|
self.skip_comments();
|
||||||
if !self.match_token(Token::Semicolon) {
|
if !self.match_token(Token::Semicolon) {
|
||||||
eprintln!("Error: Expected ';' after for-loop condition.");
|
eprintln!("Error: Expected ';' after for-loop condition.");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Increment Statement
|
self.skip_comments();
|
||||||
self.skip_comments(); // <--- CRUCIAL FIX: Skip comments before increment part
|
|
||||||
let increment: Box<Statement> = if self.check(&Token::RightParen) {
|
let increment: Box<Statement> = if self.check(&Token::RightParen) {
|
||||||
Box::new(Statement::ExpressionStatement(Expression::Literal(Literal::Nil)))
|
Box::new(Statement::ExpressionStatement(Expression::Literal(Literal::Nil)))
|
||||||
} else {
|
} else {
|
||||||
@ -541,19 +538,17 @@ impl Parser {
|
|||||||
Box::new(Statement::ExpressionStatement(incr_expr))
|
Box::new(Statement::ExpressionStatement(incr_expr))
|
||||||
};
|
};
|
||||||
|
|
||||||
self.skip_comments(); // Skip comments before ')'
|
self.skip_comments();
|
||||||
if !self.match_token(Token::RightParen) {
|
if !self.match_token(Token::RightParen) {
|
||||||
eprintln!("Error: Expected ')' after for-loop clauses.");
|
eprintln!("Error: Expected ')' after for-loop clauses.");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Body Statement (must be a block)
|
self.skip_comments();
|
||||||
self.skip_comments(); // Skip comments before '{'
|
|
||||||
if !self.check(&Token::LeftBrace) {
|
if !self.check(&Token::LeftBrace) {
|
||||||
eprintln!("Error: Expected '{{' for for-loop body.");
|
eprintln!("Error: Expected '{{' for for-loop body.");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// parse_statement() already calls skip_comments() at its beginning
|
|
||||||
let body = Box::new(self.parse_statement()?);
|
let body = Box::new(self.parse_statement()?);
|
||||||
|
|
||||||
Some(Statement::ForStatement(initializer, condition, increment, body))
|
Some(Statement::ForStatement(initializer, condition, increment, body))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user