1 {-# LANGUAGE OverloadedStrings #-}
2 {-# LANGUAGE PatternSynonyms #-}
3 {-# LANGUAGE ViewPatterns #-}
4 module Language.TCT.Tree
5 ( module Language.TCT.Tree
9 import Control.Monad (Monad(..))
11 import Data.Char (Char)
12 import Data.Eq (Eq(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($))
16 import Data.Maybe (Maybe(..))
17 import Data.Monoid (Monoid(..))
18 import Data.Ord (Ordering(..), Ord(..))
19 import Data.Semigroup (Semigroup(..))
20 import Data.Sequence ((|>), (<|), ViewR(..))
21 import Data.TreeSeq.Strict (Tree(..), Trees)
22 import Prelude (undefined, Num(..))
23 import System.FilePath (FilePath)
24 import Text.Show (Show(..))
25 import qualified Data.List as List
26 import qualified Data.Sequence as Seq
27 import qualified Data.Text.Lazy as TL
29 import Language.TCT.Cell
30 import Language.TCT.Elem
31 import Language.TCT.Debug
34 -- | A single 'Tree' to rule all the 'Node's simplifies the navigation.
35 -- For error reporting, each 'Node' is annotated with a 'Cell'
36 -- spanning over all its content (sub-'Trees' included).
37 type Root = Tree (Cell Node)
38 type Roots = Trees (Cell Node)
40 pattern Tree0 :: a -> Tree a
41 pattern Tree0 a <- Tree a (null -> True)
42 where Tree0 a = Tree a mempty
46 = NodeHeader !Header -- ^ node, from first parsing (indentation-sensitive)
47 | NodeText !TL.Text -- ^ leaf verbatim text, from first parsing (indentation-sensitive)
48 | NodePair !Pair -- ^ node, from second parsing (on some 'NodeText's)
49 | NodeToken !Token -- ^ leaf, from second parsing (on some 'NodeText's)
50 | NodeLower !Name !ElemAttrs -- ^ node, @<name a=b@
51 | NodePara -- ^ node, gather trees by paragraph,
52 -- useful to know when to generate a <para> XML node
53 | NodeGroup -- ^ node, group trees into a single tree,
54 -- useful to return many trees when only one is expected
59 = HeaderColon !Name !White -- ^ @name: @
60 | HeaderEqual !Name !White -- ^ @name=@
61 | HeaderBar !Name !White -- ^ @name|@
62 | HeaderGreat !Name !White -- ^ @name>@
63 | HeaderDot !Name -- ^ @1. @
64 | HeaderDash -- ^ @- @
65 | HeaderDashDash -- ^ @-- @
66 | HeaderSection !LevelSection -- ^ @# @
67 | HeaderBrackets !Name -- ^ @[name]@
68 | HeaderDotSlash !FilePath -- ^ @./file @
69 deriving (Eq, Ord, Show)
74 -- ** Type 'LevelSection'
75 type LevelSection = Int
79 = PairElem !ElemName !ElemAttrs -- ^ @<elem n0=v0 n1=v1>value</elem>@
80 | PairHash -- ^ @#value#@
81 | PairStar -- ^ @*value*@
82 | PairSlash -- ^ @/value/@
83 | PairUnderscore -- ^ @_value_@
84 | PairDash -- ^ @-value-@
85 | PairBackquote -- ^ @`value`@
86 | PairSinglequote -- ^ @'value'@
87 | PairDoublequote -- ^ @"value"@
88 | PairFrenchquote -- ^ @«value»@
89 | PairParen -- ^ @(value)@
90 | PairBrace -- ^ @{value}@
91 | PairBracket -- ^ @[value]@
92 deriving (Eq,Ord,Show)
110 -- | In normal order: a list of 'Header's, maybe ended by 'Value', all read on the same line.
114 -- | In reverse order: a list of nodes in scope
115 -- (hence to which the next line can append to).
118 -- | @appendRow rows row@ appends @row@ to @rows@.
120 -- [@rows@] parent 'Rows', from closest to farthest (non-strictly descending)
121 -- [@row@] next 'Row', from leftest column to rightest (non-stricly ascending)
122 appendRow :: Rows -> Row -> Rows
123 appendRow [] row = List.reverse row
124 appendRow rows [] = rows
125 appendRow rows@(old@(Tree (Cell bo eo o) os):olds)
126 row@(new@(Tree (Cell bn en n) ns):news) =
127 debug2_ "appendRow" ("row",row) ("rows",rows) $
128 case debug0 "colOld" (pos_column bo) `compare`
129 debug0 "colNew" (pos_column bn) of
130 LT -> mergeNodeText lt
134 (_, NodeHeader (HeaderSection secNew))
135 | Just (secOld, s0:ss) <- collapseSection (pos_column bn) rows ->
136 case debug0 "secOld" secOld `compare`
137 debug0 "secNew" secNew of
138 LT -> appendRow (new:s0:ss) news
139 EQ -> appendRow (new:appendChild ss s0) news
141 (NodeHeader HeaderSection{}, _) -> lt
142 (_, NodeText tn) | TL.null tn -> gt
143 (NodePara, _) | not newPara -> lt
148 newPara = pos_line bn - pos_line eo > 1
149 lt = debug "appendRow/lt" $ List.reverse row <> rows
150 eq = debug "appendRow/eq" $ appendRow (new : appendChild olds old) news
151 gt = debug "appendRow/gt" $ appendRow ( appendChild olds old) row
153 -- | Find the first section (if any), returning its level, and the path collapsed upto it.
154 collapseSection :: ColNum -> Rows -> Maybe (LevelSection,Rows)
155 collapseSection col xxs@(x:xs) | pos_column (cell_begin (unTree x)) == col =
157 Tree (unCell -> NodeHeader (HeaderSection lvl)) _ -> Just (lvl, xxs)
159 (lvl, cs) <- collapseSection col xs
160 return (lvl, appendChild cs x)
161 collapseSection _ _ = Nothing
163 mergeNodeText :: Rows -> Rows
168 (NodeText to, NodeText tn)
171 , not (TL.null tn) ->
172 -- debug "appendRow" "action" ("mergeNodeText"::TL.Text) $
173 debug0 "mergeNodeText" $
174 appendRow (merged : olds) news
176 merged = Tree (Cell bo en $ NodeText $ to<>tp<>tn) ns
178 { pos_line = pos_line bn - pos_line eo
179 , pos_column = pos_column bn - pos_column bo
183 appendChild :: Rows -> Root -> Rows
184 appendChild rows new@(Tree (Cell bn en n) ns) =
185 debug2_ "appendChild" ("new",Seq.singleton new) ("rows",rows) $
188 old@(Tree (Cell bo eo o) os) : olds ->
193 (NodePara,NodePara) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
194 (NodePara,_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,Tree (Cell bn en NodePara) $ return new]
195 (_,NodePara) -> Tree (Cell bo en o) $ os|>new
196 (NodeText{},_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
197 _ -> Tree (Cell bo en o) $ os|> newTree
200 (NodePara,NodePara) -> Tree (Cell bo en NodePara) $ os<>ns
201 (NodePara,_) -> Tree (Cell bo en NodePara) $ os|>new
202 (_,NodePara) -> Tree (Cell bo en NodePara) $ old<|ns
203 (NodeText{},_) -> Tree (Cell bo en NodeGroup) $ Seq.fromList [old,new]
206 EmptyR -> Tree (Cell bo en o) $ return newTree
207 ls :> Tree (Cell br _er r) rs ->
210 | pos_column br == pos_column bn
211 -> Tree (Cell bo en o) $ ls |> Tree (Cell br en NodePara) (rs |> new)
212 | otherwise -> Tree (Cell bo en o) $ os |> newTree
213 _ -> Tree (Cell bo en o) $ os |> new
215 newPara = pos_line bn - pos_line eo > 1
220 _ -> Tree (Cell bn en NodePara) (return new)
222 collapseRows :: Rows -> Root
224 debug1 "collapseRows" "rs" $ \case
227 child:parents -> collapseRows $ appendChild parents child