1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE Rank2Types #-}
4 {-# LANGUAGE ScopedTypeVariables #-}
5 {-# LANGUAGE TypeFamilies #-}
6 module Language.TCT.Read.Tree where
8 import Control.Applicative (Applicative(..), Alternative(..))
9 import Control.Monad (Monad(..), void)
11 import Data.Eq (Eq(..))
12 import Data.Function (($), (.))
13 import Data.Functor ((<$>), ($>), (<$))
14 import Data.Maybe (Maybe(..))
15 import Data.Monoid (Monoid(..))
16 import Data.Semigroup (Semigroup(..))
17 import Data.TreeSeq.Strict (Tree(..), Trees)
18 import qualified Data.List as List
19 import qualified Data.Sequence as Seq
20 import qualified Data.Text.Lazy as TL
21 import qualified Text.Megaparsec as P
22 import qualified Text.Megaparsec.Char as P
24 import Language.TCT.Debug
25 import Language.TCT.Cell
26 import Language.TCT.Tree
27 import Language.TCT.Read.Cell
28 import Language.TCT.Read.Elem
29 import Language.TCT.Read.Token
31 p_CellHeader :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
32 p_CellHeader row = debugParser "CellHeader" $ do
33 P.skipMany $ P.char ' '
35 header <- debugParser "Header" $
37 [ P.try $ P.char '-' >>
38 P.char ' ' $> HeaderDash <|>
39 P.string "- " $> HeaderDashDash
43 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
44 , P.try $ P.some (P.char '#') <* P.lookAhead (P.try $ P.char ' ') >>= \hs ->
45 return $ HeaderSection $ List.length hs
48 <$> P.between (P.string "[") (P.string "]") p_Name
49 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy (=='\n')))
51 (\f -> HeaderDotSlash $ "./"<>f)
53 <*> P.many (P.satisfy (/='\n'))
58 [ P.try $ HeaderColon name wh
60 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
61 , P.char '>' $> HeaderGreat name wh
62 , P.char '=' $> HeaderEqual name wh
63 , P.char '|' $> HeaderBar name wh
67 let row' = Tree (Cell pos posEnd $ NodeHeader header) mempty : row
69 HeaderSection{} -> p_CellEnd row'
70 HeaderDash{} -> p_Row row'
71 HeaderDashDash{} -> p_CellRaw row'
72 HeaderDot{} -> p_Row row'
73 HeaderColon{} -> p_Row row'
74 HeaderBrackets{} -> p_Row row'
75 HeaderGreat{} -> p_Row row'
76 HeaderEqual{} -> p_CellRaw row'
77 HeaderBar{} -> p_CellRaw row'
78 HeaderDotSlash{} -> p_CellEnd row'
80 p_Name :: P.Tokens s ~ TL.Text => Parser e s Name
83 (\h t -> Text.pack (h:t))
84 <$> (P.satisfy $ \c -> Char.isAlphaNum c || c=='_')
85 <*> P.takeWhile1P (P.satisfy $ \c -> Char.isAlphaNum c || c=='-' || c=='_')
88 p_Line :: P.Tokens s ~ TL.Text => Parser e s TL.Text
89 p_Line = P.takeWhileP (Just "Line") (/='\n')
91 p_Line1 :: P.Tokens s ~ TL.Text => Parser e s TL.Text
92 p_Line1 = P.takeWhile1P (Just "Line") (/='\n')
94 p_CellLower :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
95 p_CellLower row = debugParser "CellLower" $ do
101 posClose <- p_Position
103 Tree (Cell pos posClose $ NodeLower name attrs) .
104 Seq.singleton . Tree0 . (NodeText <$>)
105 let treeElem hasContent nod (Cell _ p t) =
106 let (o,_) = bs $ PairElem name attrs in
107 Tree0 $ Cell pos p $ nod $ o<>t
109 bs | hasContent = pairBordersDouble
110 | otherwise = pairBordersSingle
112 P.try (P.char '>' >> treeElem True NodeText <$> p_CellLinesUntilElemEnd indent name) <|>
113 P.try (P.char '\n' >> P.tokens (==) indent >> treeHere <$> p_CellLines indent) <|>
114 P.try (P.tokens (==) "/>" >> treeElem False NodeText <$> p_CellLine) <|>
115 (P.eof $> treeHere (Cell posClose posClose ""))
118 p_CellLine :: P.Tokens s ~ TL.Text => Parser e s (Cell TL.Text)
119 p_CellLine = p_Cell p_Line
120 p_CellLines :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Parser e s (Cell TL.Text)
122 -- TODO: optimize special case indent == "" ?
125 <$> P.sepBy (P.try p_Line)
126 (P.try $ P.char '\n' >> P.tokens (==) indent)
127 p_CellLinesUntilElemEnd :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Name -> Parser e s (Cell TL.Text)
128 p_CellLinesUntilElemEnd indent name =
129 p_Cell $ TL.intercalate "\n" . List.reverse <$> go []
130 -- TODO: optimize merging, and maybe case indent == ""
132 go :: P.Tokens s ~ TL.Text => [TL.Text] -> Parser e s [TL.Text]
134 let end = "</" <> name in
135 P.try ((\w l -> w <> end <> l : ls)
139 (p_Line >>= \l -> P.try $
141 >> P.tokens (==) indent
144 p_CellText1 :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
145 p_CellText1 row = debugParser "CellText" $ do
146 P.skipMany $ P.char ' '
147 n <- p_Cell $ NodeText <$> p_Line1
148 return $ Tree0 n : row
150 p_CellRaw :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
151 p_CellRaw row = debugParser "CellRaw" $ do
152 P.skipMany $ P.char ' '
153 n <- p_Cell $ NodeText <$> p_Line
154 return $ Tree0 n : row
156 p_CellSpaces1 :: Row -> Parser e s Row
157 p_CellSpaces1 row = debugParser "CellSpaces" $ do
158 P.skipSome $ P.char ' '
160 return $ Tree0 (Cell pos pos $ NodeText "") : row
162 p_CellEnd :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
163 p_CellEnd row = debugParser "CellEnd" $
164 P.try (p_CellLower row) <|>
165 P.try (p_CellText1 row) <|>
166 p_CellSpaces1 row <|>
169 p_Row :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
170 p_Row row = debugParser "Row" $
171 P.try (p_CellHeader row) <|>
174 p_Rows :: P.Tokens s ~ TL.Text => Rows -> Parser e s Rows
177 let rows' = rows `mergeRow` row in
179 (P.newline >> {- USELESS: P.eof $> rows' <|>-} p_Rows rows')
181 p_Trees :: P.Tokens s ~ TL.Text => Parser e s (Trees (Cell Node))
182 p_Trees = collapseRows <$> p_Rows initRows