use crate::tokens::Token; /* --------- * * AST nodes * * --------- */ /// The AST node for the program #[derive(Default, Debug, Clone)] pub struct ProgramNode(pub Vec<StmtNode>); /// An AST node that represents a statement. #[derive(Debug, Clone)] pub enum StmtNode { /// A variable declaration VarDecl(Token, Option<ExprNode>), /// A function declaration FunDecl { name: Token, params: Vec<Token>, body: Vec<StmtNode>, }, /// An single expression Expression(ExprNode), /// The print statement Print(ExprNode), /// A block containing multiple statements. Block(Vec<StmtNode>), /// A conditional statement. If { condition: ExprNode, then_branch: Box<StmtNode>, else_branch: Option<Box<StmtNode>>, }, /// Loop statement. Loop { label: Option<Token>, condition: ExprNode, body: Box<StmtNode>, after_body: Option<Box<StmtNode>>, }, /// Break or continue statement. LoopControl { is_break: bool, loop_name: Option<Token>, }, } impl StmtNode { /// Extract the list of statements from a block. Panic if the statement /// is not a block. pub fn extract_block_statements(self) -> Vec<StmtNode> { match self { Self::Block(stmts) => stmts, _ => panic!("Statement is not a block"), } } } /// An AST node that represents an expression. #[derive(Debug, Clone)] pub enum ExprNode { /// Assignment to a variable. Assignment { name: Token, value: Box<ExprNode> }, /// Logical binary expression. Logical { left: Box<ExprNode>, operator: Token, right: Box<ExprNode>, }, /// Binary expression. Binary { left: Box<ExprNode>, operator: Token, right: Box<ExprNode>, }, /// Unary expression. Unary { operator: Token, right: Box<ExprNode>, }, /// Grouping expression, containing a sub-expression. Grouping { expression: Box<ExprNode> }, /// A litteral value, represented by the corresponding token. Litteral { value: Token }, /// A reference to a variable. Variable { name: Token }, /// A function call. Call { /// Expression that corresponds to the callable. callee: Box<ExprNode>, /// Right parenthesis that closes the list of arguments. Used to /// report errors. right_paren: Token, /// The list of function arguments. arguments: Vec<ExprNode>, }, } /* -------------------------------- * * Dumper trait and implementations * * -------------------------------- */ /// This trait should be implemented by nodes to allow AST dumps. pub trait AstDumper { /// Dump the node as a string. fn dump(&self) -> String; } impl AstDumper for ProgramNode { fn dump(&self) -> String { self.0 .iter() .map(|node| node.dump()) .collect::<Vec<String>>() .join(" ") } } impl AstDumper for StmtNode { fn dump(&self) -> String { match self { Self::VarDecl(name, Some(expr)) => format!("( var {} {} )", name.lexeme, expr.dump()), Self::VarDecl(name, None) => format!("( var {} nil )", name.lexeme), Self::FunDecl { name, params, body } => format!( "( fun {} ({}) {} )", name.lexeme, params .iter() .map(|token| &token.lexeme as &str) .collect::<Vec<&str>>() .join(" "), body.iter() .map(|stmt| stmt.dump()) .collect::<Vec<String>>() .join(" ") ), Self::Expression(expr) => expr.dump(), Self::Print(expr) => format!("(print {})", expr.dump()), Self::Block(stmts) => format!( "( {} )", stmts .iter() .map(|s| s.dump()) .collect::<Vec<String>>() .join(" ") ), Self::If { condition, then_branch, else_branch, } => match else_branch { None => format!("( if {} {} () )", condition.dump(), then_branch.dump()), Some(stmt) => format!( "( if {} {} {} )", condition.dump(), then_branch.dump(), stmt.dump() ), }, Self::Loop { label, condition, body, after_body, } => { let ltxt = if let Some(label) = label { format!("@{} ", label.lexeme) } else { "".to_string() }; let abtxt = if let Some(after_body) = after_body { format!("{} ", after_body.dump()) } else { "".to_string() }; format!( "( {}loop {} {} {})", ltxt, condition.dump(), body.dump(), abtxt ) } Self::LoopControl { is_break, loop_name, } => { let stmt = if *is_break { "break" } else { "continue" }; match loop_name { Some(name) => format!("( {} {} )", stmt, name.lexeme), None => format!("( {} )", stmt), } } } } } impl AstDumper for ExprNode { fn dump(&self) -> String { match self { Self::Assignment { name, value } => format!("( = {} {} )", name.lexeme, value.dump()), Self::Logical { left, operator, right, } => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()), Self::Binary { left, operator, right, } => format!("( {} {} {} )", operator.lexeme, left.dump(), right.dump()), Self::Unary { operator, right } => format!("( {} {} )", operator.lexeme, right.dump()), Self::Grouping { expression } => format!("( {} )", expression.dump()), Self::Variable { name } => name.lexeme.clone(), Self::Litteral { value } => { if value.is_litteral() { value.lexeme.clone() } else { panic!("Unexpected token type for token {:#?}", value) } } ExprNode::Call { callee, right_paren: _, arguments, } => { let callee = callee.dump(); if arguments.is_empty() { format!("( call {} )", callee) } else { format!( "( call {} {} )", callee, arguments .iter() .map(|arg| arg.dump()) .collect::<Vec<String>>() .join(" ") ) } } } } }