module Parsers.Brainfuck.Handrolled where import Control.Monad (Monad(..), fail) import Data.ByteString as BS import Data.Char (Char) import Data.Maybe (Maybe(..)) import Data.Text as T import qualified Data.List as List import Parsers.Utils import qualified Parsers.Utils.Handrolled as HR import Parsers.Brainfuck.Types parser :: forall inp. CoerceEnum (HR.Token inp) Char => HR.Inputable inp => inp -> Maybe [Instruction] parser input = do (acc, is) <- walk input [] if HR.null is then fail "remaining input" else Just acc where walk :: inp -> [Instruction] -> Maybe ([Instruction], inp) walk inp acc = case HR.uncons inp of Nothing -> Just (List.reverse acc, HR.empty) Just (i, is) -> case coerceEnum i of ']' -> Just (List.reverse acc, inp) '>' -> walk is (Forward:acc) '<' -> walk is (Backward:acc) '+' -> walk is (Increment:acc) '-' -> walk is (Decrement:acc) '.' -> walk is (Output:acc) ',' -> walk is (Input:acc) '[' -> do (body, is') <- loop is walk is' (Loop body:acc) _ -> walk is acc loop :: inp -> Maybe ([Instruction], inp) loop inp = do (body, rest) <- walk inp [] case HR.uncons rest of Just (i, rest') | ']' <- coerceEnum i -> return (body, rest') _ -> fail "unclosed loop" -- Specializing is essential to keep best performances. {-# SPECIALIZE parser :: T.Text -> Maybe [Instruction] #-} {-# SPECIALIZE parser :: BS.ByteString -> Maybe [Instruction] #-}