packages feed

g2-0.1.0.0: src/G2/Language/Located.hs

module G2.Language.Located
  ( Located (..)
  , Spanning (..)
  , Locatable (..)
  , Spannable (..)
  , topLeft
  , bottomRight
  , combineSpans
  , spanLookup
  , locLookup
  , SpannedName(..)
  ) where

import G2.Language.Naming
import G2.Language.Syntax
import G2.Language.ExprEnv (ExprEnv)

import Data.Hashable
import qualified Data.Map as M
import Data.Maybe

class Located l where
    loc :: l -> Maybe Loc

class Spanning l where
    spanning :: l -> Maybe Span

instance Located Span where
    loc = Just . start

instance Located Name where
    loc (Name _ _ _ s) = maybe Nothing loc s

instance Located Id where
    loc (Id n _) = loc n

instance Spanning Name where
    spanning (Name _ _ _ s) = s

instance Spanning Id where
    spanning (Id n _) = spanning n

-- Allows equality checking and sorting by Location
newtype Locatable a = Locatable a deriving (Show)

instance Located a => Located (Locatable a) where
    loc (Locatable x) = loc x

instance Spanning a => Spanning (Locatable a) where
    spanning (Locatable x) = spanning x

instance Located a => Eq (Locatable a) where
    x == y = loc x == loc y

instance Located a => Ord (Locatable a) where
    x `compare` y = loc x `compare` loc y

-- Allows equality checking and sorting by Span
newtype Spannable a = Spannable a deriving (Show)

instance Located a => Located (Spannable a) where
    loc (Spannable x) = loc x

instance Spanning a => Spanning (Spannable a) where
    spanning (Spannable x) = spanning x

instance Spanning a => Eq (Spannable a) where
    x == y = spanning x == spanning y

instance Spanning a => Ord (Spannable a) where
    x `compare` y = spanning x `compare` spanning y

-- | Returns the span that begins the closest to the top,
-- or, if both columns are the same, that is leftmost
topLeft :: Span -> Span -> Span
topLeft (s1@Span {start = st1}) (s2@Span {start = st2})
    | col st1 < col st2 = s1
    | col st1 > col st2 = s2
    | line st1 < line st2 = s1
    | otherwise = s2

-- | Returns the span that ends the closest to the bottom,
-- or, if both columns are the same, that is rightmost
bottomRight :: Span -> Span -> Span
bottomRight (s1@Span {end = en1}) (s2@Span {end = en2})
    | col en1 < col en2 = s2
    | col en1 > col en2 = s1
    | line en1 < line en2 = s2
    | otherwise = s1

-- | combineSpans
-- Combines two Spans into one, that covers all the characters from both spans.
-- Assumes the files are the same.
combineSpans :: Span -> Span -> Span
combineSpans s1 s2 =
    let
        tl = topLeft s1 s2
        br = bottomRight s1 s2
    in
    s1 {start = start tl, end = end br}

-- | Constructs a map of Spans to Names, based on all Names in the ExprEnv
spanLookup :: ExprEnv -> M.Map Span Name
spanLookup =
    M.fromList . mapMaybe (\n -> maybe Nothing (\s -> Just (s, n)) (spanning n)) . names

-- | Constructs a map of Locs to Names, based on all Names in the ExprEnv
locLookup :: ExprEnv -> M.Map Loc Name
locLookup = M.mapKeys start . spanLookup


newtype SpannedName = SpannedName Name deriving (Show)

instance Eq SpannedName where
  (==) (SpannedName (Name occ1 mod1 unq1 sp1))
       (SpannedName (Name occ2 mod2 unq2 sp2)) =
        occ1 == occ2 && mod1 == mod2 && unq1 == unq2 && sp1 == sp2

instance Hashable SpannedName where
    hashWithSalt s (SpannedName (Name n m i l)) =
        s `hashWithSalt`
        n `hashWithSalt`
        m `hashWithSalt`
        i `hashWithSalt` l