Skip to content

Commit c0b74d4

Browse files
committed
Tail-recursive pretty printing
1 parent f2df473 commit c0b74d4

2 files changed

Lines changed: 227 additions & 199 deletions

File tree

src/haz3lcore/derived/Indentation.re

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ let trim_non_content: Segment.t => Segment.t =
99

1010
let prev_pieces = (seg: Segment.t): list(option(Piece.t)) => {
1111
let rec go =
12-
(xs: list(Piece.t), prev: option(Piece.t))
12+
(acc, xs: list(Piece.t), prev: option(Piece.t))
1313
: list(option(Piece.t)) =>
1414
switch (xs) {
15-
| [] => []
16-
| [x, ...xs] => [prev, ...go(xs, Some(x))]
15+
| [] => List.rev(acc)
16+
| [x, ...xs] => go([prev, ...acc], xs, Some(x))
1717
};
18-
go(seg, None);
18+
go([], seg, None);
1919
};
2020

2121
let next_pieces = (seg: Segment.t): list(option(Piece.t)) => {
22-
let rec go = (xs: list(Piece.t)): list(option(Piece.t)) =>
22+
let rec go = (acc, xs: list(Piece.t)): list(option(Piece.t)) =>
2323
switch (xs) {
24-
| [] => []
25-
| [_] => [None]
26-
| [_, next, ...rest] => [Some(next), ...go([next, ...rest])]
24+
| [] => List.rev(acc)
25+
| [_] => List.rev([None, ...acc])
26+
| [_, next, ...rest] => go([Some(next), ...acc], [next, ...rest])
2727
};
28-
go(seg);
28+
go([], seg);
2929
};
3030

3131
let union_all =
@@ -35,20 +35,27 @@ let union_all =
3535
);
3636

3737
/* This does not strictly 'complete' a segment but rather does a
38-
* rough version of it that suffices for indentation calculation */
39-
let rec shallow_complete_segment = (seg: Segment.t): Segment.t =>
40-
switch (seg) {
41-
| [] => []
42-
| [Tile(t), ...rest] when !Tile.is_complete(t) => [
43-
Tile({
44-
...t,
45-
shards: List.init(List.length(t.label), i => i),
46-
children: t.children @ [shallow_complete_segment(rest)],
47-
/* Note: Potentially wrong number of children */
48-
}),
49-
]
50-
| [p, ...rest] => [p, ...shallow_complete_segment(rest)]
51-
};
38+
* rough version of it that suffices for indentation calculation.
39+
* Tail-recursive in segment length (recursion depth is bounded by
40+
* the number of incomplete tiles, not the number of pieces). */
41+
let rec shallow_complete_segment = (seg: Segment.t): Segment.t => {
42+
let rec go = (acc, seg: Segment.t): Segment.t =>
43+
switch (seg) {
44+
| [] => List.rev(acc)
45+
| [Tile(t), ...rest] when !Tile.is_complete(t) =>
46+
List.rev([
47+
Piece.Tile({
48+
...t,
49+
shards: List.init(List.length(t.label), i => i),
50+
children: t.children @ [shallow_complete_segment(rest)],
51+
/* Note: Potentially wrong number of children */
52+
}),
53+
...acc,
54+
])
55+
| [p, ...rest] => go([p, ...acc], rest)
56+
};
57+
go([], seg);
58+
};
5259

5360
/* Find the shortest prefix of the segment containing all incomplete tiles
5461
* followed by two consecutive linebreaks (aka a blank line) */
@@ -174,9 +181,13 @@ let rec go' = ((not_top, base: int, seg: Segment.t)) => {
174181
},
175182
(base, Id.Map.empty),
176183
complete_trimmed_seg,
177-
List.combine(
178-
prev_pieces(complete_trimmed_seg),
179-
next_pieces(complete_trimmed_seg),
184+
/* stack-safe zip (List.combine is not tail-recursive) */
185+
List.rev(
186+
List.rev_map2(
187+
(prev, next) => (prev, next),
188+
prev_pieces(complete_trimmed_seg),
189+
next_pieces(complete_trimmed_seg),
190+
),
180191
),
181192
);
182193
map;

0 commit comments

Comments
 (0)