use crate::ast::{Annotated, Expression, Parameter, Term, Token};
use crate::doc::{Doc, Emit, line};
mod absorb;
mod app;
mod base;
mod op;
mod params;
mod stmt;
mod string;
mod term;
use app::{AppCtx, emit_app, emit_app_parts};
use op::emit_operation;
use stmt::{emit_if, emit_let, emit_with};
use string::{emit_indented_string, emit_simple_string};
use term::{emit_list, emit_paren, emit_set};
#[derive(Clone, Copy, PartialEq, Eq)]
enum Width {
Regular,
Wide,
}
impl Emit for Term {
fn emit(&self, doc: &mut Doc) {
match self {
Self::Token(t) => t.emit(doc),
Self::SimpleString(s) => {
s.emit_with(doc, |d, v| emit_simple_string(d, v));
}
Self::IndentedString(s) => {
s.emit_with(doc, |d, v| emit_indented_string(d, v));
}
Self::Path(p) => p.emit_with(doc, |d, v| {
for part in v {
part.emit(d);
}
}),
Self::Parenthesized { open, expr, close } => {
emit_paren(doc, open, expr, close);
}
Self::List { open, items, close } => {
doc.group(|g| emit_list(g, open, items, close));
}
Self::Set {
rec,
open,
items: binders,
close,
} => {
emit_set(doc, Width::Regular, rec.as_ref(), open, binders, close);
}
Self::Selection {
base: term,
selectors,
default,
} => {
term.emit(doc);
match &**term {
Self::Token(Annotated {
value: Token::Integer(_),
..
}) if !selectors.is_empty() => {
doc.hardspace();
}
Self::Token(_) => {}
Self::Parenthesized { .. } => {
doc.softbreak();
}
_ => {
doc.linebreak();
}
}
doc.sep_by(&[], selectors);
if let Some(d) = default {
doc.softline();
doc.nested(|inner| {
d.or_kw.emit(inner);
inner.hardspace();
d.value.emit(inner);
});
}
}
}
}
}
impl Emit for Expression {
#[allow(clippy::too_many_lines)]
fn emit(&self, doc: &mut Doc) {
match self {
Self::Term(t) => t.emit(doc),
Self::Apply { .. } => {
emit_app(doc, AppCtx::default(), self);
}
Self::Operation {
lhs: left,
op,
rhs: right,
} => {
emit_operation(doc, self, left, op, right);
}
Self::HasAttr {
lhs: expr,
question,
path: selectors,
} => {
expr.emit(doc);
doc.softline();
question.emit(doc);
doc.hardspace();
for sel in selectors {
sel.emit(doc);
}
}
Self::Negation { minus, expr } => {
minus.emit(doc);
expr.emit(doc);
}
Self::Not { bang, expr } => {
bang.emit(doc);
expr.emit(doc);
}
Self::Let {
kw_let: let_kw,
bindings: binders,
kw_in: in_kw,
body: expr,
} => {
emit_let(doc, let_kw, binders, in_kw, expr);
}
Self::If {
kw_if,
cond,
kw_then,
then_branch,
kw_else,
else_branch,
} => {
doc.group(|g| {
emit_if(
g,
line(),
&kw_if.move_trailing_comment_up(),
cond,
kw_then,
then_branch,
kw_else,
else_branch,
);
});
}
Self::Assert {
kw_assert: assert_kw,
cond,
semi: semicolon,
body: expr,
} => {
doc.group(|g| {
let assert_term = Self::Term(Term::Token(assert_kw.clone()));
match &**cond {
Self::Apply { func, arg } => {
emit_app_parts(g, AppCtx::default(), func, arg, Some(&assert_term));
}
a => emit_app_parts(g, AppCtx::default(), &assert_term, a, None),
}
semicolon.emit(g);
g.hardline();
expr.emit(g);
});
}
Self::With {
kw_with: with_kw,
scope: env,
semi: semicolon,
body: expr,
} => {
emit_with(doc, with_kw, env, semicolon, expr);
}
Self::Lambda {
param: Parameter::Id(param),
colon,
body,
} => {
doc.group(|group_doc| {
group_doc.linebreak();
param.emit(group_doc);
colon.emit(group_doc);
body.absorb_lambda(group_doc, 1);
});
}
Self::Lambda { param, colon, body } => {
param.emit(doc);
colon.emit(doc);
doc.line();
if let Self::Term(t) = &**body
&& t.is_absorbable()
{
doc.group(|g| t.emit_wide(g));
return;
}
body.emit(doc);
}
}
}
}