esotericbot-0.0.5: hs_plugins/SK.hs
import Text.ParserCombinators.Parsec
import System.Environment
import Data.List.Stream as L
import Data.Maybe
import Data.Graph.Inductive
import qualified Data.Foldable as F
import Control.Monad.Stream as C
import Control.Monad.State
import Control.Arrow
data SK =
App
| S
| K
| I
| PH Char
deriving Eq
type SKM =
StateT SKgr IO
-- for output of results
data SKstr =
SKstr [ SKstr ]
| ASK SK
instance Show SKstr where
show ( SKstr sks ) =
"(" L.++ ( L.concat $ L.map show sks ) L.++ ")"
show ( ASK sk ) =
show sk
type SKgr =
Gr SK Int
instance Show SK where
show App =
"App"
show ( PH c ) =
[ c ]
show S =
"S"
show K =
"K"
show I =
"I"
sk_str = do
complex_sks <- node_to_str 0
let skstr = simplify complex_sks
return $
case skstr of
SKstr sks ->
sks
_ ->
[ skstr ]
simplify ( SKstr sks ) =
if L.length sks == 1
then
simplify $ L.head sks
else
SKstr $ L.map simplify sks
simplify x =
x
eval first red n can_fail = do
msk <- find_term n
maybe next
( \ ( sk , p ) -> do
input <- suck_input sk p [ ]
if L.length input >= suck_level sk
then do
sub first red ( snd $ L.head p , snd $ L.head $ L.tail p ) sk input
eval False red n False
else
next
)
msk
where
next =
if can_fail
then
sk_str
else
eval False red ( n + 1 ) True
sub first red ( n , parent ) sk input = do
oldgr <- getGr
if ( red && not first ) then sk_str >>= liftIO . output_sk_strs else return ( )
let writeNode a g = do
( x , _ ) <- valueOf a
case x of
App ->
putGr $ insEdges ( inn oldgr n L.++ ( mkEdgesFrom n $ out oldgr a ) ) $ insNode ( n , x ) $ delNode n g
_ ->
putGr $ insEdges ( inn oldgr n ) $ insNode ( n , x ) $ delNode n g
case sk of
K ->
case input of
[ a , b ] -> do
writeNode a $ delNode a $ cleanupNode b oldgr
_ ->
return ( )
S -> do
case input of
[ a , b , c ] -> do
( aVal , _ ) <- valueOf a
( bVal , _ ) <- valueOf b
( cVal , _ ) <- valueOf c
cloneNode c a
gr <- getGr
let bc = [ ( b , newb , 0 ) , ( b , newc , 1 ) ]
[ newb , newc ] = newNodes 2 gr
newbEdges = mkEdgesFrom newb $ out gr b
newcEdges = mkEdgesFrom newc $ out gr c
nEdges = mkEdgesFrom n $ out oldgr a
parentEdges = [ ( parent , n , 0 ) , ( parent , a , 1 ) , ( parent , b , 2 ) ]
without_old_nodes = delNode n $ delNode c $ delNode b $ gr
new_node_gr = insNode ( b , App )
$ insNode ( newc , cVal )
$ insNode ( newb , bVal )
$ insNode ( n , aVal )
$ without_old_nodes
new_gr = insEdges parentEdges
$ insEdges newbEdges
$ insEdges newcEdges
$ insEdges nEdges
$ insEdges bc
$ new_node_gr
putGr new_gr
_ ->
return ( )
I ->
case input of
[ a ] -> do
writeNode a $ delNode a oldgr
_ ->
return ( )
where
mkEdgesFrom n es = L.map ( \ ( _ , to , v ) -> ( n , to , v ) ) es
cloneNode from to = do
gr <- getGr
str <- node_to_str from
putGr $ delNode to gr
add_node_str to str
add_node_str n skstr = do
gr <- getGr
case skstr of
SKstr sks -> do
putGr $ insNode ( n , App ) gr
C.mapM_ ( joinToNode n ) sks
ASK x ->
putGr $ insNode ( n , x ) gr
where
joinToNode n sk = do
gr <- getGr
( _ , seq ) <- valueOf n
let newEdge = if L.null seq
then 0
else fst ( L.maximum seq ) + 1
newNode = L.head $ newNodes 1 gr
case sk of
SKstr sks -> do
putGr $ insEdge ( n , newNode , newEdge ) $ insNode ( newNode , App ) gr
C.mapM_ ( joinToNode newNode ) sks
ASK x -> do
putGr $ insEdge ( n , newNode , newEdge ) $ insNode ( newNode , x ) gr
node_to_str n = do
( sk , seq ) <- valueOf n
case sk of
App -> do
linkedVals <- C.mapM node_to_str $ L.map snd seq
return $ SKstr linkedVals
_ ->
return $ ASK sk
cleanupNode n gr =
let ( Just ( _ , _ , sk , seq ) , withoutn ) = match n gr
in
case sk of
App ->
L.foldr cleanupNode withoutn $ L.map snd seq
_ ->
withoutn
sk_panic msg =
error $ L.unlines [ "SK panic! The possible happened!!!1"
, msg
]
valueOf a = do
gr <- getGr
maybe ( sk_panic $ "Lookup for node " L.++ show a L.++ " failed!" )
( \ ( _ , _ , sk , sks ) -> return ( sk , sort sks ) )
( fst $ match a gr )
suck_input sk p sucked = do
if could_suck_more sk sucked
then do
ms <- suck_one p
maybe ( return sucked )
( \ ( newp , s ) -> suck_input sk newp $ sucked L.++ [ s ] )
ms
else
return sucked
suck_one ( ( Just edge_to_parent , _ ) : next@( ( _ , parent_address ) : rest ) ) = do
( _ , sequence ) <- valueOf parent_address
let mnext_edge_and_term = find ((<) edge_to_parent . fst ) sequence
maybe ( suck_one next )
( \ ( next_edge , next_term ) -> do
return $ Just ( ( Just next_edge , next_term ) : next , next_term )
)
( mnext_edge_and_term)
suck_one _ =
return Nothing
getGr :: SKM SKgr
getGr =
get
putGr :: SKgr -> SKM ( )
putGr graph = do
put graph
find_term n = do
terms <- first_terms ( Nothing , 0 ) [ ]
if L.length terms > n
then
return $ Just $ terms L.!! n
else
return Nothing
first_terms :: ( Maybe Int , Int ) -> [ ( Maybe Int , Int ) ] -> SKM [ ( SK , [ ( Maybe Int , Int ) ] ) ]
first_terms addr@( maybe_edge , n ) path = do
( sk , sequence ) <- valueOf n
skterm ( if L.null sequence
then
return [ ]
else do
( nVal , _ ) <- valueOf $ snd $ L.head sequence
skterm ( do
let tovisit = L.map ( first Just ) sequence
ts <- C.mapM ( flip first_terms ( addr : path ) ) tovisit
let just_ts = L.concat ts
if L.null just_ts
then
return [ ]
else
return just_ts
)
( const $ do
return [ ( nVal , ( first Just $ L.head sequence ) : addr : path ) ]
)
nVal
)
( const $
return [ ]
)
sk
skterm f g t =
case t of
App ->
f
PH c ->
f
x ->
g x
suck_level K = 2
suck_level I = 1
suck_level S = 3
sk_strP = do
sk <- noneOf ")"
case sk of
'k' -> isK
's' -> isS
'i' -> isI
'K' -> isK
'S' -> isS
'I' -> isI
'(' -> do
sks <- many sk_strP
char ')'
return $ SKstr sks
pl ->
return $ ASK $ PH pl
where
isK = return $ ASK K
isS = return $ ASK S
isI = return $ ASK I
could_suck_more sk sucked =
if L.length sucked >= suck_level sk
then
False
else
True
main = do
prog <- getContents
args <- getArgs
exec_sk ( not $ L.null args ) prog
exec_sk red skprog = do
( _ , skgr ) <- parse_sk skprog
ss <- evalStateT ( eval True red 0 False ) skgr
output_sk_strs ss
output_sk_strs :: [ SKstr] -> IO ( )
output_sk_strs sk_strs = do
putStrLn $ L.concat $ L.map show sk_strs
parse_sk sk = do
name <- getProgName
case parse ( many $ sk_strP ) name sk of
Left err ->
error $ show err
Right skstr ->
build_initial $ SKstr skstr
build_initial skstr =
flip runStateT empty $ add_node_str 0 skstr