1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 module 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 { unNat :: 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 | x >= y = 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 (UnTrans d) => Trans d => d
97 default charH :: Textable (UnTrans d) => Trans d => Char -> d
98 default stringH :: Textable (UnTrans d) => Trans d => String -> d
99 default textH :: Textable (UnTrans d) => Trans d => Text.Text -> d
100 default ltextH :: Textable (UnTrans d) => Trans d => TL.Text -> d
101 empty = noTrans empty
102 charH = noTrans . charH
103 stringH = noTrans . stringH
104 textH = noTrans . textH
105 ltextH = noTrans . 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 default align :: Indentable (UnTrans d) => Trans d => d -> d
155 align = noTrans1 align
156 -- | @('incrIndent' ind d)@ make @d@ uses current 'Indent' plus @ind@ as 'Indent' level.
157 incrIndent :: Indent -> d -> d
158 default incrIndent :: Indentable (UnTrans d) => Trans d => Indent -> d -> d
159 incrIndent = noTrans1 . incrIndent
160 -- | @('withIndent' ind d)@ make @d@ uses @ind@ as 'Indent' level.
161 withIndent :: Indent -> d -> d
162 default withIndent :: Indentable (UnTrans d) => Trans d => Indent -> d -> d
163 withIndent = noTrans1 . withIndent
164 -- | @('withNewline' nl d)@ make @d@ uses @nl@ as 'newline'.
166 -- Useful values for @nl@ are: 'empty', 'newlineWithIndent', 'newlineWithoutIndent'.
167 withNewline :: d -> d -> d
168 newlineWithoutIndent :: d
169 newlineWithIndent :: d
170 default withNewline :: Indentable (UnTrans d) => Trans d => d -> d -> d
171 default newlineWithoutIndent :: Indentable (UnTrans d) => Trans d => d
172 default newlineWithIndent :: Indentable (UnTrans d) => Trans d => d
173 withNewline = noTrans2 withNewline
174 newlineWithoutIndent = noTrans newlineWithoutIndent
175 newlineWithIndent = noTrans newlineWithIndent
176 -- | @('column' f)@ write @f@ applied to the current 'Column'.
177 column :: (Column -> d) -> d
178 default column :: Indentable (UnTrans d) => Trans d => (Column -> d) -> d
179 column f = noTrans $ column (unTrans . f)
180 -- | @('indent' f)@ write @f@ applied to the current 'Indent'.
181 indent :: (Indent -> d) -> d
182 default indent :: Indentable (UnTrans d) => Trans d => (Indent -> d) -> d
183 indent f = noTrans $ indent (unTrans . f)
185 -- | @('hang' ind d)@ make @d@ uses current 'Column' plus @ind@ as 'Indent' level.
186 hang :: Indent -> d -> d
187 hang ind = align . incrIndent ind
189 -- | @('endToEndWidth' d f)@ write @d@ then
190 -- @f@ applied to the absolute value of the difference between
191 -- the end 'Column' and start 'Column' of @d@.
193 -- Note that @f@ is given the end-to-end width,
194 -- which is not necessarily the maximal width.
195 endToEndWidth :: d -> (Column -> d) -> d
204 -- | @'spaces' ind = 'replicate' ind 'space'@
205 spaces :: Indent -> d
206 spaces i = replicate (fromIntegral i) space
208 -- | @('fill' ind d)@ write @d@,
209 -- then if @d@ is not wider than @ind@,
210 -- write the difference with 'spaces'.
211 fill :: Indent -> d -> d
213 endToEndWidth d $ \w ->
218 -- | @('breakableFill' ind d)@ write @d@,
219 -- then if @d@ is not wider than @ind@, write the difference with 'spaces'
220 -- otherwise write a 'newline' indented to to the start 'Column' of @d@ plus @ind@.
221 breakableFill :: Indent -> d -> d
224 endToEndWidth d $ \w ->
228 GT -> withIndent (c + m) newline
230 -- * Class 'Breakable'
231 class (Textable d, Indentable d) => Breakable d where
232 -- | @('breakable' f)@ write @f@ applied to whether breaks are activated or not.
233 breakable :: (Maybe Column -> d) -> d
234 default breakable :: Breakable (UnTrans d) => Trans d => (Maybe Column -> d) -> d
235 breakable f = noTrans $ breakable (unTrans . f)
236 -- | @('withBreakable' b d)@ whether to active breaks or not within @d@.
237 withBreakable :: Maybe Column -> d -> d
238 default withBreakable :: Breakable (UnTrans d) => Trans d => Maybe Column -> d -> d
239 withBreakable = noTrans1 . withBreakable
241 -- | @('ifBreak' onWrap onNoWrap)@
242 -- write @onWrap@ if @onNoWrap@ leads to a 'Column'
243 -- greater or equal to the one sets with 'withBreakable',
244 -- otherwise write @onNoWrap@.
245 ifBreak :: d -> d -> d
246 default ifBreak :: Breakable (UnTrans d) => Trans d => d -> d -> d
247 ifBreak = noTrans2 ifBreak
248 -- | @('breakpoint' onNoBreak onBreak d)@
249 -- write @onNoBreak@ then @d@ if they fit,
250 -- @onBreak@ otherwise.
251 breakpoint :: d -> d -> d -> d
252 default breakpoint :: Breakable (UnTrans d) => Trans d => d -> d -> d -> d
253 breakpoint = noTrans3 breakpoint
255 -- | @('breakableEmpty' d)@ write @d@ if it fits, 'newline' then @d@ otherwise.
256 breakableEmpty :: d -> d
257 breakableEmpty = breakpoint empty newline
259 -- | @x '><' y = x '<>' 'breakableEmpty' y@
261 x >< y = x <> breakableEmpty y
263 -- | @('breakableSpace' d)@ write 'space' then @d@ it they fit,
264 -- 'newline' then @d@ otherwise.
265 breakableSpace :: d -> d
266 breakableSpace = breakpoint space newline
268 -- | @x '>+<' y = x '<>' 'breakableSpace' y@
270 x >+< y = x <> breakableSpace y
272 -- | @'breakableSpaces' ds@ intercalate a 'breakableSpace'
273 -- between items of @ds@.
274 breakableSpaces :: Foldable f => f d -> d
275 breakableSpaces = foldWith breakableSpace
277 -- | @('intercalateHorV' sep ds)@
278 -- write @ds@ with @sep@ intercalated if the whole fits,
279 -- otherwise write 'align' of @ds@ with 'newline' and @sep@ intercalated.
280 intercalateHorV :: Foldable f => d -> f d -> d
281 intercalateHorV sep xs =
283 (align $ foldWith ((newline <> sep) <>) xs)
284 (foldWith (sep <>) xs)
286 -- * Class 'Colorable'
287 class Colorable d where
288 -- | @('colorable' f)@ write @f@ applied to whether colors are activated or not.
289 colorable :: (Bool -> d) -> d
290 default colorable :: Colorable (UnTrans d) => Trans d => (Bool -> d) -> d
291 colorable f = noTrans $ colorable (unTrans . f)
292 -- | @('withColor' b d)@ whether to active colors or not within @d@.
293 withColorable :: Bool -> d -> d
294 default withColorable :: Colorable (UnTrans d) => Trans d => Bool -> d -> d
295 withColorable = noTrans1 . withColorable
337 onMagentaer :: d -> d
341 default reverse :: Colorable (UnTrans d) => Trans d => d -> d
342 default black :: Colorable (UnTrans d) => Trans d => d -> d
343 default red :: Colorable (UnTrans d) => Trans d => d -> d
344 default green :: Colorable (UnTrans d) => Trans d => d -> d
345 default yellow :: Colorable (UnTrans d) => Trans d => d -> d
346 default blue :: Colorable (UnTrans d) => Trans d => d -> d
347 default magenta :: Colorable (UnTrans d) => Trans d => d -> d
348 default cyan :: Colorable (UnTrans d) => Trans d => d -> d
349 default white :: Colorable (UnTrans d) => Trans d => d -> d
350 default blacker :: Colorable (UnTrans d) => Trans d => d -> d
351 default redder :: Colorable (UnTrans d) => Trans d => d -> d
352 default greener :: Colorable (UnTrans d) => Trans d => d -> d
353 default yellower :: Colorable (UnTrans d) => Trans d => d -> d
354 default bluer :: Colorable (UnTrans d) => Trans d => d -> d
355 default magentaer :: Colorable (UnTrans d) => Trans d => d -> d
356 default cyaner :: Colorable (UnTrans d) => Trans d => d -> d
357 default whiter :: Colorable (UnTrans d) => Trans d => d -> d
358 default onBlack :: Colorable (UnTrans d) => Trans d => d -> d
359 default onRed :: Colorable (UnTrans d) => Trans d => d -> d
360 default onGreen :: Colorable (UnTrans d) => Trans d => d -> d
361 default onYellow :: Colorable (UnTrans d) => Trans d => d -> d
362 default onBlue :: Colorable (UnTrans d) => Trans d => d -> d
363 default onMagenta :: Colorable (UnTrans d) => Trans d => d -> d
364 default onCyan :: Colorable (UnTrans d) => Trans d => d -> d
365 default onWhite :: Colorable (UnTrans d) => Trans d => d -> d
366 default onBlacker :: Colorable (UnTrans d) => Trans d => d -> d
367 default onRedder :: Colorable (UnTrans d) => Trans d => d -> d
368 default onGreener :: Colorable (UnTrans d) => Trans d => d -> d
369 default onYellower :: Colorable (UnTrans d) => Trans d => d -> d
370 default onBluer :: Colorable (UnTrans d) => Trans d => d -> d
371 default onMagentaer :: Colorable (UnTrans d) => Trans d => d -> d
372 default onCyaner :: Colorable (UnTrans d) => Trans d => d -> d
373 default onWhiter :: Colorable (UnTrans d) => Trans d => d -> d
375 reverse = noTrans1 reverse
376 black = noTrans1 black
378 green = noTrans1 green
379 yellow = noTrans1 yellow
381 magenta = noTrans1 magenta
383 white = noTrans1 white
384 blacker = noTrans1 blacker
385 redder = noTrans1 redder
386 greener = noTrans1 greener
387 yellower = noTrans1 yellower
388 bluer = noTrans1 bluer
389 magentaer = noTrans1 magentaer
390 cyaner = noTrans1 cyaner
391 whiter = noTrans1 whiter
392 onBlack = noTrans1 onBlack
393 onRed = noTrans1 onRed
394 onGreen = noTrans1 onGreen
395 onYellow = noTrans1 onYellow
396 onBlue = noTrans1 onBlue
397 onMagenta = noTrans1 onMagenta
398 onCyan = noTrans1 onCyan
399 onWhite = noTrans1 onWhite
400 onBlacker = noTrans1 onBlacker
401 onRedder = noTrans1 onRedder
402 onGreener = noTrans1 onGreener
403 onYellower = noTrans1 onYellower
404 onBluer = noTrans1 onBluer
405 onMagentaer = noTrans1 onMagentaer
406 onCyaner = noTrans1 onCyaner
407 onWhiter = noTrans1 onWhiter
409 -- * Class 'Decorable'
410 class Decorable d where
411 -- | @('decorable' f)@ write @f@ applied to whether decorations are activated or not.
412 decorable :: (Bool -> d) -> d
413 default decorable :: Decorable (UnTrans d) => Trans d => (Bool -> d) -> d
414 decorable f = noTrans $ decorable (unTrans . f)
415 -- | @('withColor' b d)@ whether to active decorations or not within @d@.
416 withDecorable :: Bool -> d -> d
417 default withDecorable :: Decorable (UnTrans d) => Trans d => Bool -> d -> d
418 withDecorable = noTrans1 . withDecorable
423 default bold :: Decorable (UnTrans d) => Trans d => d -> d
424 default underline :: Decorable (UnTrans d) => Trans d => d -> d
425 default italic :: Decorable (UnTrans d) => Trans d => d -> d
427 underline = noTrans1 underline
428 italic = noTrans1 italic
431 class Trans repr where
432 -- | Return the underlying @repr@ of the transformer.
433 type UnTrans repr :: *
435 -- | Lift a repr to the transformer's.
436 noTrans :: UnTrans repr -> repr
437 -- | Unlift a repr from the transformer's.
438 unTrans :: repr -> UnTrans repr
440 -- | Identity transformation for a unary symantic method.
441 noTrans1 :: (UnTrans repr -> UnTrans repr) -> (repr -> repr)
442 noTrans1 f = noTrans . f . unTrans
444 -- | Identity transformation for a binary symantic method.
446 :: (UnTrans repr -> UnTrans repr -> UnTrans repr)
447 -> (repr -> repr -> repr)
448 noTrans2 f a b = noTrans (f (unTrans a) (unTrans b))
450 -- | Identity transformation for a ternary symantic method.
452 :: (UnTrans repr -> UnTrans repr -> UnTrans repr -> UnTrans repr)
453 -> (repr -> repr -> repr -> repr)
454 noTrans3 f a b c = noTrans (f (unTrans a) (unTrans b) (unTrans c))