{- |
Copyright: © 2019 James Alexander Feldman-Crough
License: MPL-2.0
-}
module ProSource.LocationOps (enrichLocation, stripLocation, sparse) where

import ProSource.Column
import ProSource.LineMap
import ProSource.Location
import ProSource.Offset
import ProSource.Source
import ProSource.SparseLocation

-- | Add lazily computed line and column number information to a 'SparseLocation'.
enrichLocation :: SparseLocation -> Location
enrichLocation :: SparseLocation -> Location
enrichLocation SparseLocation
sl = Location :: Source -> Offset -> Line -> Column -> Location
Location
    { locationSource :: Source
locationSource = Source
source
    , locationOffset :: Offset
locationOffset = Offset
offset
    , locationLine :: Line
locationLine   = Line
line
    , locationColumn :: Column
locationColumn = Column
column
    }
  where
    source :: Source
source                     = SparseLocation -> Source
sparseLocationSource SparseLocation
sl
    lineMap :: LineMap
lineMap                    = Source -> LineMap
sourceLineMap Source
source
    offset :: Offset
offset@(~(Offset Word
offsetN)) = SparseLocation -> Offset
sparseLocationOffset SparseLocation
sl
    line :: Line
line                       = Offset -> LineMap -> Line
offsetToLine Offset
offset LineMap
lineMap

    column :: Column
column = case Line -> LineMap -> Maybe Offset
lineToOffset Line
line LineMap
lineMap of
        Just (Offset Word
n) -> Word -> Column
Column (Word
offsetN Word -> Word -> Word
forall a. Num a => a -> a -> a
- Word
n)
        Maybe Offset
Nothing         -> Word -> Column
Column Word
0

-- | Remove line and column number information from a 'Location'.
stripLocation :: Location -> SparseLocation
stripLocation :: Location -> SparseLocation
stripLocation Location
l = SparseLocation :: Source -> Offset -> SparseLocation
SparseLocation
  { sparseLocationSource :: Source
sparseLocationSource = Location -> Source
locationSource Location
l
  , sparseLocationOffset :: Offset
sparseLocationOffset = Location -> Offset
locationOffset Location
l
  }

-- | 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' Location SparseLocation
sparse = (Location -> SparseLocation)
-> (SparseLocation -> Location) -> Iso' Location SparseLocation
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso Location -> SparseLocation
stripLocation SparseLocation -> Location
enrichLocation