Move example code to examples directory

The commit moves example and test files to a dedicated examples
directory and updates the code for function and variable handling. The
subject line captures the primary purpose of the move operation.

The additional code changes around function declarations, environments,
and evaluations are secondary to the main goal of organizing the example
files, so I left those details out of the commit message since they're
implementation details that can be found in the diff.
This commit is contained in:
Giulio Agostini
2025-10-06 18:52:32 +02:00
parent 827349cbad
commit fa2e9178fb
16 changed files with 769 additions and 278 deletions
+175 -70
View File
@@ -3,9 +3,10 @@ use crate::{
frontend::{
ast::{AstNode, AstNodeKind, Expr, Stmt},
source_registry::SourceSlice,
tokens::{LiteralValue, TokenType},
tokens::{LiteralValue, NativeFunction, TokenType},
},
result::{LoxError, LoxResult},
logging::display_ast::PrettyPrintExt,
result::{runtime_error, LoxError, LoxResult},
};
use std::{
fmt::{Debug, Display},
@@ -174,15 +175,15 @@ pub struct Interpreter {
}
pub trait EvaluateInterpreter<T> {
fn interpret(&mut self, stmt: T) -> LoxResult<LiteralValue>;
fn evaluate(&mut self, stmt: T) -> LoxResult<LiteralValue>;
}
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>> for Interpreter
where
Interpreter: EvaluateInterpreter<R>,
{
fn interpret(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
match self.interpret(stmt.node.clone()) {
fn evaluate(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
match self.evaluate(stmt.node.clone()) {
Ok(value) => Ok(value),
Err(err) => Err(LoxError::RuntimeError {
source_slice: stmt.source_slice,
@@ -194,40 +195,109 @@ where
// Direct Expr evaluation to avoid infinite recursion
impl EvaluateInterpreter<Expr> for Interpreter {
fn interpret(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
fn evaluate(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
match expr {
Expr::Literal { value } => Ok(value),
Expr::Variable { name } => self.enviorment.get(&name),
Expr::Identifier { name } => self.enviorment.get(&name),
Expr::Binary {
left,
operator,
right,
} => {
let left_val = self.interpret(*left)?;
let right_val = self.interpret(*right)?;
let left_val = self.evaluate(*left)?;
let right_val = self.evaluate(*right)?;
self.evaluate_binary(left_val, operator, right_val)
}
Expr::Unary { operator, operand } => {
let operand_val = self.interpret(*operand)?;
let operand_val = self.evaluate(*operand)?;
self.evaluate_unary(operator, operand_val)
}
Expr::Grouping { expression } => self.interpret(*expression),
Expr::Grouping { expression } => self.evaluate(*expression),
Expr::Call { callee, arguments } => self.evaluate_call(callee, arguments),
}
}
}
impl EvaluateInterpreter<AstNode<Stmt>> for Interpreter {
fn interpret(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
fn evaluate(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
let stmt = node.node;
let _source_slice = node.source_slice;
self.interpret_stmt_inner(stmt)
let result = self.interpret_stmt_inner(stmt);
match result {
Ok(value) => Ok(value),
Err(LoxError::RuntimeError {
message,
source_slice,
}) if source_slice == SourceSlice::default() => {
runtime_error(node.source_slice.clone(), message)
}
Err(err) => Err(err),
}
}
}
impl Interpreter {
pub fn new() -> Self {
Self {
enviorment: EnvironmentStack::new(),
let mut env = EnvironmentStack::new();
let _ = env.declare(
"clock".to_string(),
LiteralValue::NativeFunction(NativeFunction::new(0, |_args| {
LiteralValue::Number(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as f64,
)
})),
);
Self { enviorment: env }
}
fn evaluate_call(
&mut self,
callee: Box<AstNode<Expr>>,
arguments: Vec<AstNode<Expr>>,
) -> LoxResult<LiteralValue> {
let source_slice = callee.source_slice.clone();
// Estrai il nome della variabile se il callee è un identificatore
let function_name = match &callee.node {
Expr::Identifier { name } => Some(name.clone()),
_ => None,
};
let function = if let Some(name) = function_name {
// Se abbiamo un nome di variabile, ottieni la funzione dall'ambiente
self.enviorment.get(&name)?
} else {
// Altrimenti valuta l'espressione callee (per casi più complessi)
self.evaluate(*callee)?
};
if !function.is_callable() {
return runtime_error(source_slice, "Can only call functions");
}
let evaluated_arguments = arguments
.iter()
.map(|arg| self.evaluate(arg.clone()))
.collect::<Result<Vec<LiteralValue>, LoxError>>()?;
match function {
LiteralValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)),
LiteralValue::Function(func) => {
func.parameters
.iter()
.enumerate()
.map(|(index, par)| {
let value = evaluated_arguments.get(index).unwrap();
self.enviorment.declare(par.0.clone(), value.clone())
})
.collect::<LoxResult<Vec<LiteralValue>>>()?;
self.evaluate(func.body)
}
_ => runtime_error(
source_slice.clone(),
"This is callable, but apparently is not implemented",
),
}
}
@@ -248,6 +318,8 @@ impl Interpreter {
TokenType::LessEqual => Ok(LiteralValue::Boolean(left <= right)),
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)),
TokenType::BangEqual => Ok(LiteralValue::Boolean(left != right)),
TokenType::And => Ok(LiteralValue::Boolean(left.is_truthy() && right.is_truthy())),
TokenType::Or => Ok(LiteralValue::Boolean(left.is_truthy() || right.is_truthy())),
_ => Err(error(format!(
"Unsupported binary operator: {:?}",
operator
@@ -269,75 +341,108 @@ impl Interpreter {
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
match stmt {
Stmt::Expression { expression } => self.interpret(*expression),
Stmt::Expression { expression } => self.evaluate(*expression),
Stmt::Print { expression } => {
let value = self.interpret(*expression)?;
let value = self.evaluate(*expression)?;
println!("print interpreter: \t{}", value);
Ok(LiteralValue::Nil)
}
Stmt::Block { statements } => self.evaluate_block(*statements),
Stmt::Stmt { expression } => self.interpret(*expression.clone()),
Stmt::Return { expression } => self.interpret(*expression),
Stmt::Var { name, initializer } => {
let value = if let Some(expr) = initializer {
self.interpret(*expr)?
} else {
LiteralValue::Nil
Stmt::Stmt { expression } => self.evaluate(*expression.clone()),
Stmt::Return { expression } => self.evaluate(*expression),
Stmt::VarDeclaration { name, initializer } => {
let value = match initializer {
Some(expr_node) => match &expr_node.node {
Expr::Literal { value } => {
if value.is_callable() {
value.clone()
} else {
value.clone()
}
}
_ => self.evaluate(*expr_node)?,
},
None => LiteralValue::Nil,
};
self.enviorment.set(name.clone(), value);
return Ok(LiteralValue::Nil);
self.enviorment.declare(name.clone(), value)
}
Stmt::Assign { name, value } => {
let result = self.interpret(*value)?;
self.enviorment.set(name.clone(), result);
Ok(LiteralValue::Nil)
Stmt::VarAssigment { name, value } => {
let result = self.evaluate(*value)?;
self.enviorment.set(name.clone(), result)
}
Stmt::If {
condition,
then_branch,
elif_branch,
else_branch,
} => {
let condition = self.interpret(*condition)?;
match condition {
LiteralValue::Boolean(true) => self.interpret(*then_branch),
LiteralValue::Boolean(false) => {
for (elif_condition, elif_then_branch) in elif_branch {
let condition = self.interpret(*elif_condition)?;
match condition {
LiteralValue::Boolean(true) => {
return self.interpret(*elif_then_branch);
}
LiteralValue::Boolean(false) => continue,
_ => {
return Err(LoxError::TypeMismatch {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
expected: "boolean".to_string(),
found: condition.to_string(),
});
}
};
}
if let Some(else_block) = else_branch {
self.interpret(*else_block)
} else {
Ok(LiteralValue::Nil)
}
}
_ => Err(LoxError::TypeMismatch {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
expected: "boolean".to_string(),
found: condition.to_string(),
}),
}
}
} => self.evaluate_if(condition, then_branch, elif_branch, else_branch),
Stmt::While { condition, body } => {
while self.interpret(*condition.clone())?.is_truthy() {
self.interpret(*body.clone())?;
while self.evaluate(*condition.clone())?.is_truthy() {
self.evaluate(*body.clone())?;
}
Ok(LiteralValue::Nil)
}
Stmt::For { .. } => todo!(),
Stmt::For {
variable,
condition,
increment,
body,
} => {
self.enviorment.push_new_scope();
let source_slice = variable.source_slice.clone();
let val = self.evaluate(*variable)?;
if !matches!(val, LiteralValue::Number(..)) {
return runtime_error(source_slice, "Expected number literal");
}
let mut ret = LiteralValue::Nil;
while self.evaluate(*condition.clone())?.is_truthy() {
ret = self.evaluate(*body.clone())?;
self.evaluate(*increment.clone())?;
}
Ok(ret)
}
}
}
fn evaluate_if(
&mut self,
condition: Box<AstNode<Expr>>,
then_branch: Box<AstNode<Stmt>>,
elif_branch: Vec<(Box<AstNode<Expr>>, Box<AstNode<Stmt>>)>,
else_branch: Option<Box<AstNode<Stmt>>>,
) -> LoxResult<LiteralValue> {
let condition = self.evaluate(*condition)?;
match condition {
LiteralValue::Boolean(true) => self.evaluate(*then_branch),
LiteralValue::Boolean(false) => {
for (elif_condition, elif_then_branch) in elif_branch {
let condition = self.evaluate(*elif_condition)?;
match condition {
LiteralValue::Boolean(true) => {
return self.evaluate(*elif_then_branch);
}
LiteralValue::Boolean(false) => continue,
_ => {
return Err(LoxError::TypeMismatch {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
expected: "boolean".to_string(),
found: condition.to_string(),
});
}
};
}
if let Some(else_block) = else_branch {
self.evaluate(*else_block)
} else {
Ok(LiteralValue::Nil)
}
}
_ => Err(LoxError::TypeMismatch {
source_slice: SourceSlice::default(), // todo change this to the actual source slice
expected: "boolean".to_string(),
found: condition.to_string(),
}),
}
}
@@ -357,13 +462,13 @@ impl Interpreter {
// Ora elements è sempre disponibile
for statement in elements.iter() {
self.interpret((*statement).clone())?;
self.evaluate((*statement).clone())?;
}
// Gestisci l'espressione finale se presente
match final_expr {
Some(expr) => {
let res = self.interpret(*expr.clone());
let res = self.evaluate(*expr.clone());
self.enviorment.pop_scope();
res
}