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 #-}