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.Compta
54 import Hcompta.LCC.Grammar as LCC
55 import Hcompta.LCC.Document
57 import Debug.Trace (trace)
58 import Data.Semigroup ((<>))
59 dbg :: Show a => [Char] -> a -> a
60 dbg msg x = trace (msg <> " = " <> show x) x
62 -- | Convenient converter.
63 sourcePos :: P.SourcePos -> SourcePos
64 sourcePos (P.SourcePos p l c) = SourcePos p (PosFile $ P.unPos l) (PosFile $ P.unPos c)
67 -- | Convenient alias for defining instances involving 'P.ParsecT'.
68 type ParsecC e s = (P.Token s ~ Char, P.Stream s, P.ErrorComponent e)
69 instance ParsecC e s => IsString (P.ParsecT e s m [Char]) where
77 instance ParsecC e s => Sym.Gram_Reader (NonEmpty SourcePos) (P.ParsecT e s m) where
79 s <- (sourcePos <$>) . P.statePos <$> P.getParserState
83 f . (sourcePos <$>) . P.statePos <$> P.getParserState
84 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (NonEmpty SourcePos)) = 'True
85 instance ParsecC e s => MC.MonadReaderN 'MC.Zero (NonEmpty SourcePos) (P.ParsecT e s m) where
86 askN _n = (sourcePos <$>) . P.statePos <$> P.getParserState
88 instance ParsecC e s => Sym.Gram_Reader SourcePos (P.ParsecT e s m) where
90 s <- sourcePos <$> P.getPosition
94 f . sourcePos <$> P.getPosition
95 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader SourcePos) = 'True
96 instance ParsecC e s => MC.MonadReaderN 'MC.Zero SourcePos (P.ParsecT e s m) where
97 askN _n = sourcePos <$> P.getPosition
99 instance ParsecC e s => Sym.Gram_Reader () (P.ParsecT e s m) where
100 askBefore = fmap ($ ())
101 askAfter = fmap ($ ())
102 -- S.Either Exn.IOException CanonFile
103 instance (ParsecC e s, MonadIO m) => Sym.Gram_Reader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
105 sn <- P.sourceName <$> P.getPosition
108 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
109 (return . f . S.Left)
112 sn <- P.sourceName <$> P.getPosition
114 (f . S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
115 (return . f . S.Left)
116 type instance MC.CanDo (P.ParsecT e s m) (MC.EffReader (S.Either Exn.IOException CanonFile)) = 'True
117 instance (ParsecC e s, MonadIO m) => MC.MonadReaderN 'MC.Zero (S.Either Exn.IOException CanonFile) (P.ParsecT e s m) where
119 sn <- P.sourceName <$> P.getPosition
121 (S.Right . CanonFile . PathFile <$> IO.canonicalizePath sn)
129 type instance MC.CanDo (P.ParsecT e s m) (MC.EffState st) = 'False
130 instance (Monad m, MC.MonadState st m) => Sym.Gram_State st m where
159 instance (ParsecC e s, Show err) => Sym.Gram_Error err (P.ParsecT e s m) where
163 Left err -> fail $ show err
165 instance ParsecC e s => Sym.Gram_Rule (P.ParsecT e s m) where
166 rule = P.label . Text.unpack
167 instance ParsecC e s => Sym.Gram_Terminal (P.ParsecT e s m) where
172 unicat cat = P.satisfy $ (`elem` cats) . Char.generalCategory
173 where cats = unicode_categories cat
174 range (l, h) = P.satisfy $ \c -> l <= c && c <= h
175 Terminal f `but` Terminal p = Terminal $ P.notFollowedBy (P.try p) *> f
176 instance ParsecC e s => Sym.Gram_Alt (P.ParsecT e s m) where
180 instance ParsecC e s => Sym.Gram_Try (P.ParsecT e s m) where
182 instance ParsecC e s => Sym.Gram_RegR (P.ParsecT e s m) where
183 Terminal f .*> Reg x = Reg $ f <*> x
184 instance ParsecC e s => Sym.Gram_RegL (P.ParsecT e s m) where
185 Reg f <*. Terminal x = Reg $ f <*> x
186 instance ParsecC e s => Sym.Gram_App (P.ParsecT e s m) where
188 instance ParsecC e s => Sym.Gram_AltApp (P.ParsecT e s m) where
190 optional = P.optional
193 skipMany = P.skipMany
194 instance ParsecC e s => Sym.Gram_CF (P.ParsecT e s m) where
195 CF f <& Reg p = CF $ P.lookAhead f <*> p
196 Reg f &> CF p = CF $ P.lookAhead f <*> p
197 minus (CF f) (Reg p) = CF $ P.notFollowedBy (P.try p) *> f
198 instance ParsecC e s => Sym.Gram_Comment (P.ParsecT e s m)
199 instance ParsecC e s => Sym.Gram_Op (P.ParsecT e s m)
200 instance ParsecC e s => Sym.Gram_Name (P.ParsecT e s m)
201 instance -- Sym.Gram_Type
203 , Gram_Source src (P.ParsecT e s m)
204 ) => Sym.Gram_Type src (P.ParsecT e s m)
205 instance -- Sym.Gram_Term_Type
207 , Gram_Source src (P.ParsecT e s m)
208 ) => Sym.Gram_Term_Type src (P.ParsecT e s m)
209 instance -- Sym.Gram_Term
212 , MC.MonadState (Sym.Imports, Sym.Modules src ss) (P.ParsecT e s m)
213 , Sym.Gram_Source src (P.ParsecT e s m)
214 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
215 ) => Sym.Gram_Term src ss (P.ParsecT e s m)
220 instance ParsecC e s => LCC.Gram_Count (P.ParsecT e s m)
221 instance ParsecC e s => LCC.Gram_Char (P.ParsecT e s m)
222 instance ParsecC e s => LCC.Gram_Comment (P.ParsecT e s m)
223 instance ParsecC e s => LCC.Gram_Tag (P.ParsecT e s m)
224 instance ParsecC e s => LCC.Gram_Account (P.ParsecT e s m)
225 instance ParsecC e s => LCC.Gram_Amount (P.ParsecT e s m)
226 instance ParsecC e s => LCC.Gram_File (P.ParsecT e s m)
227 instance -- LCC.Gram_Date
229 , MC.MonadState Year (P.ParsecT e s m)
230 ) => Gram_Date (P.ParsecT e s m)
231 instance -- LCC.Gram_Posting
233 , MC.MonadState Chart (P.ParsecT e s m)
234 , MC.MonadState Style_Amounts (P.ParsecT e s m)
235 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
236 ) => Gram_Posting (P.ParsecT e s m)
237 instance -- LCC.Gram_Transaction
239 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
240 , MC.MonadState Chart (P.ParsecT e s m)
241 , MC.MonadState Section (P.ParsecT e s m)
242 , MC.MonadState Style_Amounts (P.ParsecT e s m)
243 , MC.MonadState Year (P.ParsecT e s m)
244 ) => Gram_Transaction (P.ParsecT e s m)
245 instance -- LCC.Gram_Chart
247 , MC.MonadState Chart (P.ParsecT e s m)
248 , MC.MonadState Section (P.ParsecT e s m)
249 ) => Gram_Chart (P.ParsecT e s m)
250 instance -- LCC.Gram_Path
253 ) => Gram_Path (P.ParsecT e s m) where
255 pf@(PathFile fp) <- g
256 liftIO $ (pf,) <$> Exn.catch
257 (Right . CanonFile . PathFile <$> IO.canonicalizePath fp)
259 instance -- LCC.Gram_IO
262 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
263 , MC.MonadState Chart (P.ParsecT e s m)
264 , MC.MonadState Style_Amounts (P.ParsecT e s m)
265 , MC.MonadState Year (P.ParsecT e s m)
266 , Sym.Gram_Source src (P.ParsecT e s m)
267 , P.MonadParsec e Text (P.ParsecT e s m)
268 ) => Gram_IO src (P.ParsecT e s m) where
273 S.Left (e::Error_Compta src) ->
274 return $ \(src::src) ->
276 S.Right (PathFile fp) ->
278 ((\inp _src -> S.Right $ (fp,) $ Enc.decodeUtf8 inp) <$> BS.readFile fp)
279 (\exn -> return $ \src -> S.Left $ At src $ Error_Compta_Read (PathFile fp) exn)
281 S.Left e -> return $ S.Left [e]
282 S.Right (fp_new, s_new) -> do
283 P.pushPosition $ P.initialPos fp_new
284 s_old <- P.getInput; P.setInput s_new
288 P.observing g >>= \case
293 P.updateParserState (\st -> st{P.statePos=P.errorPos $ trace ("ERROR: " <> show err) err})
295 (P.errorUnexpected err)
296 (P.errorExpected err)
305 instance -- LCC.Gram_Compta
308 , MC.MonadState (Sym.Modules src ss) m
309 , MC.MonadState (Sym.Name2Type src) m
310 , MC.MonadState (Sym.Imports, Sym.Modules src ss) m
311 , MC.MonadState Terms m
312 , Gram_Input (P.ParsecT e s m)
316 , SourceInj (Sym.AST_Type src) src
317 , SourceInj (Sym.KindK src) src
318 , SourceInj (Sym.TypeVT src) src
319 , P.MonadParsec e Text (P.ParsecT e s m)
320 , Sym.Gram_Source src (P.ParsecT e s m)
321 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
322 , MC.MonadReader (S.Either Exn.IOException CanonFile) (P.ParsecT e s m)
323 , MC.MonadState (Context_Read src j) (P.ParsecT e s m)
324 , MC.MonadState (Journal j) (P.ParsecT e s m)
325 , MC.MonadState (Journals j) (P.ParsecT e s m)
326 , MC.MonadState (S.Maybe Unit) (P.ParsecT e s m)
327 , MC.MonadState Chart (P.ParsecT e s m)
328 , MC.MonadState Section (P.ParsecT e s m)
329 , MC.MonadState Style_Amounts (P.ParsecT e s m)
330 , MC.MonadState Year (P.ParsecT e s m)
331 ) => Gram_Compta ss src j (P.ParsecT e s m)
332 instance -- LCC.Gram_Term_Def
334 -- , MC.MonadState (Env src ss) m
335 , MC.MonadState (Sym.Name2Type src) m
336 , MC.MonadState (Sym.Imports, Sym.Modules src ss) m
339 , SourceInj (Sym.AST_Type src) src
340 , SourceInj (Sym.KindK src) src
341 , SourceInj (Sym.TypeVT src) src
342 , Gram_Source src (P.ParsecT e s m)
343 , P.MonadParsec e Text (P.ParsecT e s m)
344 , Sym.Gram_Term_Atoms src ss (P.ParsecT e s m)
345 ) => LCC.Gram_Term_Def src ss (P.ParsecT e s m)
347 instance ParsecC e Text => Gram_Input (P.ParsecT e Text m) where
349 P.State {P.stateInput=ib, P.statePos=P.SourcePos _ lb bc:|_} <- P.getParserState
351 P.State {P.statePos=P.SourcePos _ le ce:|_} <- P.getParserState
352 return $ f $ (`Text.take` ib) $ sizeInput 0 ib
353 (P.unPos le - P.unPos lb)
354 (if lb == le then P.unPos ce - P.unPos bc else pred (P.unPos ce))
356 sizeInput :: Int -> Text -> Word -> Word -> Int
357 sizeInput s _i 0 0 = s
359 case Text.uncons i of
360 Nothing -> error "[BUG] sizeInput"
361 Just (_c, i') -> sizeInput (succ s) i' 0 (pred c)
363 case Text.uncons i of
364 Nothing -> error "[BUG] sizeInput"
365 Just ('\n', i') -> sizeInput (succ s) i' (pred l) c
366 Just (_c, i') -> sizeInput (succ s) i' l c
368 -- syntaxError :: P.ParseError Char P.Dec -> Text
374 , P.ShowErrorComponent e )
378 sourcePosStackPretty (P.errorPos e) ++ ":\n"
379 ++ parseErrorTextPretty e
381 -- | Pretty-print stack of source positions.
382 sourcePosStackPretty :: NonEmpty P.SourcePos -> String
383 sourcePosStackPretty ms = concatMap f rest ++ P.sourcePosPretty pos
386 rest = List.reverse rest'
387 f p = "in file included from " ++ P.sourcePosPretty p ++ ",\n"
392 , P.ShowErrorComponent e
396 ) => P.ParseError t e -> IO d
397 showParseError err = do
398 let (pos:|_) = P.errorPos err
399 q <- d_sourcepos $ sourcePos pos
401 [ D.stringH (sourcePosStackPretty $ P.errorPos err) <> ":"
402 , D.stringH $ parseErrorTextPretty err
406 -- | Transforms list of error messages into their textual representation.
407 messageItemsPretty :: P.ShowErrorComponent a => String -> Set a -> String
408 messageItemsPretty prefix ts
410 | otherwise = prefix ++ f ts ++ "\n"
411 where f = orList . NE.fromList . Set.toAscList . Set.map P.showErrorComponent
413 orList :: NonEmpty String -> String
415 orList (x:|[y]) = x ++ " or " ++ y
416 orList xs = List.intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs
418 -- | Pretty-print textual part of a 'ParseError', that is, everything except
419 -- stack of source positions. The rendered staring always ends with a new line.
420 parseErrorTextPretty ::
423 , P.ShowErrorComponent e )
426 parseErrorTextPretty (P.ParseError _ us ps xs) =
427 if Set.null us && Set.null ps && Set.null xs
428 then "unknown parse error\n"
430 [ messageItemsPretty "unexpected " us
431 , messageItemsPretty "expecting " ps
432 , List.unlines (P.showErrorComponent <$> Set.toAscList xs)