packages feed

jordan-0.1.0.0: lib/Jordan/FromJSON/ParseInternal.hs

{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Module containing internal helpers for our parsers.
module Jordan.FromJSON.ParseInternal
    where

import Control.Applicative (Alternative(..))
import Data.Foldable (asum)

-- | A parser for permutations.
--
-- Based on the paper Parsing Permutation Phrases by
-- Arthur Baars, Andres Loh, and S. Doaitse Swierstra.
--
-- The source code for 'Control.Applicative.Permutations' really helped
-- in writing this, although this type is structured differently (and closer to the actual paper).
-- Thank you very much to Alex Washburn!
data Permutation parser a
  = Choice [Branch parser a]
  -- ^ We have multiple options for how to parse further.
  | Empty a
  -- ^ We have reached the end and only have a single value.

-- | A branch of a permutation.
-- Permutation parsers work by building up the entire tree of
-- possible parsers, which is efficient in Haskell due to laziness.
data Branch parser a
  = forall arg. Branch (Permutation parser (arg -> a)) (parser arg)

instance (Functor m) => Functor (Branch m) where
  fmap f (Branch perm p) = Branch (fmap (f .) perm) p

instance (Functor m) => Functor (Permutation m) where
  fmap f = \case
    Choice c -> Choice $ fmap f <$> c
    Empty a -> Empty (f a)

instance (Alternative m) => Applicative (Branch m) where
  pure a = Branch (pure $ const a) (pure ())
  (Branch permuteF argF) <*> (Branch permuteA argA) =
    Branch (args <$> permuteF <*> permuteA) arguments
      where
        arguments = ((,) <$> argA <*> argF) <|> (flip (,) <$> argF <*> argA)
        args :: (arg1 -> a -> b) -> (arg2 -> a) -> (arg2, arg1) -> b
        args f a (aa, fa) = f fa (a aa)

instance (Alternative m) => Applicative (Permutation m) where
  pure = Empty
  (Empty f) <*> (Empty a) = Empty $ f a
  (Empty f) <*> (Choice choices) = Choice $ fmap f <$> choices
  (Choice f) <*> (Empty a) = Choice $ fmap ($ a) <$> f
  t1@(Choice bs1) <*> t2@(Choice bs2) = Choice (map ins2 bs1 ++ map ins1 bs2)
    where
      ins1 (Branch perm p) = Branch ((.) <$> t1 <*> perm) p
      ins2 (Branch perm p) = Branch (flip <$> perm <*> t2) p

-- | Wrap up a permutation parser with two effects:
--
-- It will first interleave an infinite number of some effects, which represent parsing "junk" or unwanted fields.
-- At every stage of the permutation, we will first try to run the effect we want, and if it fails
-- we will try to run the "junk" effect instead, then try again.
--
-- We attempt to *intersperse* the second effect afterwards.
-- It adds a new effect between every effect.
-- This is used in parsing JSON to add commas.
wrapEffect
  :: forall m a b. (Alternative m)
  => m b
  -- ^ Consume a single, \"junk\" field.
  -- Used to ignore JSON keys that we do not care about.
  -> m b
  -- ^ Consume a \"separator\" between items in the permutation.
  -- This consumption is not done at the front of the permutation
  -- or after the end of it.
  -- This is used to parse commas between JSON fields.
  -> Permutation m a
  -- ^ The permutation parser to run.
  -> m a
  -- ^ The final parser.
wrapEffect takeSingle effAfter (Empty a) = pure a
wrapEffect takeSingle effAfter (Choice choices) = consumeMany
  where
    consumeMany
      = asum (pars <$> choices)
      -- Base case above: one of the choices of the permutation matched
      <|> (takeSingle *> effAfter *> consumeMany)
      -- Interleaving case: none of the choices of the permutation matched,
      -- so run a "junk" effect, the separator, and try again.
      -- Due to the recursion here we will do this infinitely until we either cannot
      -- run the junk effect, *or* we have a field that matches one of the choices of the permutation.
    runWithEffect :: Permutation m whatever -> m whatever
    runWithEffect (Empty a) = pure a
    runWithEffect (Choice choices) = effAfter *> consumeRec
        where
          consumeRec
            = asum (pars <$> choices)
            -- Run one of the effects from the permutation
            <|> (takeSingle *> effAfter *> consumeRec)
            -- Interleave a potentially infinite number of junk effects, with the separator effect between them.
    pars :: Branch m whatever -> m whatever
    pars (Branch perm arg) = do
      a <- arg
      rest <- runWithEffect perm
      pure $ rest a

asParser :: (Alternative f) => Permutation f a -> f a
asParser (Empty a) = pure a
asParser (Choice choices) = asum (pars <$> choices)
    where
      pars :: (Alternative f) => Branch f a -> f a
      pars (Branch perm arg) = do
        a <- arg
        rest <- asParser perm
        pure $ rest a

asPermutation :: (Alternative f) => f a -> Permutation f a
asPermutation p = Choice $ pure $ Branch (pure id) p