1 {-# LANGUAGE UndecidableInstances #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
3 -- | Symantic and LCC grammar instances for Megaparsec
4 module Hcompta.LCC.Read.Megaparsec where
6 import Control.Applicative (Applicative(..))
7 import Control.Monad (Monad(..))
8 import Control.Monad.IO.Class (MonadIO(..))
10 import Data.Char (Char)
11 import Data.Either (Either(..))
12 import Data.Eq (Eq(..))
14 import Data.Function (($), (.))
15 import Data.Functor (Functor(..), (<$>))
17 import Data.List ((++))
18 import Data.List.NonEmpty (NonEmpty(..))
19 import Data.Maybe (Maybe(..))
20 import Data.Ord (Ord(..))
22 import Data.String (IsString(..), String)
23 import Data.Text (Text)
24 import Data.Typeable ()
25 import Data.Word (Word)
26 import Prelude (pred, succ, (-), error)
28 import Text.Show (Show(..))
29 import qualified Control.Applicative as Alt
30 import qualified Control.Exception.Safe as Exn
31 import qualified Control.Monad.Classes as MC
32 import qualified Data.ByteString as BS
33 import qualified Data.Char as Char
34 import qualified Data.List as List
35 import qualified Data.List.NonEmpty as NE
36 import qualified Data.Set as Set
37 import qualified Data.Strict as S
38 import qualified Data.Text as Text
39 import qualified Data.Text.Encoding as Enc
40 import qualified System.Directory as IO
41 import qualified Text.Megaparsec as P
42 import qualified Text.Megaparsec.Prim as P
44 import Language.Symantic.Grammar as Sym
45 import qualified Language.Symantic as Sym
46 import qualified Language.Symantic.Document as D
48 import Hcompta.LCC.Amount
49 import Hcompta.LCC.Chart
50 import Hcompta.LCC.Compta
51 import Hcompta.LCC.Write
52 import Hcompta.LCC.Read.Compta as LCC
54 -- import Hcompta.LCC.Journal
55 import Hcompta.LCC.Posting
57 import Debug.Trace (trace)
58 import Data.Semigroup ((<>))
60 dbg :: Show a => [Char] -> a -> a
61 dbg msg x = trace (msg <> " = " <> show x) x
63 -- | Convenient converter.
64 sourcePos :: P.SourcePos -> SourcePos
65 sourcePos (P.SourcePos p l c) = SourcePos p (PosFile $ P.unPos l) (PosFile $ P.unPos c)
68 -- | Convenient alias for defining instances involving 'P.ParsecT'.
69 type ParsecC e s = (P.Token s ~ Char, P.Stream s, P.ErrorComponent e)
70 instance ParsecC e s => IsString (P.ParsecT e s m [Char]) where
78 instance ParsecC e s => Sym.Gram_Reader (NonEmpty SourcePos) (P.ParsecT e s m) where
80 s <- (sourcePos <$>) . P.statePos <$> P.getParserState
84 f . (sourcePos <$>) . P.statePos <$> P.getParserState
85 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (NonEmpty SourcePos)) = 'True
86 instance ParsecC e s => MC.MonadReaderN 'MC.Zero (NonEmpty SourcePos) (P.ParsecT e s m) where
87 askN _n = (sourcePos <$>) . P.statePos <$> P.getParserState
89 instance ParsecC e s => Sym.Gram_Reader SourcePos (P.ParsecT e s m) where
91 s <- sourcePos <$> P.getPosition
95 f . sourcePos <$> P.getPosition
96 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader SourcePos) = 'True
97 instance ParsecC e s => MC.MonadReaderN 'MC.Zero SourcePos (P.ParsecT e s m) where
98 askN _n = sourcePos <$> P.getPosition
100 instance ParsecC e s => Sym.Gram_Reader () (P.ParsecT e s m) where
101 askBefore = fmap ($ ())
102 askAfter = fmap ($ ())
103 -- S.Either Exn.IOException CanonFile
104 instance (ParsecC e s, MonadIO m) => Sym.Gram_Reader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
106 sn <- P.sourceName <$> P.getPosition
109 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
110 (return . f . S.Left)
113 sn <- P.sourceName <$> P.getPosition
115 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
116 (return . f . S.Left)
117 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (S.Either Exn.IOException CanonFile)) = 'True
118 instance (ParsecC e s, MonadIO m) => MC.MonadReaderN 'MC.Zero (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
120 sn <- P.sourceName <$> P.getPosition
122 (S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
130 type instance MC.CanDo (P.ParsecT e s m) (MC.EffState st) = 'False
131 instance (Monad m, MC.MonadState st m) => Sym.Gram_State st m where
160 instance (ParsecC e s, Show err) => Sym.Gram_Error err (P.ParsecT e s m) where
164 Left err -> fail $ show err
166 instance ParsecC e s => Sym.Gram_Rule (P.ParsecT e s m) where
167 rule = P.label . Text.unpack
168 instance ParsecC e s => Sym.Gram_Terminal (P.ParsecT e s m) where
173 unicat cat = P.satisfy $ (`elem` cats) . Char.generalCategory
174 where cats = unicode_categories cat
175 range (l, h) = P.satisfy $ \c -> l <= c && c <= h
176 Terminal f `but` Terminal p = Terminal $ P.notFollowedBy (P.try p) *> f
177 instance ParsecC e s => Sym.Gram_Alt (P.ParsecT e s m) where
181 instance ParsecC e s => Sym.Gram_Try (P.ParsecT e s m) where
183 instance ParsecC e s => Sym.Gram_RegR (P.ParsecT e s m) where
184 Terminal f .*> Reg x = Reg $ f <*> x
185 instance ParsecC e s => Sym.Gram_RegL (P.ParsecT e s m) where
186 Reg f <*. Terminal x = Reg $ f <*> x
187 instance ParsecC e s => Sym.Gram_App (P.ParsecT e s m) where
189 instance ParsecC e s => Sym.Gram_AltApp (P.ParsecT e s m) where
191 optional = P.optional
194 manySkip = P.skipMany
195 instance ParsecC e s => Sym.Gram_CF (P.ParsecT e s m) where
196 CF f <& Reg p = CF $ P.lookAhead f <*> p
197 Reg f &> CF p = CF $ P.lookAhead f <*> p
198 minus (CF f) (Reg p) = CF $ P.notFollowedBy (P.try p) *> f
199 instance ParsecC e s => Sym.Gram_Comment (P.ParsecT e s m)
200 instance ParsecC e s => Sym.Gram_Op (P.ParsecT e s m)
201 instance ParsecC e s => Sym.Gram_Mod (P.ParsecT e s m)
202 instance ParsecC e s => Sym.Gram_Type_Name (P.ParsecT e s m)
203 instance ParsecC e s => Sym.Gram_Term_Name (P.ParsecT e s m)
204 instance -- Sym.Gram_Type
207 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
208 , Gram_Source src (P.ParsecT e s m)
209 ) => Sym.Gram_Type src (P.ParsecT e s m)
210 instance -- Sym.Gram_Term_Type
213 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
214 , Gram_Source src (P.ParsecT e s m)
215 ) => Sym.Gram_Term_Type src (P.ParsecT e s m)
216 instance -- Sym.Gram_Term
219 , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) (P.ParsecT e s m)
220 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
221 , Sym.Gram_Source src (P.ParsecT e s m)
222 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
223 ) => Sym.Gram_Term src ss (P.ParsecT e s m)
228 instance ParsecC e s => LCC.Gram_Count (P.ParsecT e s m)
229 instance ParsecC e s => LCC.Gram_Char (P.ParsecT e s m)
230 instance ParsecC e s => LCC.Gram_Comment (P.ParsecT e s m)
231 instance ParsecC e s => LCC.Gram_Tag (P.ParsecT e s m)
232 instance ParsecC e s => LCC.Gram_Account (P.ParsecT e s m)
233 instance ParsecC e s => LCC.Gram_Amount (P.ParsecT e s m)
234 instance ParsecC e s => LCC.Gram_File (P.ParsecT e s m)
235 instance -- LCC.Gram_Date
237 , MC.MonadState Year (P.ParsecT e s m)
238 ) => Gram_Date (P.ParsecT e s m)
239 instance -- LCC.Gram_Posting
241 , MC.MonadState Chart (P.ParsecT e s m)
242 , MC.MonadState Style_Amounts (P.ParsecT e s m)
243 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
244 ) => Gram_Posting (P.ParsecT e s m)
245 instance -- LCC.Gram_Transaction
247 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
248 , MC.MonadState Chart (P.ParsecT e s m)
249 , MC.MonadState Section (P.ParsecT e s m)
250 , MC.MonadState Style_Amounts (P.ParsecT e s m)
251 , MC.MonadState Year (P.ParsecT e s m)
252 ) => Gram_Transaction (P.ParsecT e s m)
253 instance -- LCC.Gram_Chart
255 , MC.MonadState Chart (P.ParsecT e s m)
256 , MC.MonadState Section (P.ParsecT e s m)
257 ) => Gram_Chart (P.ParsecT e s m)
258 instance -- LCC.Gram_Path
261 ) => Gram_Path (P.ParsecT e s m) where
263 pf@(PathFile fp) <- g
264 liftIO $ (pf,) <$> Exn.catch
265 (Right . CanonFile . PathFile <$> IO.canonicalizePath fp)
267 instance -- LCC.Gram_IO
270 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
271 , MC.MonadState Chart (P.ParsecT e s m)
272 , MC.MonadState Style_Amounts (P.ParsecT e s m)
273 , MC.MonadState Year (P.ParsecT e s m)
274 , Sym.Gram_Source src (P.ParsecT e s m)
275 , P.MonadParsec e Text (P.ParsecT e s m)
276 ) => Gram_IO src (P.ParsecT e s m) where
281 S.Left (e::Error_Compta src) ->
282 return $ \(src::src) ->
284 S.Right (PathFile fp) ->
286 ((\inp _src -> S.Right $ (fp,) $ Enc.decodeUtf8 inp) <$> BS.readFile fp)
287 (\exn -> return $ \src -> S.Left $ At src $ Error_Compta_Read (PathFile fp) exn)
289 S.Left e -> return $ S.Left [e]
290 S.Right (fp_new, s_new) -> do
291 P.pushPosition $ P.initialPos fp_new
292 s_old <- P.getInput; P.setInput s_new
296 P.observing g >>= \case
301 P.updateParserState (\st -> st{P.statePos=P.errorPos $ trace ("ERROR: " <> show err) err})
303 (P.errorUnexpected err)
304 (P.errorExpected err)
313 instance -- LCC.Gram_Compta
316 , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) m
317 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) m
318 , MC.MonadState Terms m
319 , Gram_Input (P.ParsecT e s m)
323 , SourceInj (Sym.AST_Type src) src
324 , SourceInj (Sym.KindK src) src
325 , SourceInj (Sym.TypeVT src) src
326 , P.MonadParsec e Text (P.ParsecT e s m)
327 , Sym.Gram_Source src (P.ParsecT e s m)
328 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
329 , MC.MonadReader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m)
330 , MC.MonadState (Context_Read src) (P.ParsecT e s m)
331 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
332 , MC.MonadState Chart (P.ParsecT e s m)
333 , MC.MonadState Section (P.ParsecT e s m)
334 , MC.MonadState Style_Amounts (P.ParsecT e s m)
335 , MC.MonadState Year (P.ParsecT e s m)
336 ) => Gram_Compta ss src (P.ParsecT e s m)
337 instance -- LCC.Gram_Term_Def
339 -- , MC.MonadState (Env src ss) m
340 , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) m
341 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) m
344 , SourceInj (Sym.AST_Type src) src
345 , SourceInj (Sym.KindK src) src
346 , SourceInj (Sym.TypeVT src) src
347 , Gram_Source src (P.ParsecT e s m)
348 , P.MonadParsec e Text (P.ParsecT e s m)
349 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
350 ) => LCC.Gram_Term_Def src ss (P.ParsecT e s m)
352 instance ParsecC e Text => Gram_Input (P.ParsecT e Text m) where
354 P.State {P.stateInput=ib, P.statePos=P.SourcePos _ lb bc:|_} <- P.getParserState
356 P.State {P.statePos=P.SourcePos _ le ce:|_} <- P.getParserState
357 return $ f $ (`Text.take` ib) $ sizeInput 0 ib
358 (P.unPos le - P.unPos lb)
359 (if lb == le then P.unPos ce - P.unPos bc else pred (P.unPos ce))
361 sizeInput :: Int -> Text -> Word -> Word -> Int
362 sizeInput s _i 0 0 = s
364 case Text.uncons i of
365 Nothing -> error "[BUG] sizeInput"
366 Just (_c, i') -> sizeInput (succ s) i' 0 (pred c)
368 case Text.uncons i of
369 Nothing -> error "[BUG] sizeInput"
370 Just ('\n', i') -> sizeInput (succ s) i' (pred l) c
371 Just (_c, i') -> sizeInput (succ s) i' l c
373 -- syntaxError :: P.ParseError Char P.Dec -> Text
379 , P.ShowErrorComponent e
380 ) => P.ParseError t e -> String
382 sourcePosStackPretty (P.errorPos e) ++ ":\n"
383 ++ parseErrorTextPretty e
385 -- | Pretty-print stack of source positions.
386 sourcePosStackPretty :: NonEmpty P.SourcePos -> String
387 sourcePosStackPretty ms = concatMap f rest ++ P.sourcePosPretty pos
390 rest = List.reverse rest'
391 f p = "in file included from " ++ P.sourcePosPretty p ++ ",\n"
396 , P.ShowErrorComponent e
400 ) => P.ParseError t e -> IO d
401 showParseError err = do
402 let (pos:|_) = P.errorPos err
403 q <- write $ sourcePos pos
405 [ D.stringH (sourcePosStackPretty $ P.errorPos err) <> ":"
406 , D.stringH $ parseErrorTextPretty err
410 -- | Transforms list of error messages into their textual representation.
411 messageItemsPretty :: P.ShowErrorComponent a => String -> Set a -> String
412 messageItemsPretty prefix ts
414 | otherwise = prefix ++ f ts ++ "\n"
415 where f = orList . NE.fromList . Set.toAscList . Set.map P.showErrorComponent
417 orList :: NonEmpty String -> String
419 orList (x:|[y]) = x ++ " or " ++ y
420 orList xs = List.intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs
422 -- | Pretty-print textual part of a 'ParseError', that is, everything except
423 -- stack of source positions. The rendered staring always ends with a new line.
424 parseErrorTextPretty ::
427 , P.ShowErrorComponent e )
430 parseErrorTextPretty (P.ParseError _ us ps xs) =
431 if Set.null us && Set.null ps && Set.null xs
432 then "unknown parse error\n"
434 [ messageItemsPretty "unexpected " us
435 , messageItemsPretty "expecting " ps
436 , List.unlines (P.showErrorComponent <$> Set.toAscList xs)