{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Language.SexpGrammar.Base ( SexpGrammar (..) , AtomGrammar (..) , SeqGrammar (..) , PropGrammar (..) , runParse , runGen , SexpG , SexpG_ , module Data.InvertibleGrammar ) where #if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 710 import Control.Applicative #endif import Control.Monad.State import Data.Map (Map) import qualified Data.Map as M #if !MIN_VERSION_base(4,8,0) import Data.Monoid #endif import Data.Scientific import Data.Text (Text) import qualified Data.Text.Lazy as Lazy import Data.InvertibleGrammar import Data.InvertibleGrammar.Monad import Language.Sexp.Pretty (prettySexp) import Language.Sexp.Types -- | Grammar which matches Sexp to a value of type a and vice versa. type SexpG a = forall t. Grammar SexpGrammar (Sexp :- t) (a :- t) -- | Grammar which pattern matches Sexp and produces nothing, or -- consumes nothing but generates some Sexp. type SexpG_ = forall t. Grammar SexpGrammar (Sexp :- t) t unexpectedStr :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Text -> m a unexpectedStr msg = grammarError $ unexpected msg unexpectedSexp :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Text -> Sexp -> m a unexpectedSexp exp got = grammarError $ expected exp `mappend` unexpected (Lazy.toStrict $ prettySexp got) unexpectedAtom :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Atom -> Atom -> m a unexpectedAtom expected atom = do unexpectedSexp (Lazy.toStrict $ prettySexp (Atom dummyPos expected)) (Atom dummyPos atom) unexpectedAtomType :: (MonadContextError (Propagation Position) (GrammarError Position) m) => Text-> Atom -> m a unexpectedAtomType expected atom = do unexpectedSexp ("atom of type " `mappend` expected) (Atom dummyPos atom) ---------------------------------------------------------------------- -- Top-level grammar data SexpGrammar a b where GPos :: SexpGrammar (Sexp :- t) (Position :- Sexp :- t) GAtom :: Grammar AtomGrammar (Atom :- t) t' -> SexpGrammar (Sexp :- t) t' GList :: Grammar SeqGrammar t t' -> SexpGrammar (Sexp :- t) t' GVect :: Grammar SeqGrammar t t' -> SexpGrammar (Sexp :- t) t' instance ( MonadPlus m , MonadContextError (Propagation Position) (GrammarError Position) m ) => InvertibleGrammar m SexpGrammar where forward GPos (s :- t) = return (getPos s :- s :- t) forward (GAtom g) (s :- t) = case s of Atom p a -> dive $ locate p >> forward g (a :- t) other -> locate (getPos other) >> unexpectedSexp "atom" other forward (GList g) (s :- t) = do case s of List p xs -> dive $ locate p >> parseSequence xs g t other -> locate (getPos other) >> unexpectedSexp "list" other forward (GVect g) (s :- t) = do case s of Vector p xs -> dive $ locate p >> parseSequence xs g t other -> locate (getPos other) >> unexpectedSexp "vector" other backward GPos (_ :- s :- t) = return (s :- t) backward (GAtom g) t = do (a :- t') <- dive $ backward g t return (Atom dummyPos a :- t') backward (GList g) t = do (t', SeqCtx xs) <- runStateT (dive $ backward g t) (SeqCtx []) return (List dummyPos xs :- t') backward (GVect g) t = do (t', SeqCtx xs) <- runStateT (dive $ backward g t) (SeqCtx []) return (Vector dummyPos xs :- t') ---------------------------------------------------------------------- -- Atom grammar data AtomGrammar a b where GSym :: Text -> AtomGrammar (Atom :- t) t GKw :: Kw -> AtomGrammar (Atom :- t) t GBool :: AtomGrammar (Atom :- t) (Bool :- t) GInt :: AtomGrammar (Atom :- t) (Integer :- t) GReal :: AtomGrammar (Atom :- t) (Scientific :- t) GString :: AtomGrammar (Atom :- t) (Text :- t) GSymbol :: AtomGrammar (Atom :- t) (Text :- t) GKeyword :: AtomGrammar (Atom :- t) (Kw :- t) instance ( MonadPlus m , MonadContextError (Propagation Position) (GrammarError Position) m ) => InvertibleGrammar m AtomGrammar where forward (GSym sym') (atom :- t) = case atom of AtomSymbol sym | sym' == sym -> return t _ -> unexpectedAtom (AtomSymbol sym') atom forward (GKw kw') (atom :- t) = case atom of AtomKeyword kw | kw' == kw -> return t _ -> unexpectedAtom (AtomKeyword kw') atom forward GBool (atom :- t) = case atom of AtomBool a -> return $ a :- t _ -> unexpectedAtomType "bool" atom forward GInt (atom :- t) = case atom of AtomInt a -> return $ a :- t _ -> unexpectedAtomType "int" atom forward GReal (atom :- t) = case atom of AtomReal a -> return $ a :- t _ -> unexpectedAtomType "real" atom forward GString (atom :- t) = case atom of AtomString a -> return $ a :- t _ -> unexpectedAtomType "string" atom forward GSymbol (atom :- t) = case atom of AtomSymbol a -> return $ a :- t _ -> unexpectedAtomType "symbol" atom forward GKeyword (atom :- t) = case atom of AtomKeyword a -> return $ a :- t _ -> unexpectedAtomType "keyword" atom backward (GSym sym) t = return (AtomSymbol sym :- t) backward (GKw kw) t = return (AtomKeyword kw :- t) backward GBool (a :- t) = return (AtomBool a :- t) backward GInt (a :- t) = return (AtomInt a :- t) backward GReal (a :- t) = return (AtomReal a :- t) backward GString (a :- t) = return (AtomString a :- t) backward GSymbol (a :- t) = return (AtomSymbol a :- t) backward GKeyword (a :- t) = return (AtomKeyword a :- t) ----------------------------------------------------------------------- -- Sequence grammar parseSequence :: (MonadContextError (Propagation Position) (GrammarError Position) m, InvertibleGrammar (StateT SeqCtx m) g) => [Sexp] -> g a b -> a -> m b parseSequence xs g t = do (a, SeqCtx rest) <- runStateT (forward g t) (SeqCtx xs) unless (null rest) $ unexpectedStr $ "leftover elements: " `mappend` (Lazy.toStrict $ Lazy.unwords $ map prettySexp rest) return a data SeqGrammar a b where GElem :: Grammar SexpGrammar (Sexp :- t) t' -> SeqGrammar t t' GRest :: Grammar SexpGrammar (Sexp :- t) (a :- t) -> SeqGrammar t ([a] :- t) GProps :: Grammar PropGrammar t t' -> SeqGrammar t t' newtype SeqCtx = SeqCtx { getItems :: [Sexp] } instance ( MonadPlus m , MonadState SeqCtx m , MonadContextError (Propagation Position) (GrammarError Position) m ) => InvertibleGrammar m SeqGrammar where forward (GElem g) t = do step xs <- gets getItems case xs of [] -> unexpectedStr "end of sequence" x:xs' -> do modify $ \s -> s { getItems = xs' } forward g (x :- t) forward (GRest g) t = do xs <- gets getItems modify $ \s -> s { getItems = [] } go xs t where go [] t = return $ [] :- t go (x:xs) t = do step y :- t' <- forward g (x :- t) ys :- t'' <- go xs t' return $ (y:ys) :- t'' forward (GProps g) t = do xs <- gets getItems modify $ \s -> s { getItems = [] } props <- go xs M.empty (res, PropCtx ctx) <- runStateT (forward g t) (PropCtx props) when (not $ M.null ctx) $ unexpectedStr $ "property-list keys: " `mappend` (Lazy.toStrict $ Lazy.unwords $ map (prettySexp . Atom dummyPos . AtomKeyword) (M.keys ctx)) return res where go [] props = return props go (Atom _ (AtomKeyword kwd):x:xs) props = step >> go xs (M.insert kwd x props) go other _ = unexpectedStr $ "malformed property-list: " `mappend` (Lazy.toStrict $ Lazy.unwords $ map prettySexp other) backward (GElem g) t = do step (x :- t') <- backward g t modify $ \s -> s { getItems = x : getItems s } return t' backward (GRest g) (ys :- t) = do xs :- t' <- go ys t put (SeqCtx xs) return t' where go [] t = return $ [] :- t go (y:ys) t = do step x :- t' <- backward g (y :- t) xs :- t'' <- go ys t' return $ (x : xs) :- t'' backward (GProps g) t = do step (t', PropCtx props) <- runStateT (backward g t) (PropCtx M.empty) let plist = foldr (\(name, sexp) acc -> Atom dummyPos (AtomKeyword name) : sexp : acc) [] (M.toList props) put $ SeqCtx plist return t' ---------------------------------------------------------------------- -- Property list grammar data PropGrammar a b where GProp :: Kw -> Grammar SexpGrammar (Sexp :- t) (a :- t) -> PropGrammar t (a :- t) GOptProp :: Kw -> Grammar SexpGrammar (Sexp :- t) (a :- t) -> PropGrammar t (Maybe a :- t) newtype PropCtx = PropCtx { getProps :: Map Kw Sexp } instance ( MonadPlus m , MonadState PropCtx m , MonadContextError (Propagation Position) (GrammarError Position) m ) => InvertibleGrammar m PropGrammar where forward (GProp kwd g) t = do ps <- gets getProps case M.lookup kwd ps of Nothing -> unexpectedStr $ mconcat [ "key " , Lazy.toStrict . prettySexp . Atom dummyPos . AtomKeyword $ kwd , " not found" ] Just x -> do put (PropCtx $ M.delete kwd ps) forward g $ x :- t forward (GOptProp kwd g) t = do ps <- gets getProps case M.lookup kwd ps of Nothing -> return (Nothing :- t) Just x -> do put (PropCtx $ M.delete kwd ps) (a :- t') <- forward g (x :- t) return (Just a :- t') backward (GProp kwd g) t = do x :- t' <- backward g t modify $ \ps -> ps { getProps = M.insert kwd x (getProps ps) } return t' backward (GOptProp _ _) (Nothing :- t) = do return t backward (GOptProp kwd g) (Just x :- t) = do x' :- t' <- backward g (x :- t) modify $ \ps -> ps { getProps = M.insert kwd x' (getProps ps) } return t' runParse :: (Functor m, MonadPlus m, MonadContextError (Propagation Position) (GrammarError Position) m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> Sexp -> m a runParse gram input = (\(x :- _) -> x) <$> forward gram (input :- ()) runGen :: (Functor m, MonadPlus m, MonadContextError (Propagation Position) (GrammarError Position) m, InvertibleGrammar m g) => Grammar g (Sexp :- ()) (a :- ()) -> a -> m Sexp runGen gram input = (\(x :- _) -> x) <$> backward gram (input :- ())