Files
rlox/src/main.rs
T

320 lines
9.6 KiB
Rust
Raw Normal View History

mod backend;
mod common;
mod frontend;
mod logging;
mod middleend;
use crate::{
2025-10-06 18:52:32 +02:00
backend::interpreter::{EvaluateInterpreter, Interpreter},
common::{
ast::{AstNode, Stmt},
lox_result::{LoxError, LoxResult},
},
frontend::{
lexer::Lexer,
parser::Parser,
2025-10-06 18:52:32 +02:00
source_registry::{SourceId, SourceRegistry},
},
middleend::variable_resolution::Resolver,
};
use std::env;
use std::fs;
2025-10-06 18:52:32 +02:00
#[derive(Debug, Clone, Copy, PartialEq)]
enum ExecutionStage {
Tokens,
Ast,
Full,
}
fn main() -> LoxResult<()> {
let args: Vec<String> = env::args().collect();
let (stage, file_path, debug) = parse_args(&args);
2026-02-11 16:35:05 +01:00
println!("running {file_path:?}");
let mut lox = LoxInterpreter::new(debug);
2025-10-06 18:52:32 +02:00
2026-02-11 16:35:05 +01:00
let res = match file_path {
2025-10-06 18:52:32 +02:00
Some(path) => lox.run_file(&path, stage),
None => lox.run_prompt(stage),
2025-10-04 19:02:33 +02:00
};
2026-02-11 16:35:05 +01:00
res
}
fn parse_args(args: &[String]) -> (ExecutionStage, Option<String>, bool) {
2026-02-11 16:35:05 +01:00
println!("{args:?}");
let mut stage = ExecutionStage::Full;
let mut file_path = None;
let mut debug = false;
for arg in args.iter().skip(1) {
match arg.as_str() {
"-t" | "--tokens" => stage = ExecutionStage::Tokens,
"-a" | "--ast" => stage = ExecutionStage::Ast,
"-f" | "--full" => stage = ExecutionStage::Full,
"-d" | "--debug" => debug = true,
_ => file_path = Some(arg.clone()),
2025-10-06 18:52:32 +02:00
}
}
2026-02-11 16:35:05 +01:00
(stage, file_path, debug)
2025-10-06 18:52:32 +02:00
}
struct LoxInterpreter {
source_registry: SourceRegistry,
debug: bool,
}
impl LoxInterpreter {
pub fn new(debug: bool) -> Self {
LoxInterpreter {
source_registry: SourceRegistry::new(),
debug,
}
}
// ✅ Pipeline funzionale per file
2025-10-06 18:52:32 +02:00
fn run_file(&mut self, path: &str, stage: ExecutionStage) -> LoxResult<()> {
// 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(())
}
// ✅ 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-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) => {
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;
}
}
}
Ok(())
}
// 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> {
// Static resolution pass: compute variable scope distances and report
// any resolution errors before executing.
let locals = self.resolve(&ast)?;
let mut interpreter = Interpreter::new();
interpreter.set_locals(locals);
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()),
}
}
// Static variable resolution; prints and returns the first error, if any.
fn resolve(
&self,
ast: &[AstNode<Stmt>],
) -> LoxResult<std::collections::HashMap<crate::common::ast::NodeId, usize>> {
Resolver::resolve_program(ast).map_err(|errors| {
for err in &errors {
err.print_with_context(&self.source_registry);
}
errors.into_iter().next().unwrap()
})
}
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)
})?;
// 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)
})?;
// 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: Resolution + Interpretation
let locals = self.resolve(&ast)?;
2025-10-06 18:52:32 +02:00
let mut interpreter = Interpreter::new();
interpreter.set_locals(locals);
2025-10-06 18:52:32 +02:00
let mut result = None;
for (index, stmt) in ast.iter().enumerate() {
if debug {
println!("Executing statement {}: {:?}", index, stmt);
}
2025-10-06 18:52:32 +02:00
result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| {
err.print_with_context(&self.source_registry);
Err(err)
2025-10-06 18:52:32 +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-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")
}
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")
}