Files
Doughlang/examples/to-lisp.rs
John f551e3aef3 ast: Break compatibility
- Turned `static` and `const` into scoped modifiers (like `pub`
- Added anonymous record patterns
2025-12-20 05:22:14 -05:00

259 lines
7.6 KiB
Rust

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<dyn Error>> {
read_and("\x1b[34m", "l>", " >", |line| {
let ast: Anno<Expr> = 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::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::Record => "record",
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::Const => "const",
Op::Static => "static",
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<A: Annotation>(&mut self, expr: &'a Expr<A>) -> 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::NamedRecord(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<A: Annotation>(&mut self, item: &'a Bind<A>) -> 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<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
item.children(self)
}
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
item.children(self)
}
}