GrammarProducts-0.2.0.0: FormalLanguage/GrammarProduct/Op/Greibach.hs
module FormalLanguage.GrammarProduct.Op.Greibach where
import Control.Applicative
import Control.Lens
import Control.Lens.Fold
import "newtype" Control.Newtype ()
import Data.Function (on)
import Data.List (genericReplicate)
import Data.List (groupBy)
import Data.Maybe
import Data.Monoid hiding ((<>))
import Data.Semigroup
import qualified Data.Set as S
import Text.Printf
import Text.Trifecta --
import qualified Data.ByteString.Char8 as B
import Control.Monad.Trans.State.Strict
import Data.Default
import Text.Trifecta.Delta
import FormalLanguage.CFG.Grammar
import FormalLanguage.CFG.Parser
import FormalLanguage.GrammarProduct.Op.Common
{-
-- * Proof of associativity of the 2-GNF.
-- | Wrap a grammar in 2-GNF form.
--
-- The 2-GNF has rules of the form: X -> a | aY | aYZ with "a" terminal, "Y",
-- "Z" non-terminals.
newtype TwoGNF = TwoGNF {runTwoGNF :: Grammar}
-- | Construct a grammar product for a grammar in 2-GNF form.
--
-- TODO check if grammar is in 2-GNF!
instance Semigroup TwoGNF where
(TwoGNF g) <> (TwoGNF h) = TwoGNF $ Grammar ts ns es rs s (g^.name ++ h^.name) where
ts = collectTerminals rs
ns = collectNonTerminals rs
es = g^.epsis <> h^.epsis -- this is kind of sketchy
rs = S.fromList
. map starRemove
. catMaybes
$ [ l <.> r
| l <- concatMap (starExtend $ gDim g) . S.toList $ g^.rules
, r <- concatMap (starExtend $ gDim h) . S.toList $ h^.rules
]
s = liftA2 (\l r -> Symb $ l^.symb ++ r^.symb) (g^.start) (h^.start)
(<.>) :: Rule -> Rule -> Maybe Rule
a <.> b | ((Just $ a^.lhs)==g^.start) `exactlyOne` ((Just $ b^.lhs)==h^.start) = Nothing
a <.> b = Just
$ Rule (Symb $ a^.lhs.symb ++ b^.lhs.symb)
[""]
(zipWith (\x y -> Symb $ x^.symb ++ y^.symb) (a^.rhs) (b^.rhs))
exactlyOne False True = True
exactlyOne True False = True
exactlyOne _ _ = False
-- | Extend a rule with ``epsilon-type'' productions to create 2-GNF for all rules
starExtend :: Int -> Rule -> [Rule]
starExtend k (Rule l f [t]) = [ Rule l f [t,stars k, stars k]]
starExtend k (Rule l f [t,n]) = [ Rule l f [t,n,stars k]
, Rule l f [t,stars k,n]
]
-- assuming that we have a 2-gnf at most
starExtend k r = [r]
stars :: Int -> Symb
stars k = Symb $ replicate k E
-- | Remove star-online columns.
starRemove :: Rule -> Rule
starRemove = over rhs (filter (any (not . isEpsilon) . getSymbs))
isEpsilon E = True
isEpsilon _ = False
-- | The start symbol for this instance needs to be "Just []" so as to preserve
-- the start symbol in a chain of (<>) operations.
instance Monoid TwoGNF where
mempty = TwoGNF $ Grammar S.empty S.empty S.empty (S.singleton undefined) (Just $ Symb []) ""
mappend = (<>)
-- | Takes lists of symbols and aligns according to being
-- terminal/non-terminal:
--
-- aXbc / aXYb =>
--
-- aX-bc a-Xbc
-- aXYb- aXYb-
--
-- That is, create all alignments of non-terminals, but just ``left-align'' all
-- terminals. This will create all possible "alignments" of symbols. This is
-- why we return a list of lists.
{-
aligned :: [Symb] -> [Symb] -> [[Symb]]
aligned ls' rs' = go (groupBy ((==) `on` isSymbT) ls') (groupBy ((==) `on` isSymbT) rs') where
dl = length . getSymbs . head $ ls'
dr = length . getSymbs . head $ rs'
go :: [[Symb]] -> [[Symb]] -> [[Symb]]
go [] [] = []
go (l:ls) [] = epsR l : go ls []
go [] (r:rs) = epsL r : go [] rs
go (l:ls) (r:rs)
| all isSymbT l
&& all isSymbT r = goT l r : go ls rs
| all isSymbN l
&& all isSymbN r = undefined -- [ ns : gs | ns <- goN l r, gs <- go ls rs ]
| all isSymbT l = epsR l : go ls (r:rs)
| all isSymbT r = epsL r : go (l:ls) rs
goT [] [] = []
goT ls [] = epsR ls
goT [] rs = epsL rs
goT (l:ls) (r:rs) = (Symb $ l^.symb ++ r^.symb) : goT ls rs
goN :: [Symb] -> [Symb] -> [[Symb]]
goN [] [] = [[]]
goN (l:ls) [] = epsR [l] : goN ls []
goN [] (r:rs) = epsL [r] : goN [] rs
goN lls rrs
| length lls == length rrs = [[ Symb $ l^.symb ++ r^.symb | l <- lls | r <- rrs ]]
goN lls@(l:ls) rrs@(r:rs)
| length lls < length rrs = undefined
| length lls > length rrs = undefined
epsR ls = map (\(Symb s) -> Symb $ s ++ replicate dr (T "")) ls
epsL rs = map (\(Symb s) -> Symb $ replicate dl (T "") ++ s) rs
-}
-}