1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 module Language.Symantic.Document.Sym where
5 import Data.Char (Char)
6 import Data.Eq (Eq(..))
7 import Data.Foldable (Foldable(..))
8 import Data.Function ((.), ($))
9 import Data.Functor (Functor(..))
11 import Data.Maybe (Maybe(..))
12 import Data.Ord (Ord(..), Ordering(..))
13 import Data.Semigroup (Semigroup(..))
14 import Data.String (String, IsString)
15 import Prelude (Integer, fromIntegral, Num(..), pred, undefined, Integral, Real, Enum)
16 import Text.Show (Show(..))
17 import qualified Data.List as List
18 import qualified Data.Text as Text
19 import qualified Data.Text.Lazy as TL
22 newtype Nat = Nat Integer
23 deriving (Eq, Ord, Show, Integral, Real, Enum)
24 unLength :: Nat -> Integer
26 instance Num Nat where
27 fromInteger i | 0 <= i = Nat i
28 | otherwise = undefined
29 abs = Nat . abs . unLength
30 signum = signum . signum
31 Nat x + Nat y = Nat (x + y)
32 Nat x * Nat y = Nat (x * y)
33 Nat x - Nat y | y <= x = Nat (x - y)
34 | otherwise = undefined
36 -- * Class 'Lengthable'
37 class Lengthable a where
39 instance Lengthable Char where
41 instance Lengthable [a] where
42 length = Nat . fromIntegral . List.length
43 instance Lengthable Text.Text where
44 length = Nat . fromIntegral . Text.length
45 instance Lengthable TL.Text where
46 length = Nat . fromIntegral . TL.length
55 class (IsString d, Semigroup d) => Textable d where
57 charH :: Char -- ^ XXX: MUST NOT be '\n'
59 stringH :: String -- ^ XXX: MUST NOT contain '\n'
61 textH :: Text.Text -- ^ XXX: MUST NOT contain '\n'
63 ltextH :: TL.Text -- ^ XXX: MUST NOT contain '\n'
65 default empty :: Textable (ReprOf d) => Trans d => d
66 default charH :: Textable (ReprOf d) => Trans d => Char -> d
67 default stringH :: Textable (ReprOf d) => Trans d => String -> d
68 default textH :: Textable (ReprOf d) => Trans d => Text.Text -> d
69 default ltextH :: Textable (ReprOf d) => Trans d => TL.Text -> d
72 stringH = trans . stringH
74 ltextH = trans . ltextH
78 -- | @x '<+>' y = x '<>' 'space' '<>' y@
80 -- | @x '</>' y = x '<>' 'newline' '<>' y@
83 integer :: Integer -> d
86 text :: Text.Text -> d
88 catH :: Foldable f => f d -> d
89 catV :: Foldable f => f d -> d
90 foldrWith :: Foldable f => (d -> d -> d) -> f d -> d
91 foldWith :: Foldable f => (d -> d) -> f d -> d
92 intercalate :: Foldable f => d -> f d -> d
93 between :: d -> d -> d -> d
94 replicate :: Int -> d -> d
98 x <+> y = x <> space <> y
99 x </> y = x <> newline <> y
101 integer = stringH . show
102 char = \case '\n' -> newline; c -> charH c
103 string = catV . fmap stringH . lines
104 text = catV . fmap textH . Text.lines
105 ltext = catV . fmap ltextH . TL.lines
106 catH = foldr (<>) empty
107 catV = foldrWith (\x y -> x<>newline<>y)
108 foldrWith f ds = if null ds then empty else foldr1 f ds
109 foldWith f = foldrWith $ \a acc -> a <> f acc
110 intercalate sep = foldrWith (\x y -> x<>sep<>y)
111 between o c d = o<>d<>c
112 replicate cnt t | cnt <= 0 = empty
113 | otherwise = t <> replicate (pred cnt) t
115 -- * Class 'Indentable'
116 class Textable d => Indentable d where
117 -- | @('align' d)@ make @d@ uses current 'Column' as 'Indent' level.
119 -- | @('incrIndent' ind d)@ make @d@ uses current 'Indent' plus @ind@ as 'Indent' level.
120 incrIndent :: Indent -> d -> d
121 -- | @('withIndent' ind d)@ make @d@ uses @ind@ as 'Indent' level.
122 withIndent :: Indent -> d -> d
123 -- | @('withNewline' nl d)@ make @d@ uses @nl@ as 'newline'.
125 -- Useful values for @nl@ are: 'empty', 'newlineWithIndent', 'newlineWithoutIndent'.
126 withNewline :: d -> d -> d
127 newlineWithoutIndent :: d
128 newlineWithIndent :: d
129 -- | @('column' f)@ write @f@ applied to the current 'Column'.
130 column :: (Column -> d) -> d
131 -- | @('indent' f)@ write @f@ applied to the current 'Indent'.
132 indent :: (Indent -> d) -> d
134 -- | @('hang' ind d)@ make @d@ uses current 'Column' plus @ind@ as 'Indent' level.
135 hang :: Indent -> d -> d
136 hang ind = align . incrIndent ind
138 -- | @('endToEndWidth' d f)@ write @d@ then
139 -- @f@ applied to the difference between
140 -- the end 'Column' and start 'Column' of @d@.
142 -- Note that @f@ is given the end-to-end width,
143 -- which is not necessarily the maximal width.
144 endToEndWidth :: d -> (Column -> d) -> d
145 endToEndWidth d f = column $ \c1 -> (d <>) $ column $ \c2 -> f $ c2 - c1
147 -- | @'spaces' ind = 'replicate' ind 'space'@
148 spaces :: Indent -> d
149 spaces i = replicate (fromIntegral i) space
151 -- | @('fill' ind d)@ write @d@,
152 -- then if @d@ is not wider than @ind@,
153 -- write the difference with 'spaces'.
154 fill :: Indent -> d -> d
156 endToEndWidth d $ \w ->
161 -- | @('breakableFill' ind d)@ write @d@,
162 -- then if @d@ is not wider than @ind@, write the difference with 'spaces'
163 -- otherwise write a 'newline' indented to to the start 'Column' of @d@ plus @ind@.
164 breakableFill :: Indent -> d -> d
167 endToEndWidth d $ \w ->
169 LT -> spaces (m - w) <> empty
171 GT -> withIndent (c + m) newline
173 -- * Class 'Breakable'
174 class (Textable d, Indentable d) => Breakable d where
175 -- | @('breakable' f)@ write @f@ applied to whether breaks are activated or not.
176 breakable :: (Maybe Column -> d) -> d
177 -- | @('withBreakable' b d)@ whether to active breaks or not within @d@.
178 withBreakable :: Maybe Column -> d -> d
179 -- | @('ifBreak' onWrap onNoWrap)@
180 -- write @onWrap@ if @onNoWrap@ leads to a 'Column'
181 -- greater or equal to the one sets with 'withBreakable',
182 -- otherwise write @onNoWrap@.
183 ifBreak :: d -> d -> d
184 -- | @('breakpoint' onNoBreak onBreak d)@
185 -- write @onNoBreak@ then @d@ if they fit,
186 -- @onBreak@ otherwise.
187 breakpoint :: d -> d -> d -> d
189 -- | @('breakableEmpty' d)@ write @d@ if it fits, 'newline' then @d@ otherwise.
190 breakableEmpty :: d -> d
191 breakableEmpty = breakpoint empty newline
193 -- | @x '><' y = x '<>' 'breakableEmpty' y@
195 x >< y = x <> breakableEmpty y
197 -- | @('breakableSpace' d)@ write 'space' then @d@ it they fit,
198 -- 'newline' then @d@ otherwise.
199 breakableSpace :: d -> d
200 breakableSpace = breakpoint space newline
202 -- | @x '>+<' y = x '<>' 'breakableSpace' y@
204 x >+< y = x <> breakableSpace y
206 -- | @'breakableSpaces' ds@ intercalate a 'breakableSpace'
207 -- between items of @ds@.
208 breakableSpaces :: Foldable f => f d -> d
209 breakableSpaces = foldWith breakableSpace
211 -- | @('intercalateHorV' sep ds)@
212 -- write @ds@ with @sep@ intercalated if the whole fits,
213 -- otherwise write 'align' of @ds@ with 'newline' and @sep@ intercalated.
214 intercalateHorV :: Foldable f => d -> f d -> d
215 intercalateHorV sep xs =
217 (align $ foldWith ((newline <> sep) <>) xs)
218 (foldWith (sep <>) xs)
220 -- * Class 'Colorable'
221 class Colorable d where
222 -- | @('colorable' f)@ write @f@ applied to whether colors are activated or not.
223 colorable :: (Bool -> d) -> d
224 -- | @('withColor' b d)@ whether to active colors or not within @d@.
225 withColorable :: Bool -> d -> d
267 onMagentaer :: d -> d
271 default reverse :: Colorable (ReprOf d) => Trans d => d -> d
272 default black :: Colorable (ReprOf d) => Trans d => d -> d
273 default red :: Colorable (ReprOf d) => Trans d => d -> d
274 default green :: Colorable (ReprOf d) => Trans d => d -> d
275 default yellow :: Colorable (ReprOf d) => Trans d => d -> d
276 default blue :: Colorable (ReprOf d) => Trans d => d -> d
277 default magenta :: Colorable (ReprOf d) => Trans d => d -> d
278 default cyan :: Colorable (ReprOf d) => Trans d => d -> d
279 default white :: Colorable (ReprOf d) => Trans d => d -> d
280 default blacker :: Colorable (ReprOf d) => Trans d => d -> d
281 default redder :: Colorable (ReprOf d) => Trans d => d -> d
282 default greener :: Colorable (ReprOf d) => Trans d => d -> d
283 default yellower :: Colorable (ReprOf d) => Trans d => d -> d
284 default bluer :: Colorable (ReprOf d) => Trans d => d -> d
285 default magentaer :: Colorable (ReprOf d) => Trans d => d -> d
286 default cyaner :: Colorable (ReprOf d) => Trans d => d -> d
287 default whiter :: Colorable (ReprOf d) => Trans d => d -> d
288 default onBlack :: Colorable (ReprOf d) => Trans d => d -> d
289 default onRed :: Colorable (ReprOf d) => Trans d => d -> d
290 default onGreen :: Colorable (ReprOf d) => Trans d => d -> d
291 default onYellow :: Colorable (ReprOf d) => Trans d => d -> d
292 default onBlue :: Colorable (ReprOf d) => Trans d => d -> d
293 default onMagenta :: Colorable (ReprOf d) => Trans d => d -> d
294 default onCyan :: Colorable (ReprOf d) => Trans d => d -> d
295 default onWhite :: Colorable (ReprOf d) => Trans d => d -> d
296 default onBlacker :: Colorable (ReprOf d) => Trans d => d -> d
297 default onRedder :: Colorable (ReprOf d) => Trans d => d -> d
298 default onGreener :: Colorable (ReprOf d) => Trans d => d -> d
299 default onYellower :: Colorable (ReprOf d) => Trans d => d -> d
300 default onBluer :: Colorable (ReprOf d) => Trans d => d -> d
301 default onMagentaer :: Colorable (ReprOf d) => Trans d => d -> d
302 default onCyaner :: Colorable (ReprOf d) => Trans d => d -> d
303 default onWhiter :: Colorable (ReprOf d) => Trans d => d -> d
305 reverse = trans1 reverse
309 yellow = trans1 yellow
311 magenta = trans1 magenta
314 blacker = trans1 blacker
315 redder = trans1 redder
316 greener = trans1 greener
317 yellower = trans1 yellower
319 magentaer = trans1 magentaer
320 cyaner = trans1 cyaner
321 whiter = trans1 whiter
322 onBlack = trans1 onBlack
324 onGreen = trans1 onGreen
325 onYellow = trans1 onYellow
326 onBlue = trans1 onBlue
327 onMagenta = trans1 onMagenta
328 onCyan = trans1 onCyan
329 onWhite = trans1 onWhite
330 onBlacker = trans1 onBlacker
331 onRedder = trans1 onRedder
332 onGreener = trans1 onGreener
333 onYellower = trans1 onYellower
334 onBluer = trans1 onBluer
335 onMagentaer = trans1 onMagentaer
336 onCyaner = trans1 onCyaner
337 onWhiter = trans1 onWhiter
339 -- * Class 'Decorable'
340 class Decorable d where
341 -- | @('decorable' f)@ write @f@ applied to whether decorations are activated or not.
342 decorable :: (Bool -> d) -> d
343 -- | @('withColor' b d)@ whether to active decorations or not within @d@.
344 withDecorable :: Bool -> d -> d
349 default bold :: Decorable (ReprOf d) => Trans d => d -> d
350 default underline :: Decorable (ReprOf d) => Trans d => d -> d
351 default italic :: Decorable (ReprOf d) => Trans d => d -> d
353 underline = trans1 underline
354 italic = trans1 italic
358 -- | Return the underlying @tr@ of the transformer.
361 -- | Lift a tr to the transformer's.
362 trans :: ReprOf tr -> tr
363 -- | Unlift a tr from the transformer's.
364 unTrans :: tr -> ReprOf tr
366 -- | Identity transformation for a unary symantic method.
367 trans1 :: (ReprOf tr -> ReprOf tr) -> (tr -> tr)
368 trans1 f = trans . f . unTrans
370 -- | Identity transformation for a binary symantic method.
372 :: (ReprOf tr -> ReprOf tr -> ReprOf tr)
374 trans2 f t1 t2 = trans (f (unTrans t1) (unTrans t2))
376 -- | Identity transformation for a ternary symantic method.
378 :: (ReprOf tr -> ReprOf tr -> ReprOf tr -> ReprOf tr)
379 -> (tr -> tr -> tr -> tr)
380 trans3 f t1 t2 t3 = trans (f (unTrans t1) (unTrans t2) (unTrans t3))
382 -- | Break a 'String' into lines while preserving all empty lines.
383 lines :: String -> [String]
385 case List.break (== '\n') cs of
386 (chunk, _:rest) -> chunk : lines rest
387 (chunk, []) -> [chunk]