{-# OPTIONS_GHC -fno-warn-tabs      #-}
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE TypeOperators          #-}
{-|
Module      : StackA
Description : Stack Automaton
Copyright   : (c) Jorge Santiago Alvarez Cuadros, 2016
License     : GPL-3
Maintainer  : sanjorgek@ciencias.unam.mx
Stability   : experimental
Portability : portable

Stack Automaton
-}
module Math.Model.Automaton.Stack where
import           Data.Delta
import qualified Data.Foldable   as Fold
import           Data.List
import qualified Data.Map.Strict as Map
import           Data.Sigma
import           Data.Label
import           Control.Monad.State.Lazy

{-|
Delta for stack machine, takes a state, a symbol in string input or not and a
symbol in stack head and returns next state and update stack
-}
type Delta a = (:->:) a (Maybe Symbol, Symbol) Wd

{-|
A key for a delta.
-}
type Key a = (Label a, (Maybe Symbol, Symbol))

{-|
Takes a list of tuples and lift a Delta

>>>let delta = liftD [(0,"(",'Z',0,"IZ"),(0,"",'Z',0,""),(0,"(",'I',0,"II"),(0,")",'I',0,"")]
-}
liftDelta:: Ord a => [(a, Wd, Symbol, a, Wd)]-> Delta a
liftDelta xs = let
    (as,bs,cs,ds,es) = unzip5 xs
    f = fmap Q
    g [] = Nothing
    g (x:_) = Just x
    ps = zip (fmap g bs) cs
    ks = zip (f as) ps
    rs = zip (f ds) es
  in Map.fromList (zip ks rs)

nextDTuple :: Ord a => Delta a -> Key a -> (Label a, Wd)
nextDTuple dt k = if Map.member k dt then dt Map.! k else (QE,[])

-- |Stack machine only needs a delta, an init state and an initial symbol.
--
-- This works for empty stack and final state acceptor
data StackA a = Stack {
  getDelta::Delta a
  ,getInitState::Label a
  ,getFinal::Final a
  ,getInitSymbol::Symbol} deriving(Show, Eq)

nextState::(Ord a) => Delta a -> Wd -> State (Wd, Label a) (Label a)
nextState _ [] = do
	(_, q) <- get
	return q