{-# LANGUAGE AllowAmbiguousTypes #-}
module Parsers.Brainfuck.Attoparsec where

import Control.Applicative
import Data.Attoparsec.Combinator
import Data.ByteString as BS
import Data.Functor (($>))
import Data.Text as T
import qualified Data.Attoparsec.Internal.Types as AP

import Parsers.Utils.Attoparsec as AP
import Parsers.Brainfuck.Types

parser :: forall inp. AP.Inputable inp => AP.Parser inp [Instruction]
parser = whitespace *> bf <* endOfInput
  where
  whitespace = skipMany (AP.satisfy (AP.notInClass @inp "<>+-.,[]"))
  lexeme :: AP.Parser inp a -> AP.Parser inp a
  lexeme p = p <* whitespace
  bf = many (lexeme (AP.char '>' $> Forward)
    <|> lexeme (AP.char '<' $> Backward)
    <|> lexeme (AP.char '+' $> Increment)
    <|> lexeme (AP.char '-' $> Decrement)
    <|> lexeme (AP.char '.' $> Output)
    <|> lexeme (AP.char ',' $> Input)
    <|> between (lexeme (AP.char '[')) (lexeme (AP.char ']')) (Loop <$> bf))
-- Specializing is essential to keep best performances.
{-# SPECIALIZE parser :: AP.Parser T.Text [Instruction] #-}
{-# SPECIALIZE parser :: AP.Parser BS.ByteString [Instruction] #-}