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.List.NonEmpty (NonEmpty(..))
15 import Data.Maybe (Maybe(..))
16 import Data.Monoid (Monoid(..))
17 import Data.Semigroup (Semigroup(..))
18 import Data.TreeSeq.Strict (Tree(..), Trees, tree0)
19 import qualified Data.List as List
20 import qualified Data.Sequence as Seq
21 import qualified Data.Text.Lazy as TL
22 import qualified Text.Megaparsec as P
23 import qualified Text.Megaparsec.Char as P
25 import Language.TCT.Debug
26 import Language.TCT.Cell
27 import Language.TCT.Tree
28 import Language.TCT.Read.Cell
29 import Language.TCT.Read.Elem
30 import Language.TCT.Read.Token
32 p_CellHeader :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
33 p_CellHeader row = debugParser "CellHeader" $ do
34 P.skipMany $ P.char ' '
35 Cell sp h <- p_Cell $ do
36 debugParser "Header" $
38 [ P.try $ P.char '-' >>
39 P.char ' ' $> HeaderDash <|>
40 P.string "- " $> HeaderDashDash
44 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
45 , P.try $ P.some (P.char '#') <* P.lookAhead (P.try $ P.char ' ') >>= \hs ->
46 return $ HeaderSection $ List.length hs
49 <$> P.between (P.string "[") (P.string "]") p_Name
50 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy (=='\n')))
52 (\f -> HeaderDotSlash $ "./"<>f)
54 <*> P.many (P.satisfy (/='\n'))
59 [ P.try $ HeaderColon name wh
61 <* P.lookAhead (P.try $ P.eof <|> void (P.satisfy $ \c -> c=='\n'||c==' '))
62 , P.char '>' $> HeaderGreat name wh
63 , P.char '=' $> HeaderEqual name wh
64 , P.char '|' $> HeaderBar name wh
67 let row' = Tree (Cell sp $ NodeHeader h) 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
97 Cell ssp@(Span fp bp ep:|sp) (name,attrs) <-
100 (,) <$> p_Name <*> p_ElemAttrs
102 Tree (Cell ssp $ NodeLower name attrs) .
103 Seq.singleton . tree0 . (NodeText <$>)
104 let treeElem hasContent nod (Cell (Span _fp _bp ep':|_sp) t) =
105 let (o,_) = bs $ PairElem name attrs in
106 tree0 $ Cell (Span fp bp ep':|sp) $ nod $ o<>t
108 bs | hasContent = pairBordersDouble
109 | otherwise = pairBordersSingle
111 P.try (P.char '>' >> treeElem True NodeText <$> p_CellLinesUntilElemEnd indent name) <|>
112 P.try (P.char '\n' >> P.tokens (==) indent >> treeHere <$> p_CellLines indent) <|>
113 P.try (P.tokens (==) "/>" >> treeElem False NodeText <$> p_CellLine) <|>
114 (P.eof $> treeHere (Cell (Span fp ep ep:|sp) ""))
117 p_CellLine :: P.Tokens s ~ TL.Text => Parser e s (Cell TL.Text)
118 p_CellLine = p_Cell p_Line
119 p_CellLines :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Parser e s (Cell TL.Text)
121 -- TODO: optimize special case indent == "" ?
124 <$> P.sepBy (P.try p_Line)
125 (P.try $ P.char '\n' >> P.tokens (==) indent)
126 p_CellLinesUntilElemEnd :: P.Tokens s ~ TL.Text => P.Tokens TL.Text -> Name -> Parser e s (Cell TL.Text)
127 p_CellLinesUntilElemEnd indent name =
128 p_Cell $ TL.intercalate "\n" . List.reverse <$> go []
129 -- TODO: optimize merging, and maybe case indent == ""
131 go :: P.Tokens s ~ TL.Text => [TL.Text] -> Parser e s [TL.Text]
133 let end = "</" <> name in
134 P.try ((\w l -> w <> end <> l : ls)
138 (p_Line >>= \l -> P.try $
140 >> P.tokens (==) indent
143 p_CellText1 :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
144 p_CellText1 row = debugParser "CellText" $ do
145 P.skipMany $ P.char ' '
146 n <- p_Cell $ NodeText <$> p_Line1
147 return $ tree0 n : row
149 p_CellRaw :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
150 p_CellRaw row = debugParser "CellRaw" $ do
151 P.skipMany $ P.char ' '
152 n <- p_Cell $ NodeText <$> p_Line
153 return $ tree0 n : row
155 p_CellSpaces1 :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
156 p_CellSpaces1 row = debugParser "CellSpaces" $ do
157 P.skipSome $ P.char ' '
158 cell <- p_Cell $ NodeText <$> P.string ""
159 return $ tree0 cell : row
161 p_CellEnd :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
162 p_CellEnd row = debugParser "CellEnd" $
163 P.try (p_CellLower row) <|>
164 P.try (p_CellText1 row) <|>
165 p_CellSpaces1 row <|>
168 p_Row :: P.Tokens s ~ TL.Text => Row -> Parser e s Row
169 p_Row row = debugParser "Row" $
170 P.try (p_CellHeader row) <|>
173 p_Rows :: P.Tokens s ~ TL.Text => Rows -> Parser e s Rows
176 let rows' = rows `mergeRow` row in
178 (P.newline >> {- USELESS: P.eof $> rows' <|>-} p_Rows rows')
180 p_Trees :: P.Tokens s ~ TL.Text => Parser e s (Trees (Cell Node))
181 p_Trees = collapseRows <$> p_Rows initRows