diff --git a/examples/to-lisp.rs b/examples/to-lisp.rs new file mode 100644 index 0000000..41c4a22 --- /dev/null +++ b/examples/to-lisp.rs @@ -0,0 +1,257 @@ +use std::error::Error; + +use doughlang::{ + ast::{ + visit::{Visit, Walk}, + *, + }, + lexer::Lexer, + parser::{Parser, expr::Prec}, +}; + +use repline::prebaked::{Response, read_and}; + +fn main() -> Result<(), Box> { + read_and("\x1b[34m", "l>", " >", |line| { + let ast: Anno = Parser::new(Lexer::new(line)).parse(Prec::MIN)?; + let mut to_lisp = ToLisp; + let _ = to_lisp.visit(&ast); + println!(); + Ok(Response::Accept) + })?; + + Ok(()) +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct ToLisp; + +impl ToLisp { + fn bind_op(&self, op: BindOp) -> &'static str { + match op { + BindOp::Let => "let", + BindOp::Const => "const", + BindOp::Static => "static", + BindOp::Type => "type", + BindOp::Fn => "fn", + BindOp::Mod => "mod", + BindOp::Impl => "impl", + BindOp::Struct => "struct", + BindOp::Enum => "enum", + BindOp::For => "for", + BindOp::Match => "match", + } + } + fn pat_op(&self, op: PatOp) -> &'static str { + match op { + PatOp::Pub => "pub", + PatOp::Mut => "mut", + PatOp::Ref => "ref", + PatOp::Ptr => "ptr", + PatOp::Rest => "rest", + PatOp::RangeEx => "range-ex", + PatOp::RangeIn => "range-in", + PatOp::Tuple => "tuple", + PatOp::Slice => "slice", + PatOp::ArRep => "ar-rep", + PatOp::Typed => "typed", + PatOp::Fn => "fn", + PatOp::Alt => "alt", + } + } + fn expr_op(&self, op: Op) -> &'static str { + match op { + Op::Do => "do", + Op::As => "cast", + Op::Macro => "macro", + Op::Block => "block", + Op::Array => "array", + Op::ArRep => "ar-rep", + Op::Group => "group", + Op::Tuple => "tuple", + Op::Meta => "meta", + Op::Try => "try", + Op::Index => "index", + Op::Call => "call", + Op::Pub => "pub", + Op::Loop => "loop", + Op::Match => "match", + Op::If => "if", + Op::While => "while", + Op::Break => "break", + Op::Return => "return", + Op::Continue => "continue", + Op::Dot => "dot", + Op::RangeEx => "range-ex", + Op::RangeIn => "range-in", + Op::Neg => "neg", + Op::Not => "not", + Op::Identity => "identity", + Op::Refer => "refer", + Op::Deref => "deref", + Op::Mul => "mul", + Op::Div => "div", + Op::Rem => "rem", + Op::Add => "add", + Op::Sub => "sub", + Op::Shl => "shl", + Op::Shr => "shr", + Op::And => "and", + Op::Xor => "xor", + Op::Or => "or", + Op::Lt => "lt", + Op::Leq => "leq", + Op::Eq => "eq", + Op::Neq => "neq", + Op::Geq => "geq", + Op::Gt => "gt", + Op::LogAnd => "log-and", + Op::LogXor => "log-xor", + Op::LogOr => "log-or", + Op::Set => "set", + Op::MulSet => "mul-set", + Op::DivSet => "div-set", + Op::RemSet => "rem-set", + Op::AddSet => "add-set", + Op::SubSet => "sub-set", + Op::ShlSet => "shl-set", + Op::ShrSet => "shr-set", + Op::AndSet => "and-set", + Op::XorSet => "xor-set", + Op::OrSet => "or-set", + } + } +} + +impl<'a> Visit<'a> for ToLisp { + type Error = (); + + fn visit(&mut self, walk: &'a impl Walk<'a>) -> Result<(), Self::Error> { + walk.visit_in(self) + } + + fn visit_expr(&mut self, expr: &'a Expr) -> Result<(), Self::Error> { + match expr { + Expr::Omitted => print!("()"), + Expr::Id(path) => path.visit_in(self)?, + Expr::MetId(id) => print!("`{id}"), + Expr::Lit(literal) => literal.visit_in(self)?, + Expr::Use(import) => import.visit_in(self)?, + Expr::Bind(bind) => bind.visit_in(self)?, + Expr::Make(make) => make.visit_in(self)?, + Expr::Op(op, annos) => { + print!("({}", self.expr_op(*op)); + for anno in annos { + print!(" "); + anno.visit_in(self)?; + } + print!(")"); + } + } + + Ok(()) + } + + fn visit_ident(&mut self, name: &'a str) -> Result<(), Self::Error> { + print!("{name}"); + Ok(()) + } + + fn visit_path(&mut self, path: &'a Path) -> Result<(), Self::Error> { + let Path { parts } = path; + print!("(at"); + for part in parts { + print!(" "); + part.visit_in(self)?; + } + print!(")"); + Ok(()) + } + + fn visit_literal(&mut self, lit: &'a Literal) -> Result<(), Self::Error> { + print!("{lit}"); + Ok(()) + } + + fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> { + match item { + Use::Glob => print!("(use-glob)"), + Use::Name(name) => name.visit_in(self)?, + Use::Path(name, tree) => { + print!("(use-path {name} "); + tree.visit_in(self)?; + print!(")"); + } + Use::Tree(items) => { + print!("(use-tree"); + for item in items { + print!(" "); + item.visit_in(self)?; + } + print!(")"); + } + } + Ok(()) + } + + fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> { + match item { + Pat::Ignore => print!("(ignore)"), + Pat::Never => print!("(never)"), + Pat::MetId(id) => print!("(replace {id})"), + Pat::Name(name) => print!("{name}"), + Pat::Path(path) => path.visit_in(self)?, + Pat::NamedStruct(path, pat) => { + print!("(struct-pat "); + path.visit_in(self)?; + print!(" "); + pat.visit_in(self)?; + print!(")") + } + Pat::NamedTuple(path, pat) => { + print!("(named "); + path.visit_in(self)?; + print!(" "); + pat.visit_in(self)?; + print!(")") + } + Pat::Lit(literal) => literal.visit_in(self)?, + Pat::Op(pat_op, pats) => { + print!("({}", self.pat_op(*pat_op)); + for pat in pats { + print!(" "); + pat.visit_in(self)?; + } + print!(")"); + } + } + Ok(()) + } + + fn visit_bind(&mut self, item: &'a Bind) -> Result<(), Self::Error> { + let Bind(op, generics, pattern, exprs) = item; + print!("({} ", self.bind_op(*op)); + if !generics.is_empty() { + print!("(for-some"); + for generic in generics { + print!(" {generic}"); + } + print!(") "); + } + pattern.visit_in(self)?; + for expr in exprs { + print!(" "); + expr.visit_in(self)?; + } + print!(")"); + Ok(()) + } + + fn visit_make(&mut self, item: &'a Make) -> Result<(), Self::Error> { + item.children(self) + } + + fn visit_makearm(&mut self, item: &'a MakeArm) -> Result<(), Self::Error> { + item.children(self) + } +}