mirror of
https://github.com/urinalcaketopper/fddl.git
synced 2025-06-05 21:14: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 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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user