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