wip
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
// Test script to verify error positioning
|
// Test script to verify error positioning
|
||||||
var a = 5;
|
var a := 5;
|
||||||
var b = "hello";
|
var b := "hello";
|
||||||
print 0;
|
print 0;
|
||||||
var result = a + b; // This should cause a type error
|
var result := a + b; // This should cause a type error
|
||||||
print 1;
|
print 1;
|
||||||
43 + "hello"; // This should cause a type error
|
43 + "hello"; // This should cause a type error
|
||||||
print result;
|
print result;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
// Test file for source slice tracking
|
// Test file for source slice tracking
|
||||||
var x = 42;
|
var x := 42;
|
||||||
print x;
|
print x;
|
||||||
|
|
||||||
if x > 0 then do
|
if x > 0 then do
|
||||||
print "positive";
|
print "positive";
|
||||||
var y = x + 1;
|
var y := x + 1;
|
||||||
end
|
end
|
||||||
elif x == 0 then do
|
elif x == 0 then do
|
||||||
print "zero";
|
print "zero";
|
||||||
@@ -19,7 +19,7 @@ while x > 0 do
|
|||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
var z = 10;
|
var z := 10;
|
||||||
print z * 2;
|
print z * 2;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -271,10 +271,17 @@ pub enum BaseValue {
|
|||||||
Nil,
|
Nil,
|
||||||
Function(LoxFunction),
|
Function(LoxFunction),
|
||||||
NativeFunction(NativeFunction),
|
NativeFunction(NativeFunction),
|
||||||
Return {
|
}
|
||||||
value: Box<BaseValue>,
|
|
||||||
labels: Vec<String>,
|
struct ReturnValue {
|
||||||
},
|
label: Vec<String>,
|
||||||
|
value: BaseValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReturnValue {
|
||||||
|
pub fn new(label: Vec<String>, value: BaseValue) -> Self {
|
||||||
|
Self { label, value }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseValue {
|
impl BaseValue {
|
||||||
@@ -296,9 +303,6 @@ impl Display for BaseValue {
|
|||||||
BaseValue::Nil => write!(f, "nil"),
|
BaseValue::Nil => write!(f, "nil"),
|
||||||
BaseValue::Function(..) => write!(f, "<fn lox function>"),
|
BaseValue::Function(..) => write!(f, "<fn lox function>"),
|
||||||
BaseValue::NativeFunction(..) => write!(f, "<native native function>"),
|
BaseValue::NativeFunction(..) => write!(f, "<native native function>"),
|
||||||
BaseValue::Return { labels, .. } => {
|
|
||||||
write!(f, "<return value [return:{}]>", labels.join(":"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use crate::frontend::source_registry::{SourceRegistry, SourceSlice};
|
use crate::{
|
||||||
use std::fmt;
|
common::base_value::BaseValue,
|
||||||
|
frontend::source_registry::{SourceRegistry, SourceSlice},
|
||||||
|
};
|
||||||
|
use std::{error::Error, fmt};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LoxError {
|
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<T> = Result<T, LoxError>;
|
pub type LoxResult<T> = Result<T, LoxError>;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
io::ErrorKind,
|
io::ErrorKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::lox_result::{io_error, LoxError, LoxResult};
|
use crate::common::lox_result::{io_error, LoxResult};
|
||||||
|
|
||||||
pub struct SourceRegistry {
|
pub struct SourceRegistry {
|
||||||
pub sources: Vec<SourceFile>,
|
pub sources: Vec<SourceFile>,
|
||||||
|
|||||||
@@ -263,10 +263,7 @@ impl PrettyPrint for Stmt {
|
|||||||
let indent = ctx.indent();
|
let indent = ctx.indent();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Stmt::Expression {
|
Stmt::Expression { expression, .. } => {
|
||||||
expression,
|
|
||||||
return_value,
|
|
||||||
} => {
|
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let expr_str =
|
let expr_str =
|
||||||
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||||||
@@ -279,10 +276,7 @@ impl PrettyPrint for Stmt {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Print {
|
Stmt::Print { expression, .. } => {
|
||||||
expression,
|
|
||||||
return_value,
|
|
||||||
} => {
|
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let expr_str =
|
let expr_str =
|
||||||
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||||||
@@ -296,9 +290,7 @@ impl PrettyPrint for Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::VarDeclaration {
|
Stmt::VarDeclaration {
|
||||||
name,
|
name, initializer, ..
|
||||||
initializer,
|
|
||||||
return_value,
|
|
||||||
} => {
|
} => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
match initializer {
|
match initializer {
|
||||||
@@ -325,11 +317,7 @@ impl PrettyPrint for Stmt {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::VarAssigment {
|
Stmt::VarAssigment { name, value, .. } => {
|
||||||
name,
|
|
||||||
value,
|
|
||||||
return_value,
|
|
||||||
} => {
|
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let value_str =
|
let value_str =
|
||||||
pretty_print_with_config(value.as_ref(), &PrettyConfig::compact());
|
pretty_print_with_config(value.as_ref(), &PrettyConfig::compact());
|
||||||
@@ -343,10 +331,7 @@ impl PrettyPrint for Stmt {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Return {
|
Stmt::Return { expression, .. } => {
|
||||||
expression,
|
|
||||||
return_value,
|
|
||||||
} => {
|
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let expr_str =
|
let expr_str =
|
||||||
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
pretty_print_with_config(expression.as_ref(), &PrettyConfig::compact());
|
||||||
@@ -360,9 +345,7 @@ impl PrettyPrint for Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Block {
|
Stmt::Block {
|
||||||
statements,
|
statements, label, ..
|
||||||
label,
|
|
||||||
return_value,
|
|
||||||
} => {
|
} => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
write!(f, "{{ ... [{}] ({} statements) }}", label, statements.len())
|
write!(f, "{{ ... [{}] ({} statements) }}", label, statements.len())
|
||||||
@@ -387,7 +370,7 @@ impl PrettyPrint for Stmt {
|
|||||||
then_branch,
|
then_branch,
|
||||||
elif_branch,
|
elif_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
return_value,
|
..
|
||||||
} => {
|
} => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let cond_str =
|
let cond_str =
|
||||||
@@ -438,9 +421,7 @@ impl PrettyPrint for Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::While {
|
Stmt::While {
|
||||||
condition,
|
condition, body, ..
|
||||||
body,
|
|
||||||
return_value,
|
|
||||||
} => {
|
} => {
|
||||||
write!(f, "{}while (", ctx.child_context(true).indent())?;
|
write!(f, "{}while (", ctx.child_context(true).indent())?;
|
||||||
condition.pretty_print(&ctx.child_context(true), f)?;
|
condition.pretty_print(&ctx.child_context(true), f)?;
|
||||||
@@ -453,7 +434,7 @@ impl PrettyPrint for Stmt {
|
|||||||
condition,
|
condition,
|
||||||
increment,
|
increment,
|
||||||
body,
|
body,
|
||||||
return_value,
|
..
|
||||||
} => {
|
} => {
|
||||||
write!(f, "{}for (", ctx.child_context(true).indent())?;
|
write!(f, "{}for (", ctx.child_context(true).indent())?;
|
||||||
variable.pretty_print(&ctx.child_context(true), f)?;
|
variable.pretty_print(&ctx.child_context(true), f)?;
|
||||||
|
|||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user