{-# language BangPatterns #-}
{-# language BinaryLiterals #-}
{-# language DataKinds #-}
{-# language DeriveFunctor #-}
{-# language DerivingStrategies #-}
{-# language GADTSyntax #-}
{-# language KindSignatures #-}
{-# language LambdaCase #-}
{-# language MagicHash #-}
{-# language MultiWayIf #-}
{-# language PolyKinds #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language StandaloneDeriving #-}
{-# language TypeApplications #-}
{-# language UnboxedSums #-}
{-# language UnboxedTuples #-}

-- | Everything in this module is unsafe and can lead to
-- nondeterministic output or segfaults if used incorrectly.
module Data.Parser.Unsafe
  ( -- * Types
    Parser(..)
    -- * Functions
  , unconsume
  ) where

import GHC.Exts (Int(I#),Int#)
import Data.Bytes.Parser (Result(..),Slice(..))
import Data.Kind (Type)
import Data.Primitive.SmallArray (SmallArray(SmallArray))
import GHC.Exts (TYPE,State#,SmallArray#)

import qualified Control.Monad

type SmallVector# a = (# SmallArray# a, Int#, Int# #)
type ST# s (a :: TYPE r) = State# s -> (# State# s, a #)
type Result# e (a :: TYPE r) =
  (# e
  | (# a, Int#, Int# #) #) -- ints are offset and length

-- | A non-resumable toke parser.
newtype Parser :: Type -> Type -> Type -> Type -> Type where
  Parser :: { runParser :: SmallVector# a -> ST# s (Result# e b) }
         -> Parser a e s b

instance Functor (Parser a e s) where
  {-# inline fmap #-}
  fmap f (Parser g) = Parser
    (\x s0 -> case g x s0 of
      (# s1, r #) -> case r of
        (# e | #) -> (# s1, (# e | #) #)
        (# | (# a, b, c #) #) -> (# s1, (# | (# f a, b, c #) #) #)
    )

instance Applicative (Parser a e s) where
  pure = pureParser
  (<*>) = Control.Monad.ap

instance Monad (Parser a e s) where
  {-# inline return #-}
  {-# inline (>>=) #-}
  return = pureParser
  Parser f >>= g = Parser
    (\x@(# arr, _, _ #) s0 -> case f x s0 of
      (# s1, r0 #) -> case r0 of
        (# e | #) -> (# s1, (# e | #) #)
        (# | (# y, b, c #) #) ->
          runParser (g y) (# arr, b, c #) s1
    )

pureParser :: b -> Parser a e s b
pureParser a = Parser
  (\(# _, b, c #) s -> (# s, (# | (# a, b, c #) #) #))

-- | Move the cursor back by @n@ tokens. Precondition: you
-- must have previously consumed at least @n@ tokens.
unconsume :: Int -> Parser a e s ()
unconsume n = uneffectful $ \_ off len ->
  Success (Slice (off - n) (len + n) ())

uneffectful ::
     (SmallArray a -> Int -> Int -> Result e b)
  -> Parser a e s b
{-# inline uneffectful #-}
uneffectful f = Parser
  ( \(# arr,off,len #) s0 -> (# s0, unboxResult (f (SmallArray arr) (I# off) (I# len)) #) )

unboxResult :: Result e a -> (# e | (# a, Int#, Int# #) #)
unboxResult (Success (Slice (I# b) (I# c) a)) = (# | (# a, b, c #) #)
unboxResult (Failure e) = (# e | #)