{-# LANGUAGE UndecidableInstances #-}
module Language.Egison.Parser.Pattern.Prim.Error
( Error(..)
, ErrorItem(..)
, Errors
, CustomError(..)
, fromParseErrorBundle
)
where
import Data.Proxy ( Proxy(..) )
import Data.List.NonEmpty ( NonEmpty )
import qualified Data.List.NonEmpty as NonEmpty
( toList )
import qualified Data.Set as Set
( toList
, size
, elemAt
)
import Language.Egison.Parser.Pattern.Prim.Source
( Tokens
, Token
)
import Language.Egison.Parser.Pattern.Prim.Location
( Position
, fromSourcePos
)
import qualified Text.Megaparsec as Parsec
( Stream
, ErrorItem(..)
, ErrorFancy(..)
, SourcePos
, ParseError(..)
, ParseErrorBundle(..)
, attachSourcePos
, errorOffset
, tokensToChunk
)
data ErrorItem s
= Tokens (Tokens s)
| Label String
| EndOfInput
deriving instance Show (Tokens s) => Show (ErrorItem s)
deriving instance Eq (Tokens s) => Eq (ErrorItem s)
data Error s
= UnexpectedToken { position :: Position
, expected :: [ErrorItem s]
, found :: Maybe (ErrorItem s)
}
| ExternalError { position :: Position
, input :: Tokens s
, message :: String
}
deriving instance Show (Tokens s) => Show (Error s)
deriving instance Eq (Tokens s) => Eq (Error s)
type Errors s = NonEmpty (Error s)
data CustomError s = ExtParserError { input :: Tokens s
, message :: String
}
deriving instance Eq (Tokens s) => Eq (CustomError s)
deriving instance Ord (Tokens s) => Ord (CustomError s)
makeErrorItem
:: forall s . Parsec.Stream s => Parsec.ErrorItem (Token s) -> ErrorItem s
makeErrorItem (Parsec.Tokens ts) =
Tokens . Parsec.tokensToChunk (Proxy @s) $ NonEmpty.toList ts
makeErrorItem (Parsec.Label cs) = Label $ NonEmpty.toList cs
makeErrorItem Parsec.EndOfInput = EndOfInput
makeFancyError
:: Parsec.SourcePos -> Parsec.ErrorFancy (CustomError s) -> Error s
makeFancyError pos (Parsec.ErrorCustom err) = extError
where
position = fromSourcePos pos
ExtParserError { input, message } = err
extError = ExternalError { position, input, message }
makeFancyError _ _ = error "unreachable: unused fancy error"
makeError
:: forall s
. Parsec.Stream s
=> (Parsec.ParseError s (CustomError s), Parsec.SourcePos)
-> Error s
makeError (Parsec.FancyError _ es, pos) | Set.size es == 1 =
makeFancyError pos $ Set.elemAt 0 es
makeError (Parsec.TrivialError _ mfound expectedSet, pos) = UnexpectedToken
{ position
, expected
, found
}
where
found = fmap (makeErrorItem @s) mfound
expected = map (makeErrorItem @s) $ Set.toList expectedSet
position = fromSourcePos pos
makeError _ = error "unreachable: unused error"
fromParseErrorBundle
:: Parsec.Stream s => Parsec.ParseErrorBundle s (CustomError s) -> Errors s
fromParseErrorBundle Parsec.ParseErrorBundle { Parsec.bundleErrors = errors, Parsec.bundlePosState = posState }
= fmap makeError errorsWithPos
where
(errorsWithPos, _) =
Parsec.attachSourcePos Parsec.errorOffset errors posState