1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 module Language.TCT.Read.Tree where
5 import Control.Applicative (Applicative(..), Alternative(..))
6 import Control.Monad (Monad(..), void)
8 import Data.Char (Char)
9 import Data.Eq (Eq(..))
10 import Data.Monoid (Monoid(..))
11 import Data.Function (($), (.))
12 import Data.Functor ((<$>), ($>))
13 import Data.Semigroup (Semigroup(..))
14 import Data.Sequence (ViewR(..))
15 import Data.String (String)
16 import Prelude (undefined, Int, Num(..), toInteger)
17 import Data.Text (Text)
18 import Text.Megaparsec.Text
19 import Text.Show (Show(..))
20 import qualified Data.Char as Char
21 import qualified Data.List as List
22 import qualified Data.Sequence as Seq
23 import qualified Data.Text as Text
24 import qualified Text.Megaparsec as P
26 import Language.TCT.Tree
28 pdbg :: (Show a, P.ShowErrorComponent e, P.ShowToken (P.Token s), P.Stream s) => String -> P.ParsecT e s m a -> P.ParsecT e s m a
29 -- pdbg m p = P.dbg m p
33 p_Position :: Parser (Line,Column)
34 p_Position = (\p -> (intOfPos $ P.sourceLine p, intOfPos $ P.sourceColumn p)) <$> P.getPosition
35 intOfPos :: P.Pos -> Int
36 intOfPos = fromInteger . toInteger . P.unPos
40 p_Line = intOfPos . P.sourceLine <$> P.getPosition
43 p_Column :: Parser Column
44 p_Column = intOfPos . P.sourceColumn <$> P.getPosition
47 p_Row :: Row -> Parser Row
48 p_Row row = pdbg "Row" $
49 P.try (p_CellKey row) <|>
50 P.try (p_CellValue row) <|>
51 p_PlainLine row -- <|>
52 -- (P.newline $> row) <|>
56 p_CellKey :: Row -> Parser Row
57 p_CellKey row = pdbg "CellKey" $ do
58 P.skipMany $ P.char ' '
62 [ P.try $ P.string "- " $> KeyDash
65 , P.try $ P.some (P.char '#') <* P.char ' ' >>= \hs ->
66 return (KeySection $ List.length hs)
68 name <- Text.pack <$> some (P.satisfy $ \c ->
69 Char.isAlphaNum c || c=='-' || c=='_')
70 P.skipMany $ P.char ' '
72 [ P.char ':' $> KeyColon name
73 , P.char '>' $> KeyGreat name
74 , P.char '=' $> KeyEqual name
75 , P.char '|' $> KeyBar name
80 let row' = Key pos posEnd key : row
82 KeySection{} -> P.try (p_CellValue row') <|> p_PlainLine row'
83 KeyDash{} -> p_Row row'
84 KeyColon{} -> p_Row row'
85 KeyGreat{} -> p_Row row'
86 KeyEqual{} -> p_PlainLine row'
87 KeyBar{} -> p_PlainLine row'
89 -- | Parse up to an end-of-line or end-of-file.
90 p_PlainLine :: Row -> Parser Row
91 p_PlainLine row = pdbg "PlainLine" $ do
92 P.skipMany $ P.char ' '
94 line <- many $ P.satisfy (/='\n')
96 P.newline $> Value pos posEnd (Text.pack line) : row
100 p_CellValue :: Row -> Parser Row
101 p_CellValue row = pdbg "CellValue" $ do
102 P.skipMany $ P.char ' '
104 val <- Text.pack <$> P.some (P.satisfy (/='\n'))
106 return $ Value pos posEnd val : row
108 p_Value :: Parser Value
109 p_Value = pdbg "Value" $
110 -- (List.foldl' (<>) (Plain "") <$>) $
113 <$> P.some (P.satisfy (/='\n'))
123 p_Tag :: Parser Value
124 p_Tag = pdbg "Tag" $ P.try p_TagGroup <|> p_TagOpen
126 p_TagGroup = P.char '#' *> p_TagName <* P.char '#'
128 p_TagName :: Parser Value
130 (\w ws -> Tag $ Text.concat $ w : ws)
132 <*> many ((<>) <$> (Text.pack <$> many (P.char ' ')) <*> p_TagNameWord)
133 p_TagNameWord = Text.pack <$> some (P.satisfy $ \c -> c/=' ' && c/='#' && Char.isPrint c)
134 p_TagOpen = P.char '#' *> p_TagName
136 p_TagName = Tag . Text.pack <$> some (P.satisfy isTagNameShortChar)
138 isTagNameShortChar :: Char -> Bool
139 isTagNameShortChar c | Char.isAlphaNum c = True
140 isTagNameShortChar '-' = True
141 isTagNameShortChar '_' = True
142 isTagNameShortChar _ = False
144 p_Escape :: Parser Value
147 P.option (Plain $ "\\") $ P.try $ do
149 [ P.char c >> pure (Plain $ Text.singleton c)
150 | c <- "<>=|@#*_\\/`'\"«»-"
153 p_Group :: Parser Value
158 , p_Group1 Underscore '_'
160 , p_Group1 Backquote '`'
161 , p_Group1 Singlequote '\''
162 , p_Group1 Doublequote '"'
163 -- , Group Frenchquote <$ p_Group2 '«' '»'
166 p_Group1 :: Group -> Char -> Parser Value
167 p_Group1 g c = (if c == '/' then pdbg "Group1" else (\p -> p)) $ do
169 P.option (Plain $ Text.singleton c) $ P.try $ do
170 P.notFollowedBy $ P.char ' '
171 v <- (if c == '/' then pdbg "Group1: Value" else (\p -> p)) $ p_Value
172 case lastCharOfValue v of
173 ' ' -> fail "grouped Value ends with space"
178 lastCharOfValue :: Value -> Char
179 lastCharOfValue = \case
180 Plain t -> Text.last t
181 Tag t | Text.all (\c -> Char.isAlphaNum c || c=='-' || c=='_') t -> Text.last t
183 Group _ v -> lastCharOfValue v
184 Values vs | _:>v <- Seq.viewr vs -> lastCharOfValue v
185 Values{} -> undefined
187 p_Plain :: Parser Value
188 p_Plain = pdbg "Plain" $
207 -- | @a-- * Type 'TCT'
209 p_TCT :: Parser (TCT Text)
211 tree <- collapsePath <$> go [Tree (Value (0,0) (0,0) "") mempty]
214 Tree (Value _ _ v) roots | Text.null v -> roots
217 go :: Rows -> Parser Rows
221 row -> go $ appendRow acc (List.reverse row)