packages feed

tlex-core-0.3.0.0: src/Language/Lexer/Tlex/Machine/DFA.hs

module Language.Lexer.Tlex.Machine.DFA (
    DFA (..),
    DFAState (..),
    DFABuilder,
    DFABuilderContext,
    buildDFA,
    newStateNum,
    insertTrans,
    accept,
    initial,
) where

import           Language.Lexer.Tlex.Prelude

import qualified Data.EnumMap.Strict                 as EnumMap
import qualified Data.IntMap                         as IntMap
import qualified Data.List                           as List
import qualified Language.Lexer.Tlex.Machine.Pattern as Pattern
import qualified Language.Lexer.Tlex.Machine.State   as MState


data DFA a = DFA
    { dfaInitials :: EnumMap.EnumMap Pattern.StartState MState.StateNum
    , dfaTrans    :: MState.StateArray (DFAState a)
    }
    deriving (Eq, Show, Functor)

data DFAState a = DState
    { dstAccepts    :: [Pattern.Accept a]
    , dstTrans      :: IntMap.IntMap MState.StateNum
    , dstOtherTrans :: Maybe MState.StateNum
    }
    deriving (Eq, Show, Functor)


data DFABuilderContext m = DFABuilderContext
    { dfaBCtxInitials     :: EnumMap.EnumMap Pattern.StartState MState.StateNum
    , dfaBCtxNextStateNum :: MState.StateNum
    , dfaBCtxStateMap     :: MState.StateMap (DFAState m)
    }
    deriving (Eq, Show, Functor)

type DFABuilder m = State (DFABuilderContext m)

buildDFA :: DFABuilder m () -> DFA m
buildDFA builder =
    let bctx = execState builder initialBCtx
        arr = MState.totalStateMapToArray
            do dfaBCtxNextStateNum bctx
            do dfaBCtxStateMap bctx
    in DFA
        { dfaInitials = dfaBCtxInitials bctx
        , dfaTrans = arr
        }
    where
        initialBCtx = DFABuilderContext
            { dfaBCtxInitials = EnumMap.empty
            , dfaBCtxNextStateNum = MState.initialStateNum
            , dfaBCtxStateMap = MState.emptyMap
            }

newStateNum :: DFABuilder m MState.StateNum
newStateNum = do
    ctx0 <- get
    let nextStateNum = dfaBCtxNextStateNum ctx0
    put do ctx0
            { dfaBCtxNextStateNum = succ nextStateNum
            }
    pure nextStateNum

insertTrans :: MState.StateNum -> DFAState m -> DFABuilder m ()
insertTrans sf st = modify' \ctx0@DFABuilderContext{ dfaBCtxStateMap } -> ctx0
    { dfaBCtxStateMap = addCondTrans dfaBCtxStateMap
    }
    where
        addCondTrans n = MState.insertMap sf st n

accept :: MState.StateNum -> Pattern.Accept m -> DFABuilder m ()
accept s x = modify' \ctx0@DFABuilderContext{ dfaBCtxStateMap } -> ctx0
    { dfaBCtxStateMap = addAccept dfaBCtxStateMap
    }
    where
        addAccept n = MState.insertOrUpdateMap s
            do DState
                { dstAccepts = [x]
                , dstTrans = IntMap.empty
                , dstOtherTrans = Nothing
                }
            do \ds@DState { dstAccepts } -> ds
                { dstAccepts = List.insertBy
                    Pattern.compareAcceptsByPriority
                    x
                    dstAccepts
                }
            do n

initial :: MState.StateNum -> Pattern.StartState -> DFABuilder m ()
initial s x = modify' \ctx0@DFABuilderContext{ dfaBCtxInitials } -> ctx0
    { dfaBCtxInitials = EnumMap.insert x s dfaBCtxInitials
    }