{-# LANGUAGE LambdaCase #-}
module Text.Puggle.AST where
import Control.Applicative
-- | A definition has a name (which must be a unique, valid Haskell
-- identifer), a Haskell type, and an expression it matches. It also
data PGDefinition = PGDefinition
{ defName :: String
, defDelim :: Bool
, defType :: String
, defRules :: Expr
} deriving (Eq, Show)
-- | The set of possible indentation modifiers
data Indent = IEq | IGE | IGt | IAny
deriving (Eq, Show)
type Pred = Char -> Bool
data Expr
= ELiteral String -- ^ Matches a literal string
| ECharset Pred -- ^ Matches a set of characters
| EChoice Expr Expr -- ^ Matches either the left or the right expr,
-- in that order
| ESequence Expr Expr -- ^ Matches both exprs in sequence
| EAction Expr String -- ^ Performs an action on the matched expr
| EStar Expr -- ^ Matches zero or more repetitions of the expr
| EQues Expr -- ^ Matches zero or one repetions of the expr
| EPred Bool Expr -- ^ Matches without consuming input if the
-- boolean is true and the expr matches; or if
-- the boolean is false and the expr fails
| EIndent Expr Indent -- ^ Specifies an indentation level for the expr,
-- and fails if it does not fall into that level
| EBars Expr -- ^ blah
| EAny -- ^ Matches any terminal
| ENamed -- ^ Matches a named terminal or non-terminal
deriving (Eq, Show)
newtype Parser a =
Parser { runParser :: String -> Either String (String, a) }
instance Functor Parser where
fmap f p = Parser $ \s -> case runParser p s of
Left err -> Left err
Right (s', a) -> Right (s', f a)
instance Applicative Parser where
pure a = Parser $ \ s -> Right (s, a)
f <*> x = Parser $ \ s -> do
(s', f') <- runParser f s
(s'', x') <- runParser x s'
return (s'', f' x')
instance Alternative Parser where
empty = Parser (\ _ -> Left "Parsing failed")
l <|> r = Parser $ \ s ->
case runParser l s of
Left _ -> runParser r s
Right x -> return x
instance Monad Parser where
return a = Parser $ \ s -> Right (s, a)
p >>= f = Parser $ \ s -> do
(s', x) <- runParser p s
runParser (f x) s'
(??) :: Parser a -> String -> Parser a
p ?? err = Parser $ \s -> case runParser p s of
Left _ -> Left err
Right x -> return x
satisfies :: (Char -> Bool) -> Parser Char
satisfies f = Parser $ \case
(x:xs)
| f x -> return (xs, x)
| otherwise -> Left ("in `satisfies`: did not satisfy predicate")
_ -> Left ("in `satisfies`: no more input")
char :: Char -> Parser Char
char c = satisfies (== c) ?? ("Did not match char " ++ show c)