packages feed

hat-2.7.0.0: tools/Explore.hs

module Explore  (Coord(..),noCoord,isNoCoord,isNextCoord,beforeCoord
                ,afterCoord,Row,Col
                ,Location(..),isVirtLocation,isNoLocation
                ,isImproperLocation,mkLocation,getLocation
                ,getDefLocation,getUseLocation,hasUseLocation
                ,redexParent) where
import SrcRef   (readSrcRef,defnSrcRef)
import LowLevel (FileNode(..),nil,getSrcRef,getDefnRef,getParentNode
                ,NodeType(..),nodeType)
import Ident    (getIdentAt)
import System.IO.Unsafe (unsafePerformIO)
import Control.Monad (liftM)
import qualified SrcRef (SrcRef(..))

type Row = Int -- starts at 1, 0 for special purpose
type Col = Int -- starts at 1, 0 for special purpose

data Coord = Coord {row :: Row, col :: Col} deriving (Eq,Ord,Show)
  -- both always >= 0, start counting at 1 

noCoord :: Coord
noCoord = Coord 0 0

isNoCoord :: Coord -> Bool
isNoCoord c = row c == 0 || col c == 0

isNextCoord :: Coord -> Coord -> Bool
-- only for same row, because impossible to say if end of row reached
-- and hence wrap around to next row should be made
isNextCoord c1 c2 = row c1 == row c2 && col c1 + 1 == col c2

beforeCoord :: Coord -> Coord
-- not for noCoord
beforeCoord c = 
  if col c <= 1 then 
    if row c <= 1 then
      error "beforeCoord start"
    else
      Coord{row=row c-1,col=maxBound}
  else
    c{col=col c-1}

afterCoord :: Coord -> Coord
-- not for noCoord
afterCoord c =
  if col c == maxBound then
    Coord{row=row c+1,col=1}
  else
    c{col=col c+1}


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

data Location = Location {begin :: Coord, end :: Coord} deriving (Eq,Ord,Show)
  -- always begin <= end

isVirtLocation :: Location -> Bool
isVirtLocation l = isNoCoord (end l) && not (isNoCoord (begin l))

isNoLocation :: Location -> Bool
isNoLocation l = isNoCoord (begin l) && isNoCoord (end l)

isImproperLocation :: Location -> Bool
isImproperLocation = isNoCoord . begin

mkLocation :: Coord -> Coord -> Location
mkLocation b e = Location{begin=b,end=if e < b then noCoord else e} 

getLocation :: FileNode -> (FilePath,Location)
-- gets use location if it exists, 
-- otherwise use location of parent, otherwise def location
getLocation node =
  if hasUseLocation node then getUseLocation node 
    else let parent = redexParent node
         in if parent /= nil && hasUseLocation parent 
              then getUseLocation parent 
              else getDefLocation node

getDefLocation :: FileNode -> (FilePath,Location)
-- assumes given node is ValueUse, ConstUse or ValueApp (or App?)
getDefLocation node =
  (filename,Location{begin=Coord{row=rowBegin,col=colBegin}
                    ,end=Coord{row=rowEnd,col=colEnd}})
  where
  atomNode = getDefnRef node
  (SrcRef.SrcRef filename rowBegin colBegin rowEnd colEnd)
    | atomNode==nil = SrcRef.SrcRef "" 0 0 0 0
    | otherwise     = unsafePerformIO (liftM defnSrcRef (getIdentAt atomNode))

getUseLocation :: FileNode -> (FilePath,Location)
-- assumes given node has a location
getUseLocation node = 
  (filename,Location{begin=Coord{row=rowBegin,col=colBegin}
                    ,end=Coord{row=rowEnd,col=colEnd}})
  where
  srcRefNode = getSrcRef node
  (SrcRef.SrcRef filename rowBegin colBegin rowEnd colEnd)
    | srcRefNode==nil  = SrcRef.SrcRef "" 0 0 0 0
    | otherwise        = readSrcRef srcRefNode

hasUseLocation :: FileNode -> Bool
hasUseLocation node = fst (getUseLocation node) /= ""

redexParent :: FileNode -> FileNode
-- returned parent redex is application or constDef, 
-- never guard, case, if, update etc.
-- may however be nil
redexParent redex =
  let parent = getParentNode redex 
  in if parent == nil then
       parent
     else
       case nodeType parent of
         ExpGuard -> redexParent parent
         ExpCase -> redexParent parent
         ExpIf -> redexParent parent
         ExpFieldUpdate -> redexParent parent  -- occurs?
         ExpHidden -> redexParent parent
         _ -> parent