hat-2.7.0.0: tools/Detect.hs
{-# LANGUAGE EmptyDataDecls #-}
-- Low-level routines specifically for hat-detect.
module Detect
( findMain, findMainCall, edtNextChild
, doObserve, doTrail, doDelta, doDetect, doAnim, doExplore, doView
, identifyBug, identifyCycle
, questionText, showHeuristic
, ParentSet, newParentSet
, anySuspect
, DetectCommand(..), toCommand
, DeltaOption(..), HeuristicMode(..), HeuristicBool(..)
) where
import Data.Char (toLower,ord)
import System.Cmd (system)
import System.Environment (getArgs,getProgName)
import System.Exit (exitWith,ExitCode(..))
import Control.Monad (when)
import LowLevel (FileNode(..),nil,unevaluated,SimpleNodeType(..)
,simpleNodeType,NodeType(..),nodeType,getSrcRef)
import Foreign.Ptr (Ptr)
import System.IO.Unsafe (unsafePerformIO)
import Numeric (showHex)
import CommonUI (Options(..),hatAnim,hatDelta,hatDetect,hatExplore
,hatTrail,hatObserve,hatView)
import Explore (Location(..),getLocation, getDefLocation,redexParent
,Coord(..))
import HighlightStyle (Highlight(..),Colour(..),highlight,getTerminalSize)
import PrettyLibHighlight
(text, simple)
import SExp (prettySExp)
import ADT (ADT(..))
import NodeExp (NodeExp(..),fullEval,flatEval,getNode,nodeExp2SExp
,removeResultCycles,nodeExpForNode,removeNonResultCycles
,fullEvalText,flatEvalText,children,finalResult,children)
import SrcRef (readSrcRef)
import Slice (Slice(..),sliceStartLine,sliceEndLine,sliceFile,offsetSlice
,highlightSlice,makeSlice,srcRef2Slice,RangeOrd(..))
data DeltaOption = Version
| ShowHelp
| QuickCheckMode
| SliceDepth Int
| DepthLimit Int
| DisableADTCompression
| NoSliceValue Float
| Heuristic HeuristicMode
| TreeType String
deriving Eq
data HeuristicMode = ValueHeuristic Float
| Correct
| Incorrect
| Add HeuristicMode HeuristicMode
| Negate HeuristicMode
| Multiply HeuristicMode HeuristicMode
| Invert HeuristicMode
| If HeuristicBool HeuristicMode HeuristicMode
deriving (Eq,Show,Read)
data HeuristicBool = TrueBool
| FalseBool
| Not HeuristicBool
| And HeuristicBool HeuristicBool
| Or HeuristicBool HeuristicBool
| Eq HeuristicMode HeuristicMode
| Gt HeuristicMode HeuristicMode
| Lt HeuristicMode HeuristicMode deriving (Eq,Show,Read)
data PS
type ParentSet = Ptr PS
data DetectCommand = Yes
| No
-- | Maybe
-- | MaybeYes
-- | MaybeNo
| Quit
| Help
| Observe
| Trail
| Detect
| Delta
| Anim
| Explore
| View
| Split
| ShowADT
| ShowADTHs
| Children
| Set String String
| Get String
| Undo
| Unknown
toCommand :: String -> DetectCommand
toCommand cmd =
case map toLower $ dropWhile (== ':') $ takeWhile (/=' ') cmd of
"yes" -> Yes
"y" -> Yes
"true" -> Yes
"t" -> Yes
"no" -> No
"n" -> No
"false" -> No
"f" -> No
"quit" -> Quit
"q" -> Quit
"help" -> Help
"h" -> Help
"?" -> Help
"u" -> Undo
"undo" -> Undo
"observe" -> Observe
"trail" -> Trail
"detect" -> Detect
"delta" -> Delta
"anim" -> Anim
"animate" -> Anim
"explore" -> Explore
"source" -> View
"split" -> Split
"show" -> ShowADT
"adt" -> ShowADT
"adth" -> ShowADTHs
"children" -> Children
"set" -> Set var val
where
setText = dropWhile (==' ') $ dropWhile (/=' ') cmd
var = map toLower $ takeWhile (/=' ') setText
val = dropWhile (==' ') $ dropWhile (/=' ') setText
"get" -> Get var
where var = dropWhile (==' ') $ dropWhile (/=' ') cmd
_ -> Unknown
doAnim :: NodeExp -> FilePath -> IO ()
doAnim exp f =
do errCode <- system $ hatAnim f (getNode exp)
checkOK errCode "hat-anim"
doDelta :: NodeExp -> FilePath -> IO ()
doDelta exp f =
do errCode <- system $ hatDelta f (getNode exp)
checkOK errCode "hat-delta"
doDetect :: NodeExp -> FilePath -> IO ()
doDetect exp f =
do errCode <- system $ hatDetect f (getNode exp)
checkOK errCode "hat-detect"
doExplore :: NodeExp -> FilePath -> IO ()
doExplore exp f =
do errCode <- system $ hatExplore f (getNode exp)
checkOK errCode "hat-explore"
doObserve :: NodeExp -> FilePath -> IO ()
doObserve exp f =
do errCode <- system $ hatObserve f (flatEvalText 200 exp)
checkOK errCode "hat-observe"
doTrail :: NodeExp -> FilePath -> IO ()
doTrail exp f = do errCode <- system $ hatTrail f (getNode exp)
checkOK errCode "hat-trail"
doView :: NodeExp -> IO ()
doView exp = do errCode <- system
$ hatView
$ readSrcRef
$ getSrcRef
$ getNode exp
checkOK errCode "hat-view"
identifyBug :: [DeltaOption] -> NodeExp -> String
identifyBug opts exp
= highlight [Background Red] "Bug identified:"
++ "\n "
++ (showLocation opts exp)
identifyCycle :: [DeltaOption] -> [ADT] -> String
identifyCycle opts l
= highlight [Background Red] "Bug identified in cycling functions:"
++ "\n "
++ concatMap ( (++ "\n---\n")
. (showLocation opts)
. (\(Branch _ n _ _) -> n))
l
showLocation :: [DeltaOption] -> NodeExp -> String
showLocation opts exp =
show file ++ ":"
++ show functionStartOffset ++ "-"
++ show functionEndOffset ++ ":\n"
++ (if not(SliceDepth 0 `elem` opts) && maybeSlice /= Nothing then
(highlightSlice offsetSubFunctionSlice $
getFileRange functionStartOffset functionEndOffset file)
else getFileRange functionStartOffset functionEndOffset file)
-- ++ "\nSlice: " ++ show subFunctionSlice
-- ++ "\nNode: " ++ (show $ getNode exp)
where
(file,(Location {begin=Coord{row=functionStartOffset}
,end=Coord{row=functionEndOffset}})) =
getDefLocation (getNode exp)
subFunctionSlice = (\(Just x) -> x) $ maybeSlice
offsetSubFunctionSlice = offsetSlice (-functionStartOffset,0) 0
subFunctionSlice
maybeSlice = makeSlice 1 exp
getFileRange :: Int -> Int -> FilePath -> String
getFileRange s e = unlines
. map (" "++)
. drop (s - 1)
. take e
. lines
. unsafePerformIO
. readFile
showHeuristic :: (NodeExp -> Float)
-> NodeExp
-> String
-> String
showHeuristic f e s =
s ++ show (f e)
indent :: Int -> String -> String
indent n = tail . concatMap (("\n"++) . ((take n $ repeat ' ')++)) . lines
padTo :: Int -> String -> String
padTo n s = pad n (take n s)
where
pad n s = take (n - (length s)) (repeat ' ') ++ s
questionText :: Int ->
(NodeExp -> String) ->
(NodeExp -> String) ->
NodeExp ->
String
questionText w f g exp =
mainQText w f g exp
++ withinText f exp
where
withinText :: (NodeExp -> String) ->
NodeExp ->
String
withinText f exp =
if isQuestionable parent
&& pF == eF
&& ( (pSt <= eSt && pEnd > eEnd)
|| (pSt < eSt && pEnd >= eEnd))
then let pExp = ( removeNonResultCycles
$ removeResultCycles
$ nodeExpForNode parent)
in "\nWithin:\n"
++ f pExp
++ withinText f pExp
else ""
where
parent = redexParent $ getNode exp
(pF,(Location {begin=Coord{row=pSt}
,end=Coord{row=pEnd}})) = getDefLocation parent
(eF,(Location {begin=Coord{row=eSt}
,end=Coord{row=eEnd}})) = getDefLocation (getNode exp)
subFunctionSlice = (\(Just x) -> x) $ makeSlice 1 exp
isQuestionable :: FileNode -> Bool
isQuestionable x = x > FileNode 5 &&
(t == ExpApp || t == ExpConstUse || t == ExpConstDef)
where t = nodeType x
mainQText :: Int ->
(NodeExp -> String) ->
(NodeExp -> String) ->
NodeExp ->
String
mainQText w f g exp =
if numLines lhs == 1
&& numLines rhs == 1
&& (width lhs + width rhs) < (w - 4)
then lhs ++ " = " ++ rhs
else lhs ++ "\n=\n" ++ rhs
where
lhs = f exp
rhs = g exp
numLines :: String -> Int
numLines = length . lines
width :: String -> Int
width = maximum . (map length) . lines . stripHidden
stripHidden :: String -> String
stripHidden [] = []
stripHidden (x:xs) = if ord x == 27 then stripToM xs
else x:stripHidden xs
where
stripToM [] = []
stripToM ('m':xs) = stripHidden xs
stripToM (x:xs) = stripToM xs
removeEscaping :: String -> String
removeEscaping [] = []
removeEscaping ('\ESC':xs) = removeEscaping $ tail $ dropWhile (/= 'm') xs
removeEscaping (x:xs) = x:(removeEscaping xs)
checkOK errcode s = when (errcode/=ExitSuccess)
(putStrLn ("ERROR: Unable to start "++s++"."))
file :: NodeExp -> FilePath
file = takeWhile (/= '.') . fst . getDefLocation . getNode
foreign import ccall "parentset.h" newParentSet :: FileNode -> IO ParentSet
foreign import ccall "parentset.h" freeParentSet :: ParentSet -> IO ()
foreign import ccall "parentset.h" extendParentSet :: ParentSet -> FileNode -> IO ()
foreign import ccall "detectutils.h" findMainUse :: Bool -> IO FileNode
foreign import ccall "detectutils.h" nextChild :: ParentSet -> IO FileNode
foreign import ccall "detectutils.h" anySuspect :: FileNode -> IO Bool
findMain :: IO FileNode
findMain = findMainUse False
findMainCall :: IO FileNode
findMainCall = findMainUse True
-- For debugging:
-- foreign import ccall showParentSet :: ParentSet -> IO ()
edtNextChild :: ParentSet -> IO FileNode
edtNextChild ps = do
-- candidate ps
c <- candidate ps
b <- anySuspect c
if c==LowLevel.nil || b then return c else edtNextChild ps
where
candidate ps = do
c <- nextChild ps
-- putStrLn ("edtNextChild: "++showHex (int c) "")
if c==LowLevel.unevaluated -- actually EOF
then return LowLevel.nil
else if c==LowLevel.nil
then candidate ps
else case simpleNodeType c of
NodeConditional -> do extendParentSet ps c
--showParentSet ps
candidate ps
NodeIdentifier -> candidate ps
NodeBasicValue -> candidate ps
NodeCAF -> return c
NodeApplication -> case nodeType c of
ExpApp -> return c
ExpValueApp -> candidate ps
NodeSugar -> return c
NodeSpecial -> case nodeType c of
ExpProjection -> return c
_ -> do extendParentSet ps c
candidate ps
_ -> error "unexpected node in edtNextChild"