use std::fmt; use std::fmt::Formatter; use crate::error::Error; use crate::lex::token; use crate::lex::token::Token; #[derive(Debug)] pub enum Node { Break, Ident(String), DepList(Box), SourceList(Box), Bool(bool), Int(i128), String(String), Array(Vec), Block(Vec), Fn { name: Option>, params: Vec, body: Box, }, ArrayExpr { // array access array: Box, index: Box, }, CallExpr { // function call func: Box, params: Vec, }, IfStmt { condition: Box, then_block: Box, else_block: Option>, }, ReturnStmt(Box), ForStmt { setup: Option>, condition: Option>, exec: Option>, body: Box, }, WhileStmt { condition: Box, block: Box, }, UnaryExpr { op: Operator, node: Box, }, BinaryExpr { op: Operator, lhs: Box, rhs: Box, }, TypeExpr(Box), SetExpr { name: Box, val: Box, }, Target { name: Box, content: Box, }, File { name: String, content: Vec, }, } #[derive(Debug, PartialEq)] pub enum Operator { Dot, Eq, EqEq, Bang, BangEq, Gt, GtGt, GtEq, GtGtEq, Lt, LtLt, LtEq, LtLtEq, Plus, PlusEq, Minus, MinusEq, Asterisk, AsteriskEq, Slash, SlashEq, Percent, PercentEq, Pipe, PipePipe, PipeEq, Amp, AmpAmp, AmpEq, Caret, CaretEq, } impl Node { pub fn walk(&self, cb: fn(node: &Node, depth: u32)) { self.visit(cb, 0); } fn visit(&self, cb: fn(node: &Node, depth: u32), current_depth: u32) { cb(self, current_depth); let depth = current_depth + 1; match self { Node::DepList(list) => list.visit(cb, depth), Node::SourceList(list) => list.visit(cb, depth), Node::Array(elements) => { for node in elements { node.visit(cb, depth); } } Node::Block(statements) => { for node in statements { node.visit(cb, depth); } } Node::Fn { name, params, body } => { if let Some(n) = name { n.visit(cb, depth); } for p in params { p.visit(cb, depth); } body.visit(cb, depth); } Node::ArrayExpr { array, index } => { array.visit(cb, depth); index.visit(cb, depth); } Node::CallExpr { func, params } => { func.visit(cb, depth); for p in params { p.visit(cb, depth); } } Node::IfStmt { condition, then_block, else_block, } => { condition.visit(cb, depth); then_block.visit(cb, depth); if let Some(eb) = else_block { eb.visit(cb, depth); } } Node::ForStmt { setup, condition, exec, body } => { if let Some(s) = setup { s.visit(cb, depth); } if let Some(c) = condition { c.visit(cb, depth); } if let Some(e) = exec { e.visit(cb, depth); } body.visit(cb, depth); } Node::WhileStmt { condition, block } => { condition.visit(cb, depth); block.visit(cb, depth); } Node::ReturnStmt(node) => node.visit(cb, depth), Node::UnaryExpr { op, node } => node.visit(cb, depth), Node::BinaryExpr { op, lhs, rhs } => { lhs.visit(cb, depth); rhs.visit(cb, depth); } Node::TypeExpr(node) => node.visit(cb, depth), Node::SetExpr { name, val } => { name.visit(cb, depth); val.visit(cb, depth); } Node::Target { name, content } => { name.visit(cb, depth); content.visit(cb, depth); } Node::File { name, content } => { for n in content { n.visit(cb, depth); } } _ => return, } } } impl fmt::Display for Node { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let tmp: String; write!( f, "{}", match self { Node::Break => "", Node::Ident(name) => name.as_str(), Node::Bool(b) => { tmp = format!("{}", b); tmp.as_str() } Node::Int(i) => { tmp = format!("{}", i); tmp.as_str() } Node::String(s) => s.as_str(), Node::Block(_) => "", Node::Fn { name, params, body } => "fn", Node::ReturnStmt(_) => "return", Node::DepList(_) => "depend", Node::SourceList(_) => "source", Node::Array(_) => "", Node::ArrayExpr { array, index } => "", Node::CallExpr { func, params } => "", Node::IfStmt { condition, then_block, else_block, } => "", Node::ForStmt { setup, condition, exec, body } => "", Node::WhileStmt { condition, block } => "", Node::UnaryExpr { op, node } => op.raw(), Node::BinaryExpr { op, lhs, rhs } => op.raw(), Node::TypeExpr(_) => "type", Node::SetExpr { name, val } => "set", Node::Target { name, content } => "target", Node::File { name, content } => "file", } ) } } impl Operator { pub fn from_token(token: &Token) -> Result { match token.kind { token::Kind::Eq => Ok(Operator::Eq), token::Kind::EqEq => Ok(Operator::EqEq), token::Kind::BangEq => Ok(Operator::BangEq), token::Kind::Bang => Ok(Operator::Bang), token::Kind::Gt => Ok(Operator::Gt), token::Kind::GtGt => Ok(Operator::GtGt), token::Kind::GtEq => Ok(Operator::GtEq), token::Kind::GtGtEq => Ok(Operator::GtGtEq), token::Kind::Lt => Ok(Operator::Lt), token::Kind::LtLt => Ok(Operator::LtLt), token::Kind::LtEq => Ok(Operator::LtEq), token::Kind::LtLtEq => Ok(Operator::LtLtEq), token::Kind::Plus => Ok(Operator::Plus), token::Kind::PlusEq => Ok(Operator::PlusEq), token::Kind::Minus => Ok(Operator::Minus), token::Kind::MinusEq => Ok(Operator::MinusEq), token::Kind::Asterisk => Ok(Operator::Asterisk), token::Kind::AsteriskEq => Ok(Operator::AsteriskEq), token::Kind::Slash => Ok(Operator::Slash), token::Kind::SlashEq => Ok(Operator::SlashEq), token::Kind::Percent => Ok(Operator::Percent), token::Kind::PercentEq => Ok(Operator::PercentEq), token::Kind::Pipe => Ok(Operator::Pipe), token::Kind::PipePipe => Ok(Operator::PipePipe), token::Kind::PipeEq => Ok(Operator::PipeEq), token::Kind::Amp => Ok(Operator::Amp), token::Kind::AmpAmp => Ok(Operator::AmpAmp), token::Kind::AmpEq => Ok(Operator::AmpEq), token::Kind::Caret => Ok(Operator::Caret), token::Kind::CaretEq => Ok(Operator::CaretEq), _ => Err(Error::syntax_error( token.pos.clone(), format!("\"{}\" is not an operator", token.raw), )), } } pub fn raw(&self) -> &'static str { match self { Operator::Dot => ".", Operator::Eq => "=", Operator::EqEq => "==", Operator::BangEq => "!=", Operator::Gt => ">", Operator::GtGt => ">>", Operator::GtEq => ">=", Operator::GtGtEq => ">>=", Operator::Lt => "<", Operator::LtLt => "<<", Operator::LtEq => "<=", Operator::LtLtEq => "<<=", Operator::Plus => "+", Operator::PlusEq => "+=", Operator::Minus => "-", Operator::MinusEq => "-=", Operator::Asterisk => "*", Operator::AsteriskEq => "*=", Operator::Slash => "/", Operator::SlashEq => "/=", Operator::Percent => "%", Operator::PercentEq => "%=", Operator::Bang => "!", Operator::Pipe => "|", Operator::PipePipe => "||", Operator::PipeEq => "|=", Operator::Amp => "&", Operator::AmpAmp => "&&", Operator::AmpEq => "&=", Operator::Caret => "^", Operator::CaretEq => "^=", } } } impl fmt::Display for Operator { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.raw()) } } #[derive(Debug, Clone, Copy, PartialEq)] pub enum Type { /// For identifier tokens (evaluates at runtime) Unknown, /// For expressions that don't emit a value None, Int, String, } impl Type { pub fn from_token(token: &Token) -> Option { match token.kind { token::Kind::IntLiteral => Some(Type::Int), token::Kind::StringLiteral => Some(Type::String), _ => None, } } } impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}", match self { Type::Unknown => "", Type::None => "()", Type::Int => "int", Type::String => "string", } ) } }