2025-10-03 19:07:12 +02:00
|
|
|
mod backend;
|
2025-10-07 14:28:24 +02:00
|
|
|
mod common;
|
2025-10-03 19:07:12 +02:00
|
|
|
mod frontend;
|
|
|
|
|
mod logging;
|
2025-10-10 10:18:02 +02:00
|
|
|
mod middleend;
|
2025-10-03 19:07:12 +02:00
|
|
|
|
|
|
|
|
use crate::{
|
2025-10-06 18:52:32 +02:00
|
|
|
backend::interpreter::{EvaluateInterpreter, Interpreter},
|
2025-10-07 14:28:24 +02:00
|
|
|
common::{
|
|
|
|
|
ast::{AstNode, Stmt},
|
|
|
|
|
lox_result::{LoxError, LoxResult},
|
|
|
|
|
},
|
2025-10-03 19:07:12 +02:00
|
|
|
frontend::{
|
|
|
|
|
lexer::Lexer,
|
|
|
|
|
parser::Parser,
|
2025-10-06 18:52:32 +02:00
|
|
|
source_registry::{SourceId, SourceRegistry},
|
2025-10-03 19:07:12 +02:00
|
|
|
},
|
|
|
|
|
};
|
2025-10-04 21:05:00 +02:00
|
|
|
use std::env;
|
2025-10-03 19:07:12 +02:00
|
|
|
use std::fs;
|
|
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
enum ExecutionStage {
|
|
|
|
|
Tokens,
|
|
|
|
|
Ast,
|
|
|
|
|
Full,
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 19:07:12 +02:00
|
|
|
fn main() -> LoxResult<()> {
|
|
|
|
|
let args: Vec<String> = env::args().collect();
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
let (stage, file_path, debug) = parse_args(&args);
|
|
|
|
|
let mut lox = LoxInterpreter::new(debug);
|
2025-10-06 18:52:32 +02:00
|
|
|
|
|
|
|
|
let _ = match file_path {
|
|
|
|
|
Some(path) => lox.run_file(&path, stage),
|
|
|
|
|
None => lox.run_prompt(stage),
|
2025-10-04 19:02:33 +02:00
|
|
|
};
|
2025-10-03 19:07:12 +02:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
fn parse_args(args: &[String]) -> (ExecutionStage, Option<String>, bool) {
|
2025-10-06 18:52:32 +02:00
|
|
|
if args.len() == 1 {
|
|
|
|
|
// Solo il nome del programma: modalità interattiva completa
|
2025-10-07 14:28:24 +02:00
|
|
|
(ExecutionStage::Full, None, false)
|
2025-10-06 18:52:32 +02:00
|
|
|
} else if args.len() == 2 {
|
|
|
|
|
// Un argomento: potrebbe essere file o flag
|
|
|
|
|
let arg = &args[1];
|
2025-10-07 14:28:24 +02:00
|
|
|
if arg == "--tokens" || arg == "--ast" || arg == "--full" || arg == "--debug" {
|
2025-10-06 18:52:32 +02:00
|
|
|
// Flag senza file: modalità interattiva
|
|
|
|
|
let stage = match arg.as_str() {
|
|
|
|
|
"--tokens" => ExecutionStage::Tokens,
|
|
|
|
|
"--ast" => ExecutionStage::Ast,
|
|
|
|
|
"--full" => ExecutionStage::Full,
|
2025-10-07 14:28:24 +02:00
|
|
|
"--debug" => ExecutionStage::Full,
|
2025-10-06 18:52:32 +02:00
|
|
|
_ => ExecutionStage::Full,
|
|
|
|
|
};
|
2025-10-07 14:28:24 +02:00
|
|
|
let debug = arg == "--debug";
|
|
|
|
|
(stage, None, debug)
|
2025-10-06 18:52:32 +02:00
|
|
|
} else {
|
|
|
|
|
// File senza flag: esecuzione completa del file
|
2025-10-07 14:28:24 +02:00
|
|
|
(ExecutionStage::Full, Some(arg.clone()), false)
|
2025-10-06 18:52:32 +02:00
|
|
|
}
|
|
|
|
|
} else if args.len() == 3 {
|
2025-10-07 14:28:24 +02:00
|
|
|
// Due argomenti: potrebbero essere flag + file o due flag
|
|
|
|
|
let mut debug = false;
|
|
|
|
|
let mut stage = ExecutionStage::Full;
|
|
|
|
|
let mut file_path = None;
|
|
|
|
|
|
|
|
|
|
for arg in &args[1..3] {
|
|
|
|
|
if arg == "--debug" {
|
|
|
|
|
debug = true;
|
|
|
|
|
} else if arg == "--tokens" || arg == "--ast" || arg == "--full" {
|
|
|
|
|
stage = match arg.as_str() {
|
|
|
|
|
"--tokens" => ExecutionStage::Tokens,
|
|
|
|
|
"--ast" => ExecutionStage::Ast,
|
|
|
|
|
"--full" => ExecutionStage::Full,
|
|
|
|
|
_ => ExecutionStage::Full,
|
|
|
|
|
};
|
|
|
|
|
} else if !arg.starts_with("--") {
|
|
|
|
|
file_path = Some(arg.clone());
|
|
|
|
|
} else {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"Unknown flag: {}. Use --tokens, --ast, --full, or --debug",
|
|
|
|
|
arg
|
|
|
|
|
);
|
|
|
|
|
eprintln!(
|
|
|
|
|
"Usage: {} [--tokens|--ast|--full] [--debug] [script]",
|
|
|
|
|
args[0]
|
|
|
|
|
);
|
2025-10-06 18:52:32 +02:00
|
|
|
std::process::exit(64);
|
|
|
|
|
}
|
2025-10-07 14:28:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(stage, file_path, debug)
|
|
|
|
|
} else if args.len() == 4 {
|
|
|
|
|
// Tre argomenti: flag + flag + file
|
|
|
|
|
let mut debug = false;
|
|
|
|
|
let mut stage = ExecutionStage::Full;
|
|
|
|
|
let mut file_path = None;
|
|
|
|
|
|
|
|
|
|
for arg in &args[1..4] {
|
|
|
|
|
if arg == "--debug" {
|
|
|
|
|
debug = true;
|
|
|
|
|
} else if arg == "--tokens" || arg == "--ast" || arg == "--full" {
|
|
|
|
|
stage = match arg.as_str() {
|
|
|
|
|
"--tokens" => ExecutionStage::Tokens,
|
|
|
|
|
"--ast" => ExecutionStage::Ast,
|
|
|
|
|
"--full" => ExecutionStage::Full,
|
|
|
|
|
_ => ExecutionStage::Full,
|
|
|
|
|
};
|
|
|
|
|
} else if !arg.starts_with("--") {
|
|
|
|
|
file_path = Some(arg.clone());
|
|
|
|
|
} else {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"Unknown flag: {}. Use --tokens, --ast, --full, or --debug",
|
|
|
|
|
arg
|
|
|
|
|
);
|
|
|
|
|
eprintln!(
|
|
|
|
|
"Usage: {} [--tokens|--ast|--full] [--debug] [script]",
|
|
|
|
|
args[0]
|
|
|
|
|
);
|
|
|
|
|
std::process::exit(64);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(stage, file_path, debug)
|
2025-10-06 18:52:32 +02:00
|
|
|
} else {
|
2025-10-07 14:28:24 +02:00
|
|
|
eprintln!(
|
|
|
|
|
"Usage: {} [--tokens|--ast|--full] [--debug] [script]",
|
|
|
|
|
args[0]
|
|
|
|
|
);
|
2025-10-06 18:52:32 +02:00
|
|
|
std::process::exit(64);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 19:07:12 +02:00
|
|
|
struct LoxInterpreter {
|
|
|
|
|
source_registry: SourceRegistry,
|
2025-10-07 14:28:24 +02:00
|
|
|
debug: bool,
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl LoxInterpreter {
|
2025-10-07 14:28:24 +02:00
|
|
|
pub fn new(debug: bool) -> Self {
|
2025-10-03 19:07:12 +02:00
|
|
|
LoxInterpreter {
|
|
|
|
|
source_registry: SourceRegistry::new(),
|
2025-10-07 14:28:24 +02:00
|
|
|
debug,
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ Pipeline funzionale per file
|
2025-10-06 18:52:32 +02:00
|
|
|
fn run_file(&mut self, path: &str, stage: ExecutionStage) -> LoxResult<()> {
|
2025-10-07 14:28:24 +02:00
|
|
|
// Fase 1: Lettura del file e creazione del source
|
|
|
|
|
let source = fs::read_to_string(path).map_err(|e| LoxError::IoError {
|
|
|
|
|
message: e.to_string(),
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let source_id = self.source_registry.add_source_string(source)?;
|
|
|
|
|
|
|
|
|
|
// Se debug è attivo, mostra sempre tokens e AST
|
|
|
|
|
if self.debug {
|
|
|
|
|
let tokens = self.tokenize(source_id)?;
|
|
|
|
|
println!("=== TOKENS ===\n{}", format_tokens(&tokens));
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fase 2: Parsing completo - costruzione dell'AST
|
|
|
|
|
let ast = self.parse_to_ast(source_id)?;
|
|
|
|
|
|
|
|
|
|
if self.debug {
|
|
|
|
|
println!("=== AST ===\n{}", format_ast(&ast));
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fase 3: Esecuzione (se richiesta)
|
|
|
|
|
match stage {
|
|
|
|
|
ExecutionStage::Tokens => {
|
|
|
|
|
if !self.debug {
|
|
|
|
|
let tokens = self.tokenize(source_id)?;
|
|
|
|
|
println!("=== TOKENS ===\n{}", format_tokens(&tokens));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ExecutionStage::Ast => {
|
|
|
|
|
if !self.debug {
|
|
|
|
|
println!("=== AST ===\n{}", format_ast(&ast));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ExecutionStage::Full => {
|
|
|
|
|
let result = self.execute_ast(ast, self.debug)?;
|
|
|
|
|
println!("=== EXECUTION RESULT ===\n{}", result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ Pipeline funzionale per prompt
|
2025-10-06 18:52:32 +02:00
|
|
|
fn run_prompt(&mut self, stage: ExecutionStage) -> LoxResult<()> {
|
|
|
|
|
use std::io::{self, Write};
|
2025-10-03 19:07:12 +02:00
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
println!("Lox REPL (Stage: {:?}) - Type 'exit' to quit", stage);
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
print!("> ");
|
|
|
|
|
io::stdout().flush().ok();
|
|
|
|
|
|
|
|
|
|
let mut input = String::new();
|
|
|
|
|
match io::stdin().read_line(&mut input) {
|
|
|
|
|
Ok(_) => {
|
|
|
|
|
let line = input.trim();
|
|
|
|
|
if line == "exit" || line.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match self.source_registry.add_source_string(line.to_string()) {
|
|
|
|
|
Ok(source_id) => {
|
2025-10-07 14:28:24 +02:00
|
|
|
match self.process_source(source_id, stage, self.debug) {
|
2025-10-06 18:52:32 +02:00
|
|
|
Ok(result) => match stage {
|
|
|
|
|
ExecutionStage::Tokens => println!("Tokens: {}", result),
|
|
|
|
|
ExecutionStage::Ast => println!("AST: {}", result),
|
|
|
|
|
ExecutionStage::Full => println!("Result: {}", result),
|
|
|
|
|
},
|
|
|
|
|
Err(_) => {} // Errore già stampato in process_source
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => eprintln!("Error: {}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Error reading input: {}", e);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-03 19:07:12 +02:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
// Funzione per tokenizzare il codice
|
|
|
|
|
fn tokenize(&self, source_id: SourceId) -> LoxResult<Vec<crate::frontend::tokens::Token>> {
|
|
|
|
|
let source_content = self.source_registry.get_by_id(source_id).content.clone();
|
|
|
|
|
let mut lexer = Lexer::new(source_content, source_id);
|
|
|
|
|
|
|
|
|
|
lexer.scans_tokens().or_else(|err| {
|
|
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Funzione per parsare fino all'AST
|
|
|
|
|
fn parse_to_ast(&self, source_id: SourceId) -> LoxResult<Vec<AstNode<Stmt>>> {
|
|
|
|
|
let tokens = self.tokenize(source_id)?;
|
|
|
|
|
|
|
|
|
|
Parser::new(tokens).parse().or_else(|err| {
|
|
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Funzione per eseguire l'AST
|
|
|
|
|
fn execute_ast(&self, ast: Vec<AstNode<Stmt>>, debug: bool) -> LoxResult<String> {
|
|
|
|
|
let mut interpreter = Interpreter::new();
|
|
|
|
|
let mut result = None;
|
|
|
|
|
|
|
|
|
|
for (index, stmt) in ast.iter().enumerate() {
|
|
|
|
|
if debug {
|
|
|
|
|
println!("Executing statement {}: {:?}", index, stmt);
|
|
|
|
|
}
|
|
|
|
|
result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| {
|
|
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
|
|
|
|
})?);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match result {
|
|
|
|
|
Some(res) => Ok(format!("{}", res)),
|
|
|
|
|
None => Ok("No statements executed".to_string()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn process_source(
|
|
|
|
|
&self,
|
|
|
|
|
source_id: SourceId,
|
|
|
|
|
stage: ExecutionStage,
|
|
|
|
|
debug: bool,
|
|
|
|
|
) -> LoxResult<String> {
|
2025-10-06 18:52:32 +02:00
|
|
|
let source_content = self.source_registry.get_by_id(source_id).content.clone();
|
|
|
|
|
let mut lexer = Lexer::new(source_content, source_id);
|
|
|
|
|
|
|
|
|
|
// Stage 1: Tokenization
|
|
|
|
|
let tokens = lexer.scans_tokens().or_else(|err| {
|
|
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
// Se debug è attivo, mostra sempre i tokens
|
|
|
|
|
if debug && stage != ExecutionStage::Tokens {
|
|
|
|
|
println!("=== TOKENS ===\n{}", format_tokens(&tokens));
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
if stage == ExecutionStage::Tokens {
|
|
|
|
|
return Ok(format_tokens(&tokens));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stage 2: Parsing
|
|
|
|
|
let ast = Parser::new(tokens).parse().or_else(|err| {
|
|
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
// Se debug è attivo, mostra sempre l'AST
|
|
|
|
|
if debug && stage != ExecutionStage::Ast {
|
|
|
|
|
println!("=== AST ===\n{}", format_ast(&ast));
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
if stage == ExecutionStage::Ast {
|
|
|
|
|
return Ok(format_ast(&ast));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stage 3: Interpretation
|
|
|
|
|
let mut interpreter = Interpreter::new();
|
|
|
|
|
let mut result = None;
|
|
|
|
|
|
|
|
|
|
for (index, stmt) in ast.iter().enumerate() {
|
2025-10-07 14:28:24 +02:00
|
|
|
if debug {
|
|
|
|
|
println!("Executing statement {}: {:?}", index, stmt);
|
|
|
|
|
}
|
2025-10-06 18:52:32 +02:00
|
|
|
result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| {
|
2025-10-03 19:07:12 +02:00
|
|
|
err.print_with_context(&self.source_registry);
|
|
|
|
|
Err(err)
|
2025-10-06 18:52:32 +02:00
|
|
|
})?);
|
|
|
|
|
}
|
2025-10-03 19:07:12 +02:00
|
|
|
|
2025-10-06 18:52:32 +02:00
|
|
|
match result {
|
|
|
|
|
Some(res) => Ok(format!("{}", res)),
|
|
|
|
|
None => Ok("No statements executed".to_string()),
|
|
|
|
|
}
|
2025-10-03 19:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 18:52:32 +02:00
|
|
|
|
|
|
|
|
fn format_tokens(tokens: &[crate::frontend::tokens::Token]) -> String {
|
|
|
|
|
tokens
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.map(|(i, token)| format!("{:3}: {:?}", i, token))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join("\n")
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 14:28:24 +02:00
|
|
|
fn format_ast(ast: &[AstNode<Stmt>]) -> String {
|
2025-10-06 18:52:32 +02:00
|
|
|
use crate::logging::display_ast::{pretty_print_with_config, PrettyConfig};
|
|
|
|
|
|
|
|
|
|
let config = PrettyConfig {
|
|
|
|
|
indent: " ".to_string(),
|
|
|
|
|
max_depth: None,
|
|
|
|
|
show_positions: false,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ast.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
.map(|(i, stmt)| {
|
|
|
|
|
format!(
|
|
|
|
|
"Statement {}:\n{}",
|
|
|
|
|
i,
|
|
|
|
|
pretty_print_with_config(&stmt.node, &config)
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join("\n\n")
|
|
|
|
|
}
|