Add much more efficient special-case for flat s-expr serialization
Getty Ritter
7 years ago
| 22 | 22 | import Data.Monoid ((<>)) |
| 23 | 23 | import Data.Text (Text) |
| 24 | 24 | import qualified Data.Text as T |
| 25 | import qualified Data.Text.Lazy as TL | |
| 26 | import qualified Data.Text.Lazy.Builder as B | |
| 25 | 27 | |
| 26 | 28 | import Data.SCargot.Repr |
| 27 | 29 | |
| 176 | 178 | -- | Pretty-print a 'SExpr' according to the options in a |
| 177 | 179 | -- 'LayoutOptions' value. |
| 178 | 180 | prettyPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> Text |
| 179 | prettyPrintSExpr SExprPrinter { .. } = pHead 0 | |
| 180 | where pHead _ SNil = "()" | |
| 181 | pHead _ (SAtom a) = atomPrinter a | |
| 182 | pHead ind (SCons x xs) = gather ind x xs id | |
| 183 | gather ind h (SCons x xs) k = gather ind h xs (k . (x:)) | |
| 184 | gather ind h end k = "(" <> hd <> body <> tl <> ")" | |
| 185 | where tl = case end of | |
| 186 | SNil -> "" | |
| 187 | SAtom a -> " . " <> atomPrinter a | |
| 188 | SCons _ _ -> error "[unreachable]" | |
| 189 | hd = indentSubsequent ind [pHead (ind+1) h] | |
| 190 | lst = k [] | |
| 191 | flat = T.unwords (map (pHead (ind+1)) lst) | |
| 192 | headWidth = T.length hd + 1 | |
| 193 | indented = | |
| 194 | case swingIndent h of | |
| 195 | SwingAfter n -> | |
| 196 | let (l, ls) = splitAt n lst | |
| 197 | t = T.unwords (map (pHead (ind+1)) l) | |
| 198 | ts = indentAll (ind + indentAmount) | |
| 199 | (map (pHead (ind + indentAmount)) ls) | |
| 200 | in t <> ts | |
| 201 | Swing -> | |
| 202 | indentAll (ind + indentAmount) | |
| 203 | (map (pHead (ind + indentAmount)) lst) | |
| 204 | Align -> | |
| 205 | indentSubsequent (ind + headWidth + 1) | |
| 206 | (map (pHead (ind + headWidth + 1)) lst) | |
| 207 | body | |
| 208 | | length lst == 0 = "" | |
| 209 | | Just maxAmt <- maxWidth | |
| 210 | , T.length flat + ind > maxAmt = " " <> indented | |
| 211 | | otherwise = " " <> flat | |
| 181 | prettyPrintSExpr pr@SExprPrinter { .. } expr = case maxWidth of | |
| 182 | Nothing -> flatPrintSExpr (fmap atomPrinter (fromCarrier expr)) | |
| 183 | Just _ -> indentPrintSExpr pr expr | |
| 184 | ||
| 185 | indentPrintSExpr :: SExprPrinter a (SExpr a) -> SExpr a -> Text | |
| 186 | indentPrintSExpr SExprPrinter { .. } = pHead 0 | |
| 187 | where | |
| 188 | pHead _ SNil = "()" | |
| 189 | pHead _ (SAtom a) = atomPrinter a | |
| 190 | pHead ind (SCons x xs) = gather ind x xs id | |
| 191 | gather ind h (SCons x xs) k = gather ind h xs (k . (x:)) | |
| 192 | gather ind h end k = "(" <> hd <> body <> tl <> ")" | |
| 193 | where tl = case end of | |
| 194 | SNil -> "" | |
| 195 | SAtom a -> " . " <> atomPrinter a | |
| 196 | SCons _ _ -> error "[unreachable]" | |
| 197 | hd = indentSubsequent ind [pHead (ind+1) h] | |
| 198 | lst = k [] | |
| 199 | flat = T.unwords (map (pHead (ind+1)) lst) | |
| 200 | headWidth = T.length hd + 1 | |
| 201 | indented = | |
| 202 | case swingIndent h of | |
| 203 | SwingAfter n -> | |
| 204 | let (l, ls) = splitAt n lst | |
| 205 | t = T.unwords (map (pHead (ind+1)) l) | |
| 206 | ts = indentAll (ind + indentAmount) | |
| 207 | (map (pHead (ind + indentAmount)) ls) | |
| 208 | in t <> ts | |
| 209 | Swing -> | |
| 210 | indentAll (ind + indentAmount) | |
| 211 | (map (pHead (ind + indentAmount)) lst) | |
| 212 | Align -> | |
| 213 | indentSubsequent (ind + headWidth + 1) | |
| 214 | (map (pHead (ind + headWidth + 1)) lst) | |
| 215 | body | |
| 216 | | length lst == 0 = "" | |
| 217 | | Just maxAmt <- maxWidth | |
| 218 | , T.length flat + ind > maxAmt = " " <> indented | |
| 219 | | otherwise = " " <> flat | |
| 220 | ||
| 221 | -- where | |
| 222 | -- -- this is the base-case that knows how to print empty lists and | |
| 223 | -- -- atoms | |
| 224 | -- pHead _ SNil = B.fromString "()" | |
| 225 | -- pHead _ (SAtom a) = B.fromText a | |
| 226 | -- pHead ind (SCons x xs) = gather ind x xs id 0 | |
| 227 | ||
| 228 | -- -- otherwise, we trawl through the list grabbing every element... | |
| 229 | -- gather ind h (SCons x xs) k r = gather ind h xs (k . (x:)) (r + T.length x) | |
| 230 | -- gather ind h end k r = B.fromString "(" <> hd <> body <> tl <> B.fromString ")" | |
| 231 | -- where | |
| 232 | -- tl = case end of | |
| 233 | -- SNil -> mempty | |
| 234 | -- SAtom a -> B.fromString " . " <> B.fromText a | |
| 235 | -- SCons _ _ -> error "[unreachable]" | |
| 236 | -- hd = indentSubsequent ind [pHead (ind+1) h] | |
| 237 | -- lst = k [] | |
| 238 | -- flat = T.unwords (map (pHead (ind+1)) lst) | |
| 239 | -- headWidth = T.length hd + 1 | |
| 240 | -- indented = | |
| 241 | -- case swingIndent h of | |
| 242 | -- SwingAfter n -> | |
| 243 | -- let (l, ls) = splitAt n lst | |
| 244 | -- t = T.unwords (map (pHead (ind+1)) l) | |
| 245 | -- ts = indentAll (ind + indentAmount) | |
| 246 | -- (map (pHead (ind + indentAmount)) ls) | |
| 247 | -- in t <> ts | |
| 248 | -- Swing -> | |
| 249 | -- indentAll (ind + indentAmount) | |
| 250 | -- (map (pHead (ind + indentAmount)) lst) | |
| 251 | -- Align -> | |
| 252 | -- indentSubsequent (ind + headWidth + 1) | |
| 253 | -- (map (pHead (ind + headWidth + 1)) lst) | |
| 254 | -- body | |
| 255 | -- | length lst == 0 = B.fromString "" | |
| 256 | -- | Just maxAmt <- maxWidth | |
| 257 | -- , T.length flat + ind > maxAmt = B.fromString " " <> indented | |
| 258 | -- | otherwise = B.fromString " " <> flat | |
| 259 | ||
| 260 | -- if we don't indent anything, then we can ignore a bunch of the | |
| 261 | -- details above | |
| 262 | flatPrintSExpr :: SExpr Text -> Text | |
| 263 | flatPrintSExpr = TL.toStrict . B.toLazyText . pHead | |
| 264 | where | |
| 265 | pHead (SCons x xs) = | |
| 266 | B.fromString "(" <> pHead x <> pTail xs | |
| 267 | pHead (SAtom t) = | |
| 268 | B.fromText t | |
| 269 | pHead SNil = | |
| 270 | B.fromString "()" | |
| 271 | ||
| 272 | pTail (SCons x xs) = | |
| 273 | B.fromString " " <> pHead x <> pTail xs | |
| 274 | pTail (SAtom t) = | |
| 275 | B.fromString " . " <> B.fromText t <> B.fromString ")" | |
| 276 | pTail SNil = | |
| 277 | B.fromString ")" | |
| 212 | 278 | |
| 213 | 279 | -- | Turn a single s-expression into a string according to a given |
| 214 | 280 | -- 'SExprPrinter'. |