1 {-# LANGUAGE UndecidableInstances #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
3 -- | Symantic and LCC grammar instances for Megaparsec
4 module Hcompta.LCC.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.Monoid (Monoid(..))
21 import Data.Ord (Ord(..))
23 import Data.String (IsString(..), String)
24 import Data.Text (Text)
25 import Data.Typeable ()
26 import Data.Word (Word)
27 import Prelude (pred, succ, (-), error)
29 import Text.Show (Show(..))
30 import qualified Control.Applicative as Alt
31 import qualified Control.Exception.Safe as Exn
32 import qualified Control.Monad.Classes as MC
33 import qualified Data.ByteString as BS
34 import qualified Data.Char as Char
35 import qualified Data.List as List
36 import qualified Data.List.NonEmpty as NE
37 import qualified Data.Set as Set
38 import qualified Data.Strict as S
39 import qualified Data.Text as Text
40 import qualified Data.Text.Encoding as Enc
41 import qualified System.Directory as IO
42 import qualified Text.Megaparsec as P
43 import qualified Text.Megaparsec.Prim as P
45 import Language.Symantic.Grammar as Sym
46 import qualified Language.Symantic as Sym
47 import qualified Language.Symantic.Document as D
49 import Hcompta.LCC.Amount
50 import Hcompta.LCC.Chart
51 import Hcompta.LCC.Posting
52 import Hcompta.LCC.Journal
53 import Hcompta.LCC.Grammar as LCC
54 import Hcompta.LCC.Document
56 import Debug.Trace (trace)
57 import Data.Semigroup ((<>))
58 dbg :: Show a => [Char] -> a -> a
59 dbg msg x = trace (msg <> " = " <> show x) x
61 -- | Convenient converter.
62 sourcePos :: P.SourcePos -> SourcePos
63 sourcePos (P.SourcePos p l c) = SourcePos p (PosFile $ P.unPos l) (PosFile $ P.unPos c)
66 -- | Convenient alias for defining instances involving 'P.ParsecT'.
67 type ParsecC e s = (P.Token s ~ Char, P.Stream s, P.ErrorComponent e)
68 instance ParsecC e s => IsString (P.ParsecT e s m [Char]) where
76 instance ParsecC e s => Sym.Gram_Reader (NonEmpty SourcePos) (P.ParsecT e s m) where
78 s <- (sourcePos <$>) . P.statePos <$> P.getParserState
82 f . (sourcePos <$>) . P.statePos <$> P.getParserState
83 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (NonEmpty SourcePos)) = 'True
84 instance ParsecC e s => MC.MonadReaderN 'MC.Zero (NonEmpty SourcePos) (P.ParsecT e s m) where
85 askN _n = (sourcePos <$>) . P.statePos <$> P.getParserState
87 instance ParsecC e s => Sym.Gram_Reader SourcePos (P.ParsecT e s m) where
89 s <- sourcePos <$> P.getPosition
93 f . sourcePos <$> P.getPosition
94 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader SourcePos) = 'True
95 instance ParsecC e s => MC.MonadReaderN 'MC.Zero SourcePos (P.ParsecT e s m) where
96 askN _n = sourcePos <$> P.getPosition
98 instance ParsecC e s => Sym.Gram_Reader () (P.ParsecT e s m) where
99 g_ask_before = fmap ($ ())
100 g_ask_after = fmap ($ ())
101 -- S.Either Exn.IOException CanonFile
102 instance (ParsecC e s, MonadIO m) => Sym.Gram_Reader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
104 sn <- P.sourceName <$> P.getPosition
107 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
108 (return . f . S.Left)
111 sn <- P.sourceName <$> P.getPosition
113 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
114 (return . f . S.Left)
115 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (S.Either Exn.IOException CanonFile)) = 'True
116 instance (ParsecC e s, MonadIO m) => MC.MonadReaderN 'MC.Zero (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
118 sn <- P.sourceName <$> P.getPosition
120 (S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
128 type instance MC.CanDo (P.ParsecT e s m) (MC.EffState st) = 'False
129 instance (Monad m, MC.MonadState st m) => Sym.Gram_State st m where
130 g_state_before g = do
158 instance (ParsecC e s, Show err) => Sym.Gram_Error err (P.ParsecT e s m) where
162 Left err -> fail $ show err
164 instance ParsecC e s => Sym.Gram_Rule (P.ParsecT e s m) where
165 rule = P.label . Text.unpack
166 instance ParsecC e s => Sym.Gram_Terminal (P.ParsecT e s m) where
171 unicat cat = P.satisfy $ (`elem` cats) . Char.generalCategory
172 where cats = unicode_categories cat
173 range (l, h) = P.satisfy $ \c -> l <= c && c <= h
174 Terminal f `but` Terminal p = Terminal $ P.notFollowedBy (P.try p) *> f
175 instance ParsecC e s => Sym.Gram_Alt (P.ParsecT e s m) where
179 instance ParsecC e s => Sym.Gram_Try (P.ParsecT e s m) where
181 instance ParsecC e s => Sym.Gram_RegR (P.ParsecT e s m) where
182 Terminal f .*> Reg x = Reg $ f <*> x
183 instance ParsecC e s => Sym.Gram_RegL (P.ParsecT e s m) where
184 Reg f <*. Terminal x = Reg $ f <*> x
185 instance ParsecC e s => Sym.Gram_App (P.ParsecT e s m) where
187 instance ParsecC e s => Sym.Gram_AltApp (P.ParsecT e s m) where
189 optional = P.optional
192 skipMany = P.skipMany
193 instance ParsecC e s => Sym.Gram_CF (P.ParsecT e s m) where
194 CF f <& Reg p = CF $ P.lookAhead f <*> p
195 Reg f &> CF p = CF $ P.lookAhead f <*> p
196 minus (CF f) (Reg p) = CF $ P.notFollowedBy (P.try p) *> f
197 instance ParsecC e s => Sym.Gram_Comment (P.ParsecT e s m)
198 instance ParsecC e s => Sym.Gram_Op (P.ParsecT e s m)
199 instance ParsecC e s => Sym.Gram_Name (P.ParsecT e s m)
200 instance -- Sym.Gram_Type
202 , Gram_Source src (P.ParsecT e s m)
203 ) => Sym.Gram_Type src (P.ParsecT e s m)
204 instance -- Sym.Gram_Term_Type
206 , Gram_Source src (P.ParsecT e s m)
207 ) => Sym.Gram_Term_Type src (P.ParsecT e s m)
208 instance -- Sym.Gram_Term
211 , MC.MonadState (Sym.Imports, Sym.Modules src ss) (P.ParsecT e s m)
212 , Sym.Gram_Source src (P.ParsecT e s m)
213 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
214 ) => Sym.Gram_Term src ss (P.ParsecT e s m)
219 instance ParsecC e s => LCC.Gram_Count (P.ParsecT e s m)
220 instance ParsecC e s => LCC.Gram_Char (P.ParsecT e s m)
221 instance ParsecC e s => LCC.Gram_Comment (P.ParsecT e s m)
222 instance ParsecC e s => LCC.Gram_Tag (P.ParsecT e s m)
223 instance ParsecC e s => LCC.Gram_Account (P.ParsecT e s m)
224 instance ParsecC e s => LCC.Gram_Amount (P.ParsecT e s m)
225 instance ParsecC e s => LCC.Gram_File (P.ParsecT e s m)
226 instance -- LCC.Gram_Date
228 , MC.MonadState Year (P.ParsecT e s m)
229 ) => Gram_Date (P.ParsecT e s m)
230 instance -- LCC.Gram_Posting
232 , MC.MonadState Chart (P.ParsecT e s m)
233 , MC.MonadState Style_Amounts (P.ParsecT e s m)
234 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
235 ) => Gram_Posting (P.ParsecT e s m)
236 instance -- LCC.Gram_Transaction
238 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
239 , MC.MonadState Chart (P.ParsecT e s m)
240 , MC.MonadState Section (P.ParsecT e s m)
241 , MC.MonadState Style_Amounts (P.ParsecT e s m)
242 , MC.MonadState Year (P.ParsecT e s m)
243 ) => Gram_Transaction (P.ParsecT e s m)
244 instance -- LCC.Gram_Chart
246 , MC.MonadState Chart (P.ParsecT e s m)
247 , MC.MonadState Section (P.ParsecT e s m)
248 ) => Gram_Chart (P.ParsecT e s m)
249 instance -- LCC.Gram_Path
252 ) => Gram_Path (P.ParsecT e s m) where
254 pf@(PathFile fp) <- g
255 liftIO $ (pf,) <$> Exn.catch
256 (Right . CanonFile . PathFile <$> IO.canonicalizePath fp)
258 instance -- LCC.Gram_IO
261 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
262 , MC.MonadState Chart (P.ParsecT e s m)
263 , MC.MonadState Style_Amounts (P.ParsecT e s m)
264 , MC.MonadState Year (P.ParsecT e s m)
265 , Sym.Gram_Source src (P.ParsecT e s m)
266 , P.MonadParsec e Text (P.ParsecT e s m)
267 ) => Gram_IO src (P.ParsecT e s m) where
272 S.Left (e::Error_Compta src) ->
273 return $ \(src::src) ->
275 S.Right (PathFile fp) ->
277 ((\inp _src -> S.Right $ (fp,) $ Enc.decodeUtf8 inp) <$> BS.readFile fp)
278 (\exn -> return $ \src -> S.Left $ At src $ Error_Compta_Read (PathFile fp) exn)
280 S.Left e -> return $ S.Left [e]
281 S.Right (fp_new, s_new) -> do
282 P.pushPosition $ P.initialPos fp_new
283 s_old <- P.getInput; P.setInput s_new
287 P.observing g >>= \case
292 P.updateParserState (\st -> st{P.statePos=P.errorPos $ trace ("ERROR: " <> show err) err})
294 (P.errorUnexpected err)
295 (P.errorExpected err)
304 instance -- LCC.Gram_Compta
307 , MC.MonadState (Sym.Modules src ss) m
308 , MC.MonadState (Sym.Name2Type src) m
309 , MC.MonadState (Sym.Imports, Sym.Modules src ss) m
310 , MC.MonadState Terms m
311 , Gram_Input (P.ParsecT e s m)
315 , Inj_Source (Sym.AST_Type src) src
316 , Inj_Source (Sym.KindK src) src
317 , Inj_Source (Sym.TypeVT src) src
318 , P.MonadParsec e Text (P.ParsecT e s m)
319 , Sym.Gram_Source src (P.ParsecT e s m)
320 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
321 , MC.MonadReader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m)
322 , MC.MonadState (Context_Read src j) (P.ParsecT e s m)
323 , MC.MonadState (Journal j) (P.ParsecT e s m)
324 , MC.MonadState (Journals j) (P.ParsecT e s m)
325 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
326 , MC.MonadState Chart (P.ParsecT e s m)
327 , MC.MonadState Section (P.ParsecT e s m)
328 , MC.MonadState Style_Amounts (P.ParsecT e s m)
329 , MC.MonadState Year (P.ParsecT e s m)
330 ) => Gram_Compta ss src j (P.ParsecT e s m)
331 instance -- LCC.Gram_Term_Def
333 -- , MC.MonadState (Env src ss) m
334 , MC.MonadState (Sym.Name2Type src) m
335 , MC.MonadState (Sym.Imports, Sym.Modules src ss) m
338 , Inj_Source (Sym.AST_Type src) src
339 , Inj_Source (Sym.KindK src) src
340 , Inj_Source (Sym.TypeVT src) src
341 , Gram_Source src (P.ParsecT e s m)
342 , P.MonadParsec e Text (P.ParsecT e s m)
343 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
344 ) => LCC.Gram_Term_Def src ss (P.ParsecT e s m)
346 instance ParsecC e Text => Gram_Input (P.ParsecT e Text m) where
348 P.State {P.stateInput=ib, P.statePos=P.SourcePos _ lb bc:|_} <- P.getParserState
350 P.State {P.statePos=P.SourcePos _ le ce:|_} <- P.getParserState
351 return $ f $ (`Text.take` ib) $ sizeInput 0 ib
352 (P.unPos le - P.unPos lb)
353 (if lb == le then P.unPos ce - P.unPos bc else pred (P.unPos ce))
355 sizeInput :: Int -> Text -> Word -> Word -> Int
356 sizeInput s _i 0 0 = s
358 case Text.uncons i of
359 Nothing -> error "[BUG] sizeInput"
360 Just (_c, i') -> sizeInput (succ s) i' 0 (pred c)
362 case Text.uncons i of
363 Nothing -> error "[BUG] sizeInput"
364 Just ('\n', i') -> sizeInput (succ s) i' (pred l) c
365 Just (_c, i') -> sizeInput (succ s) i' l c
367 -- syntaxError :: P.ParseError Char P.Dec -> Text
373 , P.ShowErrorComponent e )
377 sourcePosStackPretty (P.errorPos e) ++ ":\n"
378 ++ parseErrorTextPretty e
380 -- | Pretty-print stack of source positions.
381 sourcePosStackPretty :: NonEmpty P.SourcePos -> String
382 sourcePosStackPretty ms = concatMap f rest ++ P.sourcePosPretty pos
385 rest = List.reverse rest'
386 f p = "in file included from " ++ P.sourcePosPretty p ++ ",\n"
391 , P.ShowErrorComponent e
395 ) => P.ParseError t e -> IO d
396 showParseError err = do
397 let (pos:|_) = P.errorPos err
398 q <- d_sourcepos $ sourcePos pos
400 [ D.stringH (sourcePosStackPretty $ P.errorPos err) <> ":"
401 , D.stringH $ parseErrorTextPretty err
405 -- | Transforms list of error messages into their textual representation.
406 messageItemsPretty :: P.ShowErrorComponent a => String -> Set a -> String
407 messageItemsPretty prefix ts
409 | otherwise = prefix ++ f ts ++ "\n"
410 where f = orList . NE.fromList . Set.toAscList . Set.map P.showErrorComponent
412 orList :: NonEmpty String -> String
414 orList (x:|[y]) = x ++ " or " ++ y
415 orList xs = List.intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs
417 -- | Pretty-print textual part of a 'ParseError', that is, everything except
418 -- stack of source positions. The rendered staring always ends with a new line.
419 parseErrorTextPretty ::
422 , P.ShowErrorComponent e )
425 parseErrorTextPretty (P.ParseError _ us ps xs) =
426 if Set.null us && Set.null ps && Set.null xs
427 then "unknown parse error\n"
429 [ messageItemsPretty "unexpected " us
430 , messageItemsPretty "expecting " ps
431 , List.unlines (P.showErrorComponent <$> Set.toAscList xs)