1 {-# OPTIONS_GHC -fno-warn-orphans #-}
2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE LambdaCase #-}
4 {-# LANGUAGE NoMonomorphismRestriction #-}
5 {-# LANGUAGE TupleSections #-}
6 {-# LANGUAGE TypeFamilies #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE ViewPatterns #-}
9 {-# LANGUAGE DeriveFunctor #-}
10 module Language.TCT.Tree where
13 import Data.Eq (Eq(..))
14 import Data.Function (($), (.))
15 import Data.Functor (Functor, (<$>))
16 import Data.Foldable (foldr)
17 import Data.Monoid (Monoid(..))
18 import Data.Maybe (Maybe(..))
19 import Data.Ord (Ordering(..), Ord(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence (Seq, ViewL(..), ViewR(..), (|>), (<|))
22 import Data.String (String)
23 import Data.Text (Text)
24 import Data.Tuple (fst,snd)
25 import Prelude (undefined, Int, Num(..))
26 import Text.Show (Show(..))
27 import qualified Data.List as List
28 import qualified Data.Sequence as Seq
29 import qualified Data.Text as Text
33 deriving (Eq, Show, Functor)
34 type Forest a = Seq (Tree a)
37 -- import Debug.Trace (trace)
38 trac :: String -> a -> a
39 dbg :: Show a => String -> a -> a
40 dbg m x = trac (m <> ": " <> show x) x
48 newtype PrettyTree a = PrettyTree (Forest a)
49 instance Show a => Show (PrettyTree a) where
50 show (PrettyTree t) = Text.unpack $ prettyForest t
52 -- | Neat 2-dimensional prettying of a tree.
53 prettyTree :: Show a => Tree a -> Text
54 prettyTree = Text.unlines . pretty
56 -- | Neat 2-dimensional prettying of a forest.
57 prettyForest :: Show a => Forest a -> Text
58 prettyForest = foldr (\t acc -> prettyTree t <> "\n" <> acc) ""
60 pretty :: Show a => Tree a -> [Text]
61 pretty (Tree x ts0) = Text.pack (show x) : prettySubTrees ts0
66 t:<ts | Seq.null ts -> "|" : shift "`- " " " (pretty t)
67 | otherwise -> "|" : shift "+- " "| " (pretty t) <> prettySubTrees ts
68 shift first other = List.zipWith (<>) (first : List.repeat other)
71 type TCT a = Forest (Cell a)
74 type Pos = (Line,Column)
77 -- | Line in the source file, counting from 1.
79 linePos :: Pos -> Line
83 -- | Column in the source file, counting from 1.
85 colPos :: Pos -> Column
89 -- | A list of 'Key's, maybe ended by 'Value', all read on the same 'Line'.
90 type Row = [Cell Text]
93 -- | NOTE: every 'Cell' as a 'Pos',
94 -- which is useful to indicate matches/errors/warnings/whatever,
95 -- or outputing in a format somehow preserving
96 -- the original input style.
97 data Cell a = Key Pos Pos Key
101 posCell :: Cell a -> Pos
102 posCell (Key pos _ _) = pos
103 posCell (Value pos _ _) = pos
105 posEndCell :: Cell a -> Pos
106 posEndCell (Key _ pos _) = pos
107 posEndCell (Value _ pos _) = pos
109 lineCell :: Cell a -> Line
110 lineCell = fst . posCell
111 columnCell :: Cell a -> Column
112 columnCell = snd . posCell
115 data Key = KeyColon Name -- ^ @name :@ begin 'Cell'
116 | KeyGreat Name -- ^ @name >@ continue 'Cell'
117 | KeyEqual Name -- ^ @name =@ begin 'Value'
118 | KeyBar Name -- ^ @name |@ continue 'Value'
119 | KeyDash -- ^ @- @ begin item
120 | KeySection LevelSection -- ^ @### @ begin section
126 -- *** Type 'LevelSection'
127 type LevelSection = Int
130 data Value = Plain Text
135 instance Semigroup Value where
136 Plain (Text.null -> True) <> y = y
137 x <> Plain (Text.null -> True) = x
138 Plain x <> Plain y = Plain (x<>y)
139 Values (Seq.viewr -> xs:>x@Plain{}) <> y@Plain{} = Values (xs|>(x<>y))
140 x@Plain{} <> Values (Seq.viewl -> y@Plain{}:<ys) = Values ((x<>y)<|ys)
142 Values x <> Values y = Values (x<>y)
143 Values x <> y = Values (x|>y)
144 x <> Values y = Values (x<|y)
146 x <> y = Values $ Seq.fromList [x,y]
149 data Group = Star -- ^ @*value*@
150 | Slash -- ^ @/value/@
151 | Underscore -- ^ @_value_@
152 | Dash -- ^ @-value-@
153 | Backquote -- ^ @`value`@
154 | Singlequote -- ^ @'value'@
155 | Doublequote -- ^ @"value"@
156 | Frenchquote -- ^ @«value»@
157 | Paren -- ^ @(value)@
158 | Brace -- ^ @{value}@
159 | Bracket -- ^ @[value]@
163 type Rows = [Tree (Cell Text)]
165 -- | @appendRow rows row@ appends @row@ to @rows@.
167 -- [@rows@] parent 'Rows', from closed to farest (non-strictly descending)
168 -- [@row@] next 'Row', from leftest column to rightest (non-stricly ascending)
169 appendRow :: Rows -> Row -> Rows
170 appendRow [] row = (`Tree` mempty) <$> List.reverse row
171 appendRow parents [] = parents
172 appendRow ps@(parent@(Tree cellParent treesParent):parents)
174 trac ("appendRow: ps=" <> show ps) $
175 trac ("appendRow: rs=" <> show rs) $
177 let colParent = columnCell cellParent in
178 let colRow = columnCell cellRow in
179 case dbg "colParent" colParent`compare`dbg "colRow" colRow of
181 case (dbg "cellParent" cellParent,dbg "cellRow" cellRow) of
182 (Value{}, Key{}) -> eq
183 (Value _ _ p, Value{}) | Text.null p -> eq -- FIXME: useful?
184 (Value _ _ p, Value _ _ r) -> appendValues p r
187 case (dbg "cellParent" cellParent,dbg "cellRow" cellRow) of
188 (Value _ _ p, Value _ _ r) -> appendValues p r
189 (_, Key _ _ (KeySection sectionRow))
190 | Just (sectionParent, secPar:secPars) <- collapseSection colRow ps ->
191 case dbg "sectionParent" sectionParent`compare`dbg "sectionRow" sectionRow of
192 LT -> appendRow (Tree cellRow mempty:secPar:secPars) rows
193 EQ -> appendRow (Tree cellRow mempty:insertChild secPar secPars) rows
195 (Key _ _ KeySection{}, Value{}) -> lt
196 (Key _ _ KeySection{}, Key{}) -> lt
197 (Value{}, Key{}) -> eq
199 (Key{}, Value{}) -> eq
202 case (dbg "cellParent" cellParent,dbg "cellRow" cellRow) of
203 (Value _ _ p, Value _ _ r) -> appendValues p r
208 trac ("appendValues: p="<>show cellParent) $
209 trac ("appendValues: r="<>show cellRow) $
211 appendRow (Tree cell treesParent : parents) rows
213 cell = Value (posCell cellParent) (posEndCell cellRow) $ p <> pad <> r
215 if linePos (posEndCell cellParent) == linePos (posCell cellRow)
216 then padding (colPos $ posEndCell cellParent) (columnCell cellRow)
217 else "\n" <> padding (columnCell cellParent) (columnCell cellRow)
218 padding x y = Text.replicate (y - x) " "
219 lt = appendRow [] rs <> ps
220 eq = appendRow (Tree cellRow mempty:insertChild parent parents) rows
221 gt = appendRow (insertChild parent parents) rs
222 -- | Find the first section (if any), returning its level, and the path collapsed upto it.
223 collapseSection :: Column -> Rows -> Maybe (Int,Rows)
224 collapseSection col pars@(x@(Tree c _):xs) | columnCell c == col =
226 Tree (Key _ _ (KeySection lvl)) _ -> Just (lvl,pars)
227 _ -> (\(lvl,cs) -> (lvl,insertChild x cs)) <$> collapseSection col xs
228 collapseSection _ _ = Nothing
230 insertChild :: Tree (Cell Text) -> Rows -> Rows
231 insertChild cellChild ps@[] =
232 trac ("insertChild: cellChild="<>show cellChild) $
233 trac ("insertChild: ps="<>show ps) $
236 insertChild child@(Tree cellChild _) ps@(Tree cellParent treesParent:parents) =
237 trac ("insertChild: child="<>show child) $
238 trac ("insertChild: ps="<>show ps) $
240 case dbg "colParent" (columnCell cellParent)`compare`dbg "colChild" (columnCell cellChild) of
241 LT -> Tree cellParent (treesParent |> child) : parents
242 EQ -> Tree cellParent (treesParent |> child) : parents
245 collapsePath :: Rows -> Tree (Cell Text)
246 collapsePath [] = undefined
247 collapsePath [child] = dbg "collapsePath" $ child
248 collapsePath (child:parents) = dbg "collapsePath" $ collapsePath $ insertChild child parents