1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE Rank2Types #-}
4 {-# LANGUAGE ScopedTypeVariables #-}
5 {-# LANGUAGE TupleSections #-}
6 {-# LANGUAGE TypeFamilies #-}
7 {-# LANGUAGE ViewPatterns #-}
8 module Language.TCT.Read.Tree where
10 -- import Data.String (IsString(..))
11 -- import qualified Data.TreeSeq.Strict as TreeSeq
12 import Control.Applicative (Applicative(..), Alternative(..))
13 import Control.Monad (Monad(..), void)
15 import Data.Eq (Eq(..))
16 import Data.Function (($), (.))
17 import Data.Functor ((<$>), ($>), (<$))
18 import Data.Foldable (toList)
19 import Data.Maybe (Maybe(..))
20 import Data.Monoid (Monoid(..))
21 import Data.Semigroup (Semigroup(..))
22 import Data.TreeSeq.Strict (Tree(..), Trees)
23 import qualified Data.List as List
24 import qualified Data.Sequence as Seq
25 import qualified Data.Text.Lazy as TL
26 import qualified Text.Megaparsec as P
27 import qualified Text.Megaparsec.Char as P
29 import Language.TCT.Cell
30 -- import Language.TCT.Token
31 import Language.TCT.Tree
32 import Language.TCT.Read.Cell
33 import Language.TCT.Read.Elem
34 import Language.TCT.Read.Token
36 p_CellHeader :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
37 p_CellHeader row = pdbg "CellHeader" $ do
38 P.skipMany $ P.char ' '
40 header <- pdbg "Header" $
42 [ P.try $ P.char '-' >>
43 P.char ' ' $> HeaderDash <|>
44 P.string "- " $> HeaderDashDash
48 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
49 , P.try $ P.some (P.char '#') <* P.lookAhead (P.try $ P.char ' ') >>= \hs ->
50 return $ HeaderSection $ List.length hs
53 <$> P.between (P.string "[") (P.string "]") p_Name
54 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy (=='\n')))
56 (\f -> HeaderDotSlash $ "./"<>f)
58 <*> P.many (P.satisfy (/='\n'))
63 [ P.try $ HeaderColon name wh
65 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
66 , P.char '>' $> HeaderGreat name wh
67 , P.char '=' $> HeaderEqual name wh
68 , P.char '|' $> HeaderBar name wh
72 let row' = Tree (Cell pos posEnd $ NodeHeader header) mempty : row
74 HeaderSection{} -> p_CellEnd row'
75 HeaderDash{} -> p_Row row'
76 HeaderDashDash{} -> p_CellText row'
77 HeaderDot{} -> p_Row row'
78 HeaderColon{} -> p_Row row'
79 HeaderBrackets{} -> p_Row row'
80 HeaderGreat{} -> p_Row row'
81 HeaderEqual{} -> p_CellEnd row'
82 HeaderBar{} -> p_CellEnd row'
83 HeaderDotSlash{} -> p_CellEnd row'
84 -- HeaderLower{} -> undefined -- NOTE: handled in 'p_CellLower'
85 -- TODO: move to a NodeLower
86 -- HeaderPara -> undefined -- NOTE: only introduced later in 'appendRow'
88 p_Name :: P.Tokens s ~ TL.Text => Parser e s Name
91 (\h t -> Text.pack (h:t))
92 <$> (P.satisfy $ \c -> Char.isAlphaNum c || c=='_')
93 <*> P.takeWhile1P (P.satisfy $ \c -> Char.isAlphaNum c || c=='-' || c=='_')
96 p_Line :: P.Tokens s ~ TL.Text => Parser e s TL.Text
97 p_Line = P.takeWhileP (Just "Line") (/='\n')
99 p_Line1 :: P.Tokens s ~ TL.Text => Parser e s TL.Text
100 p_Line1 = P.takeWhile1P (Just "Line") (/='\n')
102 p_CellLower :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
103 p_CellLower row = pdbg "CellLower" $ do
109 posClose <- p_Position
111 Tree (Cell pos posClose $ NodeLower name attrs) .
112 Seq.singleton . Tree0 . (NodeText <$>)
113 let treeElem hasContent nod (Cell _ p t) =
114 let (o,_) = bs $ PairElem name attrs in
115 Tree0 $ Cell pos p $ nod $ o<>t
117 bs | hasContent = pairBorders
118 | otherwise = pairBordersWithoutContent
120 P.try (P.char '>' >> treeElem True NodeText <$> p_CellLinesUntilElemEnd indent name) <|>
121 P.try (P.char '\n' >> P.tokens (==) indent >> treeHere <$> p_CellLines indent) <|>
122 P.try (P.tokens (==) "/>" >> treeElem False NodeText <$> p_CellLine) <|>
123 (P.eof $> treeHere (Cell posClose posClose ""))
126 p_CellLine :: P.Tokens s ~ TL.Text => Parser e s (Cell TL.Text)
127 p_CellLine = p_Cell p_Line
128 p_CellLines :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Parser e s (Cell TL.Text)
130 -- TODO: optimize special case indent == "" ?
133 <$> P.sepBy (P.try p_Line)
134 (P.try $ P.char '\n' >> P.tokens (==) indent)
135 p_CellLinesUntilElemEnd :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Name -> Parser e s (Cell TL.Text)
136 p_CellLinesUntilElemEnd indent name =
137 p_Cell $ TL.intercalate "\n" . List.reverse <$> go []
138 -- TODO: optimize merging, and maybe case indent == ""
140 go :: P.Tokens s ~ TL.Text => [TL.Text] -> Parser e s [TL.Text]
142 let end = "</" <> name in
143 P.try ((\w l -> w <> end <> l : ls)
147 (p_Line >>= \l -> P.try $
149 >> P.tokens (==) indent
152 p_CellText :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
153 p_CellText row = pdbg "CellText" $ do
154 P.skipMany $ P.char ' '
155 n <- p_Cell $ NodeText <$> p_Line1
156 return $ Tree0 n : row
158 p_CellSpaces :: Row -> Parser e s Row
159 p_CellSpaces row = pdbg "CellSpaces" $ do
160 P.skipSome $ P.char ' '
162 return $ Tree0 (Cell pos pos $ NodeText "") : row
164 p_CellEnd :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
165 p_CellEnd row = pdbg "CellEnd" $
166 P.try (p_CellLower row) <|>
167 P.try (p_CellText row) <|>
171 p_Row :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
172 p_Row row = pdbg "Row" $
173 P.try (p_CellHeader row) <|>
176 p_Rows :: P.Tokens s ~ TL.Text => Rows -> Parser e s Rows
179 let rows' = appendRow rows (List.reverse row) in
181 (P.newline >> P.eof $> rows' <|> p_Rows rows')
183 p_Trees :: P.Tokens s ~ TL.Text => Parser e s (Trees (Cell Node))
184 p_Trees = unNodePara . subTrees . collapseRows <$> p_Rows [root]
186 root = Tree (cell0 $ NodeHeader HeaderDashDash) mempty
187 unNodePara :: Trees (Cell Node) -> Trees (Cell Node)
188 unNodePara (toList -> [(Tree (unCell -> NodePara) ts)]) = ts