packages feed

prosidy-1.6.0.2: src/Prosidy/Optics/Source.hs

{-|
Module      : Prosidy.Optics.Source
Description : Profunctor optics over Prosidy types.
Copyright   : ©2020 James Alexander Feldman-Crough
License     : MPL-2.0
Maintainer  : alex@fldcr.com
-}
{-# LANGUAGE Safe #-}
module Prosidy.Optics.Source
    ( -- * Classy optics; implementable on all types with a location
      HasLocation(..)
    , offset
      -- ** Read-only optics.
    , column
    , line
    , source

      -- * Conversion utilities
    , sparse
    )
where

import           Prosidy.Types
import           Prosidy.Source
import           Prosidy.Optics.Internal

-- | A classy optic for selecting the 'Location' from a value. Note that
-- 'location' is affine: a 'Location' can't be attached to a value which does 
-- not -- already have one, and not all values with an instance of 
-- 'HasLocation' have a location.
class HasLocation t where
    location :: Affine' t Location

instance HasLocation Location where
    location = id
    {-# INLINE location #-}

instance HasLocation (Tag a) where
    location = affine' tagLocation (\d l -> d { tagLocation = Just l })
    {-# INLINE location #-}

instance HasLocation (Region a) where
    location = affine' regionLocation (\d l -> d { regionLocation = Just l })
    {-# INLINE location #-}

instance HasLocation Fragment where
    location =
        affine' fragmentLocation (\d l -> d { fragmentLocation = Just l })
    {-# INLINE location #-}

instance HasLocation Paragraph where
    location =
        affine' paragraphLocation (\d l -> d { paragraphLocation = Just l })
    {-# INLINE location #-}

instance HasLocation Block where
    location = affine' get set
      where
        get (BlockLiteral   lit) = tagLocation lit
        get (BlockParagraph p  ) = paragraphLocation p
        get (BlockTag       tag) = tagLocation tag
        set (BlockLiteral lit) l = BlockLiteral lit { tagLocation = Just l }
        set (BlockParagraph p) l =
            BlockParagraph p { paragraphLocation = Just l }
        set (BlockTag tag) l = BlockTag tag { tagLocation = Just l }
    {-# INLINE location #-}

instance HasLocation Inline where
    location = affine' get set
      where
        get Break            = Nothing
        get (InlineTag  tag) = tagLocation tag
        get (InlineText f  ) = fragmentLocation f
        set Break            _ = Break
        set (InlineTag  tag) l = InlineTag tag { tagLocation = Just l }
        set (InlineText f  ) l = InlineText f { fragmentLocation = Just l }
    {-# INLINE location #-}

-- | Focus on the 'Offset' from a value parsed from a source file. If the 
-- 'Offset' is modified, note that the resulting 'column' and 'line' will /also/ be
-- modified as they are denormalizations of this value.
offset :: HasLocation l => Affine' l Offset
offset = location . sparse . lens
    sparseLocationOffset
    (\sl x -> sl { sparseLocationOffset = x })
{-# INLINE offset #-}

-- | Fetch the 'Column' from a value parsed from a source file. Modifications
-- are not allowed as the 'offset' and 'line' may become inconsistent.
column
    :: (HasLocation l, Contravariant f, Applicative f) => Optic' (->) f l Column
column = location . to locationColumn
{-# INLINE column #-}

-- | Fetch the 'Line' from a value parsed from a source file. Modifications
-- are not allowed as the 'offset' and 'column' may become inconsistent.
line :: (HasLocation l, Contravariant f, Applicative f) => Optic' (->) f l Line
line = location . to locationLine
{-# INLINE line #-}

-- | Fetch the 'Source' a value was parsed from. Modifications are not allowed 
-- as the 'line', 'offset', and 'column' may become inconsistent.
source
    :: (HasLocation l, Contravariant f, Applicative f) => Optic' (->) f l Source
source = location . to locationSource
{-# INLINE source #-}

-- | An isomorphism between 'Location' and 'SparseLocation'. This is allowed
-- because although a 'Location' has strictly more data than a 'SparseLocation',
-- those values are denormalizations of values within 'SparseLocation'.
sparse :: Iso' Location SparseLocation
sparse = iso stripLocation enrichLocation
{-# INLINE sparse #-}