{- Copyright 2010 Dominique Devriese This file is part of the grammar-combinators library. The grammar-combinators library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Foobar is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Foobar. If not, see . -} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} module Text.GrammarCombinators.Utils.LiftGrammar ( liftGrammar, unfoldAndLiftGrammar ) where import Text.GrammarCombinators.Base import Language.Haskell.TH.Syntax import Control.Monad data LiftedRule (phi :: * -> *) (r :: * -> *) t v = MkLR { liftRule :: Q Exp -> Q Exp -> Q Exp -> Q Exp } instance ProductionRule (LiftedRule phi r t) where endOfInput = MkLR $ \_ _ _ -> [| endOfInput |] a >>> b = MkLR $ \r mr m1r -> [| $(liftRule a r mr m1r) >>> $(liftRule b r mr m1r) |] a ||| b = MkLR $ \r mr m1r -> [| $(liftRule a r mr m1r) ||| $(liftRule b r mr m1r) |] die = MkLR $ \_ _ _ -> [| die |] instance BiasedProductionRule (LiftedRule phi r t) where a >||| b = MkLR $ \r mr m1r -> [| $(liftRule a r mr m1r) >||| $(liftRule b r mr m1r) |] a <||| b = MkLR $ \r mr m1r -> [| $(liftRule a r mr m1r) <||| $(liftRule b r mr m1r) |] instance PenaltyProductionRule (LiftedRule phi r t) where penalty p br = MkLR $ \r mr m1r -> [| penalty p $(liftRule br r mr m1r) |] instance LiftableProductionRule (LiftedRule phi r t) where epsilonL _ q = MkLR $ \_ _ _ -> [|epsilon $(q)|] instance (Token t) => TokenProductionRule (LiftedRule phi r t) t where token tt = MkLR $ \_ _ _ -> [| token $(lift tt) |] anyToken = MkLR $ \_ _ _ -> [| anyToken |] instance (LiftFam phi) => RecProductionRule (LiftedRule phi r t) phi r where ref idx = MkLR $ \r _ _ -> [| $(r) $(return $ liftIdxE idx) |] instance (LiftFam phi) => LoopProductionRule (LiftedRule phi r t) phi r where manyRef idx = MkLR $ \_ mr _ -> [| $(mr) $(return $ liftIdxE idx) |] many1Ref idx = MkLR $ \_ _ m1r -> [| $(m1r) $(return $ liftIdxE idx) |] liftGrammar' :: forall phi t r rr. (FoldFam phi, LiftFam phi, Token t) => GLAnyExtendedContextFreeGrammar phi t r rr -> Name -> Q Exp -> Q Exp -> Q Exp -> Q Dec liftGrammar' gram name refQ manyRefQ many1RefQ = let clause :: phi ix -> Q Clause clause idx = do lr <- liftRule (gram idx) refQ manyRefQ many1RefQ return $ Clause [liftIdxP idx] (NormalB lr) [] addClause idx b = do c <- clause idx cs <- b return (c:cs) clauses = foldFam addClause (return []) in liftM (FunD name) clauses -- | Lift a given grammar to Template Haskell liftGrammar :: forall phi t r rr. (FoldFam phi, LiftFam phi, Token t) => GLAnyExtendedContextFreeGrammar phi t r rr -> Name -> Q Type -> Q [Dec] liftGrammar gram name grammarType = let sig = do t <- grammarType return $ SigD name t fundef = liftGrammar' gram name [|ref|] [|manyRef|] [|many1Ref|] in do s <- sig f <- fundef return [s,f] -- | Lift a given grammar to Template Haskell and replace recursion and loops with -- infinite-tree style recursive calls to the grammar itself. This allows GHC to do -- a much better optimization (x20 speed-ups in one realistic test, compared with -- result of 'liftGrammar'). unfoldAndLiftGrammar :: forall phi t r rr. (FoldFam phi, LiftFam phi, Token t) => GLAnyExtendedContextFreeGrammar phi t r rr -> Name -> Q Type -> Q [Dec] unfoldAndLiftGrammar gram name gramType = let refQ = return $ VarE name manyRefQ = return $ AppE (AppE (VarE '(.)) (VarE 'manyInf)) $ VarE name many1RefQ = return $ AppE (AppE (VarE '(.)) (VarE 'many1Inf)) $ (VarE name) sig = do t <- gramType return $ SigD name t fundef = liftGrammar' gram name refQ manyRefQ many1RefQ in do s <- sig d <- fundef return [s,d]