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.Source
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, P.ShowErrorComponent 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 newtype NoShow a = NoShow a
167 instance Show (NoShow a) where show _ = "NoShow"
168 instance ParsecC e s => Sym.Gram_Rule (P.ParsecT e s m) where
169 rule = P.label . Text.unpack
172 NoShow a <- P.dbg (Text.unpack n) $ NoShow <$> g
175 instance ParsecC e s => Sym.Gram_Terminal (P.ParsecT e s m) where
180 unicat cat = P.satisfy $ (`elem` cats) . Char.generalCategory
181 where cats = unicode_categories cat
182 range (l, h) = P.satisfy $ \c -> l <= c && c <= h
183 Terminal f `but` Terminal p = Terminal $ P.notFollowedBy (P.try p) *> f
184 instance ParsecC e s => Sym.Gram_Alt (P.ParsecT e s m) where
188 instance ParsecC e s => Sym.Gram_Try (P.ParsecT e s m) where
190 instance ParsecC e s => Sym.Gram_RegR (P.ParsecT e s m) where
191 Terminal f .*> Reg x = Reg $ f <*> x
192 instance ParsecC e s => Sym.Gram_RegL (P.ParsecT e s m) where
193 Reg f <*. Terminal x = Reg $ f <*> x
194 instance ParsecC e s => Sym.Gram_App (P.ParsecT e s m) where
196 instance ParsecC e s => Sym.Gram_AltApp (P.ParsecT e s m) where
198 optional = P.optional
201 manySkip = P.skipMany
202 instance ParsecC e s => Sym.Gram_CF (P.ParsecT e s m) where
203 CF f <& Reg p = CF $ P.lookAhead f <*> p
204 Reg f &> CF p = CF $ P.lookAhead f <*> p
205 minus (CF f) (Reg p) = CF $ P.notFollowedBy (P.try p) *> f
206 instance ParsecC e s => Sym.Gram_Comment (P.ParsecT e s m)
207 instance ParsecC e s => Sym.Gram_Op (P.ParsecT e s m)
208 instance ParsecC e s => Sym.Gram_Mod (P.ParsecT e s m)
209 instance ParsecC e s => Sym.Gram_Type_Name (P.ParsecT e s m)
210 instance ParsecC e s => Sym.Gram_Term_Name (P.ParsecT e s m)
211 instance -- Sym.Gram_Type
214 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
215 , Gram_Source src (P.ParsecT e s m)
216 ) => Sym.Gram_Type src (P.ParsecT e s m)
217 instance -- Sym.Gram_Term_Type
220 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
221 , Gram_Source src (P.ParsecT e s m)
222 ) => Sym.Gram_Term_Type src (P.ParsecT e s m)
223 instance -- Sym.Gram_Term
226 , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) (P.ParsecT e s m)
227 , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) (P.ParsecT e s m)
228 , Gram_Source src (P.ParsecT e s m)
229 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
230 ) => Sym.Gram_Term src ss (P.ParsecT e s m)
235 instance ParsecC e s => LCC.Gram_Count (P.ParsecT e s m)
236 instance ParsecC e s => LCC.Gram_Char (P.ParsecT e s m)
237 instance ParsecC e s => LCC.Gram_Comment (P.ParsecT e s m)
238 instance ParsecC e s => LCC.Gram_Tag (P.ParsecT e s m)
239 instance ParsecC e s => LCC.Gram_Account (P.ParsecT e s m)
240 instance ParsecC e s => LCC.Gram_Amount (P.ParsecT e s m)
241 instance ParsecC e s => LCC.Gram_File (P.ParsecT e s m)
242 instance -- LCC.Gram_Date
244 , MC.MonadState Year (P.ParsecT e s m)
245 ) => Gram_Date (P.ParsecT e s m)
246 instance -- LCC.Gram_Posting
248 , MC.MonadState Chart (P.ParsecT e s m)
249 , MC.MonadState Style_Amounts (P.ParsecT e s m)
250 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
251 ) => Gram_Posting (P.ParsecT e s m)
252 instance -- LCC.Gram_Transaction
254 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
255 , MC.MonadState Chart (P.ParsecT e s m)
256 , MC.MonadState Section (P.ParsecT e s m)
257 , MC.MonadState Style_Amounts (P.ParsecT e s m)
258 , MC.MonadState Year (P.ParsecT e s m)
259 ) => Gram_Transaction (P.ParsecT e s m)
260 instance -- LCC.Gram_Chart
262 , MC.MonadState Chart (P.ParsecT e s m)
263 , MC.MonadState Section (P.ParsecT e s m)
264 ) => Gram_Chart (P.ParsecT e s m)
265 instance -- LCC.Gram_Path
268 ) => Gram_Path (P.ParsecT e s m) where
270 pf@(PathFile fp) <- g
271 liftIO $ (pf,) <$> Exn.catch
272 (Right . CanonFile . PathFile <$> IO.canonicalizePath fp)
274 instance -- LCC.Gram_IO
277 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
278 , MC.MonadState Chart (P.ParsecT e s m)
279 , MC.MonadState Style_Amounts (P.ParsecT e s m)
280 , MC.MonadState Year (P.ParsecT e s m)
281 , Gram_Source src (P.ParsecT e s m)
282 , P.MonadParsec e Text (P.ParsecT e s m)
283 ) => Gram_IO src (P.ParsecT e s m) where
288 S.Left (e::Error_Compta src) ->
289 return $ \(src::src) ->
291 S.Right (PathFile fp) ->
293 ((\inp _src -> S.Right $ (fp,) $ Enc.decodeUtf8 inp) <$> BS.readFile fp)
294 (\exn -> return $ \src -> S.Left $ At src $ Error_Compta_Read (PathFile fp) exn)
296 S.Left e -> return $ S.Left [e]
297 S.Right (fp_new, s_new) -> do
298 P.pushPosition $ P.initialPos fp_new
299 s_old <- P.getInput; P.setInput s_new
303 P.observing g >>= \case
308 P.updateParserState (\st -> st{P.statePos=P.errorPos $ trace ("ERROR: " <> show err) err})
310 (P.errorUnexpected err)
311 (P.errorExpected err)
320 instance -- LCC.Gram_Compta
323 -- , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) m
324 -- , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) m
325 , MC.MonadState (Terms src) m
326 , Gram_Input (P.ParsecT e s m)
330 -- , SourceInj (NonEmpty SourcePos) src
331 -- , SourceInj (Sym.AST_Type src) src
332 -- , SourceInj (Sym.KindK src) src
333 -- , SourceInj (Sym.TypeVT src) src
334 , P.MonadParsec e Text (P.ParsecT e s m)
335 , Gram_Source src (P.ParsecT e s m)
336 -- , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
337 , MC.MonadReader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m)
338 , MC.MonadState (Context_Read src) (P.ParsecT e s m)
339 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
340 , MC.MonadState Chart (P.ParsecT e s m)
341 , MC.MonadState Section (P.ParsecT e s m)
342 , MC.MonadState Style_Amounts (P.ParsecT e s m)
343 , MC.MonadState Year (P.ParsecT e s m)
344 ) => Gram_Compta {-ss-} src (P.ParsecT e s m)
345 instance -- LCC.Gram_Term_Def
347 -- , MC.MonadState (Env src ss) m
348 -- , MC.MonadState (Sym.Imports Sym.NameTe, Sym.Modules src ss) m
349 -- , MC.MonadState (Sym.Imports Sym.NameTy, Sym.ModulesTy src) m
352 -- , SourceInj (Sym.AST_Type src) src
353 -- , SourceInj (Sym.KindK src) src
354 -- , SourceInj (Sym.TypeVT src) src
355 -- , Gram_Source src (P.ParsecT e s m)
356 , P.MonadParsec e Text (P.ParsecT e s m)
357 -- , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
358 ) => LCC.Gram_Term_Def src {-ss-} (P.ParsecT e s m)
360 instance ParsecC e Text => Gram_Input (P.ParsecT e Text m) where
362 P.State {P.stateInput=ib, P.statePos=P.SourcePos _ lb bc:|_} <- P.getParserState
364 P.State {P.statePos=P.SourcePos _ le ce:|_} <- P.getParserState
365 return $ f $ (`Text.take` ib) $ sizeInput 0 ib
366 (P.unPos le - P.unPos lb)
367 (if lb == le then P.unPos ce - P.unPos bc else pred (P.unPos ce))
369 sizeInput :: Int -> Text -> Word -> Word -> Int
370 sizeInput s _i 0 0 = s
372 case Text.uncons i of
373 Nothing -> error "[BUG] sizeInput"
374 Just (_c, i') -> sizeInput (succ s) i' 0 (pred c)
376 case Text.uncons i of
377 Nothing -> error "[BUG] sizeInput"
378 Just ('\n', i') -> sizeInput (succ s) i' (pred l) c
379 Just (_c, i') -> sizeInput (succ s) i' l c
381 -- syntaxError :: P.ParseError Char P.Dec -> Text
387 , P.ShowErrorComponent e
388 ) => P.ParseError t e -> String
390 sourcePosStackPretty (P.errorPos e) ++ ":\n"
391 ++ parseErrorTextPretty e
393 -- | Pretty-print stack of source positions.
394 sourcePosStackPretty :: NonEmpty P.SourcePos -> String
395 sourcePosStackPretty ms = concatMap f rest ++ P.sourcePosPretty pos
398 rest = List.reverse rest'
399 f p = "in file included from " ++ P.sourcePosPretty p ++ ",\n"
404 , P.ShowErrorComponent e
408 ) => P.ParseError t e -> IO d
409 showParseError err = do
410 let (pos:|_) = P.errorPos err
411 q <- write $ sourcePos pos
413 [ D.stringH (sourcePosStackPretty $ P.errorPos err) <> ":"
414 , D.stringH $ parseErrorTextPretty err
418 -- | Transforms list of error messages into their textual representation.
419 messageItemsPretty :: P.ShowErrorComponent a => String -> Set a -> String
420 messageItemsPretty prefix ts
422 | otherwise = prefix ++ f ts ++ "\n"
423 where f = orList . NE.fromList . Set.toAscList . Set.map P.showErrorComponent
425 orList :: NonEmpty String -> String
427 orList (x:|[y]) = x ++ " or " ++ y
428 orList xs = List.intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs
430 -- | Pretty-print textual part of a 'ParseError', that is, everything except
431 -- stack of source positions. The rendered staring always ends with a new line.
432 parseErrorTextPretty ::
435 , P.ShowErrorComponent e )
438 parseErrorTextPretty (P.ParseError _ us ps xs) =
439 if Set.null us && Set.null ps && Set.null xs
440 then "unknown parse error\n"
442 [ messageItemsPretty "unexpected " us
443 , messageItemsPretty "expecting " ps
444 , List.unlines (P.showErrorComponent <$> Set.toAscList xs)