{-# OPTIONS -fglasgow-exts #-} {-| Alternative approach of 'read' that composes grammars instead of parsers. Grammars describing the data types are composed dynamically, removing possible left-recursion and combining common prefixes of alternatives. The function 'gread' defined here is able to handle the associativities defined for infix operators. The function 'gread' reads data in linear time, while the function 'read' has an exponential behavior in some cases of data types with infix operators. Non uniform data types are not supported, because they generate infinite grammars. The library is documented in the paper: /Haskell, do you read me?: constructing and composing efficient top-down parsers at runtime/ Bibtex entry: For more documentation see the TTTAS webpage: . -} module Text.GRead ( -- * Grammar Description module Text.GRead.Grammar, -- * Reading Functions gread, -- * Types GReadMsg, GReadResult(..) ) where import Language.TTTAS import Text.GRead.Grammar import Text.GRead.Transformations.LeftFact import Text.GRead.Transformations.LeftCorner import Text.GRead.Transformations.Group import UU.Parsing hiding (Symbol,parse,Ok) import qualified UU.Parsing as UU import List (findIndex) -- | Type of error repair messages. type GReadMsg = Message Token (Maybe Token) -- | Type of 'gread' results. data GReadResult a = Ok a | Rep a [GReadMsg] deriving Show -- | The 'gread' reads input from a string, which must be -- completely consumed by the input process. -- Returns @Ok value@ on a successful parse. -- Otherwise returns @Rep value msgs@, where the @value@ -- results of parsing a repaired input. The list @msgs@ -- contains the \"corrections\" done to the input. -- -- For example, a 'read'-like implementation can be: -- -- > read :: (Gram a) => String -> a -- > read input = case gread input of -- > Ok a -> a -- > Rep _ (m:_) -> error $ show m gread :: (Gram a) => String -> GReadResult a gread = ( parse . compile . leftfactoring . leftcorner . group ) grammar -- COMPILE -------------------------------------------------------------------- newtype Const f a s = C {unC :: f a} compile :: forall a . Grammar a -> Parser Token a compile (Grammar (start :: Ref a env) rules) = unC (lookupEnv start result) where result = mapEnv (\ (PS ps) -> C (foldr1 (<|>) [ comp p | p <- ps])) rules comp :: forall t . Prod t env -> Parser Token t comp (End x) = pLow x comp (Seq (Term t) ss) = (flip ($)) <$> pSym t <*> comp ss comp (Seq (Nont n) ss) = (flip ($)) <$> unC (lookupEnv n result) <*> comp ss mapEnv :: (forall a . f a s -> g a s) -> Env f s env -> Env g s env mapEnv _ Empty = Empty mapEnv f (Ext r v) = Ext (mapEnv f r) (f v) instance Show Token where show (Keyw s) = s show Open = "(" show Close = ")" instance UU.Symbol Token where deleteCost _ = 5# -- PARSE ---------------------------------------------------------------------- parse :: Parser Token a -> String -> GReadResult a parse p input = case rparse p input of (a,[] ) -> Ok a (a,msgs) -> Rep a msgs rparse :: Parser Token a -> String -> (a, [GReadMsg]) rparse p input = let ((Pair a _),msgs) = eval (UU.parse p (tokenize input)) in (a,msgs) where eval :: Steps a Token (Maybe Token) -> (a, [GReadMsg]) eval (OkVal v r) = let (a,msgs) = v `seq` (eval r) in (v a,msgs) eval (UU.Ok r) = eval r eval (Cost _ r) = eval r eval (StRepair _ msg r) = let (v,msgs) = eval r in (v,msg:msgs) eval (Best _ r _) = eval r eval (NoMoreSteps v ) = (v,[]) tokenize [] = [] tokenize ('(':xs) = Open : (tokenize xs) tokenize (')':xs) = Close : (tokenize xs) tokenize (' ':xs) = tokenize xs tokenize l = case findIndex endTok l of Just i -> let (ky,rs) = splitAt i l in Keyw ky : (tokenize rs) Nothing -> [Keyw l] endTok t = t == ')' || t == ' ' || t == '('