typed-peg-0.1.0.0: src/PEG/Semantics/Simple.hs
{-# LANGUAGE FlexibleInstances, DeriveFunctor, TypeFamilies #-}
-- | A simple, continuation-based semantics for PEG expressions.
--
-- 'PExp' in this module is an alternative representation of PEG expressions
-- as explicit functions over an input type @d@, making the semantics of each
-- combinator concrete and inspectable. Useful for testing and for
-- understanding the library\'s evaluation model.
module PEG.Semantics.Simple where
import Control.Applicative
import Control.Monad (MonadPlus(..), guard)
import Data.Char (isDigit, ord, isSpace)
import Prelude hiding (not)
newtype PExp d a
= PExp {
runPExp :: d -> Result d a
} deriving Functor
data Result d a
= Pure a -- didn't consume anything, can backtrack
| Commit d a -- consumed input
| Fail String Bool -- failed, flagged if consumed
deriving Functor
instance Applicative (PExp d) where
pure a = PExp $ \ _ -> Pure a
PExp mf <*> PExp ma
= PExp $ \ d ->
case mf d of
Pure f -> fmap f (ma d)
Fail s c -> Fail s c
Commit d' f ->
case ma d' of
Pure a -> Commit d' (f a)
Fail s _ -> Fail s True
Commit d'' a -> Commit d'' (f a)
instance Alternative (PExp d) where
PExp ma <|> PExp mb
= PExp $ \ d ->
case ma d of
Fail _ False -> mb d
x -> x
empty = PExp $ \ _ -> Fail "empty" False
instance Monad (PExp d) where
PExp m >>= k = PExp $ \d ->
case m d of
Pure a -> runPExp (k a) d
Commit d' a ->
case runPExp (k a) d' of
Pure b -> Commit d' b
Fail s _ -> Fail s True
commit -> commit
Fail s c -> Fail s c
instance MonadPlus (PExp d) where
mplus = (<|>)
mzero = empty
try :: PExp d a -> PExp d a
try (PExp m)
= PExp $ \d ->
case m d of
Fail s _ -> Fail s False
x -> x
infixl 3 </>
(</>) :: PExp d a -> PExp d a -> PExp d a
p </> q = try p <|> q
class Stream d where
type Elem d
anyChar :: PExp d (Elem d)
instance Stream [a] where
type Elem [a] = a
anyChar = PExp $ \s -> case s of
(x:xs) -> Commit xs x
[] -> Fail "EOF" False
satisfy :: Stream d => (Elem d -> Bool) -> PExp d (Elem d)
satisfy p = try $ do
x <- anyChar
x <$ guard (p x)
whiteSpace :: PExp String ()
whiteSpace = () <$ many (satisfy isSpace)
phrase :: PExp String a -> PExp String a
phrase m = whiteSpace *> m <* eof
not :: PExp d a -> PExp d ()
not (PExp m)
= PExp $ \d ->
case m d of
Fail{} -> Pure ()
_ -> Fail "unexpected" False
eof :: Stream d => PExp d ()
eof = not anyChar
char :: Eq (Elem d) => Stream d => Elem d -> PExp d (Elem d)
char c = satisfy (c ==)
lexeme :: PExp String a -> PExp String a
lexeme m = m <* whiteSpace
symbol :: Char -> PExp String Char
symbol c = lexeme (char c)
digit :: PExp String Int
digit
= f <$> satisfy isDigit
where
f c = ord c - ord '0'