packages feed

cryptol-3.4.0: src/Cryptol/Parser/Position.hs

-- |
-- Module      :  Cryptol.Parser.Position
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

{-# LANGUAGE Safe #-}

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE RecordWildCards #-}
module Cryptol.Parser.Position (
  -- * Position
  Position,
  line, col, colOffset,
  start,
  startOfLine, beforeStartOfLine,
  move, moves, advanceColBy,
  replPosition,

  -- * Range
  Range(..),
  emptyRange,
  rComb, rCombs, rCombMaybe,
  rangeWithin,

  -- * Located
  Located(..),
  HasLoc(..), AddLoc(..),
  at,
  combLoc

) where

import           Data.Text(Text)
import qualified Data.Text as T

import GHC.Generics (Generic)
import Control.DeepSeq

import Cryptol.Utils.PP

data Located a  = Located { srcRange :: !Range, thing :: !a }
                  deriving (Eq, Ord, Show, Generic, NFData
                           , Functor, Foldable, Traversable )

data Position = Position {
  pLine :: !Int,
  -- ^ 1 based

  pCol :: !Int,
  -- ^ 1 based. Interpreting tabs.
  -- This is used for layout processing and pretty printing.

  pColOffset :: !Int
  -- ^ 0 based. UTF-32 offset in the line.
  -- Note that this does not interpret tabs.
  -- It is used for comparisons.
} deriving (Show, Generic, NFData)

line :: Position -> Int
line = pLine

col :: Position -> Int
col = pCol

colOffset :: Position -> Int
colOffset = pColOffset

instance Eq Position where
  x == y = line x == line y && colOffset x == colOffset y

instance Ord Position where
  compare x y =
    case compare (line x) (line y) of
      LT -> LT
      EQ -> compare (colOffset x) (colOffset y)
      GT -> GT

data Range      = Range { from   :: !Position
                        , to     :: !Position
                        , source :: FilePath }
                  deriving (Eq, Ord, Show, Generic, NFData)

-- | Returns `True` if the first range is contained in the second one.
rangeWithin :: Range -> Range -> Bool
a `rangeWithin` b =
  source a == source b && from a >= from b && to a <= to b

-- | An empty range.
--
-- Caution: using this on the LHS of a use of rComb will cause the empty source
-- to propagate.
emptyRange :: Range
emptyRange  = Range { from = start, to = start, source = "" }

replPosition :: (Int,Int) -> Position
replPosition (l,c) = Position { pLine = l, pCol = c, pColOffset = c }

start :: Position
start = Position { pLine = 1, pCol = 1, pColOffset = 0 }

startOfLine :: Int -> Position
startOfLine n = start { pLine = n }

beforeStartOfLine :: Int -> Position
beforeStartOfLine n = Position { pLine = n, pCol = 0, pColOffset = -1 }

advanceColBy :: Int -> Position -> Position
advanceColBy n p = p { pCol = pCol p + n, pColOffset = pColOffset p + n }

move :: Position -> Char -> Position
move p c = case c of
            '\t' -> p { pCol = ((col p + 7) `div` 8) * 8 + 1, pColOffset = colOffset p + 1 }
            '\n' -> p { pCol = 1, pLine = 1 + line p, pColOffset = 0 }
            _    -> p { pCol = 1 + col p, pColOffset = colOffset p + 1 }

moves :: Position -> Text -> Position
moves = T.foldl' move

rComb :: Range -> Range -> Range
rComb r1 r2  = Range { from = rFrom, to = rTo, source = source r1 }
  where rFrom = min (from r1) (from r2)
        rTo   = max (to r1)   (to r2)

rCombMaybe :: Maybe Range -> Maybe Range -> Maybe Range
rCombMaybe Nothing y = y
rCombMaybe x Nothing = x
rCombMaybe (Just x) (Just y) = Just (rComb x y)

rCombs :: [Range] -> Range
rCombs  = foldl1 rComb


--------------------------------------------------------------------------------

instance PP Position where
  ppPrec _ p = int (line p) <.> colon <.> int (col p)

instance PP Range where
  ppPrec _ r = text (source r) <.> char ':'
            <.> pp (from r) <.> text "--" <.> pp (to r)

instance PP a => PP (Located a) where
  ppPrec _ l = parens (text "at" <+> pp (srcRange l) <.> comma <+> pp (thing l))

instance PPName a => PPName (Located a) where
  ppNameFixity  Located { .. } = ppNameFixity thing
  ppPrefixName  Located { .. } = ppPrefixName thing
  ppInfixName   Located { .. } = ppInfixName  thing

--------------------------------------------------------------------------------

class HasLoc t where
  getLoc :: t -> Maybe Range

instance HasLoc Range where
  getLoc r = Just r

instance HasLoc (Located a) where
  getLoc r = Just (srcRange r)

instance (HasLoc a, HasLoc b) => HasLoc (a,b) where
  getLoc (f,t) = case getLoc f of
                   Nothing -> getLoc t
                   Just l ->
                      case getLoc t of
                        Nothing -> return l
                        Just l1 -> return (rComb l l1)

instance HasLoc a => HasLoc [a] where
  getLoc = go Nothing
    where
    go x [] = x
    go Nothing (x : xs)  = go (getLoc x) xs
    go (Just l) (x : xs) = case getLoc x of
                             Nothing -> go (Just l) xs
                             Just l1 -> go (Just (rComb l l1)) xs

instance HasLoc a => HasLoc (Maybe a) where
  getLoc Nothing = Nothing
  getLoc (Just x) = getLoc x

class HasLoc t => AddLoc t where
  addLoc  :: t -> Range -> t
  dropLoc :: t -> t

instance AddLoc (Located a) where
  addLoc t r = t { srcRange = r }
  dropLoc r  = r

at :: (HasLoc l, AddLoc t) => l -> t -> t
at l e = maybe e (addLoc e) (getLoc l)

combLoc :: (a -> b -> c) -> Located a -> Located b -> Located c
combLoc f l1 l2 = Located { srcRange = rComb (srcRange l1) (srcRange l2)
                          , thing    = f (thing l1) (thing l2)
                          }