Safe Haskell | None |
---|---|
Language | Haskell2010 |
This module provides the external interface for using Retrie as a library. All other modules should be considered internal, with APIs that are subject to change without notice.
Synopsis
- runScript :: (Options -> IO (Retrie ())) -> IO ()
- runScriptWithModifiedOptions :: (Options -> IO (Options, Retrie ())) -> IO ()
- parseImports :: [String] -> IO AnnotatedImports
- parseQueries :: Options -> [(Quantifiers, QuerySpec, v)] -> IO [Query Universe v]
- data QuerySpec
- parseRewrites :: Options -> [RewriteSpec] -> IO [Rewrite Universe]
- data RewriteSpec
- type QualifiedName = String
- data Retrie a
- apply :: [Rewrite Universe] -> Retrie ()
- applyWithStrategy :: Strategy (TransformT (WriterT Change IO)) -> [Rewrite Universe] -> Retrie ()
- applyWithUpdate :: ContextUpdater -> [Rewrite Universe] -> Retrie ()
- applyWithUpdateAndStrategy :: ContextUpdater -> Strategy (TransformT (WriterT Change IO)) -> [Rewrite Universe] -> Retrie ()
- addImports :: AnnotatedImports -> Retrie ()
- ifChanged :: Retrie () -> Retrie () -> Retrie ()
- iterateR :: Int -> Retrie () -> Retrie ()
- focus :: Data k => [Query k v] -> Retrie ()
- query :: [Query Universe v] -> Retrie [(Context, Substitution, v)]
- queryWithUpdate :: ContextUpdater -> [Query Universe v] -> Retrie [(Context, Substitution, v)]
- bottomUp :: Strategy m
- topDown :: Strategy m
- topDownPrune :: Monad m => Strategy (TransformT (WriterT Change m))
- type MatchResultTransformer = Context -> MatchResult Universe -> IO (MatchResult Universe)
- defaultTransformer :: MatchResultTransformer
- data MatchResult ast
- = MatchResult Substitution (Template ast)
- | NoMatch
- data Annotated ast
- astA :: Annotated ast -> ast
- type AnnotatedHsDecl = Annotated (LHsDecl GhcPs)
- type AnnotatedHsExpr = Annotated (LHsExpr GhcPs)
- type AnnotatedHsType = Annotated (LHsType GhcPs)
- type AnnotatedImports = Annotated [LImportDecl GhcPs]
- type AnnotatedModule = Annotated (Located HsModule)
- type AnnotatedPat = Annotated (Located (Pat GhcPs))
- type AnnotatedStmt = Annotated (LStmt GhcPs (LHsExpr GhcPs))
- parseDecl :: String -> IO AnnotatedHsDecl
- parseExpr :: String -> IO AnnotatedHsExpr
- parsePattern :: String -> IO AnnotatedPat
- parseStmt :: String -> IO AnnotatedStmt
- parseType :: String -> IO AnnotatedHsType
- transformA :: Monad m => Annotated ast1 -> (ast1 -> TransformT m ast2) -> m (Annotated ast2)
- graftA :: (Data ast, Monad m) => Annotated ast -> TransformT m ast
- pruneA :: (Data ast, Monad m) => ast -> TransformT m (Annotated ast)
- trimA :: Data ast => Annotated ast -> Annotated ast
- printA :: Annotate ast => Annotated (Located ast) -> String
- module Retrie.Expr
- data Context = Context {}
- type ContextUpdater = forall m. MonadIO m => GenericCU (TransformT m) Context
- updateContext :: forall m. MonadIO m => GenericCU (TransformT m) Context
- type Options = Options_ [Rewrite Universe] AnnotatedImports
- data Options_ rewrites imports = Options {
- additionalImports :: imports
- colorise :: ColoriseFun
- executionMode :: ExecutionMode
- extraIgnores :: [FilePath]
- fixityEnv :: FixityEnv
- iterateN :: Int
- randomOrder :: Bool
- rewrites :: rewrites
- roundtrips :: [RoundTrip]
- singleThreaded :: Bool
- targetDir :: FilePath
- targetFiles :: [FilePath]
- verbosity :: Verbosity
- data Verbosity
- module Retrie.Quantifiers
- data Query ast v = Query {
- qQuantifiers :: Quantifiers
- qPattern :: Annotated ast
- qResult :: v
- type Rewrite ast = Query ast (Template ast, MatchResultTransformer)
- data Template ast = Template {
- tTemplate :: Annotated ast
- tImports :: AnnotatedImports
- tDependents :: Maybe [Rewrite Universe]
- mkRewrite :: Quantifiers -> Annotated ast -> Annotated ast -> Rewrite ast
- ppRewrite :: Rewrite Universe -> String
- addRewriteImports :: AnnotatedImports -> Rewrite ast -> Rewrite ast
- setRewriteTransformer :: MatchResultTransformer -> Rewrite ast -> Rewrite ast
- subst :: (MonadIO m, Data ast) => Substitution -> Context -> ast -> TransformT m ast
- module Retrie.Substitution
- data Universe
- class Matchable ast where
- toURewrite :: Matchable ast => Rewrite ast -> Rewrite Universe
- fromURewrite :: Matchable ast => Rewrite Universe -> Rewrite ast
- module Retrie.GHC
Scripting
runScript :: (Options -> IO (Retrie ())) -> IO () Source #
Define a custom refactoring script.
A script is an IO
action that defines a Retrie
computation. The IO
action is run once, and the resulting Retrie
computation is run once
for each target file. Typically, rewrite parsing/construction is done in
the IO
action, so it is performed only once. Example:
module Main where main :: IO () main = runScript $ \opts -> do rr <- parseRewrites opts ["forall f g xs. map f (map g xs) = map (f . g) xs"] return $ apply rr
To run the script, compile the program and execute it.
Parsing Rewrites
Imports
parseImports :: [String] -> IO AnnotatedImports Source #
Parse import statements. Each string must be a full import statement, including the keyword 'import'. Supports full import syntax.
Queries
parseQueries :: Options -> [(Quantifiers, QuerySpec, v)] -> IO [Query Universe v] Source #
Create Query
s from string specifications of expressionstypesstatements.
Specifies which parser to use in parseQueries
.
Rewrites
parseRewrites :: Options -> [RewriteSpec] -> IO [Rewrite Universe] Source #
Create Rewrite
s from string specifications of rewrites.
data RewriteSpec Source #
Possible ways to specify rewrites to parseRewrites
.
Adhoc String | Equation in RULES-format. (e.g. |
AdhocPattern String | Equation in pattern-synonym format, _without_ the keyword |
AdhocType String | Equation in type-synonym format, _without_ the keyword 'type'. |
Fold QualifiedName | Fold a function definition. The inverse of unfolding/inlining. Replaces instances of the function body with calls to the function. |
RuleBackward QualifiedName | Apply a GHC RULE right-to-left. |
RuleForward QualifiedName | Apply a GHC RULE left-to-right. |
TypeBackward QualifiedName | Apply a type synonym right-to-left. |
TypeForward QualifiedName | Apply a type synonym left-to-right. |
Unfold QualifiedName | Unfold, or inline, a function definition. |
PatternForward QualifiedName | Unfold a pattern synonym |
PatternBackward QualifiedName | Fold a pattern synonym, replacing instances of the rhs with the synonym |
type QualifiedName = String Source #
A qualified name. (e.g. "Module.Name.functionName"
)
Retrie Computations
The Retrie
monad is essentially IO
, plus state containing the module
that is being transformed. It is run once per target file.
It is special because it also allows Retrie to extract a set of GroundTerms
from the Retrie
computation without evaluating it.
Retrie uses the ground terms to select which files to target. This is the key optimization that allows Retrie to handle large codebases.
Note: Due to technical limitations, we cannot extract ground terms if you
use liftIO
before calling one of apply
, focus
, or query
at least
once. This will cause Retrie to attempt to parse every module in the target
directory. In this case, please add a call to focus
before the call to
liftIO
.
Applying Rewrites
apply :: [Rewrite Universe] -> Retrie () Source #
Apply a set of rewrites. By default, rewrites are applied top-down,
pruning the traversal at successfully changed AST nodes. See topDownPrune
.
applyWithStrategy :: Strategy (TransformT (WriterT Change IO)) -> [Rewrite Universe] -> Retrie () Source #
Apply a set of rewrites with a custom traversal strategy.
applyWithUpdate :: ContextUpdater -> [Rewrite Universe] -> Retrie () Source #
Apply a set of rewrites with a custom context-update function.
applyWithUpdateAndStrategy :: ContextUpdater -> Strategy (TransformT (WriterT Change IO)) -> [Rewrite Universe] -> Retrie () Source #
Apply a set of rewrites with custom context-update and traversal strategy.
addImports :: AnnotatedImports -> Retrie () Source #
Add imports to the module.
Control Flow
iterateR :: Int -> Retrie () -> Retrie () Source #
Iterate given Retrie
computation until it no longer makes changes,
or N times, whichever happens first.
Focusing
focus :: Data k => [Query k v] -> Retrie () Source #
Use the given queries/rewrites to select files for rewriting.
Does not actually perform matching. This is useful if the queries/rewrites
which best determine which files to target are not the first ones you run,
and when you need to liftIO
before running any queries/rewrites.
Querying the AST
query :: [Query Universe v] -> Retrie [(Context, Substitution, v)] Source #
Query the AST. Each match returns the context of the match, a substitution mapping quantifiers to matched subtrees, and the query's value.
queryWithUpdate :: ContextUpdater -> [Query Universe v] -> Retrie [(Context, Substitution, v)] Source #
Query the AST with a custom context update function.
Traversal Strategies
topDownPrune :: Monad m => Strategy (TransformT (WriterT Change m)) Source #
Top-down traversal that does not descend into changed AST nodes.
Default strategy used by apply
.
Advanced Scripting
For advanced rewriting, Retrie provides the notion of a
MatchResultTransformer
. This is a callback function that is provided the
result of matching the left-hand side of an equation. Whatever the callback
returns is used to perform the actual rewrite.
The callback has access to the Context
of the match, the generated
Substitution
, and IO
. Helper libraries such as Annotated
and subst
make it possible to define complex transformers without too much tedium.
Transformers can check side conditions by examining the MatchResult
and
returning NoMatch
when conditions do not hold.
type MatchResultTransformer = Context -> MatchResult Universe -> IO (MatchResult Universe) Source #
A MatchResultTransformer
allows the user to specify custom logic to
modify the result of matching the left-hand side of a rewrite
(the MatchResult
). The MatchResult
generated by this function is used
to effect the resulting AST rewrite.
For example, this transformer looks at the matched expression to build the resulting expression:
fancyMigration :: MatchResultTransformer fancyMigration ctxt matchResult | MatchResult sub t <- matchResult , HoleExpr e <- lookupSubst sub "x" = do e' <- ... some fancy IO computation using 'e' ... return $ MatchResult (extendSubst sub "x" (HoleExpr e')) t | otherwise = NoMatch main :: IO () main = runScript $ \opts -> do rrs <- parseRewrites opts [Adhoc "forall x. ... = ..."] return $ apply [ setRewriteTransformer fancyMigration rr | rr <- rrs ]
Since the MatchResultTransformer
can also modify the Template
, you
can construct an entirely novel right-hand side, add additional imports,
or inject new dependent rewrites.
defaultTransformer :: MatchResultTransformer Source #
The default transformer. Returns the MatchResult
unchanged.
data MatchResult ast Source #
The result of matching the left-hand side of a Rewrite
.
Instances
Functor MatchResult Source # | |
Defined in Retrie.Types fmap :: (a -> b) -> MatchResult a -> MatchResult b # (<$) :: a -> MatchResult b -> MatchResult a # |
Annotated ASTs
Annotated
packages an AST fragment with the annotations necessary to
exactPrint
or transform
that AST.
Instances
Functor Annotated Source # | |
Foldable Annotated Source # | |
Defined in Retrie.ExactPrint.Annotated fold :: Monoid m => Annotated m -> m # foldMap :: Monoid m => (a -> m) -> Annotated a -> m # foldMap' :: Monoid m => (a -> m) -> Annotated a -> m # foldr :: (a -> b -> b) -> b -> Annotated a -> b # foldr' :: (a -> b -> b) -> b -> Annotated a -> b # foldl :: (b -> a -> b) -> b -> Annotated a -> b # foldl' :: (b -> a -> b) -> b -> Annotated a -> b # foldr1 :: (a -> a -> a) -> Annotated a -> a # foldl1 :: (a -> a -> a) -> Annotated a -> a # toList :: Annotated a -> [a] # length :: Annotated a -> Int # elem :: Eq a => a -> Annotated a -> Bool # maximum :: Ord a => Annotated a -> a # minimum :: Ord a => Annotated a -> a # | |
Traversable Annotated Source # | |
Defined in Retrie.ExactPrint.Annotated | |
(Data ast, Monoid ast) => Semigroup (Annotated ast) Source # | |
(Data ast, Monoid ast) => Monoid (Annotated ast) Source # | |
Default ast => Default (Annotated ast) Source # | |
Defined in Retrie.ExactPrint.Annotated |
Type Synonyms
type AnnotatedImports = Annotated [LImportDecl GhcPs] Source #
Parsing
Note: These parsers do not re-associate infix operators.
To do so, use fix
. For example:
do expr <- parseExpr "f <$> x <*> y" e <- transformA expr (fix (fixityEnv opts))
parsePattern :: String -> IO AnnotatedPat Source #
Parse a Pat
.
Operations
transformA :: Monad m => Annotated ast1 -> (ast1 -> TransformT m ast2) -> m (Annotated ast2) Source #
Transform an Annotated
thing.
graftA :: (Data ast, Monad m) => Annotated ast -> TransformT m ast Source #
Graft an Annotated
thing into the current transformation.
The resulting AST will have proper annotations within the TransformT
computation. For example:
mkDeclList :: IO (Annotated [LHsDecl GhcPs]) mkDeclList = do ad1 <- parseDecl "myId :: a -> a" ad2 <- parseDecl "myId x = x" transformA ad1 $ \ d1 -> do d2 <- graftA ad2 return [d1, d2]
trimA :: Data ast => Annotated ast -> Annotated ast Source #
Trim the annotation data to only include annotations for ast
.
(Usually, the annotation data is a superset of what is necessary.)
Also freshens all source locations, so filename information
in annotation keys is discarded.
Note: not commonly needed, but useful if you want to inspect the annotation data directly and don't want to wade through a mountain of output.
Util
Collection of miscellaneous helpers for manipulating the GHC AST.
module Retrie.Expr
Types
Context
Context
maintained by AST traversals.
Context | |
|
type ContextUpdater = forall m. MonadIO m => GenericCU (TransformT m) Context Source #
Type of context update functions for apply
.
When defining your own ContextUpdater
, you probably want to extend
updateContext
using SYB combinators such as mkQ
and extQ
.
updateContext :: forall m. MonadIO m => GenericCU (TransformT m) Context Source #
Default context update function.
Options
data Options_ rewrites imports Source #
Options | |
|
Quantifiers
module Retrie.Quantifiers
Queries
Query
is the primitive way to specify a matchable pattern (quantifiers
and expression). Whenever the pattern is matched, the associated result
will be returned.
Query | |
|
Rewrites
The right-hand side of a Rewrite
.
Template | |
|
mkRewrite :: Quantifiers -> Annotated ast -> Annotated ast -> Rewrite ast Source #
Make a Rewrite
from given quantifiers and left- and right-hand sides.
addRewriteImports :: AnnotatedImports -> Rewrite ast -> Rewrite ast Source #
setRewriteTransformer :: MatchResultTransformer -> Rewrite ast -> Rewrite ast Source #
Set the MatchResultTransformer
for a Rewrite
.
Substitution
subst :: (MonadIO m, Data ast) => Substitution -> Context -> ast -> TransformT m ast Source #
Perform the given Substitution
on an AST, avoiding variable capture
by alpha-renaming binders as needed.
See Retrie.Substitution for the Substitution
type.
module Retrie.Substitution
Universe
A sum type to collect all possible top-level rewritable types.
Instances
Data Universe Source # | |
Defined in Retrie.Universe gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Universe -> c Universe # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Universe # toConstr :: Universe -> Constr # dataTypeOf :: Universe -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c Universe) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Universe) # gmapT :: (forall b. Data b => b -> b) -> Universe -> Universe # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Universe -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Universe -> r # gmapQ :: (forall d. Data d => d -> u) -> Universe -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Universe -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Universe -> m Universe # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Universe -> m Universe # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Universe -> m Universe # | |
Matchable Universe Source # | |
class Matchable ast where Source #
Class of types which can be injected into the Universe
type.
inject :: ast -> Universe Source #
Inject an AST into Universe
project :: Universe -> ast Source #
Project an AST from a Universe
.
Can fail if universe contains the wrong type.
getOrigin :: ast -> SrcSpan Source #
Get the original location of the AST.
toURewrite :: Matchable ast => Rewrite ast -> Rewrite Universe Source #
Inject a type-specific rewrite into the universal type.
fromURewrite :: Matchable ast => Rewrite Universe -> Rewrite ast Source #
Project a type-specific rewrite from the universal type.
GHC API
Retrie.GHC re-exports the GHC API, with some helpers for consistency across versions.
module Retrie.GHC