hermit-0.7.0.0: src/HERMIT/External.hs
{-# LANGUAGE GADTs, TypeFamilies, FlexibleContexts, FlexibleInstances, DeriveDataTypeable #-}
module HERMIT.External
( -- * Externals
External
, ExternalName
, ExternalHelp
, externName
, externDyn
, externHelp
, externTypeString
, externTypeArgResString
, splitFunTyArgs
, toHelp
, external
, Extern(..)
, matchingExternals
-- * Tags
, CmdTag(..)
, TagE
, Tag((.+),remTag,tagMatch)
, (.&)
, (.||)
, notT
, externTags
, dictionaryOfTags
-- * Boxes
-- | Boxes are used by the 'Extern' class.
, CoreString(..)
, CrumbBox(..)
, IntBox(..)
, IntListBox(..)
, PathBox(..)
, StringBox(..)
, StringListBox(..)
, TagBox(..)
-- ** LCore Boxes
, TransformLCoreStringBox(..)
, TransformLCoreUnitBox(..)
, TransformLCorePathBox(..)
, RewriteLCoreBox(..)
, BiRewriteLCoreBox(..)
, RewriteLCoreListBox(..)
-- ** LCoreTC Boxes
, TransformLCoreTCStringBox(..)
, TransformLCoreTCUnitBox(..)
, TransformLCoreTCLCoreBox(..)
, TransformLCoreTCPathBox(..)
, RewriteLCoreTCBox(..)
, BiRewriteLCoreTCBox(..)
, RewriteLCoreTCListBox(..)
) where
import Data.Map hiding (map)
import Data.Dynamic
import Data.List
import Data.Typeable.Internal (TypeRep(..), funTc)
import HERMIT.Core
import HERMIT.Context (LocalPathH)
import HERMIT.Kure
import HERMIT.Lemma
-----------------------------------------------------------------
-- | 'External' names are just strings.
type ExternalName = String
-- | Help information for 'External's is stored as a list of strings, designed for multi-line displaying.
type ExternalHelp = [String]
-- Tags --------------------------------------------------------
-- | Requirement: commands cannot have the same name as any 'CmdTag'
-- (or the help function will not find it).
-- These should be /user facing/, because they give the user
-- a way of sub-dividing our confusing array of commands.
data CmdTag = Shell -- ^ Shell-specific command.
| Eval -- ^ The arrow of evaluation (reduces a term).
| KURE -- ^ 'Language.KURE' command.
| Loop -- ^ Command may operate multiple times.
| Deep -- ^ Command may make a deep change, can be O(n).
| Shallow -- ^ Command operates on local nodes only, O(1).
| Navigation -- ^ Uses 'Path' or 'Lens' to focus onto something.
| Query -- ^ Extract information from an expression.
| Predicate -- ^ Something that passes or fails.
| Introduce -- ^ Introduce something, like a new name.
| Commute -- ^ Commute is when you swap nested terms.
| PreCondition -- ^ Operation has a (perhaps undocumented) precondition.
| Strictness -- ^ Alters the strictness of the expression.
| Debug -- ^ Commands specifically to help debugging.
| VersionControl -- ^ Version control for Core syntax.
| Context -- ^ A command that uses its context, such as inlining.
| Unsafe -- ^ Commands that are not type safe (may cause Core Lint to fail),
-- or may otherwise change the semantics of the program.
-- Only available in unsafe mode!
| Safe -- ^ Include in Strict Safety mode (currently unused)
| Proof -- ^ Commands related to proving lemmas.
| TODO -- ^ An incomplete or potentially buggy command.
| Experiment -- ^ Things we are trying out.
| Deprecated -- ^ A command that will be removed in a future release;
-- it has probably been renamed or subsumed by another command.
deriving (Eq, Show, Read, Bounded, Enum)
-- | Lists all the tags paired with a short description of what they're about.
dictionaryOfTags :: [(CmdTag,String)]
dictionaryOfTags = notes ++ [ (tag,"(unknown purpose)")
| tag <- [minBound..maxBound]
, tag `notElem` map fst notes
]
where notes =
-- These should give the user a clue about what the sub-commands might do
[ (Shell, "Shell-specific command.")
, (Eval, "The arrow of evaluation (reduces a term).")
, (KURE, "Direct reflection of a combinator from the KURE DSL.")
, (Loop, "Command may operate multiple times.")
, (Deep, "Command may make a deep change, can be O(n).")
, (Shallow, "Command operates on local nodes only, O(1).")
, (Navigation, "Navigate via focus, or directional command.")
, (Query, "Extract information from an expression.")
, (Predicate, "Something that passes or fails.")
, (Introduce, "Introduce something, like a new name.")
, (Commute, "Commute is when you swap nested terms.")
, (PreCondition, "Operation has a (perhaps undocumented) precondition.")
, (Strictness, "Alters the strictness of an expression.")
, (Debug, "A command specifically to help debugging.")
, (VersionControl, "Version control for Core syntax.")
, (Context, "A command that uses its context, such as inlining.")
, (Unsafe, "Commands that are not type safe (may cause Core Lint to fail), or may otherwise change the semantics of the program.")
, (Proof, "Commands related to proving lemmas.")
, (TODO, "An incomplete or potentially buggy command.")
, (Experiment, "Things we are trying out, use at your own risk.")
, (Deprecated, "A command that will be removed in a future release; it has probably been renamed or subsumed by another command.")
]
-- Unfortunately, record update syntax seems to associate to the right.
-- These operators save us some parentheses.
infixl 3 .+
infixr 4 .||
infixr 5 .&
-- | A data type of logical operations on tags.
data TagE :: * where
Tag :: Tag a => a -> TagE
NotTag :: TagE -> TagE
AndTag :: TagE -> TagE -> TagE
OrTag :: TagE -> TagE -> TagE
-- | Tags are meta-data that we add to 'External's to make them sortable and searchable.
class Tag a where
toTagE :: a -> TagE
-- | Add a 'Tag' to an 'External'.
(.+) :: External -> a -> External
-- | Remove a 'Tag' from an 'External'.
remTag :: a -> External -> External
-- | Check if an 'External' has the specified 'Tag'.
tagMatch :: a -> External -> Bool
instance Tag TagE where
toTagE = id
e .+ (Tag t) = e .+ t
e .+ (NotTag t) = remTag t e
e .+ (AndTag t1 t2) = e .+ t1 .+ t2
e .+ (OrTag t1 t2) = e .+ t1 .+ t2 -- not sure what else to do
remTag (Tag t) e = remTag t e
remTag (NotTag t) e = e .+ t
remTag (AndTag t1 t2) e = remTag t1 (remTag t2 e)
remTag (OrTag t1 t2) e = remTag t1 (remTag t2 e) -- again
tagMatch (Tag t) e = tagMatch t e
tagMatch (NotTag t) e = not (tagMatch t e)
tagMatch (AndTag t1 t2) e = tagMatch t1 e && tagMatch t2 e
tagMatch (OrTag t1 t2) e = tagMatch t1 e || tagMatch t2 e
instance Tag CmdTag where
toTagE = Tag
ex@(External {externTags = ts}) .+ t = ex {externTags = t:ts}
remTag t ex@(External {externTags = ts}) = ex { externTags = [ t' | t' <- ts, t' /= t ] }
tagMatch t (External {externTags = ts}) = t `elem` ts
-- | An \"and\" on 'Tag's.
(.&) :: (Tag a, Tag b) => a -> b -> TagE
t1 .& t2 = AndTag (toTagE t1) (toTagE t2)
-- | An \"or\" on 'Tag's.
(.||) :: (Tag a, Tag b) => a -> b -> TagE
t1 .|| t2 = OrTag (toTagE t1) (toTagE t2)
-- how to make a unary operator?
-- | A \"not\" on 'Tag's.
notT :: Tag a => a -> TagE
notT = NotTag . toTagE
-----------------------------------------------------------------
-- | An 'External' is a 'Dynamic' value with some associated meta-data (name, help string and tags).
data External = External
{ externName :: ExternalName -- ^ Get the name of an 'External'.
, externDyn :: Dynamic -- ^ Get the 'Dynamic' value stored in an 'External'.
, externHelp :: ExternalHelp -- ^ Get the list of help 'String's for an 'External'.
, externTags :: [CmdTag] -- ^ List all the 'CmdTag's associated with an 'External'
}
-- | The primitive way to build an 'External'.
external :: Extern a => ExternalName -> a -> ExternalHelp -> External
external nm fn help = External
{ externName = nm
, externDyn = toDyn (box fn)
, externHelp = map (" " ++) help
, externTags = []
}
-- | Get all the 'External's which match a given tag predicate
-- and box a Transform of the appropriate type.
matchingExternals :: (Extern tr, Tag t) => t -> [External] -> [(External, tr)]
matchingExternals tag exts = [ (e,tr) | e <- exts, tagMatch tag e
, Just tr <- [fmap unbox $ fromDynamic $ externDyn e] ]
-- | Build a 'Data.Map' from names to help information.
toHelp :: [External] -> Map ExternalName ExternalHelp
toHelp = fromListWith (++) . map toH
where
toH :: External -> (ExternalName,ExternalHelp)
toH e = (externName e, spaceout (externName e ++ " :: " ++ externTypeString e)
(show (externTags e)) : externHelp e)
spaceout xs ys = xs ++ replicate (width - (length xs + length ys)) ' ' ++ ys
width = 78
-- | Get a string representation of the (monomorphic) type of an 'External'
externTypeString :: External -> String
externTypeString = deBoxify . show . dynTypeRep . externDyn
-- | Remove the word 'Box' from a string.
deBoxify :: String -> String
deBoxify s | "CLSBox -> " `isPrefixOf` s = go (drop 10 s)
| "PrettyPrinter -> " `isPrefixOf` s = go (drop 17 s)
| otherwise = go s
where go xs
| "Box" `isPrefixOf` xs = go (drop 3 xs)
go (x:xs) = x : go xs
go [] = []
externTypeArgResString :: External -> ([String], String)
externTypeArgResString e = (map (deBoxify . show) aTys, deBoxify (show rTy))
where (aTys, rTy) = splitExternFunType e
splitExternFunType :: External -> ([TypeRep], TypeRep)
splitExternFunType = splitFunTyArgs . dynTypeRep . externDyn
splitFunTyArgs :: TypeRep -> ([TypeRep], TypeRep)
splitFunTyArgs tr = case splitFunTyMaybe tr of
Nothing -> ([], tr)
Just (a, r) -> let (as, r') = splitFunTyArgs r
in (a:as, r')
splitFunTyMaybe :: TypeRep -> Maybe (TypeRep, TypeRep)
splitFunTyMaybe (TypeRep _ tc [a,r]) | tc == funTc = Just (a,r)
splitFunTyMaybe _ = Nothing
-----------------------------------------------------------------
-- | The class of things that can be made into 'External's.
-- To be an 'Extern' there must exist an isomorphic 'Box' type that is an instance of 'Typeable'.
class Typeable (Box a) => Extern a where
-- | An isomorphic wrapper.
type Box a
-- | Wrap a value in a 'Box'.
box :: a -> Box a
-- | Unwrap a value from a 'Box'.
unbox :: Box a -> a
-----------------------------------------------------------------
instance (Extern a, Extern b) => Extern (a -> b) where
type Box (a -> b) = Box a -> Box b
box f = box . f . unbox
unbox f = unbox . f . box
-----------------------------------------------------------------
data TagBox = TagBox TagE deriving Typeable
instance Extern TagE where
type Box TagE = TagBox
box = TagBox
unbox (TagBox t) = t
-----------------------------------------------------------------
data IntBox = IntBox Int deriving Typeable
instance Extern Int where
type Box Int = IntBox
box = IntBox
unbox (IntBox i) = i
-----------------------------------------------------------------
-- TODO: Considering unifying CrumbBox and PathBox under TransformLCoreTCPathBox.
data CrumbBox = CrumbBox Crumb deriving Typeable
instance Extern Crumb where
type Box Crumb = CrumbBox
box = CrumbBox
unbox (CrumbBox cr) = cr
-----------------------------------------------------------------
data PathBox = PathBox LocalPathH deriving Typeable
instance Extern LocalPathH where
type Box LocalPathH = PathBox
box = PathBox
unbox (PathBox p) = p
-----------------------------------------------------------------
newtype CoreString = CoreString { unCoreString :: String } deriving Typeable
instance Extern CoreString where
type Box CoreString = CoreString
box = id
unbox = id
-----------------------------------------------------------------
data StringBox = StringBox String deriving Typeable
instance Extern String where
type Box String = StringBox
box = StringBox
unbox (StringBox s) = s
-----------------------------------------------------------------
data StringListBox = StringListBox [String] deriving Typeable
instance Extern [String] where
type Box [String] = StringListBox
box = StringListBox
unbox (StringListBox l) = l
-----------------------------------------------------------------
data IntListBox = IntListBox [Int] deriving Typeable
instance Extern [Int] where
type Box [Int] = IntListBox
box = IntListBox
unbox (IntListBox l) = l
-----------------------------------------------------------------
instance Extern LemmaName where
type Box LemmaName = LemmaName
box = id
unbox = id
-----------------------------------------------------------------
data RewriteLCoreBox = RewriteLCoreBox (RewriteH LCore) deriving Typeable
instance Extern (RewriteH LCore) where
type Box (RewriteH LCore) = RewriteLCoreBox
box = RewriteLCoreBox
unbox (RewriteLCoreBox r) = r
-----------------------------------------------------------------
data TransformLCoreStringBox = TransformLCoreStringBox (TransformH LCore String) deriving Typeable
instance Extern (TransformH LCore String) where
type Box (TransformH LCore String) = TransformLCoreStringBox
box = TransformLCoreStringBox
unbox (TransformLCoreStringBox t) = t
-----------------------------------------------------------------
data TransformLCoreUnitBox = TransformLCoreUnitBox (TransformH LCore ()) deriving Typeable
instance Extern (TransformH LCore ()) where
type Box (TransformH LCore ()) = TransformLCoreUnitBox
box = TransformLCoreUnitBox
unbox (TransformLCoreUnitBox t) = t
-----------------------------------------------------------------
data TransformLCorePathBox = TransformLCorePathBox (TransformH LCore LocalPathH) deriving Typeable
instance Extern (TransformH LCore LocalPathH) where
type Box (TransformH LCore LocalPathH) = TransformLCorePathBox
box = TransformLCorePathBox
unbox (TransformLCorePathBox t) = t
-----------------------------------------------------------------
data BiRewriteLCoreBox = BiRewriteLCoreBox (BiRewriteH LCore) deriving Typeable
instance Extern (BiRewriteH LCore) where
type Box (BiRewriteH LCore) = BiRewriteLCoreBox
box = BiRewriteLCoreBox
unbox (BiRewriteLCoreBox b) = b
-----------------------------------------------------------------
data RewriteLCoreListBox = RewriteLCoreListBox [RewriteH LCore] deriving Typeable
instance Extern [RewriteH LCore] where
type Box [RewriteH LCore] = RewriteLCoreListBox
box = RewriteLCoreListBox
unbox (RewriteLCoreListBox l) = l
-----------------------------------------------------------------
data RewriteLCoreTCBox = RewriteLCoreTCBox (RewriteH LCoreTC) deriving Typeable
instance Extern (RewriteH LCoreTC) where
type Box (RewriteH LCoreTC) = RewriteLCoreTCBox
box = RewriteLCoreTCBox
unbox (RewriteLCoreTCBox r) = r
-----------------------------------------------------------------
data TransformLCoreTCStringBox = TransformLCoreTCStringBox (TransformH LCoreTC String) deriving Typeable
instance Extern (TransformH LCoreTC String) where
type Box (TransformH LCoreTC String) = TransformLCoreTCStringBox
box = TransformLCoreTCStringBox
unbox (TransformLCoreTCStringBox t) = t
-----------------------------------------------------------------
data TransformLCoreTCUnitBox = TransformLCoreTCUnitBox (TransformH LCoreTC ()) deriving Typeable
instance Extern (TransformH LCoreTC ()) where
type Box (TransformH LCoreTC ()) = TransformLCoreTCUnitBox
box = TransformLCoreTCUnitBox
unbox (TransformLCoreTCUnitBox t) = t
-----------------------------------------------------------------
data TransformLCoreTCLCoreBox = TransformLCoreTCLCoreBox (TransformH LCoreTC LCore) deriving Typeable
instance Extern (TransformH LCoreTC LCore) where
type Box (TransformH LCoreTC LCore) = TransformLCoreTCLCoreBox
box = TransformLCoreTCLCoreBox
unbox (TransformLCoreTCLCoreBox t) = t
-----------------------------------------------------------------
data TransformLCoreTCPathBox = TransformLCoreTCPathBox (TransformH LCoreTC LocalPathH) deriving Typeable
instance Extern (TransformH LCoreTC LocalPathH) where
type Box (TransformH LCoreTC LocalPathH) = TransformLCoreTCPathBox
box = TransformLCoreTCPathBox
unbox (TransformLCoreTCPathBox t) = t
-----------------------------------------------------------------
data BiRewriteLCoreTCBox = BiRewriteLCoreTCBox (BiRewriteH LCoreTC) deriving Typeable
instance Extern (BiRewriteH LCoreTC) where
type Box (BiRewriteH LCoreTC) = BiRewriteLCoreTCBox
box = BiRewriteLCoreTCBox
unbox (BiRewriteLCoreTCBox b) = b
-----------------------------------------------------------------
data RewriteLCoreTCListBox = RewriteLCoreTCListBox [RewriteH LCoreTC] deriving Typeable
instance Extern [RewriteH LCoreTC] where
type Box [RewriteH LCoreTC] = RewriteLCoreTCListBox
box = RewriteLCoreTCListBox
unbox (RewriteLCoreTCListBox l) = l
-----------------------------------------------------------------