packages feed

haskell-brainfuck-0.1.0.0: src/Main.hs

{- |
Module      : Main
Description : Evaluate a BrainFuck program
Copyright   : (c) Sebastian Galkin, 2014
License     : MIT
Maintainer  : paraseba@gmail.com
Stability   : experimental

Usage:

> brainfuck path/to/program.bf

It will parse the program and evaluate it by doing I/O to the console. In case
of parsing or execution errors it reports them to stderr

-}

module Main(main) where

import HaskBF.Eval
  ( evalBS, EvalResult ( EvalSuccess, EvalExecError, EvalParseError )
  , defaultIOMachine, BFExError, Tape ( Tape ), BFTape, errMsg, errTape, rTape )

import System.Exit
  ( ExitCode (..), exitWith )

import System.Environment
  ( getArgs, getProgName )

import qualified Data.ByteString.Lazy as BS

main :: IO ExitCode
main =
  getProgram                >>=
    BS.readFile             >>=
    evalBS defaultIOMachine >>=
    reportResults           >>=
    exitWith

{- | Read command line and obtain the path to the program. Display error message
 - if missing argument -}
getProgram :: IO FilePath
getProgram = do
  args <- getArgs
  if length args /= 1
    then do
           exe <- getProgName
           error $ "Usage: " ++ exe ++ " filepath"
    else return $ head args

-- | Report result of program parsing and evaluation
reportResults :: EvalResult -> IO ExitCode

reportResults (EvalSuccess _) = return ExitSuccess

reportResults (EvalParseError parseError) = do
  putStrLn "Error parsing program:"
  print parseError
  return $ ExitFailure 1

reportResults (EvalExecError err) = do
  putStrLn $ "Error evaluating program: " ++ errMsg err
  putStrLn $ "Current tape value: " ++ (show . rTape . errTape) err
  putStrLn $ "Consumed tape: " ++ (showConsumed . errTape) err
  return $ ExitFailure 2

{- | Use heuristic to display tape state. It estimates consumed right tape by
 - calling 'consumed' -}
showConsumed :: BFTape -> String
showConsumed (Tape _ _ r) =
  "[" ++ concatMap ( (++ ",") . show ) (consumed r ++ [0, 0, 0]) ++ "..."

-- | Return consumed tape by assuming that it is unused after 10 zeros.
consumed :: (Eq a, Num a) => [a] -> [a]
consumed (0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 : 0 : _) = []
consumed (x : xs) = x : consumed xs