> module Main where > import Parsers > import System -- 1.3 (partain) > import IO--1.3 > infixr 8 +.+ , +.. , ..+ > infixl 7 <<< , <<* > infixr 6 ||| > > (+.+) = thn > (..+) = xthn > (+..) = thnx > (|||) = alt > (<<<) = using > (<<*) = using2 > lit :: Eq a => a -> Parser a a > lit = literal > star = rpt > anyC = satisfy (const True) > butC cs = satisfy (not.(`elem` cs)) > noC "" = [("","")] > noC _ = [] ---------------------------------------------------------------------- > main = getArgs >>= \ args -> parse_args args > parse_args :: [String] -> IO () > parse_args (regexp: files) = > let acc = acceptor (fst(head(nnRegexp regexp))) > acc' = unlines . filter acc . lines > in > getContents >>= \ inp -> > putStr (acc' inp) > parse_args _ = > getProgName >>= \progName -> > hPutStr stderr ("Usage: " ++ progName ++ " regexp\n") {- Atom = character | "\\" character | "." | "\\(" Regexp "\\) . ExtAtom = Atom ["*" | "+" | "?"] . Factor = ExtAtom + . Regexp = Factor / "\\|" ["$"]. -} > data NFANode > = NFAChar Char NFANode > | NFAAny NFANode > | NFAEps [NFANode] > | NFAEnd NFANode > | NFAFinal > | NFATable [(Char, NFANode)] [NFANode] [NFANode] Bool NFAChar c next - a state with arc on character c to next state NFAAny next - a state with arc on any character NFAEps nexts - a state with a set of epsilon transitions NFAEnd - a state with an arc if end of string is reached NFAFinal - a final state NFATable charTrans anyTrans endTrans final - a state with character arcs according to charTrans, any character arcs according to anyTrans, end arcs according to endTrans, and a boolean flag indicating a final state > nfaChar = NFAChar > nfaAny = NFAAny > -- nfaEps = NFAEps > nfaEps = mkTable [] [] [] False . epsClosure > nfaEnd = NFAEnd > nfaFinal= NFAFinal just wrappers for the NFANode constructors, modified such that epsilon transitions are compressed into tables > mkTable pairs anys ends final [] = NFATable pairs anys ends final > mkTable pairs anys ends final (NFAChar c n:ns) = mkTable ((c,n):pairs) anys ends final ns > mkTable pairs anys ends final (NFAAny n:ns) = mkTable pairs (n:anys) ends final ns > mkTable pairs anys ends final (NFATable pairs' anys' ends' final':ns) = mkTable (pairs'++pairs) (anys'++anys) (ends'++ends) (final' || final) ns > mkTable pairs anys ends final (NFAEnd n:ns) = mkTable pairs anys (n:ends) final ns > mkTable pairs anys ends final (NFAFinal:ns) = mkTable pairs anys ends True ns > mkTable _ _ _ _ _ = error "illegal argument to mkTable" > > type NFAproducer = NFANode -> NFANode An NFAproducer takes a final state and produces the initial state of a non-deterministic automaton. > nnAtom :: Parser Char NFAproducer > nnAtom = > lit '\\' ..+ lit '(' ..+ nnRegexp +.. lit '\\' +.. lit ')' > ||| lit '\\' ..+ butC "|()" <<< nfaChar > ||| lit '.' <<< const NFAAny > ||| butC "\\.$" <<< nfaChar > ||| lit '$' `followedBy` anyC <<< nfaChar > nnExtAtom :: Parser Char NFAproducer > nnExtAtom = > nnAtom +.+ opt (lit '*' <<< const (\ at final -> > let at_init = at (nfaEps [final, at_init]) > in nfaEps [at_init, final]) > ||| lit '+' <<< const (\ at final -> > let at_init = at (nfaEps [final, at_init]) > in nfaEps [at_init]) > ||| lit '?' <<< const (\ at final -> > let at_init = at (nfaEps [final]) > in nfaEps [final, at_init])) > <<< helper > where > helper (ea, []) = ea > helper (ea, [f]) = f ea > > nnFactor :: Parser Char NFAproducer > nnFactor = > plus nnExtAtom <<< foldr (.) id > nnRegexp :: Parser Char NFAproducer > nnRegexp = > nnFactor +.+ star (lit '\\' ..+ lit '|' ..+ nnFactor) +.+ opt (lit '$') > <<< helper > where > helper (ef, (efs, [])) = foldl combine ef efs > helper (ef, (efs, _ )) = foldl combine ef efs . nfaEnd > combine f1 f2 final = nfaEps [f1 final, f2 final] Step function for the NFA interpreter. Note if epsilon compression is removed above, all {- epsClosure -} must be uncommented! > nfaStep states c = {- epsClosure -} (concat (map step states)) > where > step (NFAChar c' n') | c == c' = [n'] > step (NFAAny n') = [n'] > step (NFATable pairs anys ends finals) = [ n' | (c',n') <- pairs, c == c' ] ++ anys > step _ = [] precondition: there are no epsilon cycles! > epsClosure [] = [] > epsClosure (NFAEps ns:ns') = epsClosure (ns++ns') > epsClosure (n:ns) = n:epsClosure ns > acceptor :: NFAproducer -> String -> Bool > acceptor nfa str = nfaRun ( {- epsClosure -} [nfa nfaFinal]) str The NFA interpreter > nfaRun :: [NFANode] -> String -> Bool > nfaRun ns (c:cs) = nfaRun (nfaStep ns c) cs > nfaRun ns [] = not (null ( {- epsClosure -} (concat (map step ns)))) > where > step (NFAEnd n') = [n'] > step (NFAFinal) = [NFAFinal] > step (NFATable pairs anys ends True) = [NFAFinal] > step (NFATable pairs anys ends finals) = ends > step _ = []