Switch interpreter to use new base value types
The commit changes the interpreter and related code to use the new `BaeValue` and `Number` types, moving away from direct floats. The main changes include: - Replace LiteralValue with BaeValue across the codebase - Add Number enum for type-safe numeric values - Move AST definitions to common module - Update operators to handle new numeric types - Add 'is' token type and parser support Switch to common base_value library for value types This commit represents a significant refactoring to switch the interpreter to use a new shared base value type system. The main changes include: 1. Move value types to a new common/base_value.rs module 2. Add richer numeric type support with promotion rules 3. Update interpreter to use new BaseValue enum 4. Move AST types to common module for sharing 5. Consolidate value operations like Add, Sub etc 6. Add proper test coverage for numeric operations The commit improves type safety and adds more robust numeric handling while keeping the interpreter's core logic clean.
This commit is contained in:
+189
-38
@@ -1,16 +1,19 @@
|
||||
mod backend;
|
||||
mod common;
|
||||
mod frontend;
|
||||
mod logging;
|
||||
mod result;
|
||||
|
||||
use crate::{
|
||||
backend::interpreter::{EvaluateInterpreter, Interpreter},
|
||||
common::{
|
||||
ast::{AstNode, Stmt},
|
||||
lox_result::{LoxError, LoxResult},
|
||||
},
|
||||
frontend::{
|
||||
lexer::Lexer,
|
||||
parser::Parser,
|
||||
source_registry::{SourceId, SourceRegistry},
|
||||
},
|
||||
result::{LoxError, LoxResult},
|
||||
};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
@@ -24,9 +27,9 @@ enum ExecutionStage {
|
||||
|
||||
fn main() -> LoxResult<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut lox = LoxInterpreter::new();
|
||||
|
||||
let (stage, file_path) = parse_args(&args);
|
||||
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),
|
||||
@@ -36,71 +39,158 @@ fn main() -> LoxResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_args(args: &[String]) -> (ExecutionStage, Option<String>) {
|
||||
fn parse_args(args: &[String]) -> (ExecutionStage, Option<String>, bool) {
|
||||
if args.len() == 1 {
|
||||
// Solo il nome del programma: modalità interattiva completa
|
||||
(ExecutionStage::Full, None)
|
||||
(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" {
|
||||
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,
|
||||
};
|
||||
(stage, None)
|
||||
let debug = arg == "--debug";
|
||||
(stage, None, debug)
|
||||
} else {
|
||||
// File senza flag: esecuzione completa del file
|
||||
(ExecutionStage::Full, Some(arg.clone()))
|
||||
(ExecutionStage::Full, Some(arg.clone()), false)
|
||||
}
|
||||
} 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]);
|
||||
// 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, Some(file.clone()))
|
||||
}
|
||||
|
||||
(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] [script]", args[0]);
|
||||
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() -> Self {
|
||||
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<()> {
|
||||
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, stage))
|
||||
.map(|result| match stage {
|
||||
ExecutionStage::Tokens => println!("=== TOKENS ===\n{}", result),
|
||||
ExecutionStage::Ast => println!("=== AST ===\n{}", result),
|
||||
ExecutionStage::Full => println!("=== EXECUTION RESULT ===\n{}", result),
|
||||
})
|
||||
// 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
|
||||
@@ -123,7 +213,7 @@ impl LoxInterpreter {
|
||||
|
||||
match self.source_registry.add_source_string(line.to_string()) {
|
||||
Ok(source_id) => {
|
||||
match self.process_source(source_id, stage) {
|
||||
match self.process_source(source_id, stage, self.debug) {
|
||||
Ok(result) => match stage {
|
||||
ExecutionStage::Tokens => println!("Tokens: {}", result),
|
||||
ExecutionStage::Ast => println!("AST: {}", result),
|
||||
@@ -145,7 +235,54 @@ impl LoxInterpreter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_source(&self, source_id: SourceId, stage: ExecutionStage) -> LoxResult<String> {
|
||||
// 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);
|
||||
|
||||
@@ -155,6 +292,12 @@ impl LoxInterpreter {
|
||||
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));
|
||||
}
|
||||
@@ -165,6 +308,12 @@ impl LoxInterpreter {
|
||||
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));
|
||||
}
|
||||
@@ -174,7 +323,9 @@ impl LoxInterpreter {
|
||||
let mut result = None;
|
||||
|
||||
for (index, stmt) in ast.iter().enumerate() {
|
||||
println!("Executing statement {}: {:?}", index, stmt);
|
||||
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)
|
||||
@@ -197,7 +348,7 @@ fn format_tokens(tokens: &[crate::frontend::tokens::Token]) -> String {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn format_ast(ast: &[crate::frontend::ast::AstNode<crate::frontend::ast::Stmt>]) -> String {
|
||||
fn format_ast(ast: &[AstNode<Stmt>]) -> String {
|
||||
use crate::logging::display_ast::{pretty_print_with_config, PrettyConfig};
|
||||
|
||||
let config = PrettyConfig {
|
||||
|
||||
Reference in New Issue
Block a user