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.
345 lines
12 KiB
Rust
345 lines
12 KiB
Rust
use crate::frontend::{
|
|
source_registry::SourceSlice,
|
|
tokens::{LiteralValue, TokenType},
|
|
};
|
|
use std::fmt::{Debug, Display};
|
|
/*
|
|
* grammar:
|
|
* program -> statement* EOF
|
|
* statement -> expression_statement
|
|
* | print_statement
|
|
* | var_statement
|
|
* | block_statement
|
|
* | assignment_statement
|
|
* | if_statement
|
|
*
|
|
* expression_statement -> expression ";"
|
|
* print_statement -> "print" 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"
|
|
* while_statement -> "while" expression "do" statement "end"
|
|
*
|
|
* expression -> assignment
|
|
* assignment -> IDENTIFIER "=" assignment | logical_or
|
|
* logical_or -> logical_and (("or" logical_and)*
|
|
* logical_and -> equality (("and" equality)*
|
|
* equality -> comparison (("==" | "!=") comparison)*
|
|
* comparison -> term ((">" | ">=" | "<" | "<=") term)*
|
|
* term -> factor (("+" | "-") factor)*
|
|
* factor -> unary (("*" | "/") unary)*
|
|
* unary -> ("!" | "-") unary | call
|
|
* call -> primary ("(" arguments ")")*
|
|
* arguments -> expression ("," expression)*
|
|
* primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
|
*/
|
|
#[derive(Clone, PartialEq)]
|
|
pub enum Expr {
|
|
Literal {
|
|
value: LiteralValue,
|
|
},
|
|
Binary {
|
|
left: Box<AstNode<Expr>>,
|
|
operator: TokenType,
|
|
right: Box<AstNode<Expr>>,
|
|
},
|
|
Unary {
|
|
operator: TokenType,
|
|
operand: Box<AstNode<Expr>>,
|
|
},
|
|
Grouping {
|
|
expression: Box<AstNode<Expr>>,
|
|
},
|
|
Identifier {
|
|
name: String,
|
|
},
|
|
Call {
|
|
callee: Box<AstNode<Expr>>,
|
|
arguments: Vec<AstNode<Expr>>,
|
|
},
|
|
}
|
|
|
|
// Implementazione Display per Expr (per debugging)
|
|
impl std::fmt::Display for Expr {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Expr::Literal { value } => write!(f, "Literal {}", value),
|
|
Expr::Binary {
|
|
left,
|
|
operator,
|
|
right,
|
|
} => 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::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(", ")
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for Expr {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Expr::Literal { value } => write!(f, "{:?}", value),
|
|
Expr::Binary {
|
|
left,
|
|
operator,
|
|
right,
|
|
} => 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::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, PartialEq)]
|
|
pub enum Stmt {
|
|
Expression {
|
|
expression: Box<AstNode<Expr>>,
|
|
},
|
|
Print {
|
|
expression: Box<AstNode<Expr>>,
|
|
},
|
|
Stmt {
|
|
expression: Box<AstNode<Expr>>,
|
|
},
|
|
VarDeclaration {
|
|
name: String,
|
|
initializer: Option<Box<AstNode<Expr>>>,
|
|
},
|
|
VarAssigment {
|
|
name: String,
|
|
value: Box<AstNode<Expr>>,
|
|
},
|
|
Return {
|
|
expression: Box<AstNode<Expr>>,
|
|
},
|
|
Block {
|
|
statements: Box<Vec<AstNode<Stmt>>>,
|
|
},
|
|
If {
|
|
condition: Box<AstNode<Expr>>,
|
|
then_branch: Box<AstNode<Stmt>>,
|
|
elif_branch: Vec<(Box<AstNode<Expr>>, Box<AstNode<Stmt>>)>,
|
|
else_branch: Option<Box<AstNode<Stmt>>>,
|
|
},
|
|
While {
|
|
condition: Box<AstNode<Expr>>,
|
|
body: Box<AstNode<Stmt>>,
|
|
},
|
|
For {
|
|
variable: Box<AstNode<Stmt>>,
|
|
condition: Box<AstNode<Expr>>,
|
|
increment: Box<AstNode<Stmt>>,
|
|
body: Box<AstNode<Stmt>>,
|
|
},
|
|
}
|
|
|
|
impl Display for Stmt {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Stmt::Expression { expression } => write!(f, "Expression ({})", expression.node),
|
|
Stmt::If {
|
|
condition,
|
|
then_branch,
|
|
elif_branch,
|
|
else_branch,
|
|
} => {
|
|
let mut result = format!("IF ({}) {{\n{}\n}}", condition.node, then_branch.node);
|
|
for (condition, branch) in elif_branch {
|
|
result.push_str(&format!(
|
|
" ELIF ({}) {{\n{}\n}}",
|
|
condition.node, branch.node
|
|
));
|
|
}
|
|
if let Some(else_branch) = else_branch {
|
|
result.push_str(&format!(" ELSE {{\n{}\n}}", else_branch.node));
|
|
}
|
|
write!(f, "IfStmt {}", result)
|
|
}
|
|
Stmt::Print { expression } => write!(f, "Print({});", expression.node),
|
|
Stmt::Stmt { expression } => write!(f, "Stmt({});", expression.node),
|
|
Stmt::VarDeclaration { name, initializer } => match initializer {
|
|
Some(init) => write!(f, "Var({} = {});", name, init.node),
|
|
None => write!(f, "Var({});", name),
|
|
},
|
|
Stmt::VarAssigment { name, value } => write!(f, "Assign({} = {});", name, value.node),
|
|
Stmt::Return { expression } => write!(f, "Return({});", expression.node),
|
|
Stmt::Block { statements } => write!(
|
|
f,
|
|
"Block([\n{}\n])",
|
|
statements
|
|
.iter()
|
|
.map(|stmt| format!("\t \t{}", stmt.node))
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
),
|
|
Stmt::While { condition, body } => {
|
|
write!(f, "While({}) {{\n{}\n}}", condition.node, body.node)
|
|
}
|
|
Stmt::For {
|
|
variable,
|
|
condition,
|
|
increment,
|
|
body,
|
|
} => write!(
|
|
f,
|
|
"For({} = {} in {}) {{\n{}\n}}",
|
|
variable.node, condition.node, increment.node, body.node
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for Stmt {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Stmt::Expression { expression } => write!(f, " Expression ({:?})", expression.node),
|
|
Stmt::If {
|
|
condition,
|
|
then_branch,
|
|
elif_branch,
|
|
else_branch,
|
|
} => {
|
|
let mut result =
|
|
format!("IF ({:?}) {{\n{:?}\n}}", condition.node, then_branch.node);
|
|
for (condition, branch) in elif_branch {
|
|
result.push_str(&format!(
|
|
" ELIF ({:?}) {{\n{:?}\n}}",
|
|
condition.node, branch.node
|
|
));
|
|
}
|
|
if let Some(else_branch) = else_branch {
|
|
result.push_str(&format!(" ELSE {{\n{:?}\n}}", else_branch.node));
|
|
}
|
|
write!(f, "IfStmt {:?}", result)
|
|
}
|
|
Stmt::Print { expression } => write!(f, "Print({:?});", expression.node),
|
|
Stmt::Stmt { expression } => write!(f, "Stmt({:?});", expression.node),
|
|
Stmt::VarDeclaration { name, initializer } => match initializer {
|
|
Some(init) => write!(f, "Var({:?} = {:?});", name, init.node),
|
|
None => write!(f, "Var({:?});", name),
|
|
},
|
|
Stmt::VarAssigment { name, value } => {
|
|
write!(f, "Assign({:?} = {:?});", name, value.node)
|
|
}
|
|
Stmt::Return { expression } => write!(f, "Return({:?});", expression.node),
|
|
Stmt::Block { statements } => write!(
|
|
f,
|
|
"Block([\n{:?}\n])",
|
|
statements
|
|
.iter()
|
|
.map(|stmt| format!("{:?}", stmt.node))
|
|
.collect::<Vec<_>>()
|
|
.join("\t\t\n")
|
|
),
|
|
Stmt::While { condition, body } => {
|
|
write!(f, "While({:?}) {{\n{:?}\n}}", condition.node, body.node)
|
|
}
|
|
Stmt::For {
|
|
variable,
|
|
condition,
|
|
increment,
|
|
body,
|
|
} => write!(
|
|
f,
|
|
"For({:?} = {:?} in {:?}) {{\n{:?}\n}}",
|
|
variable.node, condition.node, increment.node, body.node
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait AstNodeKind {
|
|
fn kind(&self) -> &'static str;
|
|
}
|
|
|
|
impl AstNodeKind for Expr {
|
|
fn kind(&self) -> &'static str {
|
|
match self {
|
|
Expr::Literal { value: _ } => "literal",
|
|
Expr::Binary {
|
|
left: _,
|
|
operator: _,
|
|
right: _,
|
|
} => "binary expression",
|
|
Expr::Unary { .. } => "unary expression",
|
|
Expr::Grouping { .. } => "grouping expression",
|
|
Expr::Identifier { .. } => "variable expression",
|
|
Expr::Call { .. } => "call expression",
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AstNodeKind for Stmt {
|
|
fn kind(&self) -> &'static str {
|
|
match self {
|
|
Stmt::Expression { .. } => "expression",
|
|
Stmt::VarDeclaration { .. } => "variable declaration",
|
|
Stmt::VarAssigment { .. } => "assignment",
|
|
Stmt::Return { .. } => "return statement",
|
|
Stmt::Block { .. } => "block statement",
|
|
Stmt::If { .. } => "if statement",
|
|
Stmt::Print { .. } => "print statement",
|
|
Stmt::Stmt { .. } => "statement",
|
|
Stmt::While { .. } => "while statement",
|
|
Stmt::For { .. } => "for statement",
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Default)]
|
|
pub struct AstNode<T: AstNodeKind + Debug + Display> {
|
|
pub node: T,
|
|
pub source_slice: SourceSlice,
|
|
}
|
|
|
|
impl<T: AstNodeKind + Debug + Display> Display for AstNode<T> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"AstNode \n{{ \n\tnode: {},\n\tsource_slice: {}, \n}}",
|
|
self.node, self.source_slice
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<T: AstNodeKind + Debug + Display> Debug for AstNode<T> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(
|
|
f,
|
|
"AstNode \n{{ \n\tnode: {:?},\n\tsource_slice: {:?}, \n}}",
|
|
self.node, self.source_slice
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<T: AstNodeKind + Debug + Display> AstNode<T> {
|
|
pub fn new(node: T, source_slice: SourceSlice) -> Self {
|
|
AstNode { node, source_slice }
|
|
}
|
|
}
|