use std::fmt;
use bumpalo::collections::Vec;
use tattle::Loc;
#[derive(PartialEq, Debug)]
pub enum FExp<'a> {
L(Loc, FExp0<'a>),
}
pub use FExp::L;
impl<'a> FExp<'a> {
pub fn loc(&self) -> Loc {
match self {
Self::L(l, _) => *l,
}
}
pub fn ast0(&self) -> &FExp0<'a> {
match self {
Self::L(_, s) => s,
}
}
}
#[derive(PartialEq, Eq)]
pub enum IsInfix {
Yes,
No,
}
#[derive(PartialEq, Debug)]
pub enum FExp0<'a> {
App1(&'a FExp<'a>, &'a FExp<'a>),
App2(&'a FExp<'a>, &'a FExp<'a>, &'a FExp<'a>),
Var(&'a str),
Keyword(&'a str),
Tuple(Vec<'a, &'a FExp<'a>>),
Block(Vec<'a, &'a FExp<'a>>, Option<&'a FExp<'a>>),
Int(u64),
Float(f64),
Str(&'a str),
Prim(&'a str),
Special(&'a str),
Field(&'a str),
Tag(&'a str),
Error,
}
pub use FExp0::*;
use pretty::RcDoc;
fn bexpr<'a>(args: &'a [&'a FExp<'a>]) -> RcDoc<'a> {
RcDoc::text("[")
.append(
RcDoc::intersperse(
args.iter().map(|x| x.to_doc()),
RcDoc::text(",").append(RcDoc::line()),
)
.nest(1)
.group(),
)
.append(RcDoc::text("]"))
}
impl<'a> FExp<'a> {
fn parens(&self) -> RcDoc {
let should_parenthesize = match self.ast0() {
App1(_, _) | App2(_, _, _) => true,
_ => false,
};
if should_parenthesize {
RcDoc::text("(")
.append(self.to_doc().nest(1).group())
.append(RcDoc::text(")"))
} else {
self.to_doc()
}
}
pub fn collect_args(&'a self) -> (&'a FExp<'a>, std::vec::Vec<&'a FExp<'a>>) {
let mut revargs = std::vec::Vec::new();
let mut cur = self;
loop {
match cur.ast0() {
App1(f, x) => {
cur = f;
revargs.push(*x);
}
_ => break,
}
}
revargs.reverse();
(cur, revargs)
}
fn to_doc(&self) -> RcDoc {
match self.ast0() {
App1(f, arg) => f
.to_doc()
.append(RcDoc::line())
.append(arg.parens())
.group(),
App2(f, l, r) => l
.parens()
.append(RcDoc::line())
.append(f.to_doc())
.append(RcDoc::line())
.append(r.parens())
.group(),
Prim(name) => RcDoc::text(format!("@{}", name)),
Special(name) => RcDoc::text(format!("%{}", name)),
Tag(name) => RcDoc::text(format!("'{}", name)),
Field(name) => RcDoc::text(format!(".{}", name)),
Tuple(args) => bexpr(args),
Int(i) => RcDoc::text(format!("{}", i)),
Float(x) => RcDoc::text(format!("{}", x)),
Str(s) => RcDoc::text(format!("\"{}\"", s)),
Var(s) => RcDoc::text(s.to_string()),
Keyword(s) => RcDoc::text(s.to_string()),
Block(stmts, res) => RcDoc::text("{")
.append(RcDoc::line())
.append(RcDoc::intersperse(
stmts.iter().map(|x| x.to_doc().append(RcDoc::text(";"))),
RcDoc::line(),
))
.append(RcDoc::line())
.append(
res.map(|e| e.to_doc().append(RcDoc::line()))
.unwrap_or(RcDoc::nil()),
)
.append(RcDoc::text("}"))
.group(),
Error => RcDoc::text("!!!"),
}
}
}
impl<'a> fmt::Display for FExp<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.to_doc().render_fmt(100, f)
}
}