\documentclass{article} \usepackage{alltt} \newenvironment{code} {\begin{alltt}} {\end{alltt}} \title{Deterministic Finite Automaton with Effects} \author{Jo\~ao Saraiva} \date{\today} \begin{document} \maketitle \begin{code}
module Language.HaLex.DfaMonad where

import Data.Char
import Data.List
import Language.HaLex.Ndfa
import Control.Monad
import Control.Monad.State

import System.IO.Unsafe
import Data.IORef

--
-- Generic Functions to Implement a Recognizer based on Finite State Machines
-- (Deterministic Finite Automata)
--

data Dfa m st sy  = Dfa [sy]                -- Vocabulary
                        [st]                -- Finite set of states
                        st                  -- The start state
                        [st]                -- The set of final states
                        (st -> sy -> m st)  -- Monadic Transition Function
\end{code} The function \texttt{dfawalk} recurses over a list. As a consequence, we can re-write that function using the pre.defined Monadic fold function \texttt{foldM}.. \begin{code}
dfawalk :: Monad m => (st -> sy -> m st) -> st -> [sy] -> m st
dfawalk delta s = foldM delta s
\end{code} \begin{code}
dfaaccept' :: (Monad m , Eq st) => Dfa m st sy -> [sy] -> m Bool
dfaaccept' (Dfa _ _ s z delta) sent =
  do st <- foldM delta s sent
     return (st `elem` z)

dfaaccept :: (Monad m, Eq st) => Dfa m st sy -> [sy] -> m Bool
dfaaccept (Dfa _ _ s z delta) sent =
  do { st <- dfawalk delta s sent
     ; return (st `elem` z)
     }
\end{code} \begin{code}

runDfa :: Eq st => Dfa (State s) st sy -> [sy] -> s -> (Bool,s)
runDfa dfa str initSt = runState (dfaaccept dfa str) initSt

\end{code} \begin{code}

showDfa :: (Monad m, Show st, Show sy) => Dfa m st sy -> m String
showDfa (Dfa v q s z delta) =
  do let a = showString ("dfa = Dfa v q s z delta") .
             showString ("\n  where \n\t v = ") .
             showList v .
             showString ("\n\t q = ") .
             showList q .
             showString ("\n\t s = ") .
             shows s .
             showString ("\n\t z = ") .
             showList z .
             showString ("\n\t -- delta :: st -> sy -> st \n")
     b <- showDfaDelta q v delta
     return ((a . b) "" )

showDfaDelta :: (Monad m, Show st, Show sy) => [st] -> [sy] -> (st -> sy -> m st) ->
                                               m ([Char] -> [Char])
showDfaDelta q v d =
  do let l     = [(a,b) | a <- q , b <- v]
     let (m,n) = unzip l
     q'        <- mapM (uncurry d) l
     let f     = zipWith3 showF m n q'
     return (foldr (.) (showChar '\n') f)
  where
    showF st sy st' = showString("\t delta ") .
                      shows st .
                      showChar(' ') .
                      shows sy .
                      showString(" = return ") .
                      shows st' .
                      showChar('\n')
\end{code} \begin{code}

showInDot :: (Monad m , Eq st , Show st , Show sy)
          => Dfa m  st sy             -- The Monadic Deterministic Finite Automaton
          -> Bool                     -- Result's Type: True -> Show Dead States
                                      --                 False -> Don't Show Dead States
          -> m [Char]                 -- The Dot string
showInDot dfa@(Dfa v q s z delta) t =
   do dsts <- deadstates dfa
      let a = showString ("digraph fa {\n") .
              showString ("rankdir = LR ;\n") .
              showString ("orientation=land;\n") .
              (showInitialState s) . showChar '\n' .
              showString (showElemsListPerLine (showFinalStates' z)) .
              showString ("node [shape=circle , color=black];\n")
      b <- showArrows dfa dsts t
      return ((a (concat b)) ++ "}\n")


showElemsListPerLine :: [String] -> String
showElemsListPerLine []    = ""
showElemsListPerLine (h:t) = ((showString h) "\n") ++ (showElemsListPerLine t)

showInitialState s = showString("\"") . shows s .  showChar('\"') .
                     showString (" [shape=circle , color=green];\n")

-- showFinalStates' :: Show a => [a] -> [String]
showFinalStates' z = [ "\"" ++ (show f) ++ "\""
                       ++ " [shape=doublecircle , color=red];" | f <- z ]

showArrows :: (Monad m, Eq st , Show st, Show sy)
           => Dfa m st sy -> [st] -> Bool -> m [[Char]]
showArrows (Dfa v qs s z delta) dsts t  =
   do let qs' = if t then qs else qs <-> dsts
      xpto [ mapM (buildLine q delta dsts t) v | q <- qs'  ]

buildLine :: (Monad m , Eq st , Show st, Show sy)
          => st -> (st -> sy -> m st) -> [st] -> Bool -> sy -> m [Char]
buildLine q delta dsts t v =
  do let a = showChar('\"') . shows q . showChar('\"') . showString (" -> \"")
     q' <-  delta q v
     let line = if ( (not t) && q' `elem` dsts ) then ""
                else ((a . shows q' . showString ("\" [label = \"") . shows v .
                      showString("\"];\n") ) "")
     return line


(<->) :: Eq a => [a] -> [a] -> [a]
l1 <-> l2 = [ x | x <- l1 , not (x `elem` l2) ]



xpto :: Monad m => [m [a]] -> m [a]
xpto [] = return []
xpto (x:xs) = do rx <- x
                 rxs <- xpto xs
                 return (rx++rxs)
\end{code} \begin{code}

deadstates :: (Monad m , Eq st) => Dfa m st sy -> m [st]
deadstates (Dfa v qs s z d) = deadstates' qs v d

deadstates' :: (Monad m, Eq st) => [st] -> [sy] -> (st -> sy -> m st) -> m [st]
deadstates' [] _ _ = return []
deadstates' (q:qs) v delta =
  do rq <- isStDead q (mapM (delta q) v)
     rqs <- deadstates' qs v delta
     return (if rq then q : rqs else rqs)

--
-- A state a is dead whenever a final state can not be reached from a.
-- (this function checks for synv states only: states that for every symbol
--

isStDead  :: (Monad m, Eq st) => st -> m [st] -> m Bool
isStDead = isSyncState

--
-- We've implemented a simpler function only: isSyncState
-- A non initial, non final state A is said to be a sync state
-- iff for every vocabulary symbol the automaton moves to A.
--

isSyncState :: (Monad m, Eq st) => st -> m [st] -> m Bool
isSyncState st msts = do sts <- msts
                         return (and (isin' st sts))
  where isin' e []     = [True]
        isin' e (x:xs) = (e==x) : isin' e xs

\end{code} \section{Maybe Monad based Automata} \begin{code}

robot :: Dfa Maybe [Char] [Char]
robot = Dfa ["esquerda","direita","largar","pegar"]
            ["C0 sem pepita","C0 com pepita","C1 sem pepita","C1 com pepita"]
            "C0 sem pepita"
            ["C0 sem pepita"]
            delta
  where
    delta "C0 sem pepita" "direita"  = -- do putStrLn "Vou para C1 sem pepita"
                                          return "C1 sem pepita"
    delta "C0 sem pepita" _          = return "C0 sem pepita"

    delta "C0 com pepita" "largar"   = return "C0 sem pepita"
    delta "C0 com pepita" "direita"  = return "C1 com pepita"
    delta "C0 com pepita" _          = return "C0 com pepita"

    delta "C1 sem pepita" "esquerda" = return "C0 sem pepita"
    delta "C1 sem pepita" "pegar"    = return "C1 com pepita"
    delta "C1 sem pepita" _          = return "C1 sem pepita"

    delta "C1 com pepita" "esquerda" = -- do putStrLn "Vou para C0 com pepita"
                                          return "C0 com pepita"
    delta "C1 com pepita" "largar"   = return "C1 sem pepita"
    delta "C1 com pepita" _          = return "C1 com pepita"


moves = ["direita","pegar","esquerda","largar"]
moves2 = ["esquerda","pegar","direita","pegar","esquerda","largar"]
moves3 = ["esquerda","pegar","direita"]
moves4 = moves2 ++ moves ++ moves3

acc = dfaaccept robot moves4

{-
exShow = do d <- showInDot robot True
            writeFile "xxx" d
-}

\end{code} \begin{verbatim} Dfa> dfaaccept ex "aba" Just True \end{verbatim} \section{IO Monad based Automata} \begin{code}
varGlob = unsafePerformIO $ newIORef ""

ex2 :: Dfa IO Char Char
ex2 = Dfa ['a','b']
         ['A','B','C','D']
         'A'
         ['C']
         delta
  where
    delta 'A' 'a' = do  v <- readIORef varGlob
                        writeIORef varGlob ('A':v)
                        putStrLn ('A':v)
                        return 'A'
    delta 'A' 'b' = do  putStrLn "b"
                        return 'B'
    delta 'B' 'a' = return 'C'
    delta _ _ = return 'D'
\end{code} \begin{verbatim} Dfa> dfaaccept ex2 "aba" A b True \end{verbatim} \section{State Monad Based Automata} The State Monad \begin{code}


{-
data State s v = State (s -> (v,s))

instance Show (State s v) where
  show = showState

apply :: State s v -> s -> (v,s)
apply (State f) s = f s


instance Monad (State s) where
  return x        = State f
                      where f s = (x,s)
  p >>= q         = State f
                      where f s = let
                                     (x,s') = apply p s
                                  in apply (q x) s'

runState :: State s v -> s -> (v,s)
runState (State f) s0 = let (v,s) = f s0 in (v,s)
-}

\end{code} One Example \begin{code}

-- er: a* b a
-- Conta o numero de ocorrĂȘncia dos caracter a

ex3 :: Dfa (State Integer) Char Char
ex3 = Dfa ['a','b']
         ['A','B','C']
         'A'
         ['C']
         delta
  where
    delta 'A' 'a' = do countA
		       return 'A'
    delta 'A' 'b' = return 'B'
    delta 'B' 'a' = do countA
                       return 'C'

    countA = state f
      where f s = ((),s+1)

runAccept dfa str = runState (dfaaccept dfa str) 0


\end{code} \begin{code}

-- er: a* b a
-- Accumulates in the State the occurrences of caracter a

ex4 :: Dfa (State [Char]) Char Char
ex4 = Dfa ['a','b']
          ['A','B','C']
          'A'
          ['C']
          delta
  where
    delta 'A' 'a' = do modify (\ s -> 'a':s)
		       return 'A'
    delta 'A' 'b' = return 'B'
    delta 'B' 'a' = do modify (\ s -> 'a':s)
                       return 'C'


runAccept_ex4 dfa str = runState (dfaaccept dfa str) ""


\end{code} \subsection{Computes Tracing Information} It computes the visited states (nodes). \begin{code}

ex5 :: Dfa (State [Char]) Char Char
ex5 = Dfa ['a','b']
          ['A','B','C']
          'A'
          ['C']
          delta
  where
    delta 'A' 'a' = do { accum 'A' ; return 'A' }
    delta 'A' 'b' = do { accum 'B' ; return 'B' }
    delta 'B' 'a' = do { accum 'C' ; return 'C' }

    accum x = modify (\ s -> x:s)

runAccept_ex5 str = runState (dfaaccept ex5 str) ""
\end{code} \subsection{The language of Integer Numbers} \begin{code}

-- er: ('+'|'-')?d+
-- Accumulates in the State the occurrences of characters '-' and 'd'

ex_int :: Dfa (State [Char]) Integer Char
ex_int = Dfa ['+','-','d']
             [1,2,3]
             1
             [3]
             delta
  where
    delta 1 '+' = return 2
    delta 1 '-' = do accumM
                     return 2
    delta 1 'd' = do accumD
                     return 3
    delta 2 'd' = do accumD
                     return 3
    delta 3 'd' = do accumD
                     return 3

    accumM = state f
      where f s = (s,'-':s)

    accumD = state f
      where f s = (s,'d':s)


runAccept_int str = runState (dfaaccept ex_int str) ""
\end{code} The language of Integer Numbers Follow by " " \begin{code}

-- er: ('+'|'-')?d+
-- Accumulates in the State the occurrences of characters '-' and 'd'

ex6 :: Dfa (State ([Char],Int)) Integer Char
ex6 = Dfa ['+','-','1','2',' ']
          [1,2,3,4,5]
          1
          [4]
          delta
  where
    delta 1 '+' = return 2
    delta 1 '-' = do { accum '-' ; return 2 }
    delta 1 '1' = do accum '1'
                     return 3
    delta 1 '2' = do accum '2'
                     return 3
    delta 2 '1' = do accum '1'
                     return 3
    delta 2 '2' = do accum '2'
                     return 3
    delta 3 '1' = do accum '1'
                     return 3
    delta 3 '2' = do accum '2'
                     return 3
    delta 3 ' ' = do convert
                     return 4

    delta _ _ = return 5                      -- To complete the DFA

    accum x = state f
      where f s = ((),((fst s)++[x],snd s))

    convert = state f
      where f s = ((),(fst s,(read (fst s))::Int))


runAccept_ex6 str = runState (dfaaccept ex6 str) ("",0::Int)


\end{code} \begin{verbatim} Dfa> runAccept_ex6 "-12 " (True,("-12",-12)) \end{verbatim} \section{A Text Editor Interpreter} \begin{code}
type Instr = [Code]

data Code = Open   String
          | Locate Int
          | Insert String
          | Delete [Int]
          | Save
          | End
          deriving Show
\end{code} \begin{code}
te :: Dfa (State ([Char],[Code])) Integer Char
te = Dfa ['A','x','1','0',' ']
         [1,2,3,4,9,10,20]
         1
         [3,4]
         delta
  where
    delta 1 'A' = do init
                     return 2
    delta 2 x | isLower x  = do accum x
                                return 3
              | otherwise  = return 20
    delta 3 'R' = do open
                     return 4
    delta 3 x | isLower x || isDigit x  = do accum x
                                             return 3
              | otherwise = return 20
    delta 4 'A' = do init
                     return 2

    delta 4 'P' = do init
                     return 9
    delta 9 x | isDigit x = do accum x
                               return 10
    delta 10 x | isDigit x = do accum x
                                return 10
    delta 10 'R' = do locate
                      return 4

    delta _ _ = return 20


    accum x = state f
      where f s = ((),((fst s)++[x],snd s))

    init = state f
      where f s = ((),("",snd s))

    open = state f
      where f s = ((),(fst s,(snd s) ++ [Open (fst s)]))

    locate = state f
      where f s = ((),(fst s,(snd s) ++ [Locate (read (fst s))]))


runAccept_te str = runState (dfaaccept te str) ("",[])

\end{code} \begin{verbatim} Dfa> runAccept_te "Aficheiro10RP12R" (True,("12",[Open "ficheiro10",Locate 12])) \end{verbatim} \section{Protocol} "000((0|1)(0|1)(0|1)(001(0|1)(0|1)(0|1))*)111" \begin{code}
pr :: Dfa (State ([Char],[Int])) Integer Char
pr = Dfa ['1','0']
         [1,2,3,4,5,6,7,8,9,10,11,12,13]
         1
         [12]
         delta
  where
    delta 1  '0' = return 2
    delta 2  '0' = return 3
    delta 3  '0' = return 4
    delta 4  '0' = do { accum '0' ; return 5 }
    delta 4  '1' = do { accum '1' ; return 5 }
    delta 5  '0' = do { accum '0' ; return 6 }
    delta 5  '1' = do { accum '1' ; return 6 }
    delta 6  '0' = do { accum '0' ; accumList ; return 7 }
    delta 6  '1' = do { accum '1' ; accumList ; return 7 }
    delta 7  '0' = return 8
    delta 7  '1' = return 9
    delta 8  '0' = return 10
    delta 9  '1' = return 11
    delta 10 '1' = do { init ; return 4 }
    delta 11 '1' = do { init ; return 12 }

    delta _ _ = return 13


    accum x = modify (\ s -> ((fst s)++[x],snd s))
    init        = modify (\ s -> ("",snd s))
    accumList   = modify (\ s -> (fst s,snd s ++ [converte (fst s)]))


converte :: [Char] -> Int
converte []       = 0
converte ('0':xs) = converte xs
converte ('1':xs) = expo 2 (length xs) + converte xs

expo v e | e > 0     = v * (expo v (e-1))
         | otherwise = 1

runAccept_pr :: [Char] -> (Bool,([Char],[Int]))
runAccept_pr str = runState (dfaaccept pr str) ("",[])

-- runAccept_gv = writeFile "xx.dot" (fst (runState (showDfaDiGraph  pr) ("",[])))


\end{code} \end{document}