Skip to content

Commit

Permalink
Diff traces
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Jan 2, 2025
1 parent 13409d6 commit 58f9d0a
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 162 deletions.
5 changes: 3 additions & 2 deletions src/analyses/compute_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,14 +478,15 @@ impl<'a> RenderableTypingRule<'a> {
preconditions.len_ignoring_markup(),
postconditions.len_ignoring_markup(),
);
let bar = "-".repeat(len);
let bar = "-".repeat(len).to_display_tree(a);
let name = self.name.display(self.options);
DisplayTree::sep_by(
a,
"\n",
[
preconditions,
DisplayTree::sep2_by(a, bar, " ", format!("\"{name}\"")).ignore_for_diff(),
bar.sep_then(a, " ", format!("\"{name}\""))
.ignore_for_diff(),
postconditions,
],
)
Expand Down
65 changes: 43 additions & 22 deletions src/ast/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,39 @@ impl BindingMode {
}
}

/// TODO: Currently displayed as a single leaf.
impl<'d> ToDisplayTree<'d> for Pattern<'_> {
fn to_display_tree(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
match *self {
Self::Tuple(pats) => DisplayTree::sep_by(a, ", ", pats.iter())
.surrounded(a, "[", "]")
.tag("pat_list"),
Self::Ref(mutable, pat) => {
let needs_parens = mutable == Mutability::Shared
&& matches!(pat, Self::Binding(Mutability::Mutable, ..));
let (before, after) = if needs_parens {
(format!("&{mutable}("), ")")
} else {
(format!("&{mutable}"), "")
};
DisplayTree::sep_by(
a,
"",
[
before.to_display_tree(a),
pat.to_display_tree(a),
after.to_display_tree(a),
],
)
}
Self::Binding(mutable, mode, name) => {
DisplayTree::sep_by(a, "", [&mutable.to_string(), &mode.to_string(), name])
}
Self::Abstract(name) => name.to_display_tree(a),
}
}
}

impl<'d> ToDisplayTree<'d> for Type<'_> {
fn to_display_tree(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
match self {
Expand Down Expand Up @@ -183,20 +216,24 @@ impl TypingResult<'_> {

impl<'a> TypingPredicate<'a> {
/// Display as `let ...`.
pub fn display_as_let(&self) -> String {
format!("let {}: {} = {}", self.pat, self.expr.ty, self.expr)
pub fn display_as_let<'d>(&self, a: &'d Arenas<'d>) -> DisplayTree<'d> {
self.pat
.to_display_tree(a)
.sep_then(a, ": ", self.expr.ty)
.sep_then(a, " = ", self.expr.to_string())
.preceded(a, "let ")
}

pub fn display(&self, style: PredicateStyle) -> String {
let a = &Arenas::default();
self.display_to_tree(a, style).to_string()
}

/// Display according to the given predicate style.
pub fn display_to_tree<'d>(&self, a: &'d Arenas<'d>, style: PredicateStyle) -> DisplayTree<'d> {
match style {
PredicateStyle::Expression => self
.pat
.to_string()
.to_display_tree(a)
.sep_then(a, " @ ", self.expr.to_string())
.sep_then(a, ": ", self.expr.ty),
Expand Down Expand Up @@ -273,11 +310,7 @@ impl<'a> TypingPredicate<'a> {
},
},
};
let post_turnstile = self
.pat
.to_string()
.to_display_tree(a)
.sep_then(a, ": ", ty);
let post_turnstile = self.pat.to_display_tree(a).sep_then(a, ": ", ty);

let parts: &[_] = if pre_turnstile.is_empty() {
&[post_turnstile]
Expand Down Expand Up @@ -347,20 +380,8 @@ impl Display for BindingMode {

impl Display for Pattern<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::Tuple(pats) => write!(f, "[{}]", pats.iter().format(", ")),
Self::Ref(mutable, pat) => {
let needs_parens = mutable == Mutability::Shared
&& matches!(pat, Self::Binding(Mutability::Mutable, ..));
if needs_parens {
write!(f, "&{mutable}({pat})")
} else {
write!(f, "&{mutable}{pat}")
}
}
Self::Binding(mutable, mode, name) => write!(f, "{mutable}{mode}{name}"),
Self::Abstract(name) => write!(f, "{name}"),
}
let a = &Arenas::default();
write!(f, "{}", self.to_display_tree(a))
}
}

Expand Down
126 changes: 76 additions & 50 deletions src/ast/printer/display_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ use DisplayTreeKind::*;
enum DisplayTreeKind<'a> {
/// Leaf node.
Leaf(&'a str),
/// Separated node. Considered different if the separator is different or the lengths are
/// different.
/// Separated node. Considered different if the separator is different. If the lengths are
/// different, different options are possible.
Separated {
/// The separator, intercalated between the children.
sep: &'a str,
/// The children.
children: &'a [DisplayTree<'a>],
/// If the lengths differ and this is `true`, the first elements are diffed together until
/// exhaustion. If the lengths differ and this is `false`, we consider the whole subtree to
/// differ.
compare_common_prefix: bool,
},
}

Expand All @@ -25,7 +29,7 @@ pub struct DisplayTree<'a> {
kind: DisplayTreeKind<'a>,
/// Identifies the kind of node. Two nodes with different tags are always considered different.
tag: &'static str,
/// Whether to this sub-tree unchanged for the purposes of diffing.
/// Whether to consider this sub-tree unchanged for the purposes of diffing.
ignore_for_diff: bool,
}

Expand Down Expand Up @@ -77,6 +81,14 @@ fn strip_markup(s: &str) -> String {
}

impl<'a> DisplayTree<'a> {
fn new_from_kind(kind: DisplayTreeKind<'a>) -> Self {
Self {
kind,
tag: "",
ignore_for_diff: false,
}
}

pub fn is_empty(&self) -> bool {
match self.kind {
Leaf(s) => s.is_empty(),
Expand All @@ -87,7 +99,7 @@ impl<'a> DisplayTree<'a> {
pub fn len_ignoring_markup(&self) -> usize {
match self.kind {
Leaf(s) => len_ignoring_markup(s),
Separated { sep, children } => {
Separated { sep, children, .. } => {
let sep_len = if children.len() <= 1 {
0
} else {
Expand All @@ -100,45 +112,44 @@ impl<'a> DisplayTree<'a> {
}

pub fn leaf_noalloc(s: &'a str) -> Self {
Self {
kind: Leaf(s),
tag: "",
ignore_for_diff: false,
}
Self::new_from_kind(Leaf(s))
}

pub fn leaf(a: &'a Arenas<'a>, s: &str) -> Self {
Self::leaf_noalloc(a.alloc_str(s))
}

pub fn sep_by(
fn mk_separated(
a: &'a Arenas<'a>,
sep: &str,
it: impl IntoIterator<Item: ToDisplayTree<'a>>,
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
compare_common_prefix: bool,
) -> Self {
let children = it.into_iter().map(|x| x.to_display_tree(a)).collect_vec();
Self {
kind: Separated {
sep: a.alloc_str(sep),
children: a.bump.alloc_slice_copy(&children),
},
tag: "",
ignore_for_diff: false,
}
let children = children
.into_iter()
.map(|x| x.to_display_tree(a))
.collect_vec();
Self::new_from_kind(Separated {
sep: a.alloc_str(sep),
children: a.bump.alloc_slice_copy(&children),
compare_common_prefix,
})
}

/// Constructs `self` followed by `after`.
pub fn then(&self, a: &'a Arenas<'a>, x: impl ToDisplayTree<'a>) -> Self {
self.sep_then(a, "", x)
pub fn sep_by(
a: &'a Arenas<'a>,
sep: &str,
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
) -> Self {
Self::mk_separated(a, sep, children, false)
}

/// Constructs `self` surrounded by `before` and `after`.
pub fn surrounded(&self, a: &'a Arenas<'a>, before: &'static str, after: &'static str) -> Self {
Self::sep_by(
a,
"",
[Self::leaf_noalloc(before), *self, Self::leaf_noalloc(after)],
)
pub fn sep_by_compare_prefix(
a: &'a Arenas<'a>,
sep: &str,
children: impl IntoIterator<Item: ToDisplayTree<'a>>,
) -> Self {
Self::mk_separated(a, sep, children, true)
}

/// Concatenates `self` and `x`, separated by `sep`.
Expand All @@ -148,17 +159,26 @@ impl<'a> DisplayTree<'a> {
sep: &'static str,
x: impl ToDisplayTree<'a>,
) -> Self {
Self::sep2_by(a, self, sep, x)
Self::sep_by(a, sep, [self.to_display_tree(a), x.to_display_tree(a)])
}

/// Concatenates `x` and `y`, separated by `sep`.
pub fn sep2_by(
a: &'a Arenas<'a>,
x: impl ToDisplayTree<'a>,
sep: &str,
y: impl ToDisplayTree<'a>,
) -> Self {
Self::sep_by(a, sep, [x.to_display_tree(a), y.to_display_tree(a)])
/// Constructs `self` followed by `after`.
pub fn then(&self, a: &'a Arenas<'a>, x: impl ToDisplayTree<'a>) -> Self {
self.sep_then(a, "", x)
}

/// Constructs `self` surrounded by `before` and `after`.
pub fn preceded(&self, a: &'a Arenas<'a>, before: &'static str) -> Self {
Self::sep_by(a, "", [Self::leaf_noalloc(before), *self])
}

/// Constructs `self` surrounded by `before` and `after`.
pub fn surrounded(&self, a: &'a Arenas<'a>, before: &'static str, after: &'static str) -> Self {
Self::sep_by(
a,
"",
[Self::leaf_noalloc(before), *self, Self::leaf_noalloc(after)],
)
}

pub fn ignore_for_diff(mut self) -> Self {
Expand Down Expand Up @@ -212,20 +232,30 @@ impl<'a> DisplayTree<'a> {
(Leaf(l), Leaf(r)) if strip_markup(l) == strip_markup(r) => all_same(left, right),
// The non-trivial case: the trees differ partially.
(
Separated { sep, children: c1 },
Separated {
sep,
children: c1,
compare_common_prefix: ccp1,
},
Separated {
sep: sep2,
children: c2,
compare_common_prefix: ccp2,
},
) if strip_markup(sep) == strip_markup(sep2) && c1.len() == c2.len() => {
) if strip_markup(sep) == strip_markup(sep2)
&& (c1.len() == c2.len() || ccp1 || ccp2) =>
{
let mut is_first = true;
let mut any_diff = false;
for (c1, c2) in c1.iter().zip(c2) {
if !is_first {
for either_or_both in c1.iter().copied().zip_longest(c2.iter().copied()) {
if !is_first && !either_or_both.is_right() {
write!(left, "{sep}")?;
}
if !is_first && !either_or_both.is_left() {
write!(right, "{sep}")?;
}
any_diff |= c1.diff_display_inner(c2, left, right)?;
let (c1, c2) = either_or_both.or_default();
any_diff |= c1.diff_display_inner(&c2, left, right)?;
is_first = false;
}
Ok(any_diff)
Expand All @@ -237,19 +267,15 @@ impl<'a> DisplayTree<'a> {

impl Default for DisplayTree<'_> {
fn default() -> Self {
Self {
kind: Leaf(""),
tag: "",
ignore_for_diff: false,
}
Self::leaf_noalloc("")
}
}

impl<'a> Display for DisplayTree<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.kind {
Leaf(s) => write!(f, "{s}")?,
Separated { sep, children } => {
Separated { sep, children, .. } => {
let mut is_first = true;
for child in children {
if !is_first {
Expand Down
Loading

0 comments on commit 58f9d0a

Please sign in to comment.