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:
-24
@@ -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
|
|
||||||
@@ -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)
|
||||||
@@ -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()
|
||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
result::{runtime_error, LoxResult},
|
result::{runtime_error, LoxResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct EnvironmentStack {
|
pub struct EnvironmentStack {
|
||||||
stack: Vec<HashMap<String, LiteralValue>>,
|
stack: Vec<HashMap<String, LiteralValue>>,
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,30 @@ impl EnvironmentStack {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, name: String, value: LiteralValue) {
|
pub fn declare(&mut self, name: String, value: LiteralValue) -> LoxResult<LiteralValue> {
|
||||||
self.stack.last_mut().unwrap().insert(name, value);
|
self.stack.last_mut().unwrap().insert(name, value.clone());
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, name: String, value: LiteralValue) -> LoxResult<LiteralValue> {
|
||||||
|
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),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+175
-70
@@ -3,9 +3,10 @@ use crate::{
|
|||||||
frontend::{
|
frontend::{
|
||||||
ast::{AstNode, AstNodeKind, Expr, Stmt},
|
ast::{AstNode, AstNodeKind, Expr, Stmt},
|
||||||
source_registry::SourceSlice,
|
source_registry::SourceSlice,
|
||||||
tokens::{LiteralValue, TokenType},
|
tokens::{LiteralValue, NativeFunction, TokenType},
|
||||||
},
|
},
|
||||||
result::{LoxError, LoxResult},
|
logging::display_ast::PrettyPrintExt,
|
||||||
|
result::{runtime_error, LoxError, LoxResult},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
@@ -174,15 +175,15 @@ pub struct Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait EvaluateInterpreter<T> {
|
pub trait EvaluateInterpreter<T> {
|
||||||
fn interpret(&mut self, stmt: T) -> LoxResult<LiteralValue>;
|
fn evaluate(&mut self, stmt: T) -> LoxResult<LiteralValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>> for Interpreter
|
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>> for Interpreter
|
||||||
where
|
where
|
||||||
Interpreter: EvaluateInterpreter<R>,
|
Interpreter: EvaluateInterpreter<R>,
|
||||||
{
|
{
|
||||||
fn interpret(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
|
fn evaluate(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
|
||||||
match self.interpret(stmt.node.clone()) {
|
match self.evaluate(stmt.node.clone()) {
|
||||||
Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
Err(err) => Err(LoxError::RuntimeError {
|
Err(err) => Err(LoxError::RuntimeError {
|
||||||
source_slice: stmt.source_slice,
|
source_slice: stmt.source_slice,
|
||||||
@@ -194,40 +195,109 @@ where
|
|||||||
|
|
||||||
// Direct Expr evaluation to avoid infinite recursion
|
// Direct Expr evaluation to avoid infinite recursion
|
||||||
impl EvaluateInterpreter<Expr> for Interpreter {
|
impl EvaluateInterpreter<Expr> for Interpreter {
|
||||||
fn interpret(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
|
fn evaluate(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Literal { value } => Ok(value),
|
Expr::Literal { value } => Ok(value),
|
||||||
Expr::Variable { name } => self.enviorment.get(&name),
|
Expr::Identifier { name } => self.enviorment.get(&name),
|
||||||
Expr::Binary {
|
Expr::Binary {
|
||||||
left,
|
left,
|
||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => {
|
} => {
|
||||||
let left_val = self.interpret(*left)?;
|
let left_val = self.evaluate(*left)?;
|
||||||
let right_val = self.interpret(*right)?;
|
let right_val = self.evaluate(*right)?;
|
||||||
self.evaluate_binary(left_val, operator, right_val)
|
self.evaluate_binary(left_val, operator, right_val)
|
||||||
}
|
}
|
||||||
Expr::Unary { operator, operand } => {
|
Expr::Unary { operator, operand } => {
|
||||||
let operand_val = self.interpret(*operand)?;
|
let operand_val = self.evaluate(*operand)?;
|
||||||
self.evaluate_unary(operator, operand_val)
|
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<AstNode<Stmt>> for Interpreter {
|
impl EvaluateInterpreter<AstNode<Stmt>> for Interpreter {
|
||||||
fn interpret(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
|
fn evaluate(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
|
||||||
let stmt = node.node;
|
let stmt = node.node;
|
||||||
let _source_slice = node.source_slice;
|
let result = self.interpret_stmt_inner(stmt);
|
||||||
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 {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
let mut env = EnvironmentStack::new();
|
||||||
enviorment: 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<AstNode<Expr>>,
|
||||||
|
arguments: Vec<AstNode<Expr>>,
|
||||||
|
) -> LoxResult<LiteralValue> {
|
||||||
|
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::<Result<Vec<LiteralValue>, 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::<LoxResult<Vec<LiteralValue>>>()?;
|
||||||
|
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::LessEqual => Ok(LiteralValue::Boolean(left <= right)),
|
||||||
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)),
|
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)),
|
||||||
TokenType::BangEqual => 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!(
|
_ => Err(error(format!(
|
||||||
"Unsupported binary operator: {:?}",
|
"Unsupported binary operator: {:?}",
|
||||||
operator
|
operator
|
||||||
@@ -269,75 +341,108 @@ impl Interpreter {
|
|||||||
|
|
||||||
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
|
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expression { expression } => self.interpret(*expression),
|
Stmt::Expression { expression } => self.evaluate(*expression),
|
||||||
Stmt::Print { expression } => {
|
Stmt::Print { expression } => {
|
||||||
let value = self.interpret(*expression)?;
|
let value = self.evaluate(*expression)?;
|
||||||
println!("print interpreter: \t{}", value);
|
println!("print interpreter: \t{}", value);
|
||||||
Ok(LiteralValue::Nil)
|
Ok(LiteralValue::Nil)
|
||||||
}
|
}
|
||||||
Stmt::Block { statements } => self.evaluate_block(*statements),
|
Stmt::Block { statements } => self.evaluate_block(*statements),
|
||||||
Stmt::Stmt { expression } => self.interpret(*expression.clone()),
|
Stmt::Stmt { expression } => self.evaluate(*expression.clone()),
|
||||||
Stmt::Return { expression } => self.interpret(*expression),
|
Stmt::Return { expression } => self.evaluate(*expression),
|
||||||
Stmt::Var { name, initializer } => {
|
Stmt::VarDeclaration { name, initializer } => {
|
||||||
let value = if let Some(expr) = initializer {
|
let value = match initializer {
|
||||||
self.interpret(*expr)?
|
Some(expr_node) => match &expr_node.node {
|
||||||
} else {
|
Expr::Literal { value } => {
|
||||||
LiteralValue::Nil
|
if value.is_callable() {
|
||||||
|
value.clone()
|
||||||
|
} else {
|
||||||
|
value.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.evaluate(*expr_node)?,
|
||||||
|
},
|
||||||
|
None => LiteralValue::Nil,
|
||||||
};
|
};
|
||||||
self.enviorment.set(name.clone(), value);
|
self.enviorment.declare(name.clone(), value)
|
||||||
return Ok(LiteralValue::Nil);
|
|
||||||
}
|
}
|
||||||
Stmt::Assign { name, value } => {
|
|
||||||
let result = self.interpret(*value)?;
|
Stmt::VarAssigment { name, value } => {
|
||||||
self.enviorment.set(name.clone(), result);
|
let result = self.evaluate(*value)?;
|
||||||
Ok(LiteralValue::Nil)
|
self.enviorment.set(name.clone(), result)
|
||||||
}
|
}
|
||||||
Stmt::If {
|
Stmt::If {
|
||||||
condition,
|
condition,
|
||||||
then_branch,
|
then_branch,
|
||||||
elif_branch,
|
elif_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
} => {
|
} => self.evaluate_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(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While { condition, body } => {
|
||||||
while self.interpret(*condition.clone())?.is_truthy() {
|
while self.evaluate(*condition.clone())?.is_truthy() {
|
||||||
self.interpret(*body.clone())?;
|
self.evaluate(*body.clone())?;
|
||||||
}
|
}
|
||||||
Ok(LiteralValue::Nil)
|
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<AstNode<Expr>>,
|
||||||
|
then_branch: Box<AstNode<Stmt>>,
|
||||||
|
elif_branch: Vec<(Box<AstNode<Expr>>, Box<AstNode<Stmt>>)>,
|
||||||
|
else_branch: Option<Box<AstNode<Stmt>>>,
|
||||||
|
) -> LoxResult<LiteralValue> {
|
||||||
|
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
|
// Ora elements è sempre disponibile
|
||||||
for statement in elements.iter() {
|
for statement in elements.iter() {
|
||||||
self.interpret((*statement).clone())?;
|
self.evaluate((*statement).clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestisci l'espressione finale se presente
|
// Gestisci l'espressione finale se presente
|
||||||
match final_expr {
|
match final_expr {
|
||||||
Some(expr) => {
|
Some(expr) => {
|
||||||
let res = self.interpret(*expr.clone());
|
let res = self.evaluate(*expr.clone());
|
||||||
self.enviorment.pop_scope();
|
self.enviorment.pop_scope();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-30
@@ -15,7 +15,9 @@ use std::fmt::{Debug, Display};
|
|||||||
*
|
*
|
||||||
* expression_statement -> expression ";"
|
* expression_statement -> expression ";"
|
||||||
* print_statement -> "print" 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 ";"
|
* assignment_statement -> IDENTIFIER "=" expression ";"
|
||||||
* block_statement -> "do" statement* "end"
|
* block_statement -> "do" statement* "end"
|
||||||
* if_statement -> "if" expression "then" statement ("elif" expression "then" statement)* ("else" 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)*
|
* comparison -> term ((">" | ">=" | "<" | "<=") term)*
|
||||||
* term -> factor (("+" | "-") factor)*
|
* term -> factor (("+" | "-") factor)*
|
||||||
* factor -> unary (("*" | "/") unary)*
|
* factor -> unary (("*" | "/") unary)*
|
||||||
* unary -> ("!" | "-") unary | primary
|
* unary -> ("!" | "-") unary | call
|
||||||
|
* call -> primary ("(" arguments ")")*
|
||||||
|
* arguments -> expression ("," expression)*
|
||||||
* primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
* primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
||||||
*/
|
*/
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Literal {
|
Literal {
|
||||||
value: LiteralValue,
|
value: LiteralValue,
|
||||||
@@ -49,9 +53,13 @@ pub enum Expr {
|
|||||||
Grouping {
|
Grouping {
|
||||||
expression: Box<AstNode<Expr>>,
|
expression: Box<AstNode<Expr>>,
|
||||||
},
|
},
|
||||||
Variable {
|
Identifier {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
Call {
|
||||||
|
callee: Box<AstNode<Expr>>,
|
||||||
|
arguments: Vec<AstNode<Expr>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementazione Display per Expr (per debugging)
|
// Implementazione Display per Expr (per debugging)
|
||||||
@@ -66,7 +74,17 @@ impl std::fmt::Display for Expr {
|
|||||||
} => write!(f, "Binary ({} {} {})", left.node, operator, right.node),
|
} => write!(f, "Binary ({} {} {})", left.node, operator, right.node),
|
||||||
Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand.node),
|
Expr::Unary { operator, operand } => write!(f, "Unary ({} {})", operator, operand.node),
|
||||||
Expr::Grouping { expression } => write!(f, "Grouping (group {})", expression.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::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,12 +100,22 @@ impl Debug for Expr {
|
|||||||
} => write!(f, "({:?} {:?} {:?})", operator, left.node, right.node),
|
} => write!(f, "({:?} {:?} {:?})", operator, left.node, right.node),
|
||||||
Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand.node),
|
Expr::Unary { operator, operand } => write!(f, "({:?} {:?})", operator, operand.node),
|
||||||
Expr::Grouping { expression } => write!(f, "(group {:?})", expression.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::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Expression {
|
Expression {
|
||||||
expression: Box<AstNode<Expr>>,
|
expression: Box<AstNode<Expr>>,
|
||||||
@@ -98,11 +126,11 @@ pub enum Stmt {
|
|||||||
Stmt {
|
Stmt {
|
||||||
expression: Box<AstNode<Expr>>,
|
expression: Box<AstNode<Expr>>,
|
||||||
},
|
},
|
||||||
Var {
|
VarDeclaration {
|
||||||
name: String,
|
name: String,
|
||||||
initializer: Option<Box<AstNode<Expr>>>,
|
initializer: Option<Box<AstNode<Expr>>>,
|
||||||
},
|
},
|
||||||
Assign {
|
VarAssigment {
|
||||||
name: String,
|
name: String,
|
||||||
value: Box<AstNode<Expr>>,
|
value: Box<AstNode<Expr>>,
|
||||||
},
|
},
|
||||||
@@ -123,8 +151,9 @@ pub enum Stmt {
|
|||||||
body: Box<AstNode<Stmt>>,
|
body: Box<AstNode<Stmt>>,
|
||||||
},
|
},
|
||||||
For {
|
For {
|
||||||
variable: Box<AstNode<Expr>>,
|
variable: Box<AstNode<Stmt>>,
|
||||||
iterable: Box<AstNode<Expr>>,
|
condition: Box<AstNode<Expr>>,
|
||||||
|
increment: Box<AstNode<Stmt>>,
|
||||||
body: Box<AstNode<Stmt>>,
|
body: Box<AstNode<Stmt>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -153,11 +182,11 @@ impl Display for Stmt {
|
|||||||
}
|
}
|
||||||
Stmt::Print { expression } => write!(f, "Print({});", expression.node),
|
Stmt::Print { expression } => write!(f, "Print({});", expression.node),
|
||||||
Stmt::Stmt { expression } => write!(f, "Stmt({});", 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),
|
Some(init) => write!(f, "Var({} = {});", name, init.node),
|
||||||
None => write!(f, "Var({});", name),
|
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::Return { expression } => write!(f, "Return({});", expression.node),
|
||||||
Stmt::Block { statements } => write!(
|
Stmt::Block { statements } => write!(
|
||||||
f,
|
f,
|
||||||
@@ -173,12 +202,13 @@ impl Display for Stmt {
|
|||||||
}
|
}
|
||||||
Stmt::For {
|
Stmt::For {
|
||||||
variable,
|
variable,
|
||||||
iterable,
|
condition,
|
||||||
|
increment,
|
||||||
body,
|
body,
|
||||||
} => write!(
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"For({} in {}) {{\n{}\n}}",
|
"For({} = {} in {}) {{\n{}\n}}",
|
||||||
variable.node, iterable.node, body.node
|
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::Print { expression } => write!(f, "Print({:?});", expression.node),
|
||||||
Stmt::Stmt { expression } => write!(f, "Stmt({:?});", 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),
|
Some(init) => write!(f, "Var({:?} = {:?});", name, init.node),
|
||||||
None => write!(f, "Var({:?});", name),
|
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::Return { expression } => write!(f, "Return({:?});", expression.node),
|
||||||
Stmt::Block { statements } => write!(
|
Stmt::Block { statements } => write!(
|
||||||
f,
|
f,
|
||||||
@@ -229,12 +261,13 @@ impl Debug for Stmt {
|
|||||||
}
|
}
|
||||||
Stmt::For {
|
Stmt::For {
|
||||||
variable,
|
variable,
|
||||||
iterable,
|
condition,
|
||||||
|
increment,
|
||||||
body,
|
body,
|
||||||
} => write!(
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"For({:?} in {:?}) {{\n{:?}\n}}",
|
"For({:?} = {:?} in {:?}) {{\n{:?}\n}}",
|
||||||
variable.node, iterable.node, body.node
|
variable.node, condition.node, increment.node, body.node
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,12 +286,10 @@ impl AstNodeKind for Expr {
|
|||||||
operator: _,
|
operator: _,
|
||||||
right: _,
|
right: _,
|
||||||
} => "binary expression",
|
} => "binary expression",
|
||||||
Expr::Unary {
|
Expr::Unary { .. } => "unary expression",
|
||||||
operator: _,
|
Expr::Grouping { .. } => "grouping expression",
|
||||||
operand: _,
|
Expr::Identifier { .. } => "variable expression",
|
||||||
} => "unary expression",
|
Expr::Call { .. } => "call expression",
|
||||||
Expr::Grouping { expression: _ } => "grouping expression",
|
|
||||||
Expr::Variable { name: _ } => "variable expression",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,8 +298,8 @@ impl AstNodeKind for Stmt {
|
|||||||
fn kind(&self) -> &'static str {
|
fn kind(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Stmt::Expression { .. } => "expression",
|
Stmt::Expression { .. } => "expression",
|
||||||
Stmt::Var { .. } => "variable declaration",
|
Stmt::VarDeclaration { .. } => "variable declaration",
|
||||||
Stmt::Assign { .. } => "assignment",
|
Stmt::VarAssigment { .. } => "assignment",
|
||||||
Stmt::Return { .. } => "return statement",
|
Stmt::Return { .. } => "return statement",
|
||||||
Stmt::Block { .. } => "block statement",
|
Stmt::Block { .. } => "block statement",
|
||||||
Stmt::If { .. } => "if statement",
|
Stmt::If { .. } => "if statement",
|
||||||
@@ -280,7 +311,7 @@ impl AstNodeKind for Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq, Default)]
|
||||||
pub struct AstNode<T: AstNodeKind + Debug + Display> {
|
pub struct AstNode<T: AstNodeKind + Debug + Display> {
|
||||||
pub node: T,
|
pub node: T,
|
||||||
pub source_slice: SourceSlice,
|
pub source_slice: SourceSlice,
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ fn get_keyword_token(word: &str) -> Option<TokenType> {
|
|||||||
"True" => Some(TokenType::True),
|
"True" => Some(TokenType::True),
|
||||||
"False" => Some(TokenType::False),
|
"False" => Some(TokenType::False),
|
||||||
"Nil" => Some(TokenType::Nil),
|
"Nil" => Some(TokenType::Nil),
|
||||||
|
"fn" => Some(TokenType::Fn),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,6 +147,7 @@ impl Lexer {
|
|||||||
('-', _) => Ok(Some(self.make_token(TokenType::Minus))),
|
('-', _) => Ok(Some(self.make_token(TokenType::Minus))),
|
||||||
('+', _) => Ok(Some(self.make_token(TokenType::Plus))),
|
('+', _) => Ok(Some(self.make_token(TokenType::Plus))),
|
||||||
(';', _) => Ok(Some(self.make_token(TokenType::Semicolon))),
|
(';', _) => 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::Star))),
|
||||||
('%', _) => Ok(Some(self.make_token(TokenType::Percent))),
|
('%', _) => Ok(Some(self.make_token(TokenType::Percent))),
|
||||||
('!', '=') => {
|
('!', '=') => {
|
||||||
@@ -262,7 +264,7 @@ impl Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(&mut self) -> LoxResult<Option<Token>> {
|
fn identifier(&mut self) -> LoxResult<Option<Token>> {
|
||||||
while self.peek().is_alphanumeric() {
|
while self.peek().is_alphanumeric() || self.peek() == '_' {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
let text = self.input[self.start_char..self.current_char].to_string();
|
let text = self.input[self.start_char..self.current_char].to_string();
|
||||||
|
|||||||
+217
-53
@@ -1,11 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
frontend::{
|
frontend::{
|
||||||
ast::{AstNode, Expr, Stmt},
|
ast::{AstNode, Expr, Stmt},
|
||||||
source_registry::SourceSlice,
|
source_registry::{SourcePosition, SourceSlice},
|
||||||
tokens::{LiteralValue, Token, TokenType},
|
tokens::{LiteralValue, LoxFunction, Token, TokenType},
|
||||||
},
|
},
|
||||||
logging::display_ast::{pretty_print_with_config, PrettyConfig},
|
logging::display_ast::{pretty_print_with_config, PrettyConfig},
|
||||||
result::{parse_error, LoxError, LoxResult},
|
result::{parse_error, runtime_error, LoxResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
@@ -58,10 +58,14 @@ impl Parser {
|
|||||||
|
|
||||||
fn statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
fn statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
match (&self.peek().token_type, &self.peek_next().token_type) {
|
match (&self.peek().token_type, &self.peek_next().token_type) {
|
||||||
(TokenType::Var, _) => self.var_statement(),
|
|
||||||
(TokenType::Print, _) => self.print_statement(),
|
(TokenType::Print, _) => self.print_statement(),
|
||||||
(TokenType::Return, _) => self.return_statement(),
|
(TokenType::Return, _) => self.return_statement(),
|
||||||
(TokenType::StartBlock, _) => self.block_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::Identifier, TokenType::Equal) => self.assignment_statement(),
|
||||||
(TokenType::If, _) => self.if_statement(),
|
(TokenType::If, _) => self.if_statement(),
|
||||||
(TokenType::While, _) => self.while_statement(),
|
(TokenType::While, _) => self.while_statement(),
|
||||||
@@ -72,46 +76,78 @@ impl Parser {
|
|||||||
|
|
||||||
fn for_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
fn for_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
let start_slice = self.peek().source_slice.clone();
|
let start_slice = self.peek().source_slice.clone();
|
||||||
self.advance();
|
self.advance(); // consume 'for'
|
||||||
let variable = if let Token {
|
|
||||||
token_type: TokenType::Identifier,
|
let variable = self.var_statement()?;
|
||||||
literal: Some(variable_name),
|
let condition = self.expression()?;
|
||||||
source_slice,
|
self.consume(TokenType::Semicolon, "Expected ';' after for condition")?;
|
||||||
..
|
let increment = self.assignment_statement()?;
|
||||||
} = self.peek()
|
|
||||||
{
|
self.consume(TokenType::StartBlock, "Expected 'do' after for range")?;
|
||||||
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 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(
|
let combined_slice = SourceSlice::from_positions(
|
||||||
start_slice.source_id,
|
start_slice.source_id,
|
||||||
start_slice.start_position,
|
start_slice.start_position,
|
||||||
end_slice.end_position,
|
end_slice.end_position,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(AstNode::new(
|
Ok(AstNode::new(
|
||||||
Stmt::For {
|
Stmt::For {
|
||||||
variable: Box::new(variable),
|
variable: Box::new(variable),
|
||||||
iterable: Box::new(iterable),
|
condition: Box::new(condition),
|
||||||
|
increment: Box::new(increment),
|
||||||
body: Box::new(body),
|
body: Box::new(body),
|
||||||
},
|
},
|
||||||
combined_slice,
|
combined_slice,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fn for_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
|
// 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<AstNode<Stmt>> {
|
fn while_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
let start_slice = self.peek().source_slice.clone();
|
let start_slice = self.peek().source_slice.clone();
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -178,11 +214,103 @@ impl Parser {
|
|||||||
|
|
||||||
fn var_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
fn var_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
let start_slice = self.peek().source_slice.clone();
|
let start_slice = self.peek().source_slice.clone();
|
||||||
self.advance();
|
|
||||||
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
||||||
let name_lexeme = name.lexeme.clone();
|
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<AstNode<Stmt>> {
|
||||||
|
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<Box<Expr>> = 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<AstNode<Stmt>> {
|
||||||
|
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(
|
let semicolon = self.consume(
|
||||||
TokenType::Semicolon,
|
TokenType::Semicolon,
|
||||||
"Expect ';' after variable declaration.",
|
"Expect ';' after variable declaration.",
|
||||||
@@ -194,14 +322,13 @@ impl Parser {
|
|||||||
end_slice.end_position,
|
end_slice.end_position,
|
||||||
);
|
);
|
||||||
Ok(AstNode::new(
|
Ok(AstNode::new(
|
||||||
Stmt::Var {
|
Stmt::VarDeclaration {
|
||||||
name: name_lexeme,
|
name: name_lexeme,
|
||||||
initializer: Some(Box::new(value)),
|
initializer: Some(Box::new(value)),
|
||||||
},
|
},
|
||||||
combined_slice,
|
combined_slice,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignment_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
fn assignment_statement(&mut self) -> LoxResult<AstNode<Stmt>> {
|
||||||
let start_slice = self.peek().source_slice.clone();
|
let start_slice = self.peek().source_slice.clone();
|
||||||
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
let name = self.consume(TokenType::Identifier, "Expect variable name.")?;
|
||||||
@@ -219,7 +346,7 @@ impl Parser {
|
|||||||
end_slice.end_position,
|
end_slice.end_position,
|
||||||
);
|
);
|
||||||
Ok(AstNode::new(
|
Ok(AstNode::new(
|
||||||
Stmt::Assign {
|
Stmt::VarAssigment {
|
||||||
name: name_lexeme,
|
name: name_lexeme,
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
},
|
},
|
||||||
@@ -481,7 +608,56 @@ impl Parser {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.primary()
|
self.call()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||||
|
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<Expr>) -> LoxResult<AstNode<Expr>> {
|
||||||
|
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<AstNode<Expr>> {
|
fn primary(&mut self) -> LoxResult<AstNode<Expr>> {
|
||||||
@@ -523,9 +699,7 @@ impl Parser {
|
|||||||
if let Some(literal) = literal {
|
if let Some(literal) = literal {
|
||||||
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
||||||
} else {
|
} else {
|
||||||
Err(self
|
parse_error(self.peek().source_slice.clone(), "Expected number literal")
|
||||||
.error(self.previous(), "Expected number literal")
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenType::String => {
|
TokenType::String => {
|
||||||
@@ -535,9 +709,7 @@ impl Parser {
|
|||||||
if let Some(literal) = literal {
|
if let Some(literal) = literal {
|
||||||
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
Ok(AstNode::new(Expr::Literal { value: literal }, source_slice))
|
||||||
} else {
|
} else {
|
||||||
Err(self
|
parse_error(self.peek().source_slice.clone(), "Expected string literal")
|
||||||
.error(self.previous(), "Expected string literal")
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenType::LeftParen => {
|
TokenType::LeftParen => {
|
||||||
@@ -556,9 +728,9 @@ impl Parser {
|
|||||||
let name = self.peek().lexeme.clone();
|
let name = self.peek().lexeme.clone();
|
||||||
let source_slice = self.peek().source_slice.clone();
|
let source_slice = self.peek().source_slice.clone();
|
||||||
self.advance();
|
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();
|
self.advance();
|
||||||
Ok(self.previous())
|
Ok(self.previous())
|
||||||
} else {
|
} else {
|
||||||
Err(self.error(self.peek(), message).into())
|
return parse_error(self.peek().source_slice.clone(), message);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error(&self, token: &Token, message: &str) -> LoxError {
|
|
||||||
LoxError::ParseError {
|
|
||||||
source_slice: token.source_slice.clone(),
|
|
||||||
message: message.to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +793,6 @@ impl Parser {
|
|||||||
match self.peek().token_type {
|
match self.peek().token_type {
|
||||||
TokenType::Class
|
TokenType::Class
|
||||||
| TokenType::Fun
|
| TokenType::Fun
|
||||||
| TokenType::Var
|
|
||||||
| TokenType::For
|
| TokenType::For
|
||||||
| TokenType::If
|
| TokenType::If
|
||||||
| TokenType::While
|
| TokenType::While
|
||||||
|
|||||||
+45
-1
@@ -1,6 +1,13 @@
|
|||||||
use std::fmt;
|
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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum LiteralValue {
|
pub enum LiteralValue {
|
||||||
@@ -9,6 +16,37 @@ pub enum LiteralValue {
|
|||||||
Number(f64),
|
Number(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
Function(LoxFunction),
|
||||||
|
NativeFunction(NativeFunction),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct LoxFunction {
|
||||||
|
pub parameters: Vec<(String, String)>,
|
||||||
|
pub body: AstNode<Stmt>,
|
||||||
|
pub closure: Option<EnvironmentStack>,
|
||||||
|
pub guard: Option<Box<Expr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
impl fmt::Display for LiteralValue {
|
||||||
@@ -19,6 +57,8 @@ impl fmt::Display for LiteralValue {
|
|||||||
LiteralValue::Number(n) => write!(f, "{}", n),
|
LiteralValue::Number(n) => write!(f, "{}", n),
|
||||||
LiteralValue::Boolean(b) => write!(f, "{}", b),
|
LiteralValue::Boolean(b) => write!(f, "{}", b),
|
||||||
LiteralValue::Nil => write!(f, "nil"),
|
LiteralValue::Nil => write!(f, "nil"),
|
||||||
|
LiteralValue::Function(..) => write!(f, "<fn lox function>"),
|
||||||
|
LiteralValue::NativeFunction(..) => write!(f, "<native native function>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,6 +77,7 @@ pub enum TokenType {
|
|||||||
Minus,
|
Minus,
|
||||||
Plus,
|
Plus,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
|
Colon,
|
||||||
Slash,
|
Slash,
|
||||||
Star,
|
Star,
|
||||||
Percent,
|
Percent,
|
||||||
@@ -57,6 +98,7 @@ pub enum TokenType {
|
|||||||
Number,
|
Number,
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
Fn,
|
||||||
And,
|
And,
|
||||||
Class,
|
Class,
|
||||||
StartBlock,
|
StartBlock,
|
||||||
@@ -134,6 +176,8 @@ impl fmt::Display for TokenType {
|
|||||||
TokenType::Val => write!(f, "val"),
|
TokenType::Val => write!(f, "val"),
|
||||||
TokenType::Percent => write!(f, "%"),
|
TokenType::Percent => write!(f, "%"),
|
||||||
TokenType::In => write!(f, "in"),
|
TokenType::In => write!(f, "in"),
|
||||||
|
TokenType::Colon => write!(f, ":"),
|
||||||
|
TokenType::Fn => write!(f, "fn"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-11
@@ -165,7 +165,6 @@ impl PrettyPrint for Expr {
|
|||||||
write!(f, "{}Literal {{ value: {:?} }}", indent, value)
|
write!(f, "{}Literal {{ value: {:?} }}", indent, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Binary {
|
Expr::Binary {
|
||||||
left,
|
left,
|
||||||
operator,
|
operator,
|
||||||
@@ -195,7 +194,6 @@ impl PrettyPrint for Expr {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Unary { operator, operand } => {
|
Expr::Unary { operator, operand } => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let operand_str =
|
let operand_str =
|
||||||
@@ -215,7 +213,6 @@ impl PrettyPrint for Expr {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Grouping { expression } => {
|
Expr::Grouping { expression } => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let expr_str =
|
let expr_str =
|
||||||
@@ -229,12 +226,27 @@ impl PrettyPrint for Expr {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Identifier { name } => {
|
||||||
Expr::Variable { name } => {
|
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
write!(f, "{}", name)
|
write!(f, "{}", name)
|
||||||
} else {
|
} 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)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Var { name, initializer } => {
|
Stmt::VarDeclaration { name, initializer } => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
match initializer {
|
match initializer {
|
||||||
Some(init) => {
|
Some(init) => {
|
||||||
@@ -302,7 +314,7 @@ impl PrettyPrint for Stmt {
|
|||||||
write!(f, "{}}}", indent)
|
write!(f, "{}}}", indent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Assign { name, value } => {
|
Stmt::VarAssigment { name, value } => {
|
||||||
if ctx.config.compact {
|
if ctx.config.compact {
|
||||||
let value_str =
|
let value_str =
|
||||||
pretty_print_with_config(value.as_ref(), &PrettyConfig::compact());
|
pretty_print_with_config(value.as_ref(), &PrettyConfig::compact());
|
||||||
@@ -412,13 +424,16 @@ impl PrettyPrint for Stmt {
|
|||||||
}
|
}
|
||||||
Stmt::For {
|
Stmt::For {
|
||||||
variable,
|
variable,
|
||||||
iterable,
|
condition,
|
||||||
|
increment,
|
||||||
body,
|
body,
|
||||||
} => {
|
} => {
|
||||||
write!(f, "{}for (", ctx.child_context(true).indent())?;
|
write!(f, "{}for (", ctx.child_context(true).indent())?;
|
||||||
variable.pretty_print(&ctx.child_context(true), f)?;
|
variable.pretty_print(&ctx.child_context(true), f)?;
|
||||||
write!(f, " in ")?;
|
write!(f, "; ")?;
|
||||||
iterable.pretty_print(&ctx.child_context(true), f)?;
|
condition.pretty_print(&ctx.child_context(true), f)?;
|
||||||
|
write!(f, "; ")?;
|
||||||
|
increment.pretty_print(&ctx.child_context(true), f)?;
|
||||||
writeln!(f, ") {{")?;
|
writeln!(f, ") {{")?;
|
||||||
body.pretty_print(&ctx.child_context(true), f)?;
|
body.pretty_print(&ctx.child_context(true), f)?;
|
||||||
writeln!(f, "{}}}", ctx.child_context(true).indent())
|
writeln!(f, "{}}}", ctx.child_context(true).indent())
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ impl Token {
|
|||||||
TokenType::Val => "VAL",
|
TokenType::Val => "VAL",
|
||||||
TokenType::Eof => "EOF",
|
TokenType::Eof => "EOF",
|
||||||
TokenType::In => "IN",
|
TokenType::In => "IN",
|
||||||
|
TokenType::Colon => ":",
|
||||||
|
TokenType::Fn => "FN",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +234,7 @@ impl Token {
|
|||||||
| TokenType::Super
|
| TokenType::Super
|
||||||
| TokenType::This
|
| TokenType::This
|
||||||
| TokenType::Var
|
| TokenType::Var
|
||||||
|
| TokenType::Fn
|
||||||
| TokenType::Val => ("\x1b[34m", self.token_type_symbol()),
|
| TokenType::Val => ("\x1b[34m", self.token_type_symbol()),
|
||||||
TokenType::Identifier | TokenType::String | TokenType::Number => {
|
TokenType::Identifier | TokenType::String | TokenType::Number => {
|
||||||
("\x1b[32m", self.token_type_symbol())
|
("\x1b[32m", self.token_type_symbol())
|
||||||
@@ -243,6 +246,7 @@ impl Token {
|
|||||||
| TokenType::LeftBracket
|
| TokenType::LeftBracket
|
||||||
| TokenType::RightBracket
|
| TokenType::RightBracket
|
||||||
| TokenType::Comma
|
| TokenType::Comma
|
||||||
|
| TokenType::Colon
|
||||||
| TokenType::Dot
|
| TokenType::Dot
|
||||||
| TokenType::Semicolon => ("\x1b[37m", self.token_type_symbol()),
|
| TokenType::Semicolon => ("\x1b[37m", self.token_type_symbol()),
|
||||||
TokenType::StartBlock | TokenType::EndBlock => ("\x1b[35m", self.token_type_symbol()),
|
TokenType::StartBlock | TokenType::EndBlock => ("\x1b[35m", self.token_type_symbol()),
|
||||||
|
|||||||
+167
-55
@@ -1,43 +1,82 @@
|
|||||||
mod backend;
|
mod backend;
|
||||||
mod frontend;
|
mod frontend;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod prompt;
|
|
||||||
mod result;
|
mod result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{
|
backend::interpreter::{EvaluateInterpreter, Interpreter},
|
||||||
environment::EnvironmentStack,
|
|
||||||
interpreter::{EvaluateInterpreter, Interpreter},
|
|
||||||
},
|
|
||||||
frontend::{
|
frontend::{
|
||||||
lexer::Lexer,
|
lexer::Lexer,
|
||||||
parser::Parser,
|
parser::Parser,
|
||||||
source_registry::{SourceId, SourceRegistry, SourceSlice},
|
source_registry::{SourceId, SourceRegistry},
|
||||||
},
|
},
|
||||||
prompt::prompt_lines,
|
|
||||||
result::{LoxError, LoxResult},
|
result::{LoxError, LoxResult},
|
||||||
};
|
};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum ExecutionStage {
|
||||||
|
Tokens,
|
||||||
|
Ast,
|
||||||
|
Full,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> LoxResult<()> {
|
fn main() -> LoxResult<()> {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let mut lox = LoxInterpreter::new();
|
let mut lox = LoxInterpreter::new();
|
||||||
|
|
||||||
let _ = if args.len() > 2 {
|
let (stage, file_path) = parse_args(&args);
|
||||||
eprintln!("Usage: {} [script]", args[0]);
|
|
||||||
std::process::exit(64);
|
let _ = match file_path {
|
||||||
} else if args.len() == 2 {
|
Some(path) => lox.run_file(&path, stage),
|
||||||
// Esegui file
|
None => lox.run_prompt(stage),
|
||||||
lox.run_file(&args[1])
|
|
||||||
} else {
|
|
||||||
// Modalità interattiva
|
|
||||||
lox.run_prompt()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
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 {
|
struct LoxInterpreter {
|
||||||
source_registry: SourceRegistry,
|
source_registry: SourceRegistry,
|
||||||
}
|
}
|
||||||
@@ -50,60 +89,133 @@ impl LoxInterpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Pipeline funzionale per file
|
// ✅ 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)
|
fs::read_to_string(path)
|
||||||
.map_err(|e| LoxError::IoError {
|
.map_err(|e| LoxError::IoError {
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
})
|
})
|
||||||
.and_then(|source| self.source_registry.add_source_string(source))
|
.and_then(|source| self.source_registry.add_source_string(source))
|
||||||
.and_then(|source_id| self.process_source(source_id))
|
.and_then(|source_id| self.process_source(source_id, stage))
|
||||||
.map(|result| println!("file {}", result))
|
.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
|
// ✅ Pipeline funzionale per prompt
|
||||||
fn run_prompt(&mut self) -> LoxResult<()> {
|
fn run_prompt(&mut self, stage: ExecutionStage) -> LoxResult<()> {
|
||||||
println!("Lox REPL - Type 'exit' to quit");
|
use std::io::{self, Write};
|
||||||
|
|
||||||
prompt_lines(|source| self.source_registry.add_source_string(source))
|
println!("Lox REPL (Stage: {:?}) - Type 'exit' to quit", stage);
|
||||||
.for_each(|line| println!("hi: {}", line));
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_source(&self, source_id: SourceId) -> LoxResult<String> {
|
fn process_source(&self, source_id: SourceId, stage: ExecutionStage) -> LoxResult<String> {
|
||||||
let mut lexer = Lexer::new(
|
let source_content = self.source_registry.get_by_id(source_id).content.clone();
|
||||||
self.source_registry.get_by_id(source_id).content.clone(),
|
let mut lexer = Lexer::new(source_content, source_id);
|
||||||
source_id,
|
|
||||||
);
|
// Stage 1: Tokenization
|
||||||
let res = lexer
|
let tokens = lexer.scans_tokens().or_else(|err| {
|
||||||
.scans_tokens()
|
err.print_with_context(&self.source_registry);
|
||||||
.and_then(|tokens| Parser::new(tokens).parse())
|
Err(err)
|
||||||
.and_then(|asts| {
|
})?;
|
||||||
// println!("asts: {:?}", asts);
|
|
||||||
Ok(asts)
|
if stage == ExecutionStage::Tokens {
|
||||||
})
|
return Ok(format_tokens(&tokens));
|
||||||
.and_then(|stmts| {
|
}
|
||||||
let mut interpreter = Interpreter::new();
|
|
||||||
let mut result = None;
|
// Stage 2: Parsing
|
||||||
for (index, stmt) in stmts.iter().enumerate() {
|
let ast = Parser::new(tokens).parse().or_else(|err| {
|
||||||
println!("executing stmt {}: {:?}", index, stmt);
|
err.print_with_context(&self.source_registry);
|
||||||
result = Some(interpreter.interpret(stmt.clone())?);
|
Err(err)
|
||||||
}
|
})?;
|
||||||
match result {
|
|
||||||
Some(res) => Ok(res),
|
if stage == ExecutionStage::Ast {
|
||||||
_ => Err(LoxError::RuntimeError {
|
return Ok(format_ast(&ast));
|
||||||
source_slice: SourceSlice::default(),
|
}
|
||||||
message: "what?????".to_string(),
|
|
||||||
}),
|
// Stage 3: Interpretation
|
||||||
}
|
let mut interpreter = Interpreter::new();
|
||||||
})
|
let mut result = None;
|
||||||
.and_then(|result| Ok(format!("{}", result)))
|
|
||||||
.or_else(|err| {
|
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.print_with_context(&self.source_registry);
|
||||||
Err(err)
|
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")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
use std::io::{self, Write};
|
|
||||||
|
|
||||||
use crate::{frontend::source_registry::SourceId, result::LoxResult};
|
|
||||||
|
|
||||||
pub fn prompt_lines<T>(mut process_source: T) -> impl Iterator<Item = SourceId>
|
|
||||||
where
|
|
||||||
T: FnMut(String) -> LoxResult<SourceId>,
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
+2
-1
@@ -402,6 +402,7 @@ impl std::error::Error for LoxError {}
|
|||||||
pub type LoxResult<T> = Result<T, LoxError>;
|
pub type LoxResult<T> = Result<T, LoxError>;
|
||||||
|
|
||||||
/// Helper function to create a lexical error
|
/// Helper function to create a lexical error
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn lexical_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
pub fn lexical_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
||||||
Err(LoxError::LexicalError {
|
Err(LoxError::LexicalError {
|
||||||
source_slice,
|
source_slice,
|
||||||
@@ -410,6 +411,7 @@ pub fn lexical_error<T>(source_slice: SourceSlice, message: impl Into<String>) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to create a parse error
|
/// Helper function to create a parse error
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn parse_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
pub fn parse_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
||||||
Err(LoxError::ParseError {
|
Err(LoxError::ParseError {
|
||||||
source_slice,
|
source_slice,
|
||||||
@@ -418,7 +420,6 @@ pub fn parse_error<T>(source_slice: SourceSlice, message: impl Into<String>) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to create a runtime error
|
/// Helper function to create a runtime error
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn runtime_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
pub fn runtime_error<T>(source_slice: SourceSlice, message: impl Into<String>) -> LoxResult<T> {
|
||||||
Err(LoxError::RuntimeError {
|
Err(LoxError::RuntimeError {
|
||||||
source_slice,
|
source_slice,
|
||||||
|
|||||||
Reference in New Issue
Block a user