packages feed

hls-eval-plugin-0.1.0.0: src/Ide/Plugin/Eval/Parse/Parser.hs

{-# LANGUAGE DeriveFunctor #-}

-- |Simple List Parser, used for both line and test parsing.
module Ide.Plugin.Eval.Parse.Parser (
    Parser,
    runParser,
    satisfy,
    alphaNumChar,
    letterChar,
    space,
    string,
    char,
    tillEnd,
) where

import Control.Applicative (Alternative)
import Control.Monad (MonadPlus, (>=>))
import Control.Monad.Combinators (
    empty,
    (<|>),
 )
import Data.Char (
    isAlphaNum,
    isLetter,
 )
import Data.List (isPrefixOf)

type CharParser = Parser Char

{- $setup
 >>> import Control.Monad.Combinators
-}

{- |
>>> runParser  (string "aa" <|> string "bb") "bb"
Right "bb"

>>> runParser  (some (string "aa")) "aaaaaa"
Right ["aa","aa","aa"]
-}
string :: String -> CharParser String
string t = Parser $
    \s -> if t `isPrefixOf` s then Just (t, drop (length t) s) else Nothing

letterChar :: Parser Char Char
letterChar = satisfy isLetter

alphaNumChar :: Parser Char Char
alphaNumChar = satisfy isAlphaNum

space :: Parser Char Char
space = char ' '

{- |
 >>> runParser (some $ char 'a') "aa"
 Right "aa"
-}
char :: Char -> CharParser Char
char ch = satisfy (== ch)

{- |
>>> runParser tillEnd "abc\ndef"
Right "abc\ndef"
-}
tillEnd :: Parser t [t]
tillEnd = Parser $ \s -> Just (s, [])

satisfy :: (t -> Bool) -> Parser t t
satisfy f = Parser sel
  where
    sel [] = Nothing
    sel (t : ts)
        | f t = Just (t, ts)
        | otherwise = Nothing

newtype Parser t a = Parser {parse :: [t] -> Maybe (a, [t])} deriving (Functor)

instance Applicative (Parser t) where
    pure a = Parser (\s -> Just (a, s))
    (Parser p1) <*> (Parser p2) =
        Parser (p1 >=> (\(f, s1) -> p2 s1 >>= \(a, s2) -> return (f a, s2)))

instance Alternative (Parser t) where
    empty = Parser (const Nothing)
    p <|> q = Parser $ \s -> parse p s <|> parse q s

instance Monad (Parser t) where
    return = pure
    (>>=) f g = Parser (parse f >=> (\(a, s') -> parse (g a) s'))

instance MonadPlus (Parser t)

runParser :: Show t => Parser t a -> [t] -> Either String a
runParser m s = case parse m s of
    Just (res, []) -> Right res
    Just (_, ts) ->
        Left $ "Parser did not consume entire stream, left: " ++ show ts
    Nothing -> Left "No match"