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, foldr, foldr1)
8 import Data.Function ((.), ($))
9 import Data.Functor (Functor(..))
11 import Data.Maybe (Maybe(..))
12 import Data.Monoid (Monoid(..))
13 import Data.Ord (Ord(..), Ordering(..))
14 import Data.Semigroup (Semigroup(..))
15 import Data.String (String, IsString)
16 import Prelude (Integer, fromIntegral, Num(..), pred, undefined, Integral, Real, Enum)
17 import Text.Show (Show(..))
18 import qualified Data.Foldable as Foldable
19 import qualified Data.List as List
20 import qualified Data.Text as Text
21 import qualified Data.Text.Lazy as TL
24 newtype Nat = Nat Integer
25 deriving (Eq, Ord, Show, Integral, Real, Enum)
26 unLength :: Nat -> Integer
28 instance Num Nat where
29 fromInteger i | 0 <= i = Nat i
30 | otherwise = undefined
31 abs = Nat . abs . unLength
32 signum = signum . signum
33 Nat x + Nat y = Nat (x + y)
34 Nat x * Nat y = Nat (x * y)
35 Nat x - Nat y | y <= x = Nat (x - y)
36 | otherwise = undefined
38 -- * Class 'Lengthable'
39 class Lengthable a where
41 instance Lengthable Char where
43 instance Lengthable [a] where
44 length = Nat . fromIntegral . List.length
45 instance Lengthable Text.Text where
46 length = Nat . fromIntegral . Text.length
47 instance Lengthable TL.Text where
48 length = Nat . fromIntegral . TL.length
50 -- * Class 'Splitable'
51 class Monoid a => Splitable a where
54 break :: (Char -> Bool) -> a -> (a, a)
56 lines = splitOnChar (== '\n')
58 words = splitOnChar (== ' ')
59 splitOnChar :: (Char -> Bool) -> a -> [a]
62 else let (l,a') = break c a in
63 l : if null a' then []
64 else let a'' = tail a' in
65 if null a'' then [mempty] else splitOnChar c a''
66 instance Splitable String where
70 instance Splitable Text.Text where
74 instance Splitable TL.Text where
86 class (IsString d, Semigroup d) => Textable d where
88 charH :: Char -- ^ XXX: MUST NOT be '\n'
90 stringH :: String -- ^ XXX: MUST NOT contain '\n'
92 textH :: Text.Text -- ^ XXX: MUST NOT contain '\n'
94 ltextH :: TL.Text -- ^ XXX: MUST NOT contain '\n'
96 default empty :: Textable (ReprOf d) => Trans d => d
97 default charH :: Textable (ReprOf d) => Trans d => Char -> d
98 default stringH :: Textable (ReprOf d) => Trans d => String -> d
99 default textH :: Textable (ReprOf d) => Trans d => Text.Text -> d
100 default ltextH :: Textable (ReprOf d) => Trans d => TL.Text -> d
102 charH = trans . charH
103 stringH = trans . stringH
104 textH = trans . textH
105 ltextH = trans . ltextH
109 -- | @x '<+>' y = x '<>' 'space' '<>' y@
111 -- | @x '</>' y = x '<>' 'newline' '<>' y@
114 integer :: Integer -> d
116 string :: String -> d
117 text :: Text.Text -> d
118 ltext :: TL.Text -> d
119 catH :: Foldable f => f d -> d
120 catV :: Foldable f => f d -> d
121 unwords :: Foldable f => f d -> d
122 unlines :: Foldable f => f d -> d
123 foldrWith :: Foldable f => (d -> d -> d) -> f d -> d
124 foldWith :: Foldable f => (d -> d) -> f d -> d
125 intercalate :: Foldable f => d -> f d -> d
126 between :: d -> d -> d -> d
127 replicate :: Int -> d -> d
131 x <+> y = x <> space <> y
132 x </> y = x <> newline <> y
134 integer = stringH . show
135 char = \case '\n' -> newline; c -> charH c
136 string = catV . fmap stringH . lines
137 text = catV . fmap textH . lines
138 ltext = catV . fmap ltextH . lines
139 catH = foldr (<>) empty
140 catV = foldrWith (\x y -> x<>newline<>y)
141 unwords = foldr (<>) space
142 unlines = foldr (\x y -> x<>newline<>y) empty
143 foldrWith f ds = if Foldable.null ds then empty else foldr1 f ds
144 foldWith f = foldrWith $ \a acc -> a <> f acc
145 intercalate sep = foldrWith (\x y -> x<>sep<>y)
146 between o c d = o<>d<>c
147 replicate cnt t | cnt <= 0 = empty
148 | otherwise = t <> replicate (pred cnt) t
150 -- * Class 'Indentable'
151 class Textable d => Indentable d where
152 -- | @('align' d)@ make @d@ uses current 'Column' as 'Indent' level.
154 -- | @('incrIndent' ind d)@ make @d@ uses current 'Indent' plus @ind@ as 'Indent' level.
155 incrIndent :: Indent -> d -> d
156 -- | @('withIndent' ind d)@ make @d@ uses @ind@ as 'Indent' level.
157 withIndent :: Indent -> d -> d
158 -- | @('withNewline' nl d)@ make @d@ uses @nl@ as 'newline'.
160 -- Useful values for @nl@ are: 'empty', 'newlineWithIndent', 'newlineWithoutIndent'.
161 withNewline :: d -> d -> d
162 newlineWithoutIndent :: d
163 newlineWithIndent :: d
164 -- | @('column' f)@ write @f@ applied to the current 'Column'.
165 column :: (Column -> d) -> d
166 -- | @('indent' f)@ write @f@ applied to the current 'Indent'.
167 indent :: (Indent -> d) -> d
169 -- | @('hang' ind d)@ make @d@ uses current 'Column' plus @ind@ as 'Indent' level.
170 hang :: Indent -> d -> d
171 hang ind = align . incrIndent ind
173 -- | @('endToEndWidth' d f)@ write @d@ then
174 -- @f@ applied to the difference between
175 -- the end 'Column' and start 'Column' of @d@.
177 -- Note that @f@ is given the end-to-end width,
178 -- which is not necessarily the maximal width.
179 endToEndWidth :: d -> (Column -> d) -> d
180 endToEndWidth d f = column $ \c1 -> (d <>) $ column $ \c2 -> f $ c2 - c1
182 -- | @'spaces' ind = 'replicate' ind 'space'@
183 spaces :: Indent -> d
184 spaces i = replicate (fromIntegral i) space
186 -- | @('fill' ind d)@ write @d@,
187 -- then if @d@ is not wider than @ind@,
188 -- write the difference with 'spaces'.
189 fill :: Indent -> d -> d
191 endToEndWidth d $ \w ->
196 -- | @('breakableFill' ind d)@ write @d@,
197 -- then if @d@ is not wider than @ind@, write the difference with 'spaces'
198 -- otherwise write a 'newline' indented to to the start 'Column' of @d@ plus @ind@.
199 breakableFill :: Indent -> d -> d
202 endToEndWidth d $ \w ->
204 LT -> spaces (m - w) <> empty
206 GT -> withIndent (c + m) newline
208 -- * Class 'Breakable'
209 class (Textable d, Indentable d) => Breakable d where
210 -- | @('breakable' f)@ write @f@ applied to whether breaks are activated or not.
211 breakable :: (Maybe Column -> d) -> d
212 -- | @('withBreakable' b d)@ whether to active breaks or not within @d@.
213 withBreakable :: Maybe Column -> d -> d
214 -- | @('ifBreak' onWrap onNoWrap)@
215 -- write @onWrap@ if @onNoWrap@ leads to a 'Column'
216 -- greater or equal to the one sets with 'withBreakable',
217 -- otherwise write @onNoWrap@.
218 ifBreak :: d -> d -> d
219 -- | @('breakpoint' onNoBreak onBreak d)@
220 -- write @onNoBreak@ then @d@ if they fit,
221 -- @onBreak@ otherwise.
222 breakpoint :: d -> d -> d -> d
224 -- | @('breakableEmpty' d)@ write @d@ if it fits, 'newline' then @d@ otherwise.
225 breakableEmpty :: d -> d
226 breakableEmpty = breakpoint empty newline
228 -- | @x '><' y = x '<>' 'breakableEmpty' y@
230 x >< y = x <> breakableEmpty y
232 -- | @('breakableSpace' d)@ write 'space' then @d@ it they fit,
233 -- 'newline' then @d@ otherwise.
234 breakableSpace :: d -> d
235 breakableSpace = breakpoint space newline
237 -- | @x '>+<' y = x '<>' 'breakableSpace' y@
239 x >+< y = x <> breakableSpace y
241 -- | @'breakableSpaces' ds@ intercalate a 'breakableSpace'
242 -- between items of @ds@.
243 breakableSpaces :: Foldable f => f d -> d
244 breakableSpaces = foldWith breakableSpace
246 -- | @('intercalateHorV' sep ds)@
247 -- write @ds@ with @sep@ intercalated if the whole fits,
248 -- otherwise write 'align' of @ds@ with 'newline' and @sep@ intercalated.
249 intercalateHorV :: Foldable f => d -> f d -> d
250 intercalateHorV sep xs =
252 (align $ foldWith ((newline <> sep) <>) xs)
253 (foldWith (sep <>) xs)
255 -- * Class 'Colorable'
256 class Colorable d where
257 -- | @('colorable' f)@ write @f@ applied to whether colors are activated or not.
258 colorable :: (Bool -> d) -> d
259 -- | @('withColor' b d)@ whether to active colors or not within @d@.
260 withColorable :: Bool -> d -> d
302 onMagentaer :: d -> d
306 default reverse :: Colorable (ReprOf d) => Trans d => d -> d
307 default black :: Colorable (ReprOf d) => Trans d => d -> d
308 default red :: Colorable (ReprOf d) => Trans d => d -> d
309 default green :: Colorable (ReprOf d) => Trans d => d -> d
310 default yellow :: Colorable (ReprOf d) => Trans d => d -> d
311 default blue :: Colorable (ReprOf d) => Trans d => d -> d
312 default magenta :: Colorable (ReprOf d) => Trans d => d -> d
313 default cyan :: Colorable (ReprOf d) => Trans d => d -> d
314 default white :: Colorable (ReprOf d) => Trans d => d -> d
315 default blacker :: Colorable (ReprOf d) => Trans d => d -> d
316 default redder :: Colorable (ReprOf d) => Trans d => d -> d
317 default greener :: Colorable (ReprOf d) => Trans d => d -> d
318 default yellower :: Colorable (ReprOf d) => Trans d => d -> d
319 default bluer :: Colorable (ReprOf d) => Trans d => d -> d
320 default magentaer :: Colorable (ReprOf d) => Trans d => d -> d
321 default cyaner :: Colorable (ReprOf d) => Trans d => d -> d
322 default whiter :: Colorable (ReprOf d) => Trans d => d -> d
323 default onBlack :: Colorable (ReprOf d) => Trans d => d -> d
324 default onRed :: Colorable (ReprOf d) => Trans d => d -> d
325 default onGreen :: Colorable (ReprOf d) => Trans d => d -> d
326 default onYellow :: Colorable (ReprOf d) => Trans d => d -> d
327 default onBlue :: Colorable (ReprOf d) => Trans d => d -> d
328 default onMagenta :: Colorable (ReprOf d) => Trans d => d -> d
329 default onCyan :: Colorable (ReprOf d) => Trans d => d -> d
330 default onWhite :: Colorable (ReprOf d) => Trans d => d -> d
331 default onBlacker :: Colorable (ReprOf d) => Trans d => d -> d
332 default onRedder :: Colorable (ReprOf d) => Trans d => d -> d
333 default onGreener :: Colorable (ReprOf d) => Trans d => d -> d
334 default onYellower :: Colorable (ReprOf d) => Trans d => d -> d
335 default onBluer :: Colorable (ReprOf d) => Trans d => d -> d
336 default onMagentaer :: Colorable (ReprOf d) => Trans d => d -> d
337 default onCyaner :: Colorable (ReprOf d) => Trans d => d -> d
338 default onWhiter :: Colorable (ReprOf d) => Trans d => d -> d
340 reverse = trans1 reverse
344 yellow = trans1 yellow
346 magenta = trans1 magenta
349 blacker = trans1 blacker
350 redder = trans1 redder
351 greener = trans1 greener
352 yellower = trans1 yellower
354 magentaer = trans1 magentaer
355 cyaner = trans1 cyaner
356 whiter = trans1 whiter
357 onBlack = trans1 onBlack
359 onGreen = trans1 onGreen
360 onYellow = trans1 onYellow
361 onBlue = trans1 onBlue
362 onMagenta = trans1 onMagenta
363 onCyan = trans1 onCyan
364 onWhite = trans1 onWhite
365 onBlacker = trans1 onBlacker
366 onRedder = trans1 onRedder
367 onGreener = trans1 onGreener
368 onYellower = trans1 onYellower
369 onBluer = trans1 onBluer
370 onMagentaer = trans1 onMagentaer
371 onCyaner = trans1 onCyaner
372 onWhiter = trans1 onWhiter
374 -- * Class 'Decorable'
375 class Decorable d where
376 -- | @('decorable' f)@ write @f@ applied to whether decorations are activated or not.
377 decorable :: (Bool -> d) -> d
378 -- | @('withColor' b d)@ whether to active decorations or not within @d@.
379 withDecorable :: Bool -> d -> d
384 default bold :: Decorable (ReprOf d) => Trans d => d -> d
385 default underline :: Decorable (ReprOf d) => Trans d => d -> d
386 default italic :: Decorable (ReprOf d) => Trans d => d -> d
388 underline = trans1 underline
389 italic = trans1 italic
393 -- | Return the underlying @tr@ of the transformer.
396 -- | Lift a tr to the transformer's.
397 trans :: ReprOf tr -> tr
398 -- | Unlift a tr from the transformer's.
399 unTrans :: tr -> ReprOf tr
401 -- | Identity transformation for a unary symantic method.
402 trans1 :: (ReprOf tr -> ReprOf tr) -> (tr -> tr)
403 trans1 f = trans . f . unTrans
405 -- | Identity transformation for a binary symantic method.
407 :: (ReprOf tr -> ReprOf tr -> ReprOf tr)
409 trans2 f t1 t2 = trans (f (unTrans t1) (unTrans t2))
411 -- | Identity transformation for a ternary symantic method.
413 :: (ReprOf tr -> ReprOf tr -> ReprOf tr -> ReprOf tr)
414 -> (tr -> tr -> tr -> tr)
415 trans3 f t1 t2 t3 = trans (f (unTrans t1) (unTrans t2) (unTrans t3))