From ee279425f2272006b49951af7b06bb566ceb7a30 Mon Sep 17 00:00:00 2001 From: Giulio Agostini Date: Wed, 12 Nov 2025 20:45:35 +0100 Subject: [PATCH] wip --- examples/{test_error.lox => fail_error.lox} | 6 +- examples/{example.lox => test_example.lox} | 0 ...nction_call.lox => test_function_call.lox} | 0 examples/test_source_tracking.lox | 6 +- src/common/base_value.rs | 18 +- src/common/lox_result.rs | 9 +- src/frontend/source_registry.rs | 2 +- src/logging/display_ast.rs | 37 +--- tests/test_example.rs | 207 ++++++++++++++++++ 9 files changed, 240 insertions(+), 45 deletions(-) rename examples/{test_error.lox => fail_error.lox} (67%) rename examples/{example.lox => test_example.lox} (100%) rename examples/{function_call.lox => test_function_call.lox} (100%) diff --git a/examples/test_error.lox b/examples/fail_error.lox similarity index 67% rename from examples/test_error.lox rename to examples/fail_error.lox index 698ea42..101823b 100644 --- a/examples/test_error.lox +++ b/examples/fail_error.lox @@ -1,8 +1,8 @@ // Test script to verify error positioning -var a = 5; -var b = "hello"; +var a := 5; +var b := "hello"; print 0; -var result = a + b; // This should cause a type error +var result := a + b; // This should cause a type error print 1; 43 + "hello"; // This should cause a type error print result; diff --git a/examples/example.lox b/examples/test_example.lox similarity index 100% rename from examples/example.lox rename to examples/test_example.lox diff --git a/examples/function_call.lox b/examples/test_function_call.lox similarity index 100% rename from examples/function_call.lox rename to examples/test_function_call.lox diff --git a/examples/test_source_tracking.lox b/examples/test_source_tracking.lox index 5fad933..5f879de 100644 --- a/examples/test_source_tracking.lox +++ b/examples/test_source_tracking.lox @@ -1,10 +1,10 @@ // Test file for source slice tracking -var x = 42; +var x := 42; print x; if x > 0 then do print "positive"; - var y = x + 1; + var y := x + 1; end elif x == 0 then do print "zero"; @@ -19,7 +19,7 @@ while x > 0 do end do - var z = 10; + var z := 10; print z * 2; end diff --git a/src/common/base_value.rs b/src/common/base_value.rs index 98191f9..6eb2976 100644 --- a/src/common/base_value.rs +++ b/src/common/base_value.rs @@ -271,10 +271,17 @@ pub enum BaseValue { Nil, Function(LoxFunction), NativeFunction(NativeFunction), - Return { - value: Box, - labels: Vec, - }, +} + +struct ReturnValue { + label: Vec, + value: BaseValue, +} + +impl ReturnValue { + pub fn new(label: Vec, value: BaseValue) -> Self { + Self { label, value } + } } impl BaseValue { @@ -296,9 +303,6 @@ impl Display for BaseValue { BaseValue::Nil => write!(f, "nil"), BaseValue::Function(..) => write!(f, ""), BaseValue::NativeFunction(..) => write!(f, ""), - BaseValue::Return { labels, .. } => { - write!(f, "", labels.join(":")) - } } } } diff --git a/src/common/lox_result.rs b/src/common/lox_result.rs index 9f0ec6f..8ed8f7d 100644 --- a/src/common/lox_result.rs +++ b/src/common/lox_result.rs @@ -1,5 +1,8 @@ -use crate::frontend::source_registry::{SourceRegistry, SourceSlice}; -use std::fmt; +use crate::{ + common::base_value::BaseValue, + frontend::source_registry::{SourceRegistry, SourceSlice}, +}; +use std::{error::Error, fmt}; #[derive(Debug, Clone)] pub enum LoxError { @@ -397,7 +400,7 @@ impl fmt::Display for LoxError { } } -impl std::error::Error for LoxError {} +impl Error for LoxError {} pub type LoxResult = Result; diff --git a/src/frontend/source_registry.rs b/src/frontend/source_registry.rs index 3bdaa25..27ae1ef 100644 --- a/src/frontend/source_registry.rs +++ b/src/frontend/source_registry.rs @@ -4,7 +4,7 @@ use std::{ io::ErrorKind, }; -use crate::common::lox_result::{io_error, LoxError, LoxResult}; +use crate::common::lox_result::{io_error, LoxResult}; pub struct SourceRegistry { pub sources: Vec, diff --git a/src/logging/display_ast.rs b/src/logging/display_ast.rs index 8f93d95..1418c50 100644 --- a/src/logging/display_ast.rs +++ b/src/logging/display_ast.rs @@ -263,10 +263,7 @@ impl PrettyPrint for Stmt { let indent = ctx.indent(); match self { - Stmt::Expression { - expression, - return_value, - } => { + Stmt::Expression { expression, .. } => { if ctx.config.compact { let expr_str = pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact()); @@ -279,10 +276,7 @@ impl PrettyPrint for Stmt { write!(f, "{}}}", indent) } } - Stmt::Print { - expression, - return_value, - } => { + Stmt::Print { expression, .. } => { if ctx.config.compact { let expr_str = pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact()); @@ -296,9 +290,7 @@ impl PrettyPrint for Stmt { } } Stmt::VarDeclaration { - name, - initializer, - return_value, + name, initializer, .. } => { if ctx.config.compact { match initializer { @@ -325,11 +317,7 @@ impl PrettyPrint for Stmt { write!(f, "{}}}", indent) } } - Stmt::VarAssigment { - name, - value, - return_value, - } => { + Stmt::VarAssigment { name, value, .. } => { if ctx.config.compact { let value_str = pretty_print_with_config(value.as_ref(), &PrettyConfig::compact()); @@ -343,10 +331,7 @@ impl PrettyPrint for Stmt { write!(f, "{}}}", indent) } } - Stmt::Return { - expression, - return_value, - } => { + Stmt::Return { expression, .. } => { if ctx.config.compact { let expr_str = pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact()); @@ -360,9 +345,7 @@ impl PrettyPrint for Stmt { } } Stmt::Block { - statements, - label, - return_value, + statements, label, .. } => { if ctx.config.compact { write!(f, "{{ ... [{}] ({} statements) }}", label, statements.len()) @@ -387,7 +370,7 @@ impl PrettyPrint for Stmt { then_branch, elif_branch, else_branch, - return_value, + .. } => { if ctx.config.compact { let cond_str = @@ -438,9 +421,7 @@ impl PrettyPrint for Stmt { } } Stmt::While { - condition, - body, - return_value, + condition, body, .. } => { write!(f, "{}while (", ctx.child_context(true).indent())?; condition.pretty_print(&ctx.child_context(true), f)?; @@ -453,7 +434,7 @@ impl PrettyPrint for Stmt { condition, increment, body, - return_value, + .. } => { write!(f, "{}for (", ctx.child_context(true).indent())?; variable.pretty_print(&ctx.child_context(true), f)?; diff --git a/tests/test_example.rs b/tests/test_example.rs index e69de29..8dc261f 100644 --- a/tests/test_example.rs +++ b/tests/test_example.rs @@ -0,0 +1,207 @@ +use rlox::backend::interpreter::{EvaluateInterpreter, Interpreter}; +use rlox::frontend::lexer::Lexer; +use rlox::frontend::parser::Parser; +use rlox::frontend::source_registry::SourceRegistry; +use std::fs; +use std::path::Path; + +#[test] +fn test_all_examples() { + let examples_dir = Path::new("examples"); + + // Trova tutti i file .lox nella directory examples + let entries = fs::read_dir(examples_dir).expect("Failed to read examples directory"); + + let mut test_files_passed = Vec::new(); + let mut test_files_failed = Vec::new(); + let mut fail_files_passed = Vec::new(); + let mut fail_files_failed = Vec::new(); + + for entry in entries { + let entry = entry.expect("Failed to read directory entry"); + let path = entry.path(); + + // Controlla se รจ un file .lox + if path.extension().and_then(|s| s.to_str()) == Some("lox") { + let file_name = path.file_name().unwrap().to_str().unwrap().to_string(); + + // Ignora i file che non iniziano con test_ o fail_ + if !file_name.starts_with("test_") && !file_name.starts_with("fail_") { + continue; + } + + // Leggi il contenuto del file + let source = match fs::read_to_string(&path) { + Ok(content) => content, + Err(e) => { + if file_name.starts_with("test_") { + test_files_failed.push((file_name, format!("Failed to read file: {}", e))); + } else { + fail_files_failed.push((file_name, format!("Failed to read file: {}", e))); + } + continue; + } + }; + + // Esegui il file e categorizza il risultato + match run_lox_file(source, &file_name) { + Ok(_) => { + if file_name.starts_with("test_") { + test_files_passed.push(file_name); + } else { + fail_files_passed.push(file_name); + } + } + Err(error) => { + if file_name.starts_with("test_") { + test_files_failed.push((file_name, error)); + } else { + fail_files_failed.push((file_name, error)); + } + } + } + } + } + + // Stampa i risultati + println!("\n========== TEST RESULTS =========="); + + // File test_ (dovrebbero passare) + if !test_files_passed.is_empty() || !test_files_failed.is_empty() { + println!("\n๐Ÿ“ TEST FILES (should pass):"); + if !test_files_passed.is_empty() { + println!(" โœ… Passed ({}):", test_files_passed.len()); + for file in &test_files_passed { + println!(" - {}", file); + } + } + if !test_files_failed.is_empty() { + println!(" โŒ Failed ({}):", test_files_failed.len()); + for (file, error) in &test_files_failed { + println!("\n - {}", file); + println!("{}", error); + } + } + } + + // File fail_ (dovrebbero fallire) + if !fail_files_failed.is_empty() || !fail_files_passed.is_empty() { + println!("\n๐Ÿšซ FAIL FILES (should fail):"); + if !fail_files_failed.is_empty() { + println!(" โœ… Failed as expected ({}):", fail_files_failed.len()); + for (file, error) in &fail_files_failed { + println!("\n - {}", file); + println!("{}", error); + } + } + if !fail_files_passed.is_empty() { + println!(" โŒ Passed unexpectedly ({}):", fail_files_passed.len()); + for file in &fail_files_passed { + println!(" - {} (should have failed but passed!)", file); + } + } + } + + println!("\n=================================="); + + // Riepilogo + let total_test_files = test_files_passed.len() + test_files_failed.len(); + let total_fail_files = fail_files_passed.len() + fail_files_failed.len(); + let total_files = total_test_files + total_fail_files; + + println!("Summary:"); + if total_test_files > 0 { + println!( + " test_ files: {}/{} passed", + test_files_passed.len(), + total_test_files + ); + } + if total_fail_files > 0 { + println!( + " fail_ files: {}/{} failed (as expected)", + fail_files_failed.len(), + total_fail_files + ); + } + println!(" Total files tested: {}", total_files); + + // Assicurati che almeno un file sia stato testato + assert!( + total_files > 0, + "No test_ or fail_ .lox files found in examples directory" + ); + + // Asserzioni per far fallire il test se le aspettative non sono rispettate + if !test_files_failed.is_empty() { + println!("\nโŒ TEST FAILURE: The following test_ files failed but should have passed:"); + for (file, _) in &test_files_failed { + println!(" - {}", file); + } + panic!( + "{} test_ files failed unexpectedly", + test_files_failed.len() + ); + } + + if !fail_files_passed.is_empty() { + println!("\nโŒ TEST FAILURE: The following fail_ files passed but should have failed:"); + for file in &fail_files_passed { + println!(" - {}", file); + } + panic!( + "{} fail_ files passed unexpectedly", + fail_files_passed.len() + ); + } + + println!( + "\nโœ… All {} test/fail files behaved as expected!", + total_files + ); +} + +fn run_lox_file(source: String, _filename: &str) -> Result<(), String> { + // Crea un source registry e mantienilo per il pretty printing degli errori + let mut source_registry = SourceRegistry::new(); + + // Aggiungi il source con il nome del file corretto + let source_id = match source_registry.add_source_string(source.clone()) { + Ok(id) => id, + Err(e) => return Err(format!("Failed to add source: {}", e)), + }; + + // Tokenizza + let source_content = source_registry.get_by_id(source_id).content.clone(); + let mut lexer = Lexer::new(source_content, source_id); + let tokens = match lexer.scans_tokens() { + Ok(tokens) => tokens, + Err(err) => { + // Usa il pretty printing con source context + return Err(err.display_with_source(&source_registry)); + } + }; + + // Parsa + let ast = match Parser::new(tokens).parse() { + Ok(ast) => ast, + Err(err) => { + // Usa il pretty printing con source context + return Err(err.display_with_source(&source_registry)); + } + }; + + // Esegui + let mut interpreter = Interpreter::new(); + for stmt in ast.iter() { + match interpreter.evaluate(stmt.clone()) { + Ok(_) => {} + Err(err) => { + // Usa il pretty printing con source context + return Err(err.display_with_source(&source_registry)); + } + } + } + + Ok(()) +}