[ fix ] stack overflow in Prettier

This commit is contained in:
2024-10-17 17:18:20 -07:00
parent 50ee14fc09
commit 05afc10631

View File

@@ -12,9 +12,9 @@ import Data.Nat
export
data Doc = Empty | Line | Text String | Nest Nat Doc | Seq Doc Doc | Alt Doc Doc
||| `DOC` is an intermediate form used during layout/rendering
||| The capitalization is the opposite of the paper.
data DOC = EMPTY | TEXT String DOC | LINE Nat DOC
||| The original paper had a List-like structure Doc (the above was DOC) which
||| had Empty and a tail on Text and Line.
data Item = TEXT String | LINE Nat
export
empty : Doc
@@ -31,36 +31,38 @@ flatten (Alt x y) = flatten x
group : Doc -> Doc
group x = Alt (flatten x) x
layout : DOC -> String
layout EMPTY = ""
layout (LINE k x) = "\n" ++ replicate k ' ' ++ layout x
layout (TEXT str x) = str ++ layout x
-- TODO - we can accumulate snoc and cat all at once
layout : List Item -> String
layout [] = ""
layout (LINE k :: x) = "\n" ++ replicate k ' ' ++ layout x
layout (TEXT str :: x) = str ++ layout x
||| Whether a documents first line fits.
fits : Nat -> DOC -> Bool
fits w x = if w == 0 then False else case x of
EMPTY => True
(LINE k x) => True
(TEXT s x) => fits (w `minus` length s) x
fits : Nat -> List Item -> Bool
fits 0 x = False
fits w ((TEXT s) :: xs) = fits (w `minus` length s) xs
fits w _ = True
-- The lazy is important
better : Nat -> Nat -> DOC -> Lazy DOC -> DOC
better : Nat -> Nat -> List Item -> Lazy (List Item) -> List Item
better w k x y = if fits (w `minus` k) x then x else y
be : Nat -> Nat -> List (Nat, Doc) -> DOC
be w k [] = EMPTY
be w k ((i, Empty) :: xs) = be w k xs
be w k ((i, Line) :: xs) = LINE i (be w i xs)
be w k ((i, (Text s)) :: xs) = TEXT s (be w (k + length s) xs)
be w k ((i, (Nest j x)) :: xs) = be w k ((i+j,x)::xs)
be w k ((i, (Seq x y)) :: xs) = be w k ((i,x) :: (i,y) :: xs)
be w k ((i, (Alt x y)) :: xs) = better w k (be w k ((i,x)::xs))
(be w k ((i,y)::xs))
-- vs Wadler, we're collecting the left side as a SnocList to prevent
-- blowing out the stack on the Text case. The original had DOC as
-- a Linked-List like structure (now List Item)
be : SnocList Item -> Nat -> Nat -> List (Nat, Doc) -> List Item
be acc w k [] = acc <>> []
be acc w k ((i, Empty) :: xs) = be acc w k xs
be acc w k ((i, Line) :: xs) = (be (acc :< LINE i) w i xs)
be acc w k ((i, (Text s)) :: xs) = (be (acc :< TEXT s) w (k + length s) xs)
be acc w k ((i, (Nest j x)) :: xs) = be acc w k ((i+j,x)::xs)
be acc w k ((i, (Seq x y)) :: xs) = be acc w k ((i,x) :: (i,y) :: xs)
-- We're doing extra work here - the first branch should cut if it exhausts w before the first LINE
be acc w k ((i, (Alt x y)) :: xs) = acc <>> better w k (be [<] w k ((i,x)::xs))
(be [<] w k ((i,y)::xs))
best : Nat -> Nat -> Doc -> DOC
best w k x = be w k [(0,x)]
best : Nat -> Nat -> Doc -> List Item
best w k x = be [<] w k [(0,x)]
-- Public interface
public export