{-| Module : Toml.Syntax.ParserUtils Description : Primitive operations used by the happy-generated parser Copyright : (c) Eric Mertens, 2023 License : ISC Maintainer : emertens@gmail.com This module contains all the primitives used by the Parser module. By extracting it from the @.y@ file we minimize the amount of code that has warnings disabled and get better editor support. -} module Toml.Syntax.ParserUtils ( Parser, runParser, pureP, thenP, asString, asMlString, asBareKey, asInteger, asBool, asFloat, asOffsetDateTime, asLocalDate, asLocalTime, asLocalDateTime, locVal, lexerP, errorP, -- * Lexer-state management push, pop, ) where import Data.Text (Text) import Data.Time import Data.List.NonEmpty (NonEmpty((:|))) import Data.List.NonEmpty qualified as NonEmpty import Toml.Pretty (prettyToken) import Toml.Syntax.Lexer (scanToken, Context(..)) import Toml.Syntax.Position (Located(..), Position) import Toml.Syntax.Token (Token(..)) -- continuation passing implementation of a state monad with errors newtype Parser r a = P { getP :: NonEmpty Context -> Located Text -> (NonEmpty Context -> Located Text -> a -> Either (Located String) r) -> Either (Located String) r } -- | Run the top-level parser runParser :: Parser r r -> Context -> Located Text -> Either (Located String) r runParser (P k) ctx str = k (ctx :| []) str \_ _ r -> Right r -- | Bind implementation used in the happy-generated parser thenP :: Parser r a -> (a -> Parser r b) -> Parser r b thenP (P m) f = P \ctx str k -> m ctx str \ctx' str' x -> getP (f x) ctx' str' k {-# Inline thenP #-} -- | Return implementation used in the happy-generated parser pureP :: a -> Parser r a pureP x = P \ctx str k -> k ctx str x {-# Inline pureP #-} -- | Add a new context to the lexer context stack push :: Context -> Parser r () push x = P \st str k -> k (NonEmpty.cons x st) str () {-# Inline push #-} -- | Pop the top context off the lexer context stack. It is a program -- error to pop without first pushing. pop :: Parser r () pop = P \ctx str k -> case snd (NonEmpty.uncons ctx) of Nothing -> error "toml-parser: PANIC! malformed production in parser" Just ctx' -> k ctx' str () {-# Inline pop #-} -- | Operation the parser generator uses when it reaches an unexpected token. errorP :: Located Token -> Parser r a errorP e = P \_ _ _ -> Left (fmap (\t -> "parse error: unexpected " ++ prettyToken t) e) -- | Operation the parser generator uses to request the next token. lexerP :: (Located Token -> Parser r a) -> Parser r a lexerP f = P \st str k -> case scanToken (NonEmpty.head st) str of Left le -> Left (("lexical error: " ++) <$> le) Right (t, str') -> getP (f t) st str' k {-# Inline lexerP #-} asString :: Token -> Maybe Text asString = \case TokString i -> Just i _ -> Nothing asBareKey :: Token -> Maybe Text asBareKey = \case TokBareKey i -> Just i _ -> Nothing asMlString :: Token -> Maybe Text asMlString = \case TokMlString i -> Just i _ -> Nothing asInteger :: Token -> Maybe Integer asInteger = \case TokInteger i -> Just i _ -> Nothing asBool :: Token -> Maybe Bool asBool = \case TokTrue -> Just True TokFalse -> Just False _ -> Nothing asFloat :: Token -> Maybe Double asFloat = \case TokFloat x -> Just x _ -> Nothing asOffsetDateTime :: Token -> Maybe ZonedTime asOffsetDateTime = \case TokOffsetDateTime x -> Just x _ -> Nothing asLocalDateTime :: Token -> Maybe LocalTime asLocalDateTime = \case TokLocalDateTime x -> Just x _ -> Nothing asLocalDate :: Token -> Maybe Day asLocalDate = \case TokLocalDate x -> Just x _ -> Nothing asLocalTime :: Token -> Maybe TimeOfDay asLocalTime = \case TokLocalTime x -> Just x _ -> Nothing locVal :: (Position -> a -> b) -> Located a -> b locVal f (Located l x) = f l x