adp-multi-0.2.2: tests/MCFG/MCFG.hs
-- | multiple context free grammar,
-- with CYK table parser. (Johannes Waldmann, HTWK Leipzig)
-- Note (Maik): it is actually an Unger-style parser,
-- or: top-down memoizing dynamic programming algorithm
module MCFG.MCFG where
import qualified Data.Map as M
import Control.Monad.State.Strict
import Data.List ( inits, tails )
-- | * data type for grammar
data Nonterminal = N { arity :: Int, name :: String }
deriving ( Eq, Ord, Show )
newtype Terminal = T Char
deriving ( Eq, Ord, Show )
data MCFG = MCFG
{ start :: Nonterminal
, rules :: [ Rule ] }
deriving ( Eq, Ord, Show )
data Rule = Rule
{ lhs :: Nonterminal
, function :: [[ Either (Int,Int) Terminal ]]
, rhs :: [ Nonterminal ]
}
deriving ( Eq, Ord, Show )
-- | * data types for derivation trees
data Derivation =
Derivation { result :: [[ Terminal ]]
, apply :: Rule
, children :: [ Derivation ]
}
deriving ( Show )
consistent :: Derivation -> Bool
consistent d =
and ( map consistent $ children d )
&& result d == do
xs <- function ( apply d )
return $ do
x <- xs
case x of
Right t -> [ t ]
Left (i,j) ->
result ( children d !! i ) !! j
-- | * CYK tabled parser
parse :: MCFG -> [ Terminal ] -> [ Derivation ]
parse g w = fst $ cyk g w
-- | speichert jeweils alle Ableitungen.
-- später dann: die beste Ableitung und ihre Kosten.
type Table = M.Map ( Nonterminal, [[Terminal]] )
[ Derivation ]
-- | Die Tabelle ist nur polynomiell groß, weil die
-- Schlüssel aus Teilwörtern der Eingabe bestehen
cyk :: MCFG -> [ Terminal ] -> ( [Derivation], Table )
cyk g w = flip runState M.empty
$ build (rules g) (start g , [w] )
build :: [ Rule ]
-> ( Nonterminal, [[Terminal]] )
-> State Table [ Derivation ]
build rules (var, ws) = do
t <- get
case M.lookup ( var, ws ) t of
Just r -> return r
Nothing -> do
dss <- sequence $ do
r <- rules
guard $ var == lhs r
sub <- forM ( zip ( function r ) ws ) splits
let c :: M.Map ( Int,Int ) [Terminal]
c = M.fromListWith ( error "not linear")
$ concat sub
return $ do
css <- forM ( zip (rhs r ) [0..] )
$ \ (var' @ (N a _), i) -> do
let ws' = for [ 0 .. a-1 ] $ \ j -> c M.! (i,j)
build rules ( var' , ws' )
return $ for ( combine css ) $ \ cs ->
Derivation { result = ws, apply = r, children = cs }
let ds :: [ Derivation ]
ds = concat dss
modify $ M.insert (var, ws) ds
return ds
splits :: Eq b
=> ([Either a b], [b])
-> [[(a, [b])]]
splits (f, w) = case (f,w) of
( [], [] ) -> return []
(Right x : f', y : w') | x == y ->
splits (f', w')
(Left a : f', _) -> do
( pre, post ) <- zip ( inits w ) ( tails w )
later <- splits (f', post)
return $ ( a , pre ) : later
_ -> []
for :: [a] -> (a -> b) -> [b]
for = flip map
combine :: [[a]] -> [[a]]
combine = foldr ( \ xs ys -> do x <- xs ; y <- ys ; return $ x : y ) [[]]