use crate::tokens::Position; pub trait AstNode { fn fancy_string(&self, depth: usize) -> String; } #[derive(Debug)] pub struct Node { pub value: T, pub pos: Position, } impl Node { pub fn fancy_string(&self, depth: usize) -> String { self.value.fancy_string(depth) } } #[derive(Debug)] pub enum Expr { Error(String), Unit, Id(String), Int(i64), Float(f64), String(String), Bool(bool), Array(Vec>), Object(Vec>), Tuple(Vec>), If { condition: Box>, truthy: Box>, falsy: Option>>, }, FunctionValue { parameters: Vec>, body: Box>, }, Member { subject: Box>, value: String, }, Index { subject: Box>, value: Box>, }, Call { subject: Box>, arguments: Vec>, }, Unary { unary_type: UnaryType, subject: Box>, }, Binary { binary_type: BinaryType, left: Box>, right: Box>, }, RangeExclusive { begin: Box>, end: Box>, }, RangeInclusive { begin: Box>, end: Box>, }, Assign { assign_type: AssignType, subject: Box>, value: Box>, }, Let { subject: Node, value: Option>>, }, Continue, Break, While { condition: Box>, body: Box>, }, For { subject: Node, value: Box>, body: Box>, }, Return(Option>>), Function { name: String, parameters: Vec>, body: Box>, }, Block { statements: Vec>, value: Option>>, }, Spread(Box>), } #[derive(Debug)] pub enum ObjectEntry { Error(String), Pair { key: Box>, value: Box>, }, Spread(Box>), } #[derive(Debug)] pub enum UnaryType { Not, Negate, Reference, ReferenceMut, Dereference, } #[derive(Debug)] pub enum BinaryType { Exponentiate, Multiply, Divide, Modulo, Add, Subtract, LT, LTE, GT, GTE, In, NotIn, Equal, Inequal, And, Or, } #[derive(Debug)] pub enum AssignType { Assign, Add, Subtract, Multiply, Divide, Modulo, } #[derive(Debug)] pub enum Parameter { Error(String), Id { name: String, mutable: bool }, Spread(Box>), } fn indent(depth: usize) -> String { "┊ ".repeat(depth) } pub fn escape_string(text: &str) -> String { let mut result = String::new(); for c in text.chars() { match c { '\\' => result.push_str("\\\\"), '\0' => result.push_str("\\0"), '\t' => result.push_str("\\t"), '\r' => result.push_str("\\r"), '\n' => result.push_str("\\n"), '\'' => result.push_str("\\\'"), '\"' => result.push_str("\\\""), c => result.push(c), }; } return result; } impl AstNode for Expr { fn fancy_string(&self, depth: usize) -> String { match self { Expr::Error(message) => format!("Error({message})"), Expr::Unit => format!("Unit"), Expr::Id(v) => format!("Id(`{v}`)"), Expr::Int(v) => format!("Int({v})"), Expr::Float(v) => format!("Float({v})"), Expr::String(v) => format!("String(\"{}\")", escape_string(v)), Expr::Bool(v) => format!("Bool({v})"), Expr::Array(values) => format!( "Array\n{}", values .iter() .map(|v| format!( "{}{}\n", indent(depth + 1), (*v).value.fancy_string(depth + 1) )) .reduce(|mut acc, v| { acc.push_str(&v); acc }) .unwrap_or("".to_string()), ), Expr::Object(_) => format!("Object <...>"), Expr::Tuple(_) => format!("Tuple <...>"), Expr::If { condition, truthy, falsy, } => format!( "If\n{}condition: {}\n{}truthy: {}\n{}falsy: {}", indent(depth + 1), condition.fancy_string(depth + 1), indent(depth + 1), truthy.fancy_string(depth + 1), indent(depth + 1), match falsy { Some(expr) => expr.fancy_string(depth + 1), None => "None".to_string(), }, ), Expr::FunctionValue { parameters: _, body: _, } => format!("FunctionValue <...>"), Expr::Member { subject: _, value: _, } => format!("Member <...>"), Expr::Index { subject: _, value: _, } => format!("Index <...>"), Expr::Call { subject, arguments } => format!( "Call\n{}subject: {}\n{}arguments:\n{}", indent(depth + 1), subject.fancy_string(depth + 1), indent(depth + 1), arguments .iter() .map(|v| format!( "{}{}\n", indent(depth + 2), (*v).value.fancy_string(depth + 2) )) .reduce(|mut acc, v| { acc.push_str(&v); acc }) .map(|s| s.trim_end().to_string()) .unwrap_or("".to_string()), ), Expr::Unary { unary_type, subject, } => format!( "Unary\n{}unary_type: {:?}\n{}subject: {}", indent(depth + 1), unary_type, indent(depth + 1), subject.fancy_string(depth + 1), ), Expr::Binary { binary_type, left, right, } => format!( "Binary\n{}binary_type: {:?}\n{}left: {}\n{}right: {}", indent(depth + 1), binary_type, indent(depth + 1), left.fancy_string(depth + 1), indent(depth + 1), right.fancy_string(depth + 1), ), Expr::RangeExclusive { begin: _, end: _ } => format!("RangeExclusive <...>"), Expr::RangeInclusive { begin: _, end: _ } => format!("RangeInclusive <...>"), Expr::Assign { assign_type: _, subject: _, value: _, } => format!("Assign <...>"), Expr::Let { subject, value } => format!( "Let {{\n{}subject: {}\n{}value: {}\n{}}}", indent(depth + 1), subject.fancy_string(depth + 1), indent(depth + 1), match value { Some(expr) => expr.fancy_string(depth + 1), None => "None".to_string(), }, indent(depth), ), Expr::Continue => format!("Continue"), Expr::Break => format!("Break"), Expr::While { condition: _, body: _, } => format!("While <...>"), Expr::For { subject: _, value: _, body: _, } => format!("For <...>"), Expr::Return(value) => format!( "Return{}", match value { Some(v) => format!( "\n{}value: {}", indent(depth + 1), v.fancy_string(depth + 1) ), None => "".to_string(), } ), Expr::Function { name, parameters, body, } => format!( "Function\n{}name: {}\n{}parameters:\n{}{}body: {}", indent(depth + 1), name, indent(depth + 1), parameters .iter() .map(|v| format!( "{}{}\n", indent(depth + 2), (*v).value.fancy_string(depth + 2) )) .reduce(|mut acc, v| { acc.push_str(&v); acc }) .unwrap_or("".to_string()), indent(depth + 1), body.fancy_string(depth + 1) ), Expr::Block { statements, value } => format!( "Block\n{}statements:\n{}{}value: \n{}{}", indent(depth + 1), statements .iter() .map(|v| format!( "{}{}\n", indent(depth + 2), (*v).value.fancy_string(depth + 2) )) .reduce(|mut acc, v| { acc.push_str(&v); acc }) .unwrap_or("".to_string()), indent(depth + 1), indent(depth + 2), match value { Some(expr) => expr.fancy_string(depth + 2), None => "None".to_string(), }, ), Expr::Spread(_) => format!("Spread <...>"), } } } impl AstNode for ObjectEntry { fn fancy_string(&self, _depth: usize) -> String { match self { ObjectEntry::Error(message) => format!("Error({message})"), ObjectEntry::Pair { key: _, value: _ } => format!("Pair <...>"), ObjectEntry::Spread(_) => format!("Spread <...>"), } } } impl AstNode for Parameter { fn fancy_string(&self, _depth: usize) -> String { match self { Parameter::Error(message) => format!("Error({message})"), Parameter::Id { name, mutable } => { format!("Id(`{name}`{})", if *mutable { ", mutable" } else { "" }) } Parameter::Spread(_) => format!("Spread <...>"), } } }