{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Parsers.Brainfuck.SymanticParser.Grammar where

import Data.Char (Char)
import Data.Function ((.))
import qualified Prelude

import qualified Symantic.Parser as SP

import Parsers.Utils
import Parsers.Brainfuck.Types

-- | Use with @$$(runParser @Text grammar)@,
-- but in another Haskell module to avoid
-- GHC stage restriction on such top-level splice.
grammar :: forall tok repr.
  CoerceEnum Char tok =>
  CoerceEnum tok Char =>
  SP.Grammarable tok repr =>
  repr [Instruction]
grammar = whitespace SP.*> bf
  where
  whitespace = SP.skipMany (SP.noneOf (coerceEnum @_ @tok Prelude.<$> "<>+-,.[]"))
  lexeme :: repr a -> repr a
  lexeme p = p SP.<* whitespace
  bf :: repr [Instruction]
  bf = SP.many (lexeme (SP.match (SP.look (SP.item @tok))
                                 (SP.prod . coerceEnum Prelude.<$> "<>+-,.[")
                                 op SP.empty))
  op :: SP.Production '[] tok -> repr Instruction
  op prod = case coerceEnum (SP.runValue prod) of
    '<' -> SP.item @tok SP.$> SP.prod Backward
    '>' -> SP.item @tok SP.$> SP.prod Forward
    '+' -> SP.item @tok SP.$> SP.prod Increment
    '-' -> SP.item @tok SP.$> SP.prod Decrement
    ',' -> SP.item @tok SP.$> SP.prod Input
    '.' -> SP.item @tok SP.$> SP.prod Output
    '[' -> SP.between (lexeme (SP.item @tok))
                      (SP.token (coerceEnum @_ @tok ']'))
                      ($(SP.prodCon 'Loop) SP.<$> bf)
    _ -> Prelude.undefined