From fa2e9178fb4c44bc1391a59fb621b9e6deecd164 Mon Sep 17 00:00:00 2001 From: Giulio Agostini Date: Mon, 6 Oct 2025 18:52:32 +0200 Subject: [PATCH] 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. --- example.lox | 24 -- examples/example.lox | 23 ++ examples/function_call.lox | 20 ++ test_error.lox => examples/test_error.lox | 0 .../test_source_tracking.lox | 0 src/backend/environment.rs | 29 +- src/backend/interpreter.rs | 245 +++++++++++----- src/frontend/ast.rs | 91 ++++-- src/frontend/lexer.rs | 4 +- src/frontend/parser.rs | 270 ++++++++++++++---- src/frontend/tokens.rs | 46 ++- src/logging/display_ast.rs | 37 ++- src/logging/display_token.rs | 4 + src/main.rs | 222 ++++++++++---- src/prompt.rs | 29 -- src/result.rs | 3 +- 16 files changed, 769 insertions(+), 278 deletions(-) delete mode 100644 example.lox create mode 100644 examples/example.lox create mode 100644 examples/function_call.lox rename test_error.lox => examples/test_error.lox (100%) rename test_source_tracking.lox => examples/test_source_tracking.lox (100%) delete mode 100644 src/prompt.rs diff --git a/example.lox b/example.lox deleted file mode 100644 index e72e665..0000000 --- a/example.lox +++ /dev/null @@ -1,24 +0,0 @@ -// var Person :: struct { -// name: String, -// age: Int, -// address: String, -// } -// var func_name :: fn(param, param2) {param is Int} do -do - print "ciao"; - 1 + 2; - var cavallo = 1 + 2; - cavallo = cavallo + 1; - print cavallo; - cavallo -end -do - var cavallo = 0; - print "cavallo"; - while cavallo < 10 do - cavallo = cavallo + 1; - print cavallo; - end - 89 -end -99 diff --git a/examples/example.lox b/examples/example.lox new file mode 100644 index 0000000..828bb0c --- /dev/null +++ b/examples/example.lox @@ -0,0 +1,23 @@ +// var Person :: struct { +// name: String, +// age: Int, +// address: String, +// } +//func_name :: fn(param: Number, param2: String) {param is Int} do +func_name :: fn(param: Int, param2: String) do + print param; + cavallo := 5; + print cavallo; + while cavallo < 10 do + cavallo = cavallo + 1; + print cavallo; + end + + for i := 11; i <= 21; i = i + 1; do + print i; + end + + return False or True; +end + +func_name(1,2) diff --git a/examples/function_call.lox b/examples/function_call.lox new file mode 100644 index 0000000..663d520 --- /dev/null +++ b/examples/function_call.lox @@ -0,0 +1,20 @@ + +function :: fn() do + print "Function"; +end + + +function_factory :: fn() do + print "Function Factory"; + function +end + +function_fatcoty_factory :: fn() do + print "Function Factory Factory"; + function_factory +end + +//function(); +//function_factory()(); +function_fatcoty_factory()()(); +clock() diff --git a/test_error.lox b/examples/test_error.lox similarity index 100% rename from test_error.lox rename to examples/test_error.lox diff --git a/test_source_tracking.lox b/examples/test_source_tracking.lox similarity index 100% rename from test_source_tracking.lox rename to examples/test_source_tracking.lox diff --git a/src/backend/environment.rs b/src/backend/environment.rs index 0da0e53..91681f2 100644 --- a/src/backend/environment.rs +++ b/src/backend/environment.rs @@ -5,7 +5,7 @@ use crate::{ result::{runtime_error, LoxResult}, }; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct EnvironmentStack { stack: Vec>, } @@ -39,7 +39,30 @@ impl EnvironmentStack { ) } - pub fn set(&mut self, name: String, value: LiteralValue) { - self.stack.last_mut().unwrap().insert(name, value); + pub fn declare(&mut self, name: String, value: LiteralValue) -> LoxResult { + self.stack.last_mut().unwrap().insert(name, value.clone()); + Ok(value) + } + + pub fn set(&mut self, name: String, value: LiteralValue) -> LoxResult { + let size = self.stack.len(); + + for i in (0..size).rev() { + if let Some(_) = self.stack[i].get(&name) { + let mut founded = false; + self.stack[i].entry(name.clone()).and_modify(|v| { + *v = value.clone(); + founded = true; + }); + + if founded { + return Ok(value); + } + } + } + runtime_error( + SourceSlice::default(), // todo change this to the actual source slice + format!("Undefined variable '{}'", name), + ) } } diff --git a/src/backend/interpreter.rs b/src/backend/interpreter.rs index c06497c..a361a6a 100644 --- a/src/backend/interpreter.rs +++ b/src/backend/interpreter.rs @@ -3,9 +3,10 @@ use crate::{ frontend::{ ast::{AstNode, AstNodeKind, Expr, Stmt}, source_registry::SourceSlice, - tokens::{LiteralValue, TokenType}, + tokens::{LiteralValue, NativeFunction, TokenType}, }, - result::{LoxError, LoxResult}, + logging::display_ast::PrettyPrintExt, + result::{runtime_error, LoxError, LoxResult}, }; use std::{ fmt::{Debug, Display}, @@ -174,15 +175,15 @@ pub struct Interpreter { } pub trait EvaluateInterpreter { - fn interpret(&mut self, stmt: T) -> LoxResult; + fn evaluate(&mut self, stmt: T) -> LoxResult; } impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter> for Interpreter where Interpreter: EvaluateInterpreter, { - fn interpret(&mut self, stmt: AstNode) -> LoxResult { - match self.interpret(stmt.node.clone()) { + fn evaluate(&mut self, stmt: AstNode) -> LoxResult { + match self.evaluate(stmt.node.clone()) { Ok(value) => Ok(value), Err(err) => Err(LoxError::RuntimeError { source_slice: stmt.source_slice, @@ -194,40 +195,109 @@ where // Direct Expr evaluation to avoid infinite recursion impl EvaluateInterpreter for Interpreter { - fn interpret(&mut self, expr: Expr) -> LoxResult { + fn evaluate(&mut self, expr: Expr) -> LoxResult { match expr { Expr::Literal { value } => Ok(value), - Expr::Variable { name } => self.enviorment.get(&name), + Expr::Identifier { name } => self.enviorment.get(&name), Expr::Binary { left, operator, right, } => { - let left_val = self.interpret(*left)?; - let right_val = self.interpret(*right)?; + let left_val = self.evaluate(*left)?; + let right_val = self.evaluate(*right)?; self.evaluate_binary(left_val, operator, right_val) } Expr::Unary { operator, operand } => { - let operand_val = self.interpret(*operand)?; + let operand_val = self.evaluate(*operand)?; self.evaluate_unary(operator, operand_val) } - Expr::Grouping { expression } => self.interpret(*expression), + Expr::Grouping { expression } => self.evaluate(*expression), + Expr::Call { callee, arguments } => self.evaluate_call(callee, arguments), } } } impl EvaluateInterpreter> for Interpreter { - fn interpret(&mut self, node: AstNode) -> LoxResult { + fn evaluate(&mut self, node: AstNode) -> LoxResult { let stmt = node.node; - let _source_slice = node.source_slice; - self.interpret_stmt_inner(stmt) + let result = self.interpret_stmt_inner(stmt); + match result { + Ok(value) => Ok(value), + Err(LoxError::RuntimeError { + message, + source_slice, + }) if source_slice == SourceSlice::default() => { + runtime_error(node.source_slice.clone(), message) + } + Err(err) => Err(err), + } } } impl Interpreter { pub fn new() -> Self { - Self { - enviorment: EnvironmentStack::new(), + let mut env = EnvironmentStack::new(); + let _ = env.declare( + "clock".to_string(), + LiteralValue::NativeFunction(NativeFunction::new(0, |_args| { + LiteralValue::Number( + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_millis() as f64, + ) + })), + ); + Self { enviorment: env } + } + fn evaluate_call( + &mut self, + callee: Box>, + arguments: Vec>, + ) -> LoxResult { + let source_slice = callee.source_slice.clone(); + + // Estrai il nome della variabile se il callee è un identificatore + let function_name = match &callee.node { + Expr::Identifier { name } => Some(name.clone()), + _ => None, + }; + + let function = if let Some(name) = function_name { + // Se abbiamo un nome di variabile, ottieni la funzione dall'ambiente + self.enviorment.get(&name)? + } else { + // Altrimenti valuta l'espressione callee (per casi più complessi) + self.evaluate(*callee)? + }; + + if !function.is_callable() { + return runtime_error(source_slice, "Can only call functions"); + } + + let evaluated_arguments = arguments + .iter() + .map(|arg| self.evaluate(arg.clone())) + .collect::, LoxError>>()?; + + match function { + LiteralValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)), + LiteralValue::Function(func) => { + func.parameters + .iter() + .enumerate() + .map(|(index, par)| { + let value = evaluated_arguments.get(index).unwrap(); + self.enviorment.declare(par.0.clone(), value.clone()) + }) + .collect::>>()?; + self.evaluate(func.body) + } + _ => runtime_error( + source_slice.clone(), + "This is callable, but apparently is not implemented", + ), } } @@ -248,6 +318,8 @@ impl Interpreter { TokenType::LessEqual => Ok(LiteralValue::Boolean(left <= right)), TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)), TokenType::BangEqual => Ok(LiteralValue::Boolean(left != right)), + TokenType::And => Ok(LiteralValue::Boolean(left.is_truthy() && right.is_truthy())), + TokenType::Or => Ok(LiteralValue::Boolean(left.is_truthy() || right.is_truthy())), _ => Err(error(format!( "Unsupported binary operator: {:?}", operator @@ -269,75 +341,108 @@ impl Interpreter { fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult { match stmt { - Stmt::Expression { expression } => self.interpret(*expression), + Stmt::Expression { expression } => self.evaluate(*expression), Stmt::Print { expression } => { - let value = self.interpret(*expression)?; + let value = self.evaluate(*expression)?; println!("print interpreter: \t{}", value); Ok(LiteralValue::Nil) } Stmt::Block { statements } => self.evaluate_block(*statements), - Stmt::Stmt { expression } => self.interpret(*expression.clone()), - Stmt::Return { expression } => self.interpret(*expression), - Stmt::Var { name, initializer } => { - let value = if let Some(expr) = initializer { - self.interpret(*expr)? - } else { - LiteralValue::Nil + Stmt::Stmt { expression } => self.evaluate(*expression.clone()), + Stmt::Return { expression } => self.evaluate(*expression), + Stmt::VarDeclaration { name, initializer } => { + let value = match initializer { + Some(expr_node) => match &expr_node.node { + Expr::Literal { value } => { + if value.is_callable() { + value.clone() + } else { + value.clone() + } + } + _ => self.evaluate(*expr_node)?, + }, + None => LiteralValue::Nil, }; - self.enviorment.set(name.clone(), value); - return Ok(LiteralValue::Nil); + self.enviorment.declare(name.clone(), value) } - Stmt::Assign { name, value } => { - let result = self.interpret(*value)?; - self.enviorment.set(name.clone(), result); - Ok(LiteralValue::Nil) + + Stmt::VarAssigment { name, value } => { + let result = self.evaluate(*value)?; + self.enviorment.set(name.clone(), result) } Stmt::If { condition, then_branch, elif_branch, else_branch, - } => { - let condition = self.interpret(*condition)?; - match condition { - LiteralValue::Boolean(true) => self.interpret(*then_branch), - LiteralValue::Boolean(false) => { - for (elif_condition, elif_then_branch) in elif_branch { - let condition = self.interpret(*elif_condition)?; - match condition { - LiteralValue::Boolean(true) => { - return self.interpret(*elif_then_branch); - } - LiteralValue::Boolean(false) => continue, - _ => { - return Err(LoxError::TypeMismatch { - source_slice: SourceSlice::default(), // todo change this to the actual source slice - expected: "boolean".to_string(), - found: condition.to_string(), - }); - } - }; - } - if let Some(else_block) = else_branch { - self.interpret(*else_block) - } else { - Ok(LiteralValue::Nil) - } - } - _ => Err(LoxError::TypeMismatch { - source_slice: SourceSlice::default(), // todo change this to the actual source slice - expected: "boolean".to_string(), - found: condition.to_string(), - }), - } - } + } => self.evaluate_if(condition, then_branch, elif_branch, else_branch), Stmt::While { condition, body } => { - while self.interpret(*condition.clone())?.is_truthy() { - self.interpret(*body.clone())?; + while self.evaluate(*condition.clone())?.is_truthy() { + self.evaluate(*body.clone())?; } Ok(LiteralValue::Nil) } - Stmt::For { .. } => todo!(), + Stmt::For { + variable, + condition, + increment, + body, + } => { + self.enviorment.push_new_scope(); + let source_slice = variable.source_slice.clone(); + let val = self.evaluate(*variable)?; + if !matches!(val, LiteralValue::Number(..)) { + return runtime_error(source_slice, "Expected number literal"); + } + let mut ret = LiteralValue::Nil; + while self.evaluate(*condition.clone())?.is_truthy() { + ret = self.evaluate(*body.clone())?; + self.evaluate(*increment.clone())?; + } + + Ok(ret) + } + } + } + fn evaluate_if( + &mut self, + condition: Box>, + then_branch: Box>, + elif_branch: Vec<(Box>, Box>)>, + else_branch: Option>>, + ) -> LoxResult { + let condition = self.evaluate(*condition)?; + match condition { + LiteralValue::Boolean(true) => self.evaluate(*then_branch), + LiteralValue::Boolean(false) => { + for (elif_condition, elif_then_branch) in elif_branch { + let condition = self.evaluate(*elif_condition)?; + match condition { + LiteralValue::Boolean(true) => { + return self.evaluate(*elif_then_branch); + } + LiteralValue::Boolean(false) => continue, + _ => { + return Err(LoxError::TypeMismatch { + source_slice: SourceSlice::default(), // todo change this to the actual source slice + expected: "boolean".to_string(), + found: condition.to_string(), + }); + } + }; + } + if let Some(else_block) = else_branch { + self.evaluate(*else_block) + } else { + Ok(LiteralValue::Nil) + } + } + _ => Err(LoxError::TypeMismatch { + source_slice: SourceSlice::default(), // todo change this to the actual source slice + expected: "boolean".to_string(), + found: condition.to_string(), + }), } } @@ -357,13 +462,13 @@ impl Interpreter { // Ora elements è sempre disponibile for statement in elements.iter() { - self.interpret((*statement).clone())?; + self.evaluate((*statement).clone())?; } // Gestisci l'espressione finale se presente match final_expr { Some(expr) => { - let res = self.interpret(*expr.clone()); + let res = self.evaluate(*expr.clone()); self.enviorment.pop_scope(); res } diff --git a/src/frontend/ast.rs b/src/frontend/ast.rs index fe8e7ad..9131315 100644 --- a/src/frontend/ast.rs +++ b/src/frontend/ast.rs @@ -15,7 +15,9 @@ use std::fmt::{Debug, Display}; * * expression_statement -> expression ";" * print_statement -> "print" expression ";" - * var_statement -> "var" IDENTIFIER ("=" expression)? ";" + * var_statement -> ("var")? IDENTIFIER (":" IDENTIFIER)? (("=" expression)? ";")| function_declaration + * function_declaration -> "::" "(" parameters? ")" ("->" IDENTIFIER)? block_statement + * parameters -> ( IDENTIFIER (":" IDENTIFIER)? ("," IDENTIFIER (":" IDENTIFIER)? )* ) * assignment_statement -> IDENTIFIER "=" expression ";" * block_statement -> "do" statement* "end" * if_statement -> "if" expression "then" statement ("elif" expression "then" statement)* ("else" statement)? "end" @@ -29,10 +31,12 @@ use std::fmt::{Debug, Display}; * comparison -> term ((">" | ">=" | "<" | "<=") term)* * term -> factor (("+" | "-") factor)* * factor -> unary (("*" | "/") unary)* - * unary -> ("!" | "-") unary | primary + * unary -> ("!" | "-") unary | call + * call -> primary ("(" arguments ")")* + * arguments -> expression ("," expression)* * primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER */ -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Expr { Literal { value: LiteralValue, @@ -49,9 +53,13 @@ pub enum Expr { Grouping { expression: Box>, }, - Variable { + Identifier { name: String, }, + Call { + callee: Box>, + arguments: Vec>, + }, } // Implementazione Display per Expr (per debugging) @@ -66,7 +74,17 @@ impl std::fmt::Display for Expr { } => write!(f, "Binary ({} {} {})", left.node, operator, right.node), Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand.node), Expr::Grouping { expression } => write!(f, "Grouping (group {})", expression.node), - Expr::Variable { name } => write!(f, "Variable (variable {})", name), + Expr::Identifier { name } => write!(f, "Identifier (variable {})", name), + Expr::Call { callee, arguments } => write!( + f, + "Call ({}({}))", + callee.node, + arguments + .iter() + .map(|arg| arg.node.to_string()) + .collect::>() + .join(", ") + ), } } } @@ -82,12 +100,22 @@ impl Debug for Expr { } => write!(f, "({:?} {:?} {:?})", operator, left.node, right.node), Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand.node), Expr::Grouping { expression } => write!(f, "(group {:?})", expression.node), - Expr::Variable { name } => write!(f, "(variable {:?})", name), + Expr::Identifier { name } => write!(f, "(variable {:?})", name), + Expr::Call { callee, arguments } => write!( + f, + "Call ({}({}))", + callee.node, + arguments + .iter() + .map(|arg| arg.node.to_string()) + .collect::>() + .join(", ") + ), } } } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Stmt { Expression { expression: Box>, @@ -98,11 +126,11 @@ pub enum Stmt { Stmt { expression: Box>, }, - Var { + VarDeclaration { name: String, initializer: Option>>, }, - Assign { + VarAssigment { name: String, value: Box>, }, @@ -123,8 +151,9 @@ pub enum Stmt { body: Box>, }, For { - variable: Box>, - iterable: Box>, + variable: Box>, + condition: Box>, + increment: Box>, body: Box>, }, } @@ -153,11 +182,11 @@ impl Display for Stmt { } Stmt::Print { expression } => write!(f, "Print({});", expression.node), Stmt::Stmt { expression } => write!(f, "Stmt({});", expression.node), - Stmt::Var { name, initializer } => match initializer { + Stmt::VarDeclaration { name, initializer } => match initializer { Some(init) => write!(f, "Var({} = {});", name, init.node), None => write!(f, "Var({});", name), }, - Stmt::Assign { name, value } => write!(f, "Assign({} = {});", name, value.node), + Stmt::VarAssigment { name, value } => write!(f, "Assign({} = {});", name, value.node), Stmt::Return { expression } => write!(f, "Return({});", expression.node), Stmt::Block { statements } => write!( f, @@ -173,12 +202,13 @@ impl Display for Stmt { } Stmt::For { variable, - iterable, + condition, + increment, body, } => write!( f, - "For({} in {}) {{\n{}\n}}", - variable.node, iterable.node, body.node + "For({} = {} in {}) {{\n{}\n}}", + variable.node, condition.node, increment.node, body.node ), } } @@ -209,11 +239,13 @@ impl Debug for Stmt { } Stmt::Print { expression } => write!(f, "Print({:?});", expression.node), Stmt::Stmt { expression } => write!(f, "Stmt({:?});", expression.node), - Stmt::Var { name, initializer } => match initializer { + Stmt::VarDeclaration { name, initializer } => match initializer { Some(init) => write!(f, "Var({:?} = {:?});", name, init.node), None => write!(f, "Var({:?});", name), }, - Stmt::Assign { name, value } => write!(f, "Assign({:?} = {:?});", name, value.node), + Stmt::VarAssigment { name, value } => { + write!(f, "Assign({:?} = {:?});", name, value.node) + } Stmt::Return { expression } => write!(f, "Return({:?});", expression.node), Stmt::Block { statements } => write!( f, @@ -229,12 +261,13 @@ impl Debug for Stmt { } Stmt::For { variable, - iterable, + condition, + increment, body, } => write!( f, - "For({:?} in {:?}) {{\n{:?}\n}}", - variable.node, iterable.node, body.node + "For({:?} = {:?} in {:?}) {{\n{:?}\n}}", + variable.node, condition.node, increment.node, body.node ), } } @@ -253,12 +286,10 @@ impl AstNodeKind for Expr { operator: _, right: _, } => "binary expression", - Expr::Unary { - operator: _, - operand: _, - } => "unary expression", - Expr::Grouping { expression: _ } => "grouping expression", - Expr::Variable { name: _ } => "variable expression", + Expr::Unary { .. } => "unary expression", + Expr::Grouping { .. } => "grouping expression", + Expr::Identifier { .. } => "variable expression", + Expr::Call { .. } => "call expression", } } } @@ -267,8 +298,8 @@ impl AstNodeKind for Stmt { fn kind(&self) -> &'static str { match self { Stmt::Expression { .. } => "expression", - Stmt::Var { .. } => "variable declaration", - Stmt::Assign { .. } => "assignment", + Stmt::VarDeclaration { .. } => "variable declaration", + Stmt::VarAssigment { .. } => "assignment", Stmt::Return { .. } => "return statement", Stmt::Block { .. } => "block statement", Stmt::If { .. } => "if statement", @@ -280,7 +311,7 @@ impl AstNodeKind for Stmt { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Default)] pub struct AstNode { pub node: T, pub source_slice: SourceSlice, diff --git a/src/frontend/lexer.rs b/src/frontend/lexer.rs index 0933b5c..588c15a 100644 --- a/src/frontend/lexer.rs +++ b/src/frontend/lexer.rs @@ -38,6 +38,7 @@ fn get_keyword_token(word: &str) -> Option { "True" => Some(TokenType::True), "False" => Some(TokenType::False), "Nil" => Some(TokenType::Nil), + "fn" => Some(TokenType::Fn), _ => None, } } @@ -146,6 +147,7 @@ impl Lexer { ('-', _) => Ok(Some(self.make_token(TokenType::Minus))), ('+', _) => Ok(Some(self.make_token(TokenType::Plus))), (';', _) => Ok(Some(self.make_token(TokenType::Semicolon))), + (':', _) => Ok(Some(self.make_token(TokenType::Colon))), ('*', _) => Ok(Some(self.make_token(TokenType::Star))), ('%', _) => Ok(Some(self.make_token(TokenType::Percent))), ('!', '=') => { @@ -262,7 +264,7 @@ impl Lexer { } fn identifier(&mut self) -> LoxResult> { - while self.peek().is_alphanumeric() { + while self.peek().is_alphanumeric() || self.peek() == '_' { self.advance(); } let text = self.input[self.start_char..self.current_char].to_string(); diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 4a3012b..b1283b3 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -1,11 +1,11 @@ use crate::{ frontend::{ ast::{AstNode, Expr, Stmt}, - source_registry::SourceSlice, - tokens::{LiteralValue, Token, TokenType}, + source_registry::{SourcePosition, SourceSlice}, + tokens::{LiteralValue, LoxFunction, Token, TokenType}, }, logging::display_ast::{pretty_print_with_config, PrettyConfig}, - result::{parse_error, LoxError, LoxResult}, + result::{parse_error, runtime_error, LoxResult}, }; pub struct Parser { @@ -58,10 +58,14 @@ impl Parser { fn statement(&mut self) -> LoxResult> { match (&self.peek().token_type, &self.peek_next().token_type) { - (TokenType::Var, _) => self.var_statement(), (TokenType::Print, _) => self.print_statement(), (TokenType::Return, _) => self.return_statement(), (TokenType::StartBlock, _) => self.block_statement(), + (TokenType::Var, TokenType::Identifier) => { + self.advance(); + self.var_statement() + } + (TokenType::Identifier, TokenType::Colon) => self.var_statement(), (TokenType::Identifier, TokenType::Equal) => self.assignment_statement(), (TokenType::If, _) => self.if_statement(), (TokenType::While, _) => self.while_statement(), @@ -72,46 +76,78 @@ impl Parser { fn for_statement(&mut self) -> LoxResult> { let start_slice = self.peek().source_slice.clone(); - self.advance(); - let variable = if let Token { - token_type: TokenType::Identifier, - literal: Some(variable_name), - source_slice, - .. - } = self.peek() - { - AstNode { - node: Expr::Literal { - value: variable_name.clone(), - }, - source_slice: source_slice.clone(), - } - } else { - return parse_error( - self.peek().source_slice.clone(), - "Expect variable name after 'for' keyword.", - ); - }; - self.advance(); - self.consume(TokenType::In, "Expect 'in' after for variable.")?; - let iterable = self.expression()?; + self.advance(); // consume 'for' + + let variable = self.var_statement()?; + let condition = self.expression()?; + self.consume(TokenType::Semicolon, "Expected ';' after for condition")?; + let increment = self.assignment_statement()?; + + self.consume(TokenType::StartBlock, "Expected 'do' after for range")?; + let body = self.statement()?; - let end_slice = body.source_slice.clone(); + self.consume(TokenType::EndBlock, "Expected 'end' after for body")?; + + let end_slice = self.previous().source_slice.clone(); let combined_slice = SourceSlice::from_positions( start_slice.source_id, start_slice.start_position, end_slice.end_position, ); + Ok(AstNode::new( Stmt::For { variable: Box::new(variable), - iterable: Box::new(iterable), + condition: Box::new(condition), + increment: Box::new(increment), body: Box::new(body), }, combined_slice, )) } + // fn for_statement(&mut self) -> LoxResult> { + // let start_slice = self.peek().source_slice.clone(); + // self.advance(); + // let variable = if let Token { + // token_type: TokenType::Identifier, + // literal: Some(variable_name), + // source_slice, + // .. + // } = self.peek() + // { + // AstNode { + // node: Expr::Literal { + // value: variable_name.clone(), + // }, + // source_slice: source_slice.clone(), + // } + // } else { + // return parse_error( + // self.peek().source_slice.clone(), + // "Expect variable name after 'for' keyword.", + // ); + // }; + // self.advance(); + // self.consume(TokenType::In, "Expect 'in' after for variable.")?; + // let iterable = self.expression()?; + // let body = self.statement()?; + // let end_slice = body.source_slice.clone(); + // let combined_slice = SourceSlice::from_positions( + // start_slice.source_id, + // start_slice.start_position, + // end_slice.end_position, + // ); + // Ok(AstNode::new( + // Stmt::For { + // variable: Box::new(variable), + // iterable: Box::new(iterable), + // body: Box::new(body), + // }, + // combined_slice, + // )) + // } + fn while_statement(&mut self) -> LoxResult> { let start_slice = self.peek().source_slice.clone(); self.advance(); @@ -178,11 +214,103 @@ impl Parser { fn var_statement(&mut self) -> LoxResult> { let start_slice = self.peek().source_slice.clone(); - self.advance(); let name = self.consume(TokenType::Identifier, "Expect variable name.")?; let name_lexeme = name.lexeme.clone(); - self.consume(TokenType::Equal, "Expect '=' after variable name.")?; - let value = self.expression()?; + + let mut _type_annotation = LiteralValue::Nil; + self.consume(TokenType::Colon, "Expect column")?; + + match self.peek().token_type { + TokenType::Equal | TokenType::Identifier => { + self.variable_declaration(start_slice, name_lexeme) + } + TokenType::Colon => self.function_declaration(start_slice, name_lexeme), + _ => runtime_error(start_slice, "this is not supposed to be here"), + } + } + + fn function_declaration( + &mut self, + start_slice: SourceSlice, + name_lexeme: String, + ) -> LoxResult> { + self.advance(); + self.consume(TokenType::Fn, "Expect 'fn' after '::'")?; + self.consume(TokenType::LeftParen, "Expect '(' after 'fn' ")?; + let mut parameters: Vec<(String, String)> = vec![]; + while self.peek().token_type != TokenType::RightParen { + let expr = self + .consume(TokenType::Identifier, "Expect identifier after '('")? + .clone(); + if self.peek().token_type == TokenType::Colon { + self.advance(); + let type_ = + self.consume(TokenType::Identifier, "Expected identifier after ':' ")?; + parameters.push((expr.lexeme, type_.lexeme.clone())); + } else { + parameters.push((expr.lexeme, "Any".to_string())); + } + if self.peek().token_type == TokenType::Comma { + self.advance(); + } + } + self.advance(); + let end_position = self.peek().source_slice.clone(); + let combine_position = SourceSlice { + source_id: start_slice.source_id, + start_position: start_slice.start_position.clone(), + end_position: end_position.end_position, + }; + let mut guard: Option> = None; + if self.peek().token_type == TokenType::RightBrace { + self.advance(); + guard = Some(Box::new(self.expression()?.node.clone())); + self.consume(TokenType::RightBrace, "Expected '}' after guard expression")?; + } + + let body = self.statement()?; + let node = AstNode { + node: Expr::Literal { + value: LiteralValue::Function(LoxFunction { + parameters, + body, + closure: None, + guard, + }), + }, + source_slice: combine_position.clone(), + }; + Ok(AstNode::new( + Stmt::VarDeclaration { + name: name_lexeme, + initializer: Some(Box::new(node)), + }, + combine_position.clone(), + )) + } + + fn variable_declaration( + &mut self, + start_slice: SourceSlice, + name_lexeme: String, + ) -> LoxResult> { + if self.peek().token_type == TokenType::Identifier { + self.advance(); + if self.peek().token_type == TokenType::Identifier { + self.advance(); + } + // todo: make type annotation + } + let mut value = AstNode { + node: Expr::Literal { + value: LiteralValue::Nil, + }, + source_slice: start_slice.clone(), + }; + if self.peek().token_type == TokenType::Equal { + self.advance(); + value = self.expression()?; + } let semicolon = self.consume( TokenType::Semicolon, "Expect ';' after variable declaration.", @@ -194,14 +322,13 @@ impl Parser { end_slice.end_position, ); Ok(AstNode::new( - Stmt::Var { + Stmt::VarDeclaration { name: name_lexeme, initializer: Some(Box::new(value)), }, combined_slice, )) } - fn assignment_statement(&mut self) -> LoxResult> { let start_slice = self.peek().source_slice.clone(); let name = self.consume(TokenType::Identifier, "Expect variable name.")?; @@ -219,7 +346,7 @@ impl Parser { end_slice.end_position, ); Ok(AstNode::new( - Stmt::Assign { + Stmt::VarAssigment { name: name_lexeme, value: Box::new(value), }, @@ -481,7 +608,56 @@ impl Parser { )); } - self.primary() + self.call() + } + + fn call(&mut self) -> LoxResult> { + let mut expr = self.primary()?; + + loop { + if self.peek().token_type == TokenType::LeftParen { + expr = self.finish_call(expr)?; + } else { + break; + } + } + + Ok(expr) + } + fn finish_call(&mut self, callee: AstNode) -> LoxResult> { + let start_slice = callee.source_slice.start_position.clone(); + self.advance(); // Consume '(' + + let mut arguments = Vec::new(); + + if self.peek().token_type != TokenType::RightParen { + loop { + arguments.push(self.expression()?); + if self.peek().token_type == TokenType::Comma { + self.advance(); + } else { + break; + } + } + } + + self.consume(TokenType::RightParen, "Expect ')' after arguments.")?; + + // Estendi il source_slice per includere le parentesi di chiusura + let end_slice = self.previous().source_slice.end_position.clone(); + let full_slice = SourceSlice { + source_id: callee.source_slice.source_id.clone(), + start_position: start_slice.clone(), + end_position: end_slice.clone(), + }; + + Ok(AstNode::new( + Expr::Call { + callee: Box::new(callee), + arguments, + }, + full_slice, + )) } fn primary(&mut self) -> LoxResult> { @@ -523,9 +699,7 @@ impl Parser { if let Some(literal) = literal { Ok(AstNode::new(Expr::Literal { value: literal }, source_slice)) } else { - Err(self - .error(self.previous(), "Expected number literal") - .into()) + parse_error(self.peek().source_slice.clone(), "Expected number literal") } } TokenType::String => { @@ -535,9 +709,7 @@ impl Parser { if let Some(literal) = literal { Ok(AstNode::new(Expr::Literal { value: literal }, source_slice)) } else { - Err(self - .error(self.previous(), "Expected string literal") - .into()) + parse_error(self.peek().source_slice.clone(), "Expected string literal") } } TokenType::LeftParen => { @@ -556,9 +728,9 @@ impl Parser { let name = self.peek().lexeme.clone(); let source_slice = self.peek().source_slice.clone(); self.advance(); - Ok(AstNode::new(Expr::Variable { name }, source_slice)) + Ok(AstNode::new(Expr::Identifier { name }, source_slice)) } - _ => Err(self.error(self.peek(), "Expect expression.").into()), + _ => parse_error(self.peek().source_slice.clone(), "Expect expression."), } } @@ -606,14 +778,7 @@ impl Parser { self.advance(); Ok(self.previous()) } else { - Err(self.error(self.peek(), message).into()) - } - } - - fn error(&self, token: &Token, message: &str) -> LoxError { - LoxError::ParseError { - source_slice: token.source_slice.clone(), - message: message.to_string(), + return parse_error(self.peek().source_slice.clone(), message); } } @@ -628,7 +793,6 @@ impl Parser { match self.peek().token_type { TokenType::Class | TokenType::Fun - | TokenType::Var | TokenType::For | TokenType::If | TokenType::While diff --git a/src/frontend/tokens.rs b/src/frontend/tokens.rs index 9535000..a3345c3 100644 --- a/src/frontend/tokens.rs +++ b/src/frontend/tokens.rs @@ -1,6 +1,13 @@ use std::fmt; -use crate::frontend::source_registry::SourceSlice; +use crate::{ + backend::environment::EnvironmentStack, + frontend::{ + ast::{AstNode, Expr, Stmt}, + source_registry::SourceSlice, + }, + result::{runtime_error, LoxResult}, +}; #[derive(Debug, Clone, PartialEq)] pub enum LiteralValue { @@ -9,6 +16,37 @@ pub enum LiteralValue { Number(f64), Boolean(bool), Nil, + Function(LoxFunction), + NativeFunction(NativeFunction), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct LoxFunction { + pub parameters: Vec<(String, String)>, + pub body: AstNode, + pub closure: Option, + pub guard: Option>, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct NativeFunction { + pub arity: usize, + pub function: fn(&[LiteralValue]) -> LiteralValue, +} + +impl NativeFunction { + pub fn new(arity: usize, function: fn(&[LiteralValue]) -> LiteralValue) -> Self { + NativeFunction { arity, function } + } +} + +impl LiteralValue { + pub fn is_callable(&self) -> bool { + match self { + LiteralValue::Function(..) | LiteralValue::NativeFunction(..) => true, + _ => false, + } + } } impl fmt::Display for LiteralValue { @@ -19,6 +57,8 @@ impl fmt::Display for LiteralValue { LiteralValue::Number(n) => write!(f, "{}", n), LiteralValue::Boolean(b) => write!(f, "{}", b), LiteralValue::Nil => write!(f, "nil"), + LiteralValue::Function(..) => write!(f, ""), + LiteralValue::NativeFunction(..) => write!(f, ""), } } } @@ -37,6 +77,7 @@ pub enum TokenType { Minus, Plus, Semicolon, + Colon, Slash, Star, Percent, @@ -57,6 +98,7 @@ pub enum TokenType { Number, // Keywords + Fn, And, Class, StartBlock, @@ -134,6 +176,8 @@ impl fmt::Display for TokenType { TokenType::Val => write!(f, "val"), TokenType::Percent => write!(f, "%"), TokenType::In => write!(f, "in"), + TokenType::Colon => write!(f, ":"), + TokenType::Fn => write!(f, "fn"), } } } diff --git a/src/logging/display_ast.rs b/src/logging/display_ast.rs index 4b9ae82..c21f473 100644 --- a/src/logging/display_ast.rs +++ b/src/logging/display_ast.rs @@ -165,7 +165,6 @@ impl PrettyPrint for Expr { write!(f, "{}Literal {{ value: {:?} }}", indent, value) } } - Expr::Binary { left, operator, @@ -195,7 +194,6 @@ impl PrettyPrint for Expr { write!(f, "{}}}", indent) } } - Expr::Unary { operator, operand } => { if ctx.config.compact { let operand_str = @@ -215,7 +213,6 @@ impl PrettyPrint for Expr { write!(f, "{}}}", indent) } } - Expr::Grouping { expression } => { if ctx.config.compact { let expr_str = @@ -229,12 +226,27 @@ impl PrettyPrint for Expr { write!(f, "{}}}", indent) } } - - Expr::Variable { name } => { + Expr::Identifier { name } => { if ctx.config.compact { write!(f, "{}", name) } else { - write!(f, "{}Variable {{ name: {:?} }}", indent, name) + write!(f, "{}Identifier {{ name: {:?} }}", indent, name) + } + } + Expr::Call { callee, arguments } => { + if ctx.config.compact { + write!(f, "{}", callee) + } else { + write!(f, "{}Call {{ callee: ", indent)?; + callee.pretty_print(&ctx.child_context(true), f)?; + write!(f, ", arguments: [")?; + for (i, arg) in arguments.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + arg.pretty_print(&ctx.child_context(true), f)?; + } + write!(f, "] }}") } } } @@ -276,7 +288,7 @@ impl PrettyPrint for Stmt { write!(f, "{}}}", indent) } } - Stmt::Var { name, initializer } => { + Stmt::VarDeclaration { name, initializer } => { if ctx.config.compact { match initializer { Some(init) => { @@ -302,7 +314,7 @@ impl PrettyPrint for Stmt { write!(f, "{}}}", indent) } } - Stmt::Assign { name, value } => { + Stmt::VarAssigment { name, value } => { if ctx.config.compact { let value_str = pretty_print_with_config(value.as_ref(), &PrettyConfig::compact()); @@ -412,13 +424,16 @@ impl PrettyPrint for Stmt { } Stmt::For { variable, - iterable, + condition, + increment, body, } => { write!(f, "{}for (", ctx.child_context(true).indent())?; variable.pretty_print(&ctx.child_context(true), f)?; - write!(f, " in ")?; - iterable.pretty_print(&ctx.child_context(true), f)?; + write!(f, "; ")?; + condition.pretty_print(&ctx.child_context(true), f)?; + write!(f, "; ")?; + increment.pretty_print(&ctx.child_context(true), f)?; writeln!(f, ") {{")?; body.pretty_print(&ctx.child_context(true), f)?; writeln!(f, "{}}}", ctx.child_context(true).indent()) diff --git a/src/logging/display_token.rs b/src/logging/display_token.rs index a4db9aa..df15511 100644 --- a/src/logging/display_token.rs +++ b/src/logging/display_token.rs @@ -196,6 +196,8 @@ impl Token { TokenType::Val => "VAL", TokenType::Eof => "EOF", TokenType::In => "IN", + TokenType::Colon => ":", + TokenType::Fn => "FN", } } @@ -232,6 +234,7 @@ impl Token { | TokenType::Super | TokenType::This | TokenType::Var + | TokenType::Fn | TokenType::Val => ("\x1b[34m", self.token_type_symbol()), TokenType::Identifier | TokenType::String | TokenType::Number => { ("\x1b[32m", self.token_type_symbol()) @@ -243,6 +246,7 @@ impl Token { | TokenType::LeftBracket | TokenType::RightBracket | TokenType::Comma + | TokenType::Colon | TokenType::Dot | TokenType::Semicolon => ("\x1b[37m", self.token_type_symbol()), TokenType::StartBlock | TokenType::EndBlock => ("\x1b[35m", self.token_type_symbol()), diff --git a/src/main.rs b/src/main.rs index ce2694f..957bc57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 = 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) { + 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 { - 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 { + 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::>() + .join("\n") +} + +fn format_ast(ast: &[crate::frontend::ast::AstNode]) -> 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::>() + .join("\n\n") +} diff --git a/src/prompt.rs b/src/prompt.rs deleted file mode 100644 index 88218b1..0000000 --- a/src/prompt.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::io::{self, Write}; - -use crate::{frontend::source_registry::SourceId, result::LoxResult}; - -pub fn prompt_lines(mut process_source: T) -> impl Iterator -where - T: FnMut(String) -> LoxResult, -{ - std::iter::from_fn(move || { - print!("> "); - io::stdout().flush().ok()?; - - let mut input = String::new(); - io::stdin().read_line(&mut input).ok()?; - - let line = input.trim(); - if line == "exit" || line.is_empty() { - None - } else { - match process_source(line.to_string()) { - Ok(source_id) => Some(source_id), - Err(err) => { - eprintln!("Error: {}", err); - None - } - } - } - }) -} diff --git a/src/result.rs b/src/result.rs index aa708ae..9f0ec6f 100644 --- a/src/result.rs +++ b/src/result.rs @@ -402,6 +402,7 @@ impl std::error::Error for LoxError {} pub type LoxResult = Result; /// Helper function to create a lexical error +#[allow(dead_code)] pub fn lexical_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { Err(LoxError::LexicalError { source_slice, @@ -410,6 +411,7 @@ pub fn lexical_error(source_slice: SourceSlice, message: impl Into) - } /// Helper function to create a parse error +#[allow(dead_code)] pub fn parse_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { Err(LoxError::ParseError { source_slice, @@ -418,7 +420,6 @@ pub fn parse_error(source_slice: SourceSlice, message: impl Into) -> } /// Helper function to create a runtime error -#[allow(dead_code)] pub fn runtime_error(source_slice: SourceSlice, message: impl Into) -> LoxResult { Err(LoxError::RuntimeError { source_slice,