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 default align :: Indentable (ReprOf d) => Trans d => d -> d
156 -- | @('incrIndent' ind d)@ make @d@ uses current 'Indent' plus @ind@ as 'Indent' level.
157 incrIndent :: Indent -> d -> d
158 default incrIndent :: Indentable (ReprOf d) => Trans d => Indent -> d -> d
159 incrIndent = trans1 . incrIndent
160 -- | @('withIndent' ind d)@ make @d@ uses @ind@ as 'Indent' level.
161 withIndent :: Indent -> d -> d
162 default withIndent :: Indentable (ReprOf d) => Trans d => Indent -> d -> d
163 withIndent = trans1 . 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 (ReprOf d) => Trans d => d -> d -> d
171 default newlineWithoutIndent :: Indentable (ReprOf d) => Trans d => d
172 default newlineWithIndent :: Indentable (ReprOf d) => Trans d => d
173 withNewline = trans2 withNewline
174 newlineWithoutIndent = trans newlineWithoutIndent
175 newlineWithIndent = trans newlineWithIndent
176 -- | @('column' f)@ write @f@ applied to the current 'Column'.
177 column :: (Column -> d) -> d
178 default column :: Indentable (ReprOf d) => Trans d => (Column -> d) -> d
179 column f = trans $ column (unTrans . f)
180 -- | @('indent' f)@ write @f@ applied to the current 'Indent'.
181 indent :: (Indent -> d) -> d
182 default indent :: Indentable (ReprOf d) => Trans d => (Indent -> d) -> d
183 indent f = trans $ 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 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
196 endToEndWidth d f = column $ \c1 -> (d <>) $ column $ \c2 -> f $ c2 - c1
198 -- | @'spaces' ind = 'replicate' ind 'space'@
199 spaces :: Indent -> d
200 spaces i = replicate (fromIntegral i) space
202 -- | @('fill' ind d)@ write @d@,
203 -- then if @d@ is not wider than @ind@,
204 -- write the difference with 'spaces'.
205 fill :: Indent -> d -> d
207 endToEndWidth d $ \w ->
212 -- | @('breakableFill' ind d)@ write @d@,
213 -- then if @d@ is not wider than @ind@, write the difference with 'spaces'
214 -- otherwise write a 'newline' indented to to the start 'Column' of @d@ plus @ind@.
215 breakableFill :: Indent -> d -> d
218 endToEndWidth d $ \w ->
220 LT -> spaces (m - w) <> empty
222 GT -> withIndent (c + m) newline
224 -- * Class 'Breakable'
225 class (Textable d, Indentable d) => Breakable d where
226 -- | @('breakable' f)@ write @f@ applied to whether breaks are activated or not.
227 breakable :: (Maybe Column -> d) -> d
228 default breakable :: Breakable (ReprOf d) => Trans d => (Maybe Column -> d) -> d
229 breakable f = trans $ breakable (unTrans . f)
230 -- | @('withBreakable' b d)@ whether to active breaks or not within @d@.
231 withBreakable :: Maybe Column -> d -> d
232 default withBreakable :: Breakable (ReprOf d) => Trans d => Maybe Column -> d -> d
233 withBreakable = trans1 . withBreakable
235 -- | @('ifBreak' onWrap onNoWrap)@
236 -- write @onWrap@ if @onNoWrap@ leads to a 'Column'
237 -- greater or equal to the one sets with 'withBreakable',
238 -- otherwise write @onNoWrap@.
239 ifBreak :: d -> d -> d
240 default ifBreak :: Breakable (ReprOf d) => Trans d => d -> d -> d
241 ifBreak = trans2 ifBreak
242 -- | @('breakpoint' onNoBreak onBreak d)@
243 -- write @onNoBreak@ then @d@ if they fit,
244 -- @onBreak@ otherwise.
245 breakpoint :: d -> d -> d -> d
246 default breakpoint :: Breakable (ReprOf d) => Trans d => d -> d -> d -> d
247 breakpoint = trans3 breakpoint
249 -- | @('breakableEmpty' d)@ write @d@ if it fits, 'newline' then @d@ otherwise.
250 breakableEmpty :: d -> d
251 breakableEmpty = breakpoint empty newline
253 -- | @x '><' y = x '<>' 'breakableEmpty' y@
255 x >< y = x <> breakableEmpty y
257 -- | @('breakableSpace' d)@ write 'space' then @d@ it they fit,
258 -- 'newline' then @d@ otherwise.
259 breakableSpace :: d -> d
260 breakableSpace = breakpoint space newline
262 -- | @x '>+<' y = x '<>' 'breakableSpace' y@
264 x >+< y = x <> breakableSpace y
266 -- | @'breakableSpaces' ds@ intercalate a 'breakableSpace'
267 -- between items of @ds@.
268 breakableSpaces :: Foldable f => f d -> d
269 breakableSpaces = foldWith breakableSpace
271 -- | @('intercalateHorV' sep ds)@
272 -- write @ds@ with @sep@ intercalated if the whole fits,
273 -- otherwise write 'align' of @ds@ with 'newline' and @sep@ intercalated.
274 intercalateHorV :: Foldable f => d -> f d -> d
275 intercalateHorV sep xs =
277 (align $ foldWith ((newline <> sep) <>) xs)
278 (foldWith (sep <>) xs)
280 -- * Class 'Colorable'
281 class Colorable d where
282 -- | @('colorable' f)@ write @f@ applied to whether colors are activated or not.
283 colorable :: (Bool -> d) -> d
284 default colorable :: Colorable (ReprOf d) => Trans d => (Bool -> d) -> d
285 colorable f = trans $ colorable (unTrans . f)
286 -- | @('withColor' b d)@ whether to active colors or not within @d@.
287 withColorable :: Bool -> d -> d
288 default withColorable :: Colorable (ReprOf d) => Trans d => Bool -> d -> d
289 withColorable = trans1 . withColorable
331 onMagentaer :: d -> d
335 default reverse :: Colorable (ReprOf d) => Trans d => d -> d
336 default black :: Colorable (ReprOf d) => Trans d => d -> d
337 default red :: Colorable (ReprOf d) => Trans d => d -> d
338 default green :: Colorable (ReprOf d) => Trans d => d -> d
339 default yellow :: Colorable (ReprOf d) => Trans d => d -> d
340 default blue :: Colorable (ReprOf d) => Trans d => d -> d
341 default magenta :: Colorable (ReprOf d) => Trans d => d -> d
342 default cyan :: Colorable (ReprOf d) => Trans d => d -> d
343 default white :: Colorable (ReprOf d) => Trans d => d -> d
344 default blacker :: Colorable (ReprOf d) => Trans d => d -> d
345 default redder :: Colorable (ReprOf d) => Trans d => d -> d
346 default greener :: Colorable (ReprOf d) => Trans d => d -> d
347 default yellower :: Colorable (ReprOf d) => Trans d => d -> d
348 default bluer :: Colorable (ReprOf d) => Trans d => d -> d
349 default magentaer :: Colorable (ReprOf d) => Trans d => d -> d
350 default cyaner :: Colorable (ReprOf d) => Trans d => d -> d
351 default whiter :: Colorable (ReprOf d) => Trans d => d -> d
352 default onBlack :: Colorable (ReprOf d) => Trans d => d -> d
353 default onRed :: Colorable (ReprOf d) => Trans d => d -> d
354 default onGreen :: Colorable (ReprOf d) => Trans d => d -> d
355 default onYellow :: Colorable (ReprOf d) => Trans d => d -> d
356 default onBlue :: Colorable (ReprOf d) => Trans d => d -> d
357 default onMagenta :: Colorable (ReprOf d) => Trans d => d -> d
358 default onCyan :: Colorable (ReprOf d) => Trans d => d -> d
359 default onWhite :: Colorable (ReprOf d) => Trans d => d -> d
360 default onBlacker :: Colorable (ReprOf d) => Trans d => d -> d
361 default onRedder :: Colorable (ReprOf d) => Trans d => d -> d
362 default onGreener :: Colorable (ReprOf d) => Trans d => d -> d
363 default onYellower :: Colorable (ReprOf d) => Trans d => d -> d
364 default onBluer :: Colorable (ReprOf d) => Trans d => d -> d
365 default onMagentaer :: Colorable (ReprOf d) => Trans d => d -> d
366 default onCyaner :: Colorable (ReprOf d) => Trans d => d -> d
367 default onWhiter :: Colorable (ReprOf d) => Trans d => d -> d
369 reverse = trans1 reverse
373 yellow = trans1 yellow
375 magenta = trans1 magenta
378 blacker = trans1 blacker
379 redder = trans1 redder
380 greener = trans1 greener
381 yellower = trans1 yellower
383 magentaer = trans1 magentaer
384 cyaner = trans1 cyaner
385 whiter = trans1 whiter
386 onBlack = trans1 onBlack
388 onGreen = trans1 onGreen
389 onYellow = trans1 onYellow
390 onBlue = trans1 onBlue
391 onMagenta = trans1 onMagenta
392 onCyan = trans1 onCyan
393 onWhite = trans1 onWhite
394 onBlacker = trans1 onBlacker
395 onRedder = trans1 onRedder
396 onGreener = trans1 onGreener
397 onYellower = trans1 onYellower
398 onBluer = trans1 onBluer
399 onMagentaer = trans1 onMagentaer
400 onCyaner = trans1 onCyaner
401 onWhiter = trans1 onWhiter
403 -- * Class 'Decorable'
404 class Decorable d where
405 -- | @('decorable' f)@ write @f@ applied to whether decorations are activated or not.
406 decorable :: (Bool -> d) -> d
407 default decorable :: Decorable (ReprOf d) => Trans d => (Bool -> d) -> d
408 decorable f = trans $ decorable (unTrans . f)
409 -- | @('withColor' b d)@ whether to active decorations or not within @d@.
410 withDecorable :: Bool -> d -> d
411 default withDecorable :: Decorable (ReprOf d) => Trans d => Bool -> d -> d
412 withDecorable = trans1 . withDecorable
417 default bold :: Decorable (ReprOf d) => Trans d => d -> d
418 default underline :: Decorable (ReprOf d) => Trans d => d -> d
419 default italic :: Decorable (ReprOf d) => Trans d => d -> d
421 underline = trans1 underline
422 italic = trans1 italic
426 -- | Return the underlying @tr@ of the transformer.
429 -- | Lift a tr to the transformer's.
430 trans :: ReprOf tr -> tr
431 -- | Unlift a tr from the transformer's.
432 unTrans :: tr -> ReprOf tr
434 -- | Identity transformation for a unary symantic method.
435 trans1 :: (ReprOf tr -> ReprOf tr) -> (tr -> tr)
436 trans1 f = trans . f . unTrans
438 -- | Identity transformation for a binary symantic method.
440 :: (ReprOf tr -> ReprOf tr -> ReprOf tr)
442 trans2 f t1 t2 = trans (f (unTrans t1) (unTrans t2))
444 -- | Identity transformation for a ternary symantic method.
446 :: (ReprOf tr -> ReprOf tr -> ReprOf tr -> ReprOf tr)
447 -> (tr -> tr -> tr -> tr)
448 trans3 f t1 t2 t3 = trans (f (unTrans t1) (unTrans t2) (unTrans t3))