-- | -- -- Module: Language.Egison.Parser.Pattern.Prim -- Description: Parser monad and primitive parsers -- Stability: experimental -- -- A parser monad and primitive parsers. -- -- Note that all dependencies on parser library are in this module. module Language.Egison.Parser.Pattern.Prim ( -- * Parser Configuration ParseFixity(..) , ParseMode(..) , ExtParser -- * Parser Monad , Parse , runParse -- * Primitive Parsers , extParser , space , lexeme , name , varName , valueExpr -- * Errors , Errors , Error(..) , ErrorItem(..) -- * Locations , Position(..) , Location(..) , Locate(..) -- * Source Stream Class , Source , Token , Tokens -- * Re-exports , module X ) where -- re-exports import Text.Megaparsec as X ( MonadParsec(..) , () , single , chunk ) -- main import Control.Monad ( void ) import Control.Monad.Reader ( ask ) import Control.Applicative ( Alternative((<|>)) , empty ) import qualified Text.Megaparsec as Parsec ( takeWhile1P , takeWhileP , manyTill , chunk , customFailure , single , anySingle ) import qualified Text.Megaparsec.Char.Lexer as L ( lexeme , space ) import qualified Language.Egison.Parser.Pattern.Token as Token ( isSpace , parenLeft , parenRight , newline ) import Language.Egison.Parser.Pattern.Prim.Location ( Position(..) , Location(..) , Locate(..) ) import Language.Egison.Parser.Pattern.Prim.Error ( Error(..) , ErrorItem(..) , Errors , CustomError(..) ) import Language.Egison.Parser.Pattern.Prim.Source ( Source(..) , Token , Tokens ) import Language.Egison.Parser.Pattern.Prim.ParseMode ( ParseMode(..) , ParseFixity(..) , ExtParser ) import Language.Egison.Parser.Pattern.Prim.Parse ( Parse , runParse ) skipBlockComment :: Source s => Tokens s -> Tokens s -> Parse n v e s () skipBlockComment start end = cs *> void (Parsec.manyTill Parsec.anySingle ce) where cs = Parsec.chunk start ce = Parsec.chunk end skipLineComment :: Source s => Tokens s -> Parse n v e s () skipLineComment prefix = Parsec.chunk prefix *> void (Parsec.takeWhileP (Just "chars") (/= Token.newline)) -- | Skip one or more spaces. space :: Source s => Parse n v e s () space = do ParseMode { blockComment, lineComment } <- ask let block = emptyOr (uncurry skipBlockComment) blockComment line = emptyOr skipLineComment lineComment L.space space1 line block where space1 = void $ Parsec.takeWhile1P (Just "whitespace") Token.isSpace emptyOr = maybe empty -- | Parse a lexical chunk. takeChunk :: forall n v e s . Source s => Parse n v e s (Tokens s) takeChunk = withParens <|> withoutParens where withParens = do left <- Parsec.single Token.parenLeft ck <- Parsec.takeWhileP (Just "lexical chunk (in parens)") endOfChunkInParens right <- Parsec.single Token.parenRight pure $ consTokens @s left (snocTokens @s ck right) withoutParens = Parsec.takeWhileP (Just "lexical chunk") endOfChunk endOfChunkInParens x = x /= Token.parenRight endOfChunk x = not (Token.isSpace x) && x /= Token.parenRight -- | Apply an external parser. extParser :: Source s => ExtParser s a -> Parse n v e s a extParser p = try $ do lchunk <- takeChunk case p lchunk of Left err -> Parsec.customFailure (ExtParserError lchunk err) Right x -> pure x -- | Make a lexical token. -- @lexeme p@ first applies parser @p@ then 'space' parser. lexeme :: Source s => Parse n v e s a -> Parse n v e s a lexeme = L.lexeme space -- | Parser for @n@ in @Parse n v e s@ monad. name :: Source s => Parse n v e s n name = do ParseMode { nameParser } <- ask extParser nameParser -- | Parser for @v@ in @Parse n v e s@ monad. varName :: Source s => Parse n v e s v varName = do ParseMode { varNameParser } <- ask extParser varNameParser -- | Parser for @e@ in @Parse n v e s@ monad. valueExpr :: Source s => Parse n v e s e valueExpr = do ParseMode { valueExprParser } <- ask extParser valueExprParser