module Main where
import Char (ord,chr)
import System (getArgs,system)
import List (nub,isPrefixOf)
import System.IO.Unsafe (unsafePerformIO)
import System.IO (isEOF,getChar,putChar,hSetBuffering,BufferMode(..)
,stdin,stdout,stderr,hPutStrLn,hFlush)
type Tokens = (String,String,String,String,String,String,String,String)
data Token = MRight | MLeft | Inc | Dec | In | Out | JINZ | JMatch
deriving Show
data Stmt = TMRight | TMLeft | TInc | TDec | TIn | TOut | TLoop [Stmt]
deriving Show
type Machine = ([Int],[Int])
type Interpreter = Machine -> String -> String
{- |
A brainFuck and derived languages interpreter. Currently supports Brainfuck
and Ook.
Usage:
BF [-m mode] prog
Options:
-m Sets the programming languages interpreted. Valid options are:
bf -- Brainfuck
ook -- Ook!
-}
main = do args <- System.getArgs
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
let (tokens,prog) = parseArgs args
interact $ interpret (const (const ""))
(fst $ parse $ tokenise prog tokens)
emptyMachine
emptyMachine :: Machine
emptyMachine = (repeat 0,repeat 0)
{- | Parse the arguments given to the BF program -}
parseArgs :: [String] -> (Tokens,String)
parseArgs [] = (("","","","","","","",""),"")
parseArgs [p] = ((">","<","+","-",",",".","[","]")
,unsafePerformIO $ readFile p)
parseArgs ("-m":"bf":xs) =
let (_,f) = parseArgs xs
in ((">","<","+","-",",",".","[","]"),f)
parseArgs ("-m":"ook":xs) =
let (_,f) = parseArgs xs
in (("Ook.Ook?","Ook?Ook.","Ook.Ook.","Ook!Ook!"
,"Ook.Ook!","Ook!Ook.","Ook!Ook?","Ook?Ook!"),f)
{- | Tokenise the program -}
tokenise :: String -> Tokens -> [Token]
tokenise p ts@(r,l,i,d,inp,out,j,jm) =
tokenise'
(filter ((flip elem) (nub (concat [r, l, i, d, inp, out, j, jm]))) p) ts
where
tokenise' :: String -> Tokens -> [Token]
tokenise' p ts@(r,l,i,d,inp,out,j,jm) =
if r `isPrefixOf` p then MRight : tokeniseAfter r p ts
else if l `isPrefixOf` p then MLeft : tokeniseAfter l p ts
else if i `isPrefixOf` p then Inc : tokeniseAfter i p ts
else if d `isPrefixOf` p then Dec : tokeniseAfter d p ts
else if inp `isPrefixOf` p then In : tokeniseAfter inp p ts
else if out `isPrefixOf` p then Out : tokeniseAfter out p ts
else if j `isPrefixOf` p then JINZ : tokeniseAfter j p ts
else if jm `isPrefixOf` p then JMatch : tokeniseAfter jm p ts
else case p of
[] -> []
(_:np) -> tokenise' np ts
dropToken :: String -> String -> String
dropToken t p = drop (length t) p
tokeniseAfter :: String -> String -> Tokens -> [Token]
tokeniseAfter t p ts = tokenise' (dropToken t p) ts
{- | Parse the program -}
parse :: [Token] -> ([Stmt],[Token])
parse [] = ([],[])
parse (MRight:xs) = ((TMRight:st),r) where (st,r) = parse xs
parse (MLeft:xs) = ((TMLeft:st),r) where (st,r) = parse xs
parse (Inc:xs) = ((TInc:st),r) where (st,r) = parse xs
parse (Dec:xs) = ((TDec:st),r) where (st,r) = parse xs
parse (In:xs) = ((TIn:st),r) where (st,r) = parse xs
parse (Out:xs) = ((TOut:st),r) where (st,r) = parse xs
parse (JINZ:xs) = (TLoop lst:st,rr)
where
(lst,r) = parse xs
(st,rr) = parse r
parse (JMatch:xs) = ([],xs)
{- | Interpret some brain fuck -}
interpret :: Interpreter -> [Stmt] -> Interpreter
interpret c [] x i = c x i
interpret c (TMRight:xs) (l,(h:r)) i = interpret c xs ((h:l),r) i
interpret c (TMLeft:xs) ((h:l),r) i = interpret c xs (l,(h:r)) i
interpret c (TInc:xs) (l,(h:r)) i = interpret c xs (l,(h+1:r)) i
interpret c (TDec:xs) (l,(h:r)) i = interpret c xs (l,(h-1:r)) i
interpret c (TIn:xs) (l,(_:r)) i =
case i of
[] -> interpret c xs (l,(-1:r)) []
(y:ys) -> interpret c xs (l,(ord y:r)) ys
interpret c (TOut:xs) (l,(x:r)) i = (chr x) : (interpret c xs (l,x:r) i)
interpret c (TLoop loop:xs) (l,(h:r)) i =
if h == 0 then interpret c xs (l,(h:r)) i
else interpret (interpret c (TLoop loop:xs)) loop (l,(h:r)) i