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:
+175
-70
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user