packages feed

text-loc-0.1: src/Text/Loc.hs

{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}

-- | This modules provides data types that describe line-column locations
--   within a text.
module Text.Loc
  ( HasLoc(..)
  , LocLens(..)
  , Span(..)
  , SpannedLoc(..)
  , locStart
  , locEnd
  , spanOf
  , startOf
  , endOf
  , SpannedLens(..)
  , lStart
  , lEnd
  , lSpanOf
  , lEndOf
  , lStartOf
  , Located(..)
  , LineCol(..)
  , nextLine
  , nextCol
  , LineColLoc(..)
  , locLine
  , locCol
  , lineColOf
  , lineOf
  , colOf
  , LineColLens(..)
  , lLine
  , lCol
  , lLineColOf
  , lLineOf
  , lColOf
  , SrcLoc(..)
  , srcOf
  , SrcLens(..)
  , lSrcOf
  , InSrc(..)
  , FromLoc(..)
  , fromOf
  , FromLens(..)
  , lFromOf
  , Nested(..)
  ) where

import GHC.Generics (Generic)
import Data.Typeable (Typeable)
import Data.Foldable (Foldable)
import Data.Traversable (Traversable)
import Data.Hashable (Hashable(..))
import Data.Word (Word)

-- | Values with associated source locations.
class HasLoc α where
  type LocOf α
  locOf ∷ α → LocOf α

-- | Values with editable associated source locations.
class HasLoc α ⇒ LocLens α where
  lLocOf ∷ Functor f ⇒ (LocOf α → f (LocOf α)) → α → f α

-- | A region of text between two locations.
data Span l = Span l l
              deriving (Typeable, Generic, Show, Read, Eq, Ord,
                        Functor, Foldable, Traversable)

instance Hashable l ⇒ Hashable (Span l) where
  hashWithSalt salt (Span s e) = hashWithSalt salt (s, e)
  {-# INLINE hashWithSalt #-}

instance HasLoc (Span l) where
  type LocOf (Span l) = Span l
  locOf = id
  {-# INLINE locOf #-}

instance LocLens (Span l) where
  lLocOf = id
  {-# INLINE lLocOf #-}

-- | Spanned locations.
class SpannedLoc l where
  type SpanLoc l
  locSpan ∷ l → Span (SpanLoc l)

instance SpannedLoc (Span l) where
  type SpanLoc (Span l) = l
  locSpan = id
  {-# INLINE locSpan #-}

-- | The start of a span.
locStart ∷ SpannedLoc l ⇒ l → SpanLoc l
locStart l = let Span s _ = locSpan l in s
{-# INLINE locStart #-}

-- | The end of a span.
locEnd ∷ SpannedLoc l ⇒ l → SpanLoc l
locEnd l = let Span _ e = locSpan l in e
{-# INLINE locEnd #-}

-- | The span of the associated location.
spanOf ∷ (HasLoc α, SpannedLoc (LocOf α)) ⇒ α → Span (SpanLoc (LocOf α))
spanOf = locSpan . locOf
{-# INLINE spanOf #-}

-- | The start of the associated location.
startOf ∷ (HasLoc α, SpannedLoc (LocOf α)) ⇒ α → SpanLoc (LocOf α)
startOf = locStart . locOf
{-# INLINE startOf #-}

-- | The end of the associated location.
endOf ∷ (HasLoc α, SpannedLoc (LocOf α)) ⇒ α → SpanLoc (LocOf α)
endOf = locEnd . locOf
{-# INLINE endOf #-}

-- | Editable spanned locations.
class SpannedLoc l ⇒ SpannedLens l where
  lSpan ∷ Functor f ⇒ (Span (SpanLoc l) → f (Span (SpanLoc l))) → l → f l

instance SpannedLens (Span l) where
  lSpan = id
  {-# INLINE lSpan #-}

-- | Provides access to the start of a location.
lStart ∷ (Functor f, SpannedLens l) ⇒ (SpanLoc l → f (SpanLoc l)) → l → f l
lStart f = lSpan (\(Span s e) → fmap (flip Span e) (f s))
{-# INLINE lStart #-}

-- | Provides access to the end of a location.
lEnd ∷ (Functor f, SpannedLens l) ⇒ (SpanLoc l → f (SpanLoc l)) → l → f l
lEnd f = lSpan (\(Span s e) → fmap (Span s) (f e))
{-# INLINE lEnd #-}

-- | Provides access to the span of the associated location.
lSpanOf ∷ (Functor f, LocLens α, SpannedLens (LocOf α))
        ⇒ (Span (SpanLoc (LocOf α)) → f (Span (SpanLoc (LocOf α)))) → α → f α
lSpanOf = lLocOf . lSpan
{-# INLINE lSpanOf #-}

-- | Provides access to the start of the associated location.
lStartOf ∷ (Functor f, LocLens α, SpannedLens (LocOf α))
         ⇒ (SpanLoc (LocOf α) → f (SpanLoc (LocOf α))) → α → f α
lStartOf = lLocOf . lStart
{-# INLINE lStartOf #-}

-- | Provides access to the end of the associated location.
lEndOf ∷ (Functor f, LocLens α, SpannedLens (LocOf α))
       ⇒ (SpanLoc (LocOf α) → f (SpanLoc (LocOf α))) → α → f α
lEndOf = lLocOf . lEnd
{-# INLINE lEndOf #-}

-- | A simple way to attach location to a value.
data Located l α = Located { locAt  ∷ l
                           , locVal ∷ α }
                   deriving (Typeable, Generic, Show, Read,
                             Functor, Foldable, Traversable)

instance (Hashable l, Hashable α) ⇒ Hashable (Located l α) where
  hashWithSalt salt (Located l a) = hashWithSalt salt (l, a)
  {-# INLINE hashWithSalt #-}

instance HasLoc (Located l α) where
  type LocOf (Located l α) = l
  locOf = locAt
  {-# INLINE locOf #-}

instance LocLens (Located l α) where
  lLocOf f (Located l a) = fmap (flip Located a) (f l)
  {-# INLINE lLocOf #-}

-- | Line-column location within a text.
data LineCol = LineCol {-# UNPACK #-} !Word {-# UNPACK #-} !Word
               deriving (Typeable, Generic, Show, Read, Eq, Ord, Bounded)

instance Hashable LineCol where
  hashWithSalt salt (LineCol l c) = hashWithSalt salt (l, c)
  {-# INLINE hashWithSalt #-}

instance HasLoc LineCol where
  type LocOf LineCol = LineCol
  locOf = id
  {-# INLINE locOf #-}

-- | The location of the start of the next line.
nextLine ∷ LineCol → LineCol
nextLine (LineCol l _) = LineCol (l + 1) 1
{-# INLINE nextLine #-}

-- | The location of the next column.
nextCol ∷ LineCol → LineCol
nextCol (LineCol l c) = LineCol l (c + 1)
{-# INLINE nextCol #-}

-- | Locations with line and column numbers.
class LineColLoc l where
  locLineCol ∷ l → LineCol

instance LineColLoc LineCol where
  locLineCol = id
  {-# INLINE locLineCol #-}

-- | The line number of a location.
locLine ∷ LineColLoc l ⇒ l → Word
locLine l = let LineCol ln _ = locLineCol l in ln
{-# INLINE locLine #-}

-- | The column number of a location.
locCol ∷ LineColLoc l ⇒ l → Word
locCol l = let LineCol _ c = locLineCol l in c
{-# INLINE locCol #-}

-- | The line-column numbers of the associated location.
lineColOf ∷ (HasLoc α, LineColLoc (LocOf α)) ⇒ α → LineCol
lineColOf = locLineCol . locOf
{-# INLINE lineColOf #-}

-- | The line number of the associated location.
lineOf ∷ (HasLoc α, LineColLoc (LocOf α)) ⇒ α → Word
lineOf = locLine . lineColOf
{-# INLINE lineOf #-}

-- | The column number of the associated location.
colOf ∷ (HasLoc α, LineColLoc (LocOf α)) ⇒ α → Word
colOf = locCol . lineColOf
{-# INLINE colOf #-}

-- | Locations with editable line and column numbers.
class LineColLens l where
  lLineCol ∷ Functor f ⇒ (LineCol → f LineCol) → l → f l

instance LineColLens LineCol where
  lLineCol = id
  {-# INLINE lLineCol #-}

-- | Provides access to the line number of a location.
lLine ∷ (Functor f, LineColLens l) ⇒ (Word → f Word) → l → f l
lLine f = lLineCol (\(LineCol l c) → fmap (flip LineCol c) (f l))
{-# INLINE lLine #-}

-- | Provides access to the column number of a location.
lCol ∷ (Functor f, LineColLens l) ⇒ (Word → f Word) → l → f l
lCol f = lLineCol (\(LineCol l c) → fmap (LineCol l) (f c))
{-# INLINE lCol #-}

-- | Provides access to the line-column numbers of the associated location.
lLineColOf ∷ (Functor f, LocLens α, LineColLens (LocOf α))
           ⇒ (LineCol → f LineCol) → α → f α
lLineColOf = lLocOf . lLineCol
{-# INLINE lLineColOf #-}

-- | Provides access to the line number of the associated location.
lLineOf ∷ (Functor f, LocLens α, LineColLens (LocOf α))
        ⇒ (Word → f Word) → α → f α
lLineOf = lLocOf . lLine
{-# INLINE lLineOf #-}

-- | Provides access to the column number of the associated location.
lColOf ∷ (Functor f, LocLens α, LineColLens (LocOf α))
       ⇒ (Word → f Word) → α → f α
lColOf = lLocOf . lCol
{-# INLINE lColOf #-}

-- | Locations with a source (e.g. file name).
class SrcLoc l where
  type LocSrc l
  locSrc ∷ l → LocSrc l

-- | The source of the associated location.
srcOf ∷ (HasLoc α, SrcLoc (LocOf α)) ⇒ α → LocSrc (LocOf α)
srcOf = locSrc . locOf
{-# INLINE srcOf #-}

-- | Locations with editable source (e.g. file name).
class SrcLoc l ⇒ SrcLens l where
  lSrc ∷ Functor f ⇒ (LocSrc l → f (LocSrc l)) → l → f l

-- | Provides access to the source of the associated location.
lSrcOf ∷ (Functor f, LocLens α, SrcLens (LocOf α))
       ⇒ (LocSrc (LocOf α) → f (LocSrc (LocOf α))) → α → f α
lSrcOf = lLocOf . lSrc
{-# INLINE lSrcOf #-}

-- | A simple way to add source to a location.
data InSrc s l = InSrc { srcSrc ∷ s
                       , srcLoc ∷ l }
                 deriving (Typeable, Generic, Show, Read, Eq, Ord)

instance (Hashable s, Hashable l) ⇒ Hashable (InSrc s l) where
  hashWithSalt salt (InSrc s l) = hashWithSalt salt (s, l)
  {-# INLINE hashWithSalt #-}

instance HasLoc (InSrc s l) where
  type LocOf (InSrc s l) = InSrc s l
  locOf = id
  {-# INLINE locOf #-}

instance LocLens (InSrc s l) where
  lLocOf = id
  {-# INLINE lLocOf #-}

instance SpannedLoc l ⇒ SpannedLoc (InSrc s l) where
  type SpanLoc (InSrc s l) = InSrc s (SpanLoc l)
  locSpan (InSrc s l) = fmap (InSrc s) (locSpan l)
  {-# INLINE locSpan #-}

instance SpannedLens l ⇒ SpannedLens (InSrc s l) where
  lSpan f (InSrc s l) =
    fmap (InSrc s) (lSpan (fmap (fmap srcLoc) . f . fmap (InSrc s)) l)
  {-# INLINE lSpan #-}

instance LineColLoc l ⇒ LineColLoc (InSrc s l) where
  locLineCol = locLineCol . srcLoc
  {-# INLINE locLineCol #-}

instance LineColLens l ⇒ LineColLens (InSrc s l) where
  lLineCol f (InSrc s l) = fmap (InSrc s) (lLineCol f l)
  {-# INLINE lLineCol #-}

instance SrcLoc (InSrc s l) where
  type LocSrc (InSrc s l) = s
  locSrc = srcSrc
  {-# INLINE locSrc #-}

instance SrcLens (InSrc s l) where
  lSrc f (InSrc s l) = fmap (flip InSrc l) (f s)
  {-# INLINE lSrc #-}

-- | Locations that have parent locations.
class FromLoc l where
  type LocFrom l
  locFrom ∷ l → LocFrom l

instance FromLoc l ⇒ FromLoc (InSrc s l) where
  type LocFrom (InSrc s l) = LocFrom l
  locFrom = locFrom . srcLoc
  {-# INLINE locFrom #-}

instance FromLens l ⇒ FromLens (InSrc s l) where
  lFrom f (InSrc s l) = fmap (InSrc s) (lFrom f l)
  {-# INLINE lFrom #-}

-- | The parent of the associated location.
fromOf ∷ (HasLoc α, FromLoc (LocOf α)) ⇒ α → LocFrom (LocOf α)
fromOf = locFrom . locOf
{-# INLINE fromOf #-}

-- | Locations with editable parent locations.
class FromLoc l ⇒ FromLens l where
  lFrom ∷ Functor f ⇒ (LocFrom l → f (LocFrom l)) → l → f l

-- | Provides access to the parent of the associated location.
lFromOf ∷ (Functor f, LocLens α, FromLens (LocOf α))
        ⇒ (LocFrom (LocOf α) → f (LocFrom (LocOf α))) → α → f α
lFromOf = lLocOf . lFrom
{-# INLINE lFromOf #-}

-- | A simple way to nest a location.
data Nested l p = Nested { nestedLoc  ∷ l
                         , nestedFrom ∷ p }
                  deriving (Typeable, Generic, Show, Read)

instance (Hashable l, Hashable p) ⇒ Hashable (Nested l p) where
  hashWithSalt salt (Nested l p) = hashWithSalt salt (l, p)
  {-# INLINE hashWithSalt #-}

instance HasLoc (Nested l p) where
  type LocOf (Nested l p) = Nested l p
  locOf = id
  {-# INLINE locOf #-}

instance LocLens (Nested l p) where
  lLocOf = id
  {-# INLINE lLocOf #-}

instance SpannedLoc l ⇒ SpannedLoc (Nested l p) where
  type SpanLoc (Nested l p) = Nested (SpanLoc l) p
  locSpan (Nested l p) = fmap (flip Nested p) (locSpan l)
  {-# INLINE locSpan #-}

instance SpannedLens l ⇒ SpannedLens (Nested l p) where
  lSpan f (Nested l p) =
    fmap (flip Nested p)
         (lSpan (fmap (fmap nestedLoc) . f . fmap (flip Nested p)) l)
  {-# INLINE lSpan #-}

instance LineColLoc l ⇒ LineColLoc (Nested l p) where
  locLineCol = locLineCol . nestedLoc
  {-# INLINE locLineCol #-}

instance LineColLens l ⇒ LineColLens (Nested l p) where
  lLineCol f (Nested l p) = fmap (flip Nested p) (lLineCol f l)
  {-# INLINE lLineCol #-}

instance SrcLoc l ⇒ SrcLoc (Nested l p) where
  type LocSrc (Nested l p) = LocSrc l
  locSrc = locSrc . nestedLoc
  {-# INLINE locSrc #-}

instance SrcLens l ⇒ SrcLens (Nested l p) where
  lSrc f (Nested l p) = fmap (flip Nested p) (lSrc f l)
  {-# INLINE lSrc #-}

instance FromLoc (Nested l p) where
  type LocFrom (Nested l p) = p
  locFrom = nestedFrom
  {-# INLINE locFrom #-}

instance FromLens (Nested l p) where
  lFrom f (Nested l p) = fmap (Nested l) (f p)
  {-# INLINE lFrom #-}