@@ -220,14 +220,27 @@ let neighbor_tokens = (z: t): (option(Token.t), option(Token.t)) => (
220220 neighbor_token(Right , z),
221221);
222222
223- let rec do_until_piece =
224- (action: t => option (t ), p_n: neighbors => bool , z: t ): option(t) => {
225- let * z = action(z);
226- if (p_n (Siblings . neighbors (z .relatives .siblings ))) {
227- Some (z);
228- } else {
229- do_until_piece(action, p_n, z);
223+ /* Iterative version to avoid stack overflow on large programs */
224+ let do_until_piece =
225+ (action: t => option (t ), p_n: neighbors => bool , z: t ): option(t) => {
226+ let current = ref (action(z));
227+ let result = ref (None );
228+ let done_ = ref (false );
229+ while (! done_^ ) {
230+ switch (current^ ) {
231+ | None =>
232+ result := None ;
233+ done_ := true ;
234+ | Some (z ) =>
235+ if (p_n(Siblings . neighbors(z. relatives. siblings))) {
236+ result := Some (z);
237+ done_ := true ;
238+ } else {
239+ current := action(z);
240+ }
241+ };
230242 };
243+ result^;
231244};
232245
233246/* Do `action` until the predicate on the generalized neigbors of the
@@ -236,15 +249,31 @@ let rec do_until_piece =
236249 we are at the edge of a segment, in which case it's the relevant shard
237250 of the parent. The None case strictly means the beginning/end of the program.
238251 If no such piece is found, don't move. Does not check predicate before
239- moving; caller should handle that case if necessary */
240- let rec do_until =
241- (action: t => option (t ), p_n: neighbors => bool , z: t ): option(t) => {
242- let * z = action(z);
243- if (p_n (generalized_neighbors (z ))) {
244- Some (z);
245- } else {
246- do_until(action, p_n, z);
252+ moving; caller should handle that case if necessary.
253+
254+ NOTE: This is implemented iteratively to avoid stack overflow on large
255+ programs. The previous recursive implementation would overflow when
256+ traversing documents with thousands of tokens. */
257+ let do_until =
258+ (action: t => option (t ), p_n: neighbors => bool , z: t ): option(t) => {
259+ let current = ref (action(z));
260+ let result = ref (None );
261+ let done_ = ref (false );
262+ while (! done_^ ) {
263+ switch (current^ ) {
264+ | None =>
265+ result := None ;
266+ done_ := true ;
267+ | Some (z ) =>
268+ if (p_n(generalized_neighbors(z))) {
269+ result := Some (z);
270+ done_ := true ;
271+ } else {
272+ current := action(z);
273+ }
274+ };
247275 };
276+ result^;
248277};
249278
250279let do_to_extreme = (action: t => option (t ), z: t ): t =>
0 commit comments