packages feed

GrammarProducts-0.0.0.2: FormalLanguage/GrammarProduct/Op/Linear.hs

{-# LANGUAGE FlexibleInstances #-}

-- | Direct product of two grammars.
--
-- Currently implemented for linear grammars. Once we move to context-free
-- grammars with more than one non-terminal on the RHS, things become
-- interesting.

module FormalLanguage.GrammarProduct.Op.Linear where

import Data.Semigroup
import Control.Lens
import Control.Applicative
import qualified Data.Set as S
import Data.List (groupBy)
import Data.Function (on)

import FormalLanguage.CFG.Grammar

import FormalLanguage.GrammarProduct.Op.Common



newtype Linear a = Linear {runLinear :: a}



instance Semigroup (Linear Grammar) where
  (Linear g) <> (Linear h) = Linear $ Grammar ts ns es rs s (g^.name <> h^.name) where
    ts = g^.tsyms <> h^.tsyms
    ns = collectNonTerminals rs
    es = g^.epsis <> h^.epsis
    rs = S.fromList [ direct l r | l <- g^..rules.folded, r <- h^..rules.folded ]
    s  = liftA2 (\l r -> Symb $ l^.symb ++ r^.symb) (g^.start) (h^.start)
    direct (Rule l f rs) (Rule a g bs) = Rule (Symb $ l^.symb ++ a^.symb) (f++g) (mergeRHS rs bs)

instance Monoid (Linear Grammar) where
  mempty = Linear $ Grammar S.empty S.empty S.empty (S.singleton $ Rule (Symb []) [] []) Nothing ""
  mappend = (<>)

-- | Merges right-hand sides in a linear direct product. For full-fledged CFGs
-- in different normal forms, see the GNF and CNF implementations.

mergeRHS :: [Symb] -> [Symb] -> [Symb]
mergeRHS [] rs = rs -- neutral element
mergeRHS ls [] = ls -- neutral element
mergeRHS ls' rs' = concat $ go (groupRHS ls') (groupRHS rs') where
  dl = head ls'
  dr = head rs'
  go [] [] = []
  go [] (r:rs)
    | all isSymbT r = map (\(Symb z) -> Symb $ genEps dl ++ z) r : go [] rs
    | all isSymbN r = let [Symb z] = r
                      in  [Symb $ genEps dl ++ z] : go [] rs
  go (l:ls) []
    | all isSymbT l = map (\(Symb z) -> Symb $ z ++ genEps dr) l : go ls []
    | all isSymbN l = let [Symb z] = l
                      in  [Symb $ z ++ genEps dr] : go ls []
  go (l:ls) (r:rs)
    | all isSymbT l && all isSymbT r = goT l r : go ls rs
    | all isSymbN l && all isSymbN r = let [Symb y] = l
                                           [Symb z] = r
                                       in  [Symb $ y++z] : go ls rs
    | all isSymbN l = go [l] []  ++ go ls     (r:rs)
    | all isSymbN r = go []  [r] ++ go (l:ls) rs
    | otherwise     = go [l] []  ++ go [] [r] ++ go ls rs
  goT []            []            = []
  goT []            (Symb t : rs) = Symb (genEps dl ++ t) : goT [] rs
  goT (Symb t : ls) []            = Symb (t ++ genEps dr) : goT ls []
  goT (Symb u : ls) (Symb v : rs) = Symb (u++v)           : goT ls rs

groupRHS = groupBy ((==) `on` isSymbT)