1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE ScopedTypeVariables #-}
5 {-# LANGUAGE TypeFamilies #-}
6 {-# LANGUAGE ViewPatterns #-}
7 {-# OPTIONS_GHC -fno-warn-orphans #-}
8 -- | Read DTC from TCT.
9 module Hdoc.DTC.Read.TCT where
11 import Control.Applicative (Applicative(..))
12 import Control.Monad (Monad(..))
14 import Data.Default.Class (Default(..))
15 import Data.Either (Either(..))
16 import Data.Eq (Eq(..))
17 import Data.Foldable (Foldable(..))
18 import Data.Function (($), (.), const, id)
19 import Data.Functor ((<$>), (<$))
21 import Data.List.NonEmpty (NonEmpty(..))
22 import Data.Maybe (Maybe(..), maybe)
23 import Data.Monoid (Monoid(..), First(..))
24 import Data.Ord (Ord(..))
25 import Data.Proxy (Proxy(..))
26 import Data.Ratio ((%))
27 import Data.Semigroup (Semigroup(..))
28 import Data.Sequence (ViewL(..), (|>))
29 import Data.String (String)
30 import Data.Tuple (fst, snd)
32 import Text.Read (readMaybe, Read(..))
33 import Text.Show (Show(..))
34 import qualified Control.Monad.Trans.State as S
35 import qualified Data.List as List
36 import qualified Data.Map.Strict as Map
37 import qualified Data.Ratio as Ratio
38 import qualified Data.Sequence as Seq
39 import qualified Data.Set as Set
40 import qualified Data.Text.Lazy as TL
41 import qualified GHC.Read as Read (expectP)
42 import qualified Text.Megaparsec as P
43 import qualified Text.Megaparsec.Perm as P
44 import qualified Text.Read as Read
46 import Hdoc.TCT hiding (Parser, ErrorRead)
48 import qualified Hdoc.DTC.Document as DTC
49 import qualified Hdoc.DTC.Sym as DTC
50 import qualified Hdoc.RNC.Sym as RNC
56 -- type Parser = P.Parsec ErrorRead XMLs
57 type Parser = S.StateT State (P.Parsec ErrorRead XMLs)
59 instance RNC.Sym_Rule Parser where
60 -- rule n p = P.dbg s p {-(p P.<?> s)-} where s = Text.unpack n
62 instance RNC.Sym_RNC Parser where
63 none = P.label "none" $ P.eof
64 fail = P.label "fail" $ P.failure Nothing mempty
65 any = P.label "any" $ p_satisfyMaybe $ const $ Just ()
66 anyElem p = P.label "anyElem" $ do
67 (n,ts) <- P.token check $ Just expected
70 expected = Tree (cell0 $ XmlElem "*") mempty
71 check (Tree (unCell -> XmlElem e) ts) = Right (e,ts)
73 ( Just $ P.Tokens $ pure t
74 , Set.singleton $ P.Tokens $ pure expected )
76 ts <- P.token check $ Just expected
77 let mayNameOrFigureName
78 | n == "aside" = Nothing
81 -- NOTE: special case renaming the current DTC.Pos
82 -- using the @type attribute to have positions like this:
90 , Just ty <- getFirst $ (`foldMap` ts) $ \case
91 Tree0 (unCell -> XmlAttr "type" ty) -> First $ Just ty
93 = Just $ xmlLocalName $ ty
95 case mayNameOrFigureName of
96 Nothing -> parser p ts
97 Just nameOrFigureName -> do
99 let incrPrecedingSibling name =
102 DTC.pos_PrecedingSiblings pos
103 S.put pos -- NOTE: in children, push current name incremented on ancestors
104 -- and reset preceding siblings.
105 { DTC.pos_PrecedingSiblings = mempty
106 , DTC.pos_Ancestors = DTC.pos_Ancestors pos |> (n, incrPrecedingSibling n)
107 , DTC.pos_AncestorsWithFigureNames =
108 DTC.pos_AncestorsWithFigureNames pos |>
110 , incrPrecedingSibling nameOrFigureName )
113 S.put pos -- NOTE: after current, increment current name
114 -- and reset ancestors.
115 { DTC.pos_PrecedingSiblings =
116 (if n == nameOrFigureName then id
117 else Map.insertWith (const succ) nameOrFigureName 1) $
118 Map.insertWith (const succ) n 1 $
119 DTC.pos_PrecedingSiblings pos
123 expected = Tree (cell0 $ XmlElem n) mempty
124 check (Tree (unCell -> XmlElem e) ts) | e == n = Right ts
126 ( Just $ P.Tokens $ pure t
127 , Set.singleton $ P.Tokens $ pure expected )
129 v <- P.token check $ Just expected
132 expected = Tree0 (cell0 $ XmlAttr n "")
133 check (Tree0 (Cell sp (XmlAttr k v))) | k == n =
134 Right $ Seq.singleton $ Tree0 $ Cell sp $ XmlText v
136 ( Just $ P.Tokens $ pure t
137 , Set.singleton $ P.Tokens $ pure expected )
141 Tree0 (unCell -> XmlComment c) :< ts -> do
143 c <$ setPosOnNextNode
144 t :< _ts -> P.failure (Just $ P.Tokens $ pure t) ex
145 EmptyL -> P.failure Nothing ex
147 ex = Set.singleton $ P.Tokens $ pure expected
148 expected = Tree0 (cell0 $ XmlComment "")
150 P.token check (Just expected)
153 expected = Tree0 (cell0 $ XmlText "")
154 check (Tree0 (unCell -> XmlText t)) = Right t
156 ( Just $ P.Tokens $ pure t
157 , Set.singleton $ P.Tokens $ pure expected )
158 int = RNC.rule "int" $ RNC.text >>= \t ->
159 case readMaybe (TL.unpack t) of
161 Nothing -> P.fancyFailure $
162 Set.singleton $ P.ErrorCustom $ ErrorRead_Not_Int t
163 rational = RNC.rule "rational" $ RNC.text >>= \t ->
164 case readMaybe (TL.unpack t) of
165 Just (Rational i) | 0 <= i -> return i
166 | otherwise -> P.fancyFailure $
167 Set.singleton $ P.ErrorCustom $ ErrorRead_Not_Positive t
168 Nothing -> P.fancyFailure $
169 Set.singleton $ P.ErrorCustom $ ErrorRead_Not_Rational t
170 rationalPositive = RNC.rule "rationalPositive" $ RNC.text >>= \t ->
171 case readMaybe (TL.unpack t) of
172 Just (Rational i) | 0 <= i -> return i
173 | otherwise -> P.fancyFailure $
174 Set.singleton $ P.ErrorCustom $ ErrorRead_Not_Positive t
175 Nothing -> P.fancyFailure $
176 Set.singleton $ P.ErrorCustom $ ErrorRead_Not_Rational t
177 nat = RNC.rule "nat" $ RNC.int >>= \i ->
180 else P.fancyFailure $ Set.singleton $
181 P.ErrorCustom $ ErrorRead_Not_Nat i
182 nat1 = RNC.rule "nat1" $ RNC.int >>= \i ->
185 else P.fancyFailure $ Set.singleton $
186 P.ErrorCustom $ ErrorRead_Not_Nat1 i
190 optional = P.optional
194 type instance RNC.Perm Parser = P.PermParser XMLs Parser
195 instance RNC.Sym_Interleaved Parser where
196 interleaved = P.makePermParser
201 f <$*> a = f P.<$?> ([],P.some a)
202 f <|*> a = f P.<|?> ([],P.some a)
203 instance DTC.Sym_DTC Parser where
207 DTC.Sym_DTC Parser =>
209 Either (P.ParseError (P.Token XMLs) ErrorRead) DTC.Document
210 readDTC = (fst <$>) . runParser def (P.initialPos "") DTC.document
212 -- | @runParser st pos p xs@ runs a 'Parser' @p@ to parse @xs@ entirely,
213 -- using state @st@ from position @pos@.
215 DTC.Sym_DTC Parser =>
217 P.SourcePos -> Parser a -> XMLs ->
218 Either (P.ParseError (P.Token XMLs) ErrorRead) (a, State)
219 runParser st pos p inp =
220 let p' = S.runStateT (p <* RNC.none) st in
225 , P.statePos = pure $
226 case Seq.viewl inp of
227 Tree (Cell (Span{span_begin=bp, span_file}:|_) _) _ :< _ ->
228 P.SourcePos span_file
229 (P.mkPos $ pos_line bp)
230 (P.mkPos $ pos_column bp)
232 , P.stateTabWidth = P.pos1
233 , P.stateTokensProcessed = 0
236 -- | @parser p xs@ returns a 'Parser' parsing @xs@ with @p@,
237 -- applying 'setPosOnNextNode' in case of success.
239 DTC.Sym_DTC Parser =>
240 Parser a -> XMLs -> Parser a
244 case runParser st pos p xs of
245 Left (P.TrivialError (posErr:|_) un ex) -> do
246 -- NOTE: just re-raising exception.
250 Left (P.FancyError (posErr:|_) errs) -> do
251 -- NOTE: just re-raising exception.
257 a <$ setPosOnNextNode
259 -- | Adjust the current 'P.SourcePos'
260 -- to be the begining of the following-sibling 'XML' node
261 -- so that on error 'P.toHints' keeps expected 'P.Token's in the list,
262 -- and thus makes useful error messages.
264 -- This is needed because the end of a 'Cell'
265 -- is not necessarily the begin of the next 'Cell'.
266 setPosOnNextNode :: Parser ()
267 setPosOnNextNode = do
270 , P.statePos = pos :| _
271 } <- P.getParserState
272 case Seq.viewl inp of
274 t :< _ -> P.setPosition $ P.positionAt1 (Proxy::Proxy XMLs) pos t
276 instance P.Stream XMLs where
277 type Token XMLs = XML
278 type Tokens XMLs = XMLs
281 Tree (unCell -> XmlComment{}) _ :< ts -> P.take1_ ts
284 positionAt1 _s pos (Tree (Cell (Span{span_begin=Pos l c}:|_) _n) _ts) =
285 P.SourcePos (P.sourceName pos) (P.mkPos l) (P.mkPos c)
286 positionAtN s pos ts =
288 t :< _ -> P.positionAt1 s pos t
290 advance1 _s _indent pos (Tree (Cell (Span{span_end=Pos l c}:|_) _n) _ts) =
291 -- WARNING: the end of a 'Cell' is not necessarily
292 -- the beginning of the next 'Cell'.
293 P.SourcePos (P.sourceName pos) (P.mkPos l) (P.mkPos c)
294 advanceN s = foldl' . P.advance1 s
295 takeN_ n s | n <= 0 = Just (mempty, s)
297 | otherwise = Just (Seq.splitAt n s) -- FIXME: handle XmlComment and XmlInclude
298 tokensToChunk _s = Seq.fromList
299 chunkToTokens _s = toList
300 chunkLength _s = Seq.length
301 takeWhile_ = Seq.spanl
302 instance P.ShowToken XML where
303 showTokens toks = List.intercalate ", " $ toList $ showTree <$> toks
305 showTree :: XML -> String
306 showTree (Tree a _ts) =
308 XmlElem n -> "<"<>show n<>">"
309 XmlAttr n _v -> show n<>"="
311 XmlComment _c -> "comment"
313 showCell (Cell path@(Span{span_file} :| _) a) f =
316 else f a <> foldMap (\p -> "\n in "<>show p) path
318 -- ** Type 'ErrorRead'
320 = ErrorRead_EndOfInput
321 | ErrorRead_Not_Int TL.Text
322 | ErrorRead_Not_Nat Int
323 | ErrorRead_Not_Nat1 Int
324 | ErrorRead_Not_Rational TL.Text
325 | ErrorRead_Not_Positive TL.Text
326 -- | ErrorRead_Unexpected P.sourcePos XML
327 deriving (Eq,Ord,Show)
328 instance P.ShowErrorComponent ErrorRead where
329 showErrorComponent = show
331 -- ** Type 'Rational'
332 -- | Wrapper to change the 'Read' instance.
333 newtype Rational = Rational Ratio.Rational
334 instance Read Rational where
336 x <- Read.step readPrec
337 Read.expectP (Read.Symbol "/")
338 y <- Read.step readPrec
339 return $ Rational (x % y)