for AST return value tracking The changes introduce support for tracking return values and labels across AST nodes. The key changes include: - Adding ReturnValue type to BaseValue - Adding return_type field to LoxFunction - Adding label field to Block statements - Creating middleend crawler module for AST traversal Add return value types to AST This accurately captures both changes: adding `.idea/` to gitignore and adding return value types to the AST structure. The changes include adding return value fields to statement nodes and adjusting the interpreter logic accordingly.
374 lines
12 KiB
Rust
374 lines
12 KiB
Rust
mod backend;
|
|
mod common;
|
|
mod frontend;
|
|
mod logging;
|
|
mod middleend;
|
|
|
|
use crate::{
|
|
backend::interpreter::{EvaluateInterpreter, Interpreter},
|
|
common::{
|
|
ast::{AstNode, Stmt},
|
|
lox_result::{LoxError, LoxResult},
|
|
},
|
|
frontend::{
|
|
lexer::Lexer,
|
|
parser::Parser,
|
|
source_registry::{SourceId, SourceRegistry},
|
|
},
|
|
};
|
|
use std::env;
|
|
use std::fs;
|
|
|
|
#[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);
|
|
let mut lox = LoxInterpreter::new(debug);
|
|
|
|
let _ = match file_path {
|
|
Some(path) => lox.run_file(&path, stage),
|
|
None => lox.run_prompt(stage),
|
|
};
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_args(args: &[String]) -> (ExecutionStage, Option<String>, bool) {
|
|
if args.len() == 1 {
|
|
// Solo il nome del programma: modalità interattiva completa
|
|
(ExecutionStage::Full, None, false)
|
|
} else if args.len() == 2 {
|
|
// Un argomento: potrebbe essere file o flag
|
|
let arg = &args[1];
|
|
if arg == "--tokens" || arg == "--ast" || arg == "--full" || arg == "--debug" {
|
|
// Flag senza file: modalità interattiva
|
|
let stage = match arg.as_str() {
|
|
"--tokens" => ExecutionStage::Tokens,
|
|
"--ast" => ExecutionStage::Ast,
|
|
"--full" => ExecutionStage::Full,
|
|
"--debug" => ExecutionStage::Full,
|
|
_ => ExecutionStage::Full,
|
|
};
|
|
let debug = arg == "--debug";
|
|
(stage, None, debug)
|
|
} else {
|
|
// File senza flag: esecuzione completa del file
|
|
(ExecutionStage::Full, Some(arg.clone()), false)
|
|
}
|
|
} else if args.len() == 3 {
|
|
// 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]
|
|
);
|
|
std::process::exit(64);
|
|
}
|
|
}
|
|
|
|
(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)
|
|
} else {
|
|
eprintln!(
|
|
"Usage: {} [--tokens|--ast|--full] [--debug] [script]",
|
|
args[0]
|
|
);
|
|
std::process::exit(64);
|
|
}
|
|
}
|
|
|
|
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
|
|
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
|
|
fn run_prompt(&mut self, stage: ExecutionStage) -> LoxResult<()> {
|
|
use std::io::{self, Write};
|
|
|
|
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) {
|
|
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> {
|
|
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> {
|
|
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!();
|
|
}
|
|
|
|
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!();
|
|
}
|
|
|
|
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() {
|
|
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 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 {
|
|
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")
|
|
}
|