1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE NoOverloadedLists #-}
3 {-# LANGUAGE PatternSynonyms #-}
4 {-# LANGUAGE ViewPatterns #-}
5 module Language.TCT.Tree where
7 import Control.Monad (Monad(..))
9 import Data.Char (Char)
10 import Data.Eq (Eq(..))
11 import Data.Foldable (Foldable(..))
12 import Data.Function (($))
14 import Data.Maybe (Maybe(..))
15 import Data.Monoid (Monoid(..))
16 import Data.Ord (Ordering(..), Ord(..))
17 import Data.Semigroup (Semigroup(..))
18 import Data.Sequence ((|>), (<|), ViewR(..))
19 import Data.TreeSeq.Strict (Tree(..), Trees)
20 import Prelude (undefined, Num(..))
21 import System.FilePath (FilePath)
22 import Text.Show (Show(..))
23 import qualified Data.List as List
24 import qualified Data.Sequence as Seq
25 import qualified Data.Text.Lazy as TL
27 import Language.TCT.Cell
28 import Language.TCT.Elem
29 -- import Language.TCT.Token
33 type Roots = Trees Node
35 pattern Tree0 :: a -> Tree a
36 pattern Tree0 a <- Tree a (null -> True)
37 where Tree0 a = Tree a mempty
45 | NodeLower !Name !ElemAttrs -- ^ @<name a=b@
52 = HeaderColon !Name !White -- ^ @name: @
53 | HeaderEqual !Name !White -- ^ @name=@
54 | HeaderBar !Name !White -- ^ @name|@
55 | HeaderGreat !Name !White -- ^ @name>@
56 | HeaderDot !Name -- ^ @1. @
57 | HeaderDash -- ^ @- @
58 | HeaderDashDash -- ^ @-- @
59 | HeaderSection !LevelSection -- ^ @# @
60 | HeaderBrackets !Name -- ^ @[name]@
61 | HeaderDotSlash !FilePath -- ^ @./file @
62 deriving (Eq, Ord, Show)
67 -- *** Type 'LevelSection'
68 type LevelSection = Int
72 = PairElem !ElemName !ElemAttrs -- ^ @<elem n0=v0 n1=v1>value</elem>@
73 | PairHash -- ^ @#value#@
74 | PairStar -- ^ @*value*@
75 | PairSlash -- ^ @/value/@
76 | PairUnderscore -- ^ @_value_@
77 | PairDash -- ^ @-value-@
78 | PairBackquote -- ^ @`value`@
79 | PairSinglequote -- ^ @'value'@
80 | PairDoublequote -- ^ @"value"@
81 | PairFrenchquote -- ^ @«value»@
82 | PairParen -- ^ @(value)@
83 | PairBrace -- ^ @{value}@
84 | PairBracket -- ^ @[value]@
85 deriving (Eq,Ord,Show)
102 -- | In normal order: a list of 'Header's, maybe ended by 'Value', all read on the same line.
103 type Row = [Tree (Cell Node)]
106 -- | In reverse order: a list of nodes in scope
107 -- (hence to which the next line can append to).
108 type Rows = [Tree (Cell Node)]
110 -- | @appendRow rows row@ appends @row@ to @rows@.
112 -- [@rows@] parent 'Rows', from closest to farthest (non-strictly descending)
113 -- [@row@] next 'Row', from leftest column to rightest (non-stricly ascending)
114 appendRow :: Rows -> Row -> Rows
115 appendRow [] row = List.reverse row
116 appendRow rows [] = rows
117 appendRow rows@(old@(Tree (Cell bo eo o) os):olds)
118 row@(new@(Tree (Cell bn en n) ns):news) =
119 debug "appendRow" "row" row $
120 debug "appendRow" "rows" rows $
122 case dbg "colOld" (pos_column bo) `compare`
123 dbg "colNew" (pos_column bn) of
124 LT -> mergeNodeText lt
128 (_, NodeHeader (HeaderSection secNew))
129 | Just (secOld, s0:ss) <- collapseSection (pos_column bn) rows ->
130 case dbg "secOld" secOld `compare`
131 dbg "secNew" secNew of
132 LT -> appendRow (new:s0:ss) news
133 EQ -> appendRow (new:appendChild ss s0) news
135 (NodeHeader HeaderSection{}, _) -> lt
136 (_, NodeText tn) | TL.null tn -> gt
137 (NodePara, _) | not newPara -> lt
142 newPara = pos_line bn - pos_line eo > 1
143 lt = debug "appendRow" "action" ("lt"::TL.Text) $ List.reverse row <> rows
144 eq = debug "appendRow" "action" ("eq"::TL.Text) $ appendRow (new : appendChild olds old) news
145 gt = debug "appendRow" "action" ("gt"::TL.Text) $ appendRow ( appendChild olds old) row
147 -- | Find the first section (if any), returning its level, and the path collapsed upto it.
148 collapseSection :: ColNum -> Rows -> Maybe (LevelSection,Rows)
149 collapseSection col xxs@(x:xs) | pos_column (cell_begin (unTree x)) == col =
151 Tree (unCell -> NodeHeader (HeaderSection lvl)) _ -> Just (lvl, xxs)
153 (lvl, cs) <- collapseSection col xs
154 return (lvl, appendChild cs x)
155 collapseSection _ _ = Nothing
157 mergeNodeText :: Rows -> Rows
162 (NodeText to, NodeText tn)
165 , not (TL.null tn) ->
166 -- debug "appendRow" "action" ("mergeNodeText"::TL.Text) $
167 dbg "mergeNodeText" $
168 appendRow (merged : olds) news
170 merged = Tree (Cell bo en $ NodeText $ to<>tp<>tn) ns
172 { pos_line = pos_line bn - pos_line eo
173 , pos_column = pos_column bn - pos_column bo
177 appendChild :: Rows -> Tree (Cell Node) -> Rows
178 appendChild rows new@(Tree (Cell bn en n) ns) =
179 debug "appendChild" "new" new $
180 debug "appendChild" "rows" rows $
184 old@(Tree (Cell bo eo o) os) : olds ->
189 (NodePara,NodePara) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
190 (NodePara,_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,Tree (Cell bn en NodePara) $ return new]
191 (_,NodePara) -> Tree (Cell bo en o) $ os|>new
192 (NodeText{},_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
193 _ -> Tree (Cell bo en o) $ os|>Tree (Cell bn en NodePara) (return new)
196 (NodePara,NodePara) -> Tree (Cell bo en NodePara) $ os<>ns
197 (NodePara,_) -> Tree (Cell bo en NodePara) $ os|>new
198 (_,NodePara) -> Tree (Cell bo en NodePara) $ old<|ns
199 (NodeText{},_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
203 Tree (Cell bo en o) $
204 os |> Tree (Cell bn en NodePara) (return new)
205 ls :> Tree (Cell br _er r) rs ->
208 if pos_column br == pos_column bn
209 then Tree (Cell bo en o) $ ls |> Tree (Cell br en NodePara) (rs |> new)
210 else Tree (Cell bo en o) $ os |> Tree (Cell bn en NodePara) (return new)
211 _ -> Tree (Cell bo en o) $ os |> new
212 where newPara = pos_line bn - pos_line eo > 1
214 collapseRows :: Rows -> Tree (Cell Node)
216 debug "collapseRows" "rs" rs $
221 child:parents -> collapseRows $ appendChild parents child