use std::{borrow::Cow, fmt::Display};
use super::*;
use crate::ast::*;
use pretty::BoxAllocator;
use crate::symbol::Symbol;
use literals::*;
use resugared::*;
mod debug_json;
mod to_document;
pub use debug_json::*;
pub use to_document::*;
#[macro_export]
macro_rules! todo_document {
($allocator:ident, issue $issue:literal) => {
{return $allocator.todo_document(&format!("TODO_LINE_{}", std::line!()), Some($issue));}
};
($allocator:ident, issue $issue:literal, $($tt:tt)*) => {
{
let message = format!($($tt)*);
return $allocator.todo_document(&message, Some($issue));
}
};
($allocator:ident,) => {
{return $allocator.todo_document(&format!("TODO_LINE_{}", std::line!()), None);}
};
($allocator:ident, $($tt:tt)*) => {
{
let message = format!($($tt)*);
return $allocator.todo_document(&message, None);
}
};
}
pub use todo_document;
#[macro_export]
macro_rules! pretty_ast_docs {
($printer: expr, $docs:expr) => {{
use $crate::printer::pretty_ast::{ToDocumentOwned};
$docs.to_document_owned($printer)
}};
($printer: expr, $($docs:expr),*$(,)?) => {{
use $crate::printer::pretty_ast::{ToDocumentOwned};
nil!()
$(.append($docs.to_document_owned($printer)))*
}};
}
pub use pretty_ast_docs;
#[macro_export]
macro_rules! pretty_ast_intersperse {
($printer: expr, $docs:expr, $sep: expr$(,)?) => {{
let docs = $docs;
let sep = $sep;
$crate::printer::pretty_ast::PrettyAstExt::intersperse($printer, docs, sep)
}};
}
pub use pretty_ast_intersperse;
#[macro_export]
macro_rules! install_pretty_helpers {
($allocator:ident : $allocator_type:ty) => {
$crate::printer::pretty_ast::install_pretty_helpers!(
@$allocator,
#[doc = ::std::concat!("Proxy macro for [`", stringify!($crate), "::printer::pretty_ast::todo_document`] that automatically uses `", stringify!($allocator),"` as allocator.")]
#[doc = ::std::concat!(r#"Example: `disambiguated_todo!("Error message")` or `disambiguated_todo!(issue #123, "Error message with issue attached")`."#)]
disambiguated_todo{$crate::printer::pretty_ast::todo_document!},
#[doc = ::std::concat!("Proxy macro for [`pretty::docs`] that automatically uses `", stringify!($allocator),"` as allocator.")]
docs{$crate::printer::pretty_ast::pretty_ast_docs!},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::nil`] that automatically uses `", stringify!($allocator),"` as allocator.")]
nil{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::nil},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::fail`] that automatically uses `", stringify!($allocator),"` as allocator.")]
fail{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::fail},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::hardline`] that automatically uses `", stringify!($allocator),"` as allocator.")]
hardline{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::hardline},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::space`] that automatically uses `", stringify!($allocator),"` as allocator.")]
space{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::space},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::line`] that automatically uses `", stringify!($allocator),"` as allocator.")]
disambiguated_line{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::line},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::line_`] that automatically uses `", stringify!($allocator),"` as allocator.")]
line_{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::line_},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::softline`] that automatically uses `", stringify!($allocator),"` as allocator.")]
softline{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::softline},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::softline_`] that automatically uses `", stringify!($allocator),"` as allocator.")]
softline_{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::softline_},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::as_string`] that automatically uses `", stringify!($allocator),"` as allocator.")]
as_string{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::as_string},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::text`] that automatically uses `", stringify!($allocator),"` as allocator.")]
text{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::text},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::concat`] that automatically uses `", stringify!($allocator),"` as allocator.")]
disambiguated_concat{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::concat},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::intersperse`] that automatically uses `", stringify!($allocator),"` as allocator.")]
intersperse{$crate::printer::pretty_ast::pretty_ast_intersperse!},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::column`] that automatically uses `", stringify!($allocator),"` as allocator.")]
column{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::column},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::nesting`] that automatically uses `", stringify!($allocator),"` as allocator.")]
nesting{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::nesting},
#[doc = ::std::concat!("Proxy macro for [`PrettyAstExt::reflow`] that automatically uses `", stringify!($allocator),"` as allocator.")]
reflow{<$allocator_type as $crate::printer::pretty_ast::PrettyAstExt<_>>::reflow}
);
};
(@$allocator:ident, $($(#[$($attrs:tt)*])*$name:ident{$($callable:tt)*}),*) => {
$(
#[hax_rust_engine_macros::partial_apply($($callable)*, $allocator,)]
#[allow(unused)]
$(#[$($attrs)*])*
macro_rules! $name {}
)*
};
}
pub use install_pretty_helpers;
pub trait PrettyAstExt<A: 'static>: Sized {
fn nil(&self) -> DocBuilder<A> {
pretty::DocAllocator::nil(&BoxAllocator)
}
fn fail(&self) -> DocBuilder<A> {
pretty::DocAllocator::fail(&BoxAllocator)
}
fn hardline(&self) -> DocBuilder<A> {
pretty::DocAllocator::hardline(&BoxAllocator)
}
fn space(&self) -> DocBuilder<A> {
pretty::DocAllocator::space(&BoxAllocator)
}
fn line(&self) -> DocBuilder<A> {
pretty::DocAllocator::line(&BoxAllocator)
}
fn line_(&self) -> DocBuilder<A> {
pretty::DocAllocator::line_(&BoxAllocator)
}
fn softline(&self) -> DocBuilder<A> {
pretty::DocAllocator::softline(&BoxAllocator)
}
fn softline_(&self) -> DocBuilder<A> {
pretty::DocAllocator::softline_(&BoxAllocator)
}
fn as_string<U: Display>(&self, data: U) -> DocBuilder<A> {
pretty::DocAllocator::as_string(&BoxAllocator, data)
}
fn text<'a>(&self, data: impl Into<Cow<'a, str>>) -> DocBuilder<A> {
self.as_string(data.into())
}
fn concat<I>(&self, docs: I) -> DocBuilder<A>
where
I::Item: ToDocumentOwned<Self, A>,
I: IntoIterator,
{
pretty::DocAllocator::concat(
&BoxAllocator,
docs.into_iter().map(|doc| doc.to_document_owned(self)),
)
}
fn intersperse<I, S>(&self, docs: I, separator: S) -> DocBuilder<A>
where
I::Item: ToDocumentOwned<Self, A>,
I: IntoIterator,
S: ToDocumentOwned<Self, A> + Clone,
A: Clone,
{
let separator = separator.to_document_owned(self);
pretty::DocAllocator::intersperse(
&BoxAllocator,
docs.into_iter().map(|doc| doc.to_document_owned(self)),
separator,
)
}
fn reflow(&self, text: &'static str) -> DocBuilder<A>
where
A: Clone,
{
pretty::DocAllocator::reflow(&BoxAllocator, text)
}
}
impl<A: 'static + Clone, P: PrettyAst<A>> PrettyAstExt<A> for P {}
macro_rules! make_cases_macro {
(
$macro_name:ident,
$(
$($idents:ident)|* => $target:ident,
)*
_ => $fallback:ident $(,)?
) => {
macro_rules! $macro_name {
$(
$(
($idents $tt:tt) => { $target!($tt); };
)*
)*
($anything:ident $tt:tt) => { $fallback!($tt); };
}
};
}
macro_rules! skip {
($tt:tt) => {};
}
macro_rules! keep {
({$($tt:tt)*}) => { $($tt)* };
}
make_cases_macro!(method_deny_list,
ExprKind | PatKind | TyKind | GuardKind | ImplExprKind | ImplItemKind | TraitItemKind | AttributeKind | DocCommentKind => skip,
Signedness | IntSize => skip,
ItemQuoteOrigin | ItemQuoteOriginKind | ItemQuoteOriginPosition => skip,
ControlFlowKind | LoopState | LoopKind => skip,
_ => keep
);
make_cases_macro!(span_handling,
Item | Expr | Pat | Guard | Arm | ImplItem | TraitItem | GenericParam | Attribute | Attribute => keep,
_ => skip
);
pub trait HasContextualSpan: Clone {
fn with_span(&self, _span: Span) -> Self;
fn span(&self) -> Option<Span>;
}
macro_rules! mk {
($($ty:ident),*) => {
pastey::paste! {
pub trait PrettyAst<A: 'static + Clone>: Sized + HasContextualSpan {
const NAME: &'static str;
fn emit_diagnostic(&self, kind: hax_types::diagnostics::Kind) {
let span = self.span().unwrap_or_else(|| Span::dummy());
use crate::ast::diagnostics::{DiagnosticInfo, Context};
(DiagnosticInfo {
context: Context::Printer(Self::NAME.to_string()),
span,
kind
}).emit()
}
fn todo_document(&self, message: &str, issue_id: Option<u32>) -> DocBuilder<A> {
self.emit_diagnostic(hax_types::diagnostics::Kind::Unimplemented {
issue_id,
details: Some(message.into()),
});
self.as_string(message)
}
fn unimplemented_method(&self, method: &str, ast: ast::fragment::FragmentRef<'_>) -> DocBuilder<A> {
let debug_json = DebugJSON(ast).to_string();
self.emit_diagnostic(hax_types::diagnostics::Kind::Unimplemented {
issue_id: None,
details: Some(format!("The method `{method}` is not implemented in the backend {}. To show the AST fragment that could not be printed, run {debug_json}.", Self::NAME)),
});
self.text(format!("`{method}` unimpl, {debug_json}", )).parens()
}
$(
method_deny_list!($ty{
#[doc = "Define how the printer formats a value of this AST type."]
#[doc = "Do not call this method directly. Use [`ToDocument::to_document`] instead, so annotations/spans are preserved correctly."]
#[deprecated = "Do not call this method directly. Use [`ToDocument::to_document`] instead, so annotations/spans are preserved correctly."]
fn [<$ty:snake>](&self, [<$ty:snake>]: &$ty) -> DocBuilder<A> {
mk!(@method_body $ty [<$ty:snake>] self [<$ty:snake>])
}
});
)*
}
$(
method_deny_list!($ty{
impl<A: 'static + Clone, P: PrettyAst<A>> ToDocument<P, A> for $ty {
fn to_document(&self, printer: &P) -> DocBuilder<A> {
span_handling!($ty{
let printer = &(printer.with_span(self.span()));
});
#[allow(deprecated)]
let print = <P as PrettyAst<A>>::[<$ty:snake>];
print(printer, self)
}
}
});
)*
}
};
(@method_body Symbol $meth:ident $self:ident $value:ident) => {
$self.as_string($value.to_string())
};
(@method_body LocalId $meth:ident $self:ident $value:ident) => {
$value.0.to_document($self)
};
(@method_body SpannedTy $meth:ident $self:ident $value:ident) => {
$value.ty.to_document($self)
};
(@method_body $ty:ident $meth:ident $self:ident $value:ident) => {
$self.unimplemented_method(stringify!($meth), ast::fragment::FragmentRef::from($meth))
};
}
#[hax_rust_engine_macros::replace(AstNodes => include(VisitableAstNodes))]
mk!(GlobalId, AstNodes);