1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE ScopedTypeVariables #-}
4 {-# LANGUAGE TypeFamilies #-}
5 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 module Language.Symantic.XML.Parser where
8 import Control.Applicative (Applicative(..))
9 import Control.Monad (Monad(..))
11 import Data.Either (Either(..))
12 import Data.Foldable (Foldable(..))
13 import Data.Function (($), (.))
14 import Data.Functor ((<$>), (<$))
15 import Data.Functor.Identity (Identity(..))
16 import Data.List.NonEmpty (NonEmpty(..))
17 import Data.Maybe (Maybe(..))
18 import Data.Monoid (Monoid(..))
19 import Data.Ord (Ord(..))
20 import Data.Proxy (Proxy(..))
21 import Data.Semigroup (Semigroup(..))
22 import Data.Sequence (ViewL(..))
23 import Data.String (String)
24 import Data.Tuple (snd)
25 import Text.Show (Show(..))
26 import qualified Control.Monad.Trans.State as S
27 import qualified Data.List as List
28 import qualified Data.Sequence as Seq
29 import qualified Text.Megaparsec as P
31 import Language.Symantic.XML.Document (XML, XMLs)
32 import qualified Language.Symantic.XML.Document as XML
34 -- | Whether the given 'XML.Node' must be ignored by the parser.
35 isIgnoredNode :: XML.Node -> Bool
37 XML.NodeComment{} -> True
39 XML.NodeCDATA{} -> True
42 instance P.Stream XMLs where
44 type Tokens XMLs = XMLs
48 t@(XML.Tree XML.Sourced{XML.unSourced=n} _) :< ts
49 | isIgnoredNode n -> P.take1_ ts
50 | otherwise -> Just (t, ts)
51 positionAt1 _s pos (XML.Tree (XML.Sourced (XML.FileRange{XML.fileRange_begin=XML.FilePos l c}:|_) _n) _ts) =
52 pos{ P.sourceLine = P.mkPos l
53 , P.sourceColumn = P.mkPos c }
54 positionAtN s pos ts =
56 t :< _ -> P.positionAt1 s pos t
58 advance1 _s _indent pos (XML.Tree (XML.Sourced (XML.FileRange{XML.fileRange_end=XML.FilePos l c}:|_) _n) _ts) =
59 -- WARNING: the end of a 'FileRange' is not necessarily
60 -- the beginning of the next 'FileRange'.
61 pos{ P.sourceLine = P.mkPos l
62 , P.sourceColumn = P.mkPos c }
63 advanceN s = foldl' . P.advance1 s
64 takeN_ n s | n <= 0 = Just (mempty, s)
67 let (ns,rs) = Seq.splitAt n s in
68 let (ko,ok) = Seq.partition (isIgnoredNode . XML.unSourced . XML.unTree) ns in
69 case P.takeN_ (Seq.length ko) rs of
70 Nothing -> Just (ok, rs)
71 Just (ns',rs') -> Just (ok<>ns', rs')
72 tokensToChunk _s = Seq.fromList
73 chunkToTokens _s = toList
74 chunkLength _s = Seq.length
75 takeWhile_ = Seq.spanl
77 -- | Adjust the current 'P.SourcePos'
78 -- to be the begining of the following-sibling 'XML' node
79 -- so that on error 'P.toHints' keeps expected 'P.Token's in the list,
80 -- and thus makes useful error messages.
82 -- This is needed because the end of a 'FileRange'
83 -- is not necessarily the begin of the next 'FileRange'.
85 -- type Parser = S.StateT State (P.Parsec ErrorRead XMLs)
87 setFilePosToNextNode :: P.MonadParsec e XMLs m => m ()
88 setFilePosToNextNode = do
91 , P.statePos = pos :| _
95 t :< _ -> P.setPosition $ P.positionAt1 (Proxy::Proxy XMLs) pos t
97 -- | @runParserT st pos p xs@ runs a 'Parser' @p@ to parse @xs@,
98 -- using state @st@ from position @pos@.
102 P.ParsecT err XMLs m a ->
103 NonEmpty P.SourcePos ->
105 m (Either (P.ParseError (P.Token XMLs) err) a)
106 runParserT p pos inp =
108 P.runParserT' p P.State
111 case Seq.viewl inp of
113 XML.Tree (XML.Sourced ss _) _ :< _ ->
114 (<$> ss) $ \XML.FileRange{fileRange_begin=bp, fileRange_file} ->
115 P.SourcePos fileRange_file
116 (P.mkPos $ XML.filePos_line bp)
117 (P.mkPos $ XML.filePos_column bp)
118 , P.stateTabWidth = P.pos1
119 , P.stateTokensProcessed = 0
122 -- | Like 'runParser', but using 'Identity' as the inner-monad of 'P.ParsecT'.
125 P.Parsec err XMLs a ->
126 NonEmpty P.SourcePos ->
128 Either (P.ParseError (P.Token XMLs) err) a
129 runParser p pos xs = runIdentity $ runParserT p pos xs
131 -- | @parser p xs@ returns a parser parsing @xs@ entirely with @p@,
132 -- applying 'setFilePosToNextNode' in case of success,
133 -- or resetting 'P.statePos' and re-raising the exception in case of error.
134 parser :: Ord err => P.Parsec err XMLs a -> XMLs -> P.Parsec err XMLs a
136 P.State{P.statePos=pos} <- P.getParserState
137 case runParser (p <* P.eof) pos xs of
138 Left (P.TrivialError statePos un ex) -> do
139 s <- P.getParserState
140 P.setParserState s{P.statePos}
142 Left (P.FancyError statePos errs) -> do
143 s <- P.getParserState
144 P.setParserState s{P.statePos}
146 Right a -> a <$ setFilePosToNextNode
148 -- | @stateParser p xs@ returns a stateful parser parsing @xs@ with @p@,
149 -- applying 'setFilePosToNextNode' in case of success.
152 S.StateT st (P.Parsec err XMLs) a ->
154 S.StateT st (P.Parsec err XMLs) a
155 stateParser p xs = do
157 P.State{P.statePos=pos} <- P.getParserState
158 case runParser (S.runStateT (p <* P.eof) st) pos xs of
159 Left (P.TrivialError statePos un ex) -> do
160 -- NOTE: just re-raising exception.
161 s <- P.getParserState
162 P.setParserState s{P.statePos}
164 Left (P.FancyError statePos errs) -> do
165 -- NOTE: just re-raising exception.
166 s <- P.getParserState
167 P.setParserState s{P.statePos}
171 a <$ setFilePosToNextNode