{- 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 FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} module Text.GrammarCombinators.Base.ProductionRule where import Text.GrammarCombinators.Base.Domain import Text.GrammarCombinators.Base.Token import Text.GrammarCombinators.Base.MultiRec import Language.Haskell.TH.Syntax (Q, Exp, Lift, lift) infixl 1 ||| infixl 3 >>> infixl 3 *>>> infixl 3 >>>* infixl 3 $>> infixl 3 $>>* -- | Base type class for production rule interpretations. A production rule -- interpretation that is an instance of the 'ProductionRule' type class supports -- sequencing and disjunction of rules, empty rules, dead rules and end-of-input -- rules. class ProductionRule p where -- | Sequence two rules. Result of the sequenced rule is the application -- of the result of the first rule to the result of the second. (>>>) :: p (a -> b) -> p a -> p b -- | Disjunction of two rules. (|||) :: p va -> p va -> p va -- | End of input rule. Matches only at end of input, consumes nothing, -- produces '()' as result. endOfInput :: p () -- | Dead rule. Never matches. die :: p a class (ProductionRule p) => LiftableProductionRule p where -- | Epsilon rule with lifted value. Always matches, consumes nothing, produces -- the given value (with its lifted version) as result. epsilonL :: a -> Q Exp -> p a -- | Optionally match a given rule. optionally :: p v -> p (Maybe v) optionally r = epsilonL Just [|Just|] >>> r ||| epsilonL Nothing [|Nothing|] epsilonLS :: (Lift v, LiftableProductionRule p) => v -> p v epsilonLS v = epsilonL v $ lift v class (LiftableProductionRule p) => EpsProductionRule p where -- | Epsilon rule. Always matches, consumes nothing, produces -- the given value as result. epsilon :: a -> p a -- | Type class for production rules matching tokens of a certain -- token type 't'. -- 't' should be an instance of the 'Token' type class. class TokenProductionRule p t | p -> t where -- | Match a given token of type 't' and produce its concrete -- value (of type 'ConcreteToken' t). token :: t -> p (ConcreteToken t) anyToken :: p (ConcreteToken t) class PenaltyProductionRule p where penalty :: Int -> p a -> p a -- | Sequence two rules, but drop the result of the first. (*>>>) :: (ProductionRule p, LiftableProductionRule p) => p a -> p b -> p b a *>>> b = epsilonL (flip const) [| flip const |] >>> a >>> b -- | Sequence two rules, but drop the result of the second. (>>>*) :: (ProductionRule p, LiftableProductionRule p) => p a -> p b -> p a a >>>* b = epsilonL const [|const|] >>> a >>> b -- | Apply a given function to the result of a given rule. ($>>) :: EpsProductionRule p => (a -> b) -> p a -> p b v $>> r = epsilon v >>> r -- | Replace a rule's result value with a given value. ($>>*) :: EpsProductionRule p => a -> p b -> p a v $>>* r = epsilon v >>>* r -- | Apply a given function to the result of a given rule. ($|>>) :: LiftableProductionRule p => (a -> b, Q Exp) -> p a -> p b (v,q) $|>> r = epsilonL v q >>> r -- | Replace a rule's result value with a given value. ($|>>*) :: LiftableProductionRule p => (a, Q Exp) -> p b -> p a (v,q) $|>>* r = epsilonL v q >>>* r -- | Production rule interpretations supporting the 'RecProductionRule' type class support references -- to non-terminals in a given domain 'phi'. The type of the result values of the rules is determined -- by semantic value family 'r'. class RecProductionRule p phi r | p -> phi, p -> r where -- | Reference a given non-terminal in a production rule. ref :: phi ix -> p (r ix) -- | Production rule interpretations supporting the 'LoopProductionRule' -- type class allow for Kleene-star quantified references to -- non-terminals (zero or more, see the 'manyRef' function) as well -- as '+'-quantified references to non-terminals (one or more, see -- the 'many1Ref' function). -- -- An instance can implement either manyRef or many1Ref, -- both or neither. Not implementing either produces -- old-style many and many1 combinator behaviour (discouraged -- for most situations) class (ProductionRule p, LiftableProductionRule p, RecProductionRule p phi r) => LoopProductionRule p phi r | p -> phi, p -> r where -- | Match a given non-terminal zero or more times. manyRef :: phi ix -> p [r ix] manyRef idx = epsilonL [] [| [] |] ||| many1Ref idx -- | Match a given non-terminal one or more times. many1Ref :: phi ix -> p [r ix] many1Ref idx = ((:), [| (:) |]) $|>> ref idx >>> manyRef idx -- | The 'SuperProductionRule| type class is in an experimental state, and -- currently not intended for general use. class SuperProductionRule p where subref :: (DomainEmbedding phi phi' supIxT, HFunctor phi (PF phi), ProductionRule (p phi ixT r t), ProductionRule (p phi' (IxMapSeq ixT supIxT) (SubVal supIxT r) t)) => (forall ix'. phi' ix' -> p phi' (IxMapSeq ixT supIxT) (SubVal supIxT r) t (PF phi' (SubVal supIxT r) ix')) -> phi' ix -> phi (supIxT ix) -> p phi ixT r t (PF phi r (supIxT ix)) -- | Match any token in a given range of tokens. tokenRange :: forall p t . (ProductionRule p, TokenProductionRule p t) => [t] -> p (ConcreteToken t) tokenRange [] = error "empty range" tokenRange [c] = token c tokenRange (c : r) = token c ||| tokenRange r -- | Consecutively match a given list of tokens and return their concrete token values as a list. string :: forall p t . (ProductionRule p, LiftableProductionRule p, TokenProductionRule p t) => [t] -> p [ConcreteToken t] string = foldr ((>>>) . (((:),[|(:)|]) $|>>) . token) (epsilonL [] [|[]|]) -- | An old style 'many' combinator. Produces an infinite rule similar to Parsec's many rule. -- Prefer to use the 'manyRef' function whenever possible. manyInf :: (ProductionRule p, LiftableProductionRule p) => p a -> p [a] manyInf r = epsilonL (:) [|(:)|] >>> r >>> manyInf r ||| epsilonL [] [|[]|] -- | An old style 'many' combinator. Produces an infinite rule similar to Parsec's many rule. -- Prefer to use the 'manyRef' function whenever possible. many1Inf :: (ProductionRule p, LiftableProductionRule p) => p a -> p [a] many1Inf r = epsilonL (:) [|(:)|] >>> r >>> manyInf r class ProductionRuleWithLibrary p phi r | p -> phi, p -> r where lib :: phi ix -> p (r ix) class BiasedProductionRule p where -- | Left-biased choice (>|||) :: p a -> p a -> p a (>|||) = flip (<|||) -- | Right-biased choice (<|||) :: p a -> p a -> p a (<|||) = flip (>|||)