Move example code to examples directory

The commit moves example and test files to a dedicated examples
directory and updates the code for function and variable handling. The
subject line captures the primary purpose of the move operation.

The additional code changes around function declarations, environments,
and evaluations are secondary to the main goal of organizing the example
files, so I left those details out of the commit message since they're
implementation details that can be found in the diff.
This commit is contained in:
Giulio Agostini
2025-10-06 18:52:32 +02:00
parent 827349cbad
commit fa2e9178fb
16 changed files with 769 additions and 278 deletions
+167 -55
View File
@@ -1,43 +1,82 @@
mod backend;
mod frontend;
mod logging;
mod prompt;
mod result;
use crate::{
backend::{
environment::EnvironmentStack,
interpreter::{EvaluateInterpreter, Interpreter},
},
backend::interpreter::{EvaluateInterpreter, Interpreter},
frontend::{
lexer::Lexer,
parser::Parser,
source_registry::{SourceId, SourceRegistry, SourceSlice},
source_registry::{SourceId, SourceRegistry},
},
prompt::prompt_lines,
result::{LoxError, LoxResult},
};
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 mut lox = LoxInterpreter::new();
let _ = if args.len() > 2 {
eprintln!("Usage: {} [script]", args[0]);
std::process::exit(64);
} else if args.len() == 2 {
// Esegui file
lox.run_file(&args[1])
} else {
// Modalità interattiva
lox.run_prompt()
let (stage, file_path) = parse_args(&args);
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>) {
if args.len() == 1 {
// Solo il nome del programma: modalità interattiva completa
(ExecutionStage::Full, None)
} else if args.len() == 2 {
// Un argomento: potrebbe essere file o flag
let arg = &args[1];
if arg == "--tokens" || arg == "--ast" || arg == "--full" {
// Flag senza file: modalità interattiva
let stage = match arg.as_str() {
"--tokens" => ExecutionStage::Tokens,
"--ast" => ExecutionStage::Ast,
"--full" => ExecutionStage::Full,
_ => ExecutionStage::Full,
};
(stage, None)
} else {
// File senza flag: esecuzione completa del file
(ExecutionStage::Full, Some(arg.clone()))
}
} else if args.len() == 3 {
// Due argomenti: flag + file
let flag = &args[1];
let file = &args[2];
let stage = match flag.as_str() {
"--tokens" => ExecutionStage::Tokens,
"--ast" => ExecutionStage::Ast,
"--full" => ExecutionStage::Full,
_ => {
eprintln!("Unknown flag: {}. Use --tokens, --ast, or --full", flag);
eprintln!("Usage: {} [--tokens|--ast|--full] [script]", args[0]);
std::process::exit(64);
}
};
(stage, Some(file.clone()))
} else {
eprintln!("Usage: {} [--tokens|--ast|--full] [script]", args[0]);
std::process::exit(64);
}
}
struct LoxInterpreter {
source_registry: SourceRegistry,
}
@@ -50,60 +89,133 @@ impl LoxInterpreter {
}
// ✅ Pipeline funzionale per file
fn run_file(&mut self, path: &str) -> LoxResult<()> {
fn run_file(&mut self, path: &str, stage: ExecutionStage) -> LoxResult<()> {
fs::read_to_string(path)
.map_err(|e| LoxError::IoError {
message: e.to_string(),
})
.and_then(|source| self.source_registry.add_source_string(source))
.and_then(|source_id| self.process_source(source_id))
.map(|result| println!("file {}", result))
.and_then(|source_id| self.process_source(source_id, stage))
.map(|result| match stage {
ExecutionStage::Tokens => println!("=== TOKENS ===\n{}", result),
ExecutionStage::Ast => println!("=== AST ===\n{}", result),
ExecutionStage::Full => println!("=== EXECUTION RESULT ===\n{}", result),
})
}
// ✅ Pipeline funzionale per prompt
fn run_prompt(&mut self) -> LoxResult<()> {
println!("Lox REPL - Type 'exit' to quit");
fn run_prompt(&mut self, stage: ExecutionStage) -> LoxResult<()> {
use std::io::{self, Write};
prompt_lines(|source| self.source_registry.add_source_string(source))
.for_each(|line| println!("hi: {}", line));
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) {
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(())
}
fn process_source(&self, source_id: SourceId) -> LoxResult<String> {
let mut lexer = Lexer::new(
self.source_registry.get_by_id(source_id).content.clone(),
source_id,
);
let res = lexer
.scans_tokens()
.and_then(|tokens| Parser::new(tokens).parse())
.and_then(|asts| {
// println!("asts: {:?}", asts);
Ok(asts)
})
.and_then(|stmts| {
let mut interpreter = Interpreter::new();
let mut result = None;
for (index, stmt) in stmts.iter().enumerate() {
println!("executing stmt {}: {:?}", index, stmt);
result = Some(interpreter.interpret(stmt.clone())?);
}
match result {
Some(res) => Ok(res),
_ => Err(LoxError::RuntimeError {
source_slice: SourceSlice::default(),
message: "what?????".to_string(),
}),
}
})
.and_then(|result| Ok(format!("{}", result)))
.or_else(|err| {
fn process_source(&self, source_id: SourceId, stage: ExecutionStage) -> 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)
})?;
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)
})?;
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() {
println!("Executing statement {}: {:?}", index, stmt);
result = Some(interpreter.evaluate(stmt.clone()).or_else(|err| {
err.print_with_context(&self.source_registry);
Err(err)
});
// println!("result {}", res);
})?);
}
res
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: &[crate::frontend::ast::AstNode<crate::frontend::ast::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")
}