pointful 1.0.5 → 1.0.6
raw patch · 4 files changed
+125/−498 lines, 4 filesdep +haskell-src-extsdep +transformersdep −haskell-srcdep ~basedep ~containersdep ~mtlPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: haskell-src-exts, transformers
Dependencies removed: haskell-src
Dependency ranges changed: base, containers, mtl, syb
API changes (from Hackage documentation)
- Lambdabot.FixPrecedence: class FixPrecedence a
- Lambdabot.FixPrecedence: fixPrecedence :: FixPrecedence a => a -> a
- Lambdabot.FixPrecedence: instance FixPrecedence HsDecl
- Lambdabot.FixPrecedence: instance FixPrecedence HsExp
- Lambdabot.FixPrecedence: precTable :: PrecedenceData
- Lambdabot.FixPrecedence: withPrecDecl :: PrecedenceData -> HsDecl -> (PrecedenceData, HsDecl)
- Lambdabot.FixPrecedence: withPrecExp :: PrecedenceData -> HsExp -> HsExp
- Lambdabot.Parser: parseDecl :: String -> Either String HsDecl
- Lambdabot.Parser: parseExpr :: String -> Either String HsExp
- Lambdabot.Parser: prettyPrintInLine :: Pretty a => a -> String
- Lambdabot.Parser: withParsed :: (forall a. (Data a, Eq a) => a -> a) -> String -> String
- Lambdabot.Pointful: ParseFailed :: SrcLoc -> String -> ParseResult a
- Lambdabot.Pointful: ParseOk :: a -> ParseResult a
- Lambdabot.Pointful: combinatorModule :: String
- Lambdabot.Pointful: data ParseResult a :: * -> *
- Lambdabot.Pointful: main :: IO ()
- Lambdabot.Pointful: test :: String -> IO ()
Files
- Lambdabot/FixPrecedence.hs +0/−343
- Lambdabot/Parser.hs +13/−76
- Lambdabot/Pointful.hs +96/−63
- pointful.cabal +16/−16
− Lambdabot/FixPrecedence.hs
@@ -1,343 +0,0 @@-module Lambdabot.FixPrecedence (withPrecExp, withPrecDecl, precTable, FixPrecedence(..) ) where--import qualified Data.Map as M-import Language.Haskell.Syntax-import Data.List--{-- PrecedenceData-- This is a data type to hold precedence information. It simply records,- for each operator, its precedence level (a number), and associativity- (one of HsAssocNone, HsAssocLeft, or HsAssocRight).--}-type PrecedenceData = M.Map HsQName (HsAssoc, Int)--{-- findPrec-- Looks up precedence information for a goven operator. If the operator- is not in the precedence data, the Haskell report specifies that it- should be treated as infixl 9.--}-findPrec :: PrecedenceData -> HsQName -> (HsAssoc, Int)-findPrec = flip (M.findWithDefault defaultPrec)- where defaultPrec = (HsAssocLeft, 9)--{-- precWrong-- This returns True iff the first operator should be a parent of the- second in the expression tree, when they occur consecutively left to- right in the input. This is called "wrong" because the parser in- Language.Haskell.Parser treats everything as left associative at the- same precedence, so the right-most operator will be the parent in the- expression tree in the original input.-- XXX: Currently, this function treats operators with no associativity- as if they were left associative. It also looks only at the- associativity of the left-most operator. This should work for- correct code, but it does not report errors for incorrect code.--}-precWrong :: PrecedenceData -> HsQName -> HsQName -> Bool-precWrong pd a b = let (assoc, prec) = findPrec pd a- (_, prec') = findPrec pd b- in (prec < prec')- || (prec == prec' && assoc == HsAssocRight)--{-- nameFromQOp-- Extracts the HsQName from an HsQOp.--}-nameFromQOp :: HsQOp -> HsQName-nameFromQOp (HsQVarOp s) = s-nameFromQOp (HsQConOp s) = s--nameFromOp :: HsOp -> HsQName-nameFromOp (HsVarOp n) = UnQual n-nameFromOp (HsConOp n) = UnQual n--{-- withPrecExp-- This routine fixes up an expression by applying precedence data.--}-withPrecExp :: PrecedenceData -> HsExp -> HsExp--{-- This is the heart of the whole thing. It applies an algorithm- described by LaLonde and Rivieres in ACM Transactions on Programming- Languages and Systems, January 1981. The idea is to take a parse- tree with a consistent left-associative organization, and rearrange it- to match a precedence table.-- A few changes have been made. LaLonde and Rivieres remove parentheses- from their parse tree, which isn't necessary here; and they work with- an inherently right-associative grammar, while Language.Haskell.Parser- produces a left-associative grammar.--}-withPrecExp pd (HsInfixApp k@(HsInfixApp e qop' f) qop g) =- let g' = withPrecExp pd g- op = nameFromQOp qop- op' = nameFromQOp qop'- in if precWrong pd op' op- then let e' = withPrecExp pd e- f' = withPrecExp pd f- in withPrecExp pd (HsInfixApp e' qop' (HsInfixApp f' qop g'))- else HsInfixApp (withPrecExp pd k) qop g'--withPrecExp pd (HsInfixApp e op f) =- HsInfixApp (withPrecExp pd e) op (withPrecExp pd f)--{-- The remaining cases simply propogate the correction throughout other- elements of the grammar.--}-withPrecExp _ (HsVar v) = HsVar v-withPrecExp _ (HsCon c) = HsCon c-withPrecExp _ (HsLit l) = HsLit l-withPrecExp pd (HsApp e f) =- HsApp (withPrecExp pd e) (withPrecExp pd f)-withPrecExp pd (HsNegApp e) =- HsNegApp (withPrecExp pd e)-withPrecExp pd (HsLambda loc pats e) =- let pats' = map (withPrecPat pd) pats- in HsLambda loc pats' (withPrecExp pd e)-withPrecExp pd (HsLet decls e) =- let (pd', decls') = mapAccumL withPrecDecl pd decls- in HsLet decls' (withPrecExp pd' e)-withPrecExp pd (HsIf e f g) =- HsIf (withPrecExp pd e) (withPrecExp pd f) (withPrecExp pd g)-withPrecExp pd (HsCase e alts) =- let alts' = map (withPrecAlt pd) alts- in HsCase (withPrecExp pd e) alts'-withPrecExp pd (HsDo stmts) =- let (_, stmts') = mapAccumL withPrecStmt pd stmts- in HsDo stmts'-withPrecExp pd (HsTuple exps) =- let exps' = map (withPrecExp pd) exps- in HsTuple exps'-withPrecExp pd (HsList exps) =- let exps' = map (withPrecExp pd) exps- in HsList exps'-withPrecExp pd (HsParen e) =- HsParen (withPrecExp pd e)-withPrecExp pd (HsLeftSection e op) =- HsLeftSection (withPrecExp pd e) op-withPrecExp pd (HsRightSection op e) =- HsRightSection op (withPrecExp pd e)-withPrecExp pd (HsRecConstr n upd) =- let upd' = map (withPrecUpd pd) upd- in HsRecConstr n upd'-withPrecExp pd (HsRecUpdate e upd) =- let upd' = map (withPrecUpd pd) upd- in HsRecUpdate (withPrecExp pd e) upd'-withPrecExp pd (HsEnumFrom e) =- HsEnumFrom (withPrecExp pd e)-withPrecExp pd (HsEnumFromThen e f) =- HsEnumFromThen (withPrecExp pd e) (withPrecExp pd f)-withPrecExp pd (HsEnumFromTo e f) =- HsEnumFromTo (withPrecExp pd e) (withPrecExp pd f)-withPrecExp pd (HsEnumFromThenTo e f g) =- HsEnumFromThenTo (withPrecExp pd e) (withPrecExp pd f) (withPrecExp pd g)-withPrecExp pd (HsListComp e stmts) =- let (_, stmts') = mapAccumL withPrecStmt pd stmts- in HsListComp (withPrecExp pd e) stmts'-withPrecExp pd (HsExpTypeSig l e t) =- HsExpTypeSig l (withPrecExp pd e) t-withPrecExp pd (HsAsPat n e) =- HsAsPat n (withPrecExp pd e)-withPrecExp _ (HsWildCard) =- HsWildCard-withPrecExp pd (HsIrrPat e) =- HsIrrPat (withPrecExp pd e)--{-- This function is analogous to withPrec, but operates on patterns instead- of expressions.--}-withPrecPat :: PrecedenceData -> HsPat -> HsPat--{-- This is the same algorithm based on Lalonde and Rivieres, but designed- to work with infix data constructors in pattern matching.--}-withPrecPat pd (HsPInfixApp k@(HsPInfixApp e op' f) op g) =- let g' = withPrecPat pd g- in if precWrong pd op' op- then let e' = withPrecPat pd e- f' = withPrecPat pd f- in withPrecPat pd (HsPInfixApp e' op' (HsPInfixApp f' op g'))- else HsPInfixApp (withPrecPat pd k) op g'--withPrecPat pd (HsPInfixApp e op f) =- HsPInfixApp (withPrecPat pd e) op (withPrecPat pd f)--withPrecPat _ (HsPVar n) = HsPVar n-withPrecPat _ (HsPLit l) = HsPLit l-withPrecPat pd (HsPNeg p) = HsPNeg (withPrecPat pd p)-withPrecPat pd (HsPApp n ps) = let ps' = map (withPrecPat pd) ps- in HsPApp n ps'-withPrecPat pd (HsPTuple ps) = let ps' = map (withPrecPat pd) ps- in HsPTuple ps'-withPrecPat pd (HsPList ps) = let ps' = map (withPrecPat pd) ps- in HsPList ps'-withPrecPat pd (HsPParen p) = HsPParen (withPrecPat pd p)-withPrecPat pd (HsPRec n pfs) = let pfs' = map (withPrecPatField pd) pfs- in HsPRec n pfs'-withPrecPat pd (HsPAsPat n p) = HsPAsPat n (withPrecPat pd p)-withPrecPat _ (HsPWildCard) = HsPWildCard-withPrecPat pd (HsPIrrPat p) = HsPIrrPat (withPrecPat pd p)--{-- Propogates precedence fixing through a pattern "field"--}-withPrecPatField :: PrecedenceData -> HsPatField -> HsPatField-withPrecPatField pd (HsPFieldPat n p) = HsPFieldPat n (withPrecPat pd p)--{-- Propogates precedence fixing through declaration sections. This- gets interesting, because declarations can actually change the- existing precedence, so withPrecDecl returns both the transformed- tree and an augmented precedence relation.--}-withPrecDecl :: PrecedenceData -> HsDecl -> (PrecedenceData, HsDecl)-withPrecDecl pd d@(HsInfixDecl _ assoc p ops) =- let nms = map nameFromOp ops- prec = (assoc, p)- pd' = M.union pd $ M.fromList $ map (flip (,) prec) nms- in (pd', d)-withPrecDecl pd (HsClassDecl l ctx n ns decls) =- let (pd', decls') = mapAccumL withPrecDecl pd decls- in (pd', HsClassDecl l ctx n ns decls')-withPrecDecl pd (HsInstDecl l ctx n ts decls) =- -- The question of what to do with fixity declarations here is- -- interesting. The report says they aren't allowed (4.3.2), but- -- GHC accepts them as of version 6.6 and apparently ignores them.- -- The best thing is probably to match GHC's behavior.- let decls' = map snd $ map (withPrecDecl pd) decls- in (pd, HsInstDecl l ctx n ts decls')-withPrecDecl pd (HsFunBind ms) =- let ms' = map (withPrecMatch pd) ms- in (pd, HsFunBind ms')-withPrecDecl pd (HsPatBind l p rhs decls) =- let p' = withPrecPat pd p- (pd',decls') = mapAccumL withPrecDecl pd decls- rhs' = withPrecRhs pd' rhs- in (pd, HsPatBind l p' rhs' decls')-withPrecDecl pd d = (pd, d)--{-- Propogates precedence fixing through HsMatch--}-withPrecMatch :: PrecedenceData -> HsMatch -> HsMatch-withPrecMatch pd (HsMatch l n ps rhs decls) =- let ps' = map (withPrecPat pd) ps- (pd', decls') = mapAccumL withPrecDecl pd decls- rhs' = withPrecRhs pd' rhs- in HsMatch l n ps' rhs' decls'--{-- Propogates precedence fixing through HsRhs--}-withPrecRhs :: PrecedenceData -> HsRhs -> HsRhs-withPrecRhs pd (HsUnGuardedRhs e) = HsUnGuardedRhs (withPrecExp pd e)-withPrecRhs pd (HsGuardedRhss grs) = let grs' = map (withPrecGRhs pd) grs- in HsGuardedRhss grs'--withPrecGRhs :: PrecedenceData -> HsGuardedRhs -> HsGuardedRhs-withPrecGRhs pd (HsGuardedRhs l e f) =- HsGuardedRhs l (withPrecExp pd e) (withPrecExp pd f)--{-- Propogates precedence fixing through case statement alternatives.--}-withPrecAlt :: PrecedenceData -> HsAlt -> HsAlt-withPrecAlt pd (HsAlt l p alts ds) =- let (pd', ds') = mapAccumL withPrecDecl pd ds- in HsAlt l (withPrecPat pd p) (withPrecGAlts pd' alts) ds'--withPrecGAlts :: PrecedenceData -> HsGuardedAlts -> HsGuardedAlts-withPrecGAlts pd (HsUnGuardedAlt e) = HsUnGuardedAlt (withPrecExp pd e)-withPrecGAlts pd (HsGuardedAlts alts) = let alts' = map (withPrecGAlt pd) alts- in HsGuardedAlts alts'--withPrecGAlt :: PrecedenceData -> HsGuardedAlt -> HsGuardedAlt-withPrecGAlt pd (HsGuardedAlt l e f) =- HsGuardedAlt l (withPrecExp pd e) (withPrecExp pd f)--{-- Propogates precedence fixing through do blocks. Because let statements- can change precedence, the result is both the transformed tree and an- augmented precedence relation, much like in withPrecDecl.--}-withPrecStmt :: PrecedenceData -> HsStmt -> (PrecedenceData, HsStmt)-withPrecStmt pd (HsGenerator l p e) =- (pd, HsGenerator l (withPrecPat pd p) (withPrecExp pd e))-withPrecStmt pd (HsQualifier e) = (pd, HsQualifier (withPrecExp pd e))-withPrecStmt pd (HsLetStmt ds) = let (pd', ds') = mapAccumL withPrecDecl pd ds- in (pd', HsLetStmt ds')--{-- Propogates precedence fixing through record field updates.--}-withPrecUpd :: PrecedenceData -> HsFieldUpdate -> HsFieldUpdate-withPrecUpd pd (HsFieldUpdate n e) = HsFieldUpdate n (withPrecExp pd e)--{-- This is the default precedence table used for parsing expressions.- It is taken from the precedences of the main operators in the Haskell- Prelude.-- XXX: It might be a good idea to search the standard library docs for- other operators. These are the ones listed in the Haskell Report- section 4. For example, one that is not included here is- Data.Ratio.%--}-precTable :: PrecedenceData-precTable = M.fromList- [- (UnQual (HsSymbol "!!"), (HsAssocLeft, 9)),- (UnQual (HsSymbol "."), (HsAssocRight, 9)),- (UnQual (HsSymbol "^"), (HsAssocRight, 8)),- (UnQual (HsSymbol "^^"), (HsAssocRight, 8)),- (UnQual (HsSymbol "**"), (HsAssocLeft, 8)),- (UnQual (HsSymbol "*"), (HsAssocLeft, 7)),- (UnQual (HsSymbol "/"), (HsAssocLeft, 7)),- (UnQual (HsIdent "div"), (HsAssocLeft, 7)),- (UnQual (HsIdent "mod"), (HsAssocLeft, 7)),- (UnQual (HsIdent "rem"), (HsAssocLeft, 7)),- (UnQual (HsIdent "quot"), (HsAssocLeft, 7)),- (UnQual (HsSymbol "+"), (HsAssocLeft, 6)),- (UnQual (HsSymbol "-"), (HsAssocLeft, 6)),- (UnQual (HsSymbol ":"), (HsAssocRight, 5)),- (Special HsCons, (HsAssocRight, 5)),- (UnQual (HsSymbol "++"), (HsAssocRight, 5)),- (UnQual (HsSymbol "=="), (HsAssocNone, 4)),- (UnQual (HsSymbol "/="), (HsAssocNone, 4)),- (UnQual (HsSymbol "<"), (HsAssocNone, 4)),- (UnQual (HsSymbol "<="), (HsAssocNone, 4)),- (UnQual (HsSymbol ">"), (HsAssocNone, 4)),- (UnQual (HsSymbol ">="), (HsAssocNone, 4)),- (UnQual (HsIdent "elem"), (HsAssocNone, 4)),- (UnQual (HsIdent "notElem"), (HsAssocNone, 4)),- (UnQual (HsSymbol "&&"), (HsAssocRight, 3)),- (UnQual (HsSymbol "||"), (HsAssocRight, 2)),- (UnQual (HsSymbol ">>"), (HsAssocLeft, 1)),- (UnQual (HsSymbol ">>="), (HsAssocLeft, 1)),- (UnQual (HsSymbol "$"), (HsAssocRight, 0)),- (UnQual (HsSymbol "$!"), (HsAssocRight, 0)),- (UnQual (HsIdent "seq"), (HsAssocRight, 0))- ]---class FixPrecedence a where- fixPrecedence :: a -> a--instance FixPrecedence HsExp where- fixPrecedence = withPrecExp precTable--instance FixPrecedence HsDecl where- fixPrecedence = snd . withPrecDecl precTable-
Lambdabot/Parser.hs view
@@ -2,86 +2,23 @@ -- Haskell expression parser. Big hack, but only uses documented APIs so it -- should be more robust than the previous hack.-module Lambdabot.Parser (parseExpr, parseDecl, withParsed, prettyPrintInLine) where+module Lambdabot.Parser+ ( withParsed+ , prettyPrintInLine+ ) where -import Control.Monad.Error () -- Monad Either instance-import Data.Char import Data.Generics-import Language.Haskell.Parser-import Language.Haskell.Pretty-import Language.Haskell.Syntax--import Lambdabot.FixPrecedence--parseExpr :: String -> Either String HsExp-parseExpr s- | not (balanced 0 ' ' s) = Left "Unbalanced parentheses"- | otherwise = case parseModule wrapped of- ParseOk (HsModule _ _ _ _ [HsPatBind _ _ (HsUnGuardedRhs e) _])- -> Right $ fixPrecedence $ unparen e- ParseFailed (SrcLoc _ _ col) msg- -> Left $ showParseError msg (col - length prefix) s- where- prefix = "module Main where { main = ("- wrapped = prefix ++ s ++ "\n)}"-- unparen (HsParen e) = e- unparen e = e-- -- balanced (open-parentheses) (previous-character) (remaining-string)- balanced :: Int -> Char -> String -> Bool- balanced n _ "" = n == 0- balanced n _ ('(':cs) = balanced (n+1) '(' cs- balanced n _ (')':cs) = n > 0 && balanced (n-1) ')' cs- balanced n p (c :cs)- | c `elem` "\"'" && (not (isAlphaNum p) || c /= '\'')- = balancedString c n cs- balanced n p ('-':'-':_)- | not (isSymbol p) = n == 0- balanced n _ ('{':'-':cs) = balancedComment 1 n cs- balanced n _ (c :cs) = balanced n c cs-- balancedString :: Char -> Int -> String -> Bool- balancedString _ n "" = n == 0 -- the parse error will be reported by L.H.Parser- balancedString delim n ('\\':c:cs)- | isSpace c = case dropWhile isSpace cs of- '\\':cs' -> balancedString delim n cs'- cs' -> balancedString delim n cs'- | otherwise = balancedString delim n cs- balancedString delim n (c :cs)- | delim == c = balanced n c cs- | otherwise = balancedString delim n cs-- balancedComment :: Int -> Int -> String -> Bool- balancedComment 0 n cs = balanced n ' ' cs- balancedComment _ _ "" = True -- the parse error will be reported by L.H.Parser- balancedComment m n ('{':'-':cs) = balancedComment (m+1) n cs- balancedComment m n ('-':'}':cs) = balancedComment (m-1) n cs- balancedComment m n (_ :cs) = balancedComment m n cs---parseDecl :: String -> Either String HsDecl-parseDecl s = case parseModule s of- ParseOk (HsModule _ _ _ _ [d]) -> Right $ fixPrecedence d- ParseFailed (SrcLoc _ _ col) msg -> Left $ showParseError msg col s--showParseError :: String -> Int -> String -> String-showParseError msg col s = " " ++ msg- ++ case (col < 0, drop (col - 1) s) of- (True, _) -> " at end of input" -- on the next line, which has no prefix- (_,[] ) -> " at end of input"- (_,ctx ) -> let ctx' = takeWhile (/= ' ') ctx- in " at \"" ++ (take 5 ctx')- ++ (if length ctx' > 5 then "..." else "")- ++ "\" (column " ++ show col ++ ")"---- Not really parsing+import Language.Haskell.Exts +-- |Parse a string as an 'Exp' or a 'Decl', apply the given generic transformation to it,+-- and re-render it back to text. withParsed :: (forall a. (Data a, Eq a) => a -> a) -> String -> String-withParsed f s = case (parseExpr s, parseDecl s) of- (Right a, _) -> prettyPrintInLine $ f a- (_, Right a) -> prettyPrintInLine $ f a- (Left e, _) -> e+withParsed _ "" = "Error: expected a Haskell expression or declaration"+withParsed f s = case (parseExp s, parseDecl s) of+ (ParseOk a, _) -> prettyPrintInLine $ f a+ (_, ParseOk a) -> prettyPrintInLine $ f a+ (ParseFailed l e, _) -> prettyPrint l ++ ':' : e +-- |Render haskell code in a compact format prettyPrintInLine :: Pretty a => a -> String prettyPrintInLine = prettyPrintWithMode (defaultMode { layout = PPInLine })
Lambdabot/Pointful.hs view
@@ -1,14 +1,15 @@ {-# OPTIONS -fno-warn-missing-signatures #-}-module Lambdabot.Pointful (pointful, ParseResult(..), test, main, combinatorModule) where+-- Undo pointfree transformations. Plugin code derived from Pl.hs.+module Lambdabot.Pointful (pointful) where -import Lambdabot.Parser+import Lambdabot.Parser (withParsed) import Control.Monad.State+import Data.Functor.Identity (Identity) import Data.Generics-import Data.Maybe-import Language.Haskell.Parser-import Language.Haskell.Syntax import qualified Data.Map as M+import Data.Maybe+import Language.Haskell.Exts as Hs ---- Utilities ---- @@ -16,15 +17,23 @@ extT' = extT infixl `extT'` +unkLoc :: SrcLoc unkLoc = SrcLoc "<new>" 1 1 +stabilize :: Eq a => (a -> a) -> a -> a stabilize f x = let x' = f x in if x' == x then x else stabilize f x' -namesIn h = everything (++) (mkQ [] (\x -> case x of UnQual name -> [name]; _ -> [])) h-pVarsIn h = everything (++) (mkQ [] (\x -> case x of HsPVar name -> [name]; _ -> [])) h+namesIn :: Data a => a -> [Name]+namesIn h = everything (++) (mkQ [] (\x -> case x of UnQual name' -> [name']; _ -> [])) h -succName (HsIdent s) = HsIdent . reverse . succAlpha . reverse $ s+pVarsIn :: Data a => a -> [Name]+pVarsIn h = everything (++) (mkQ [] (\x -> case x of PVar name' -> [name']; _ -> [])) h +succName :: Name -> Name+succName (Ident s) = Ident . reverse . succAlpha . reverse $ s+succName (Symbol _ ) = error "Pointful plugin error: cannot determine successor for a Symbol"++succAlpha :: String -> String succAlpha ('z':xs) = 'a' : succAlpha xs succAlpha (x :xs) = succ x : xs succAlpha [] = "a"@@ -32,106 +41,137 @@ ---- Optimization (removing explicit lambdas) and restoration of infix ops ---- -- move lambda patterns into LHS-optimizeD (HsPatBind loc (HsPVar fname) (HsUnGuardedRhs (HsLambda _ pats rhs)) [])- = HsFunBind [HsMatch loc fname pats (HsUnGuardedRhs rhs) []]+optimizeD :: Decl -> Decl+optimizeD (PatBind locat (PVar fname) (UnGuardedRhs (Lambda _ pats rhs)) (BDecls []))+ = FunBind [Match locat fname pats Nothing (UnGuardedRhs rhs) (BDecls [])] ---- combine function binding and lambda-optimizeD (HsFunBind [HsMatch loc fname pats1 (HsUnGuardedRhs (HsLambda _ pats2 rhs)) []])- = HsFunBind [HsMatch loc fname (pats1 ++ pats2) (HsUnGuardedRhs rhs) []]+optimizeD (FunBind [Match locat fname pats1 Nothing (UnGuardedRhs (Lambda _ pats2 rhs)) (BDecls [])])+ = FunBind [Match locat fname (pats1 ++ pats2) Nothing (UnGuardedRhs rhs) (BDecls [])] optimizeD x = x -- remove parens-optimizeRhs (HsUnGuardedRhs (HsParen x))- = HsUnGuardedRhs x+optimizeRhs :: Rhs -> Rhs+optimizeRhs (UnGuardedRhs (Paren x))+ = UnGuardedRhs x optimizeRhs x = x -optimizeE :: HsExp -> HsExp+optimizeE :: Exp -> Exp -- apply ((\x z -> ...x...) y) yielding (\z -> ...y...) if there is only one x or y is simple-optimizeE (HsApp (HsParen (HsLambda loc (HsPVar ident : pats) body)) arg) | single || simple- = HsParen (HsLambda loc pats (everywhere (mkT (\x -> if x == (HsVar (UnQual ident)) then arg else x)) body))- where single = gcount (mkQ False (== ident)) body == 1- simple = case arg of HsVar _ -> True; _ -> False+ -- TODO: avoid captures while substituting+optimizeE (App (Paren (Lambda locat (PVar ident : pats) body)) arg) | single || simple arg+ = Paren (Lambda locat pats (everywhere (mkT (\x -> if x == (Var (UnQual ident)) then arg else x)) body))+ where single = gcount (mkQ False (== ident)) body <= 1+ simple e = case e of Var _ -> True; Lit _ -> True; Paren e' -> simple e'; _ -> False -- apply ((\_ z -> ...) y) yielding (\z -> ...)-optimizeE (HsApp (HsParen (HsLambda loc (HsPWildCard : pats) body)) _)- = HsParen (HsLambda loc pats body)+optimizeE (App (Paren (Lambda locat (PWildCard : pats) body)) _)+ = Paren (Lambda locat pats body) -- remove 0-arg lambdas resulting from application rules-optimizeE (HsLambda _ [] b)+optimizeE (Lambda _ [] b) = b -- replace (\x -> \y -> z) with (\x y -> z)-optimizeE (HsLambda loc p1 (HsLambda _ p2 body))- = HsLambda loc (p1 ++ p2) body+optimizeE (Lambda locat p1 (Lambda _ p2 body))+ = Lambda locat (p1 ++ p2) body -- remove double parens-optimizeE (HsParen (HsParen x))- = HsParen x+optimizeE (Paren (Paren x))+ = Paren x -- remove lambda body parens-optimizeE (HsLambda l p (HsParen x))- = HsLambda l p x+optimizeE (Lambda l p (Paren x))+ = Lambda l p x -- remove var, lit parens-optimizeE (HsParen x@(HsVar _))+optimizeE (Paren x@(Var _)) = x-optimizeE (HsParen x@(HsLit _))+optimizeE (Paren x@(Lit _)) = x -- remove infix+lambda parens-optimizeE (HsInfixApp a o (HsParen l@(HsLambda _ _ _)))- = HsInfixApp a o l+optimizeE (InfixApp a o (Paren l@(Lambda _ _ _)))+ = InfixApp a o l -- remove left-assoc application parens-optimizeE (HsApp (HsParen (HsApp a b)) c)- = HsApp (HsApp a b) c+optimizeE (App (Paren (App a b)) c)+ = App (App a b) c -- restore infix-optimizeE (HsApp (HsApp (HsVar name@(UnQual (HsSymbol _))) l) r)- = (HsInfixApp l (HsQVarOp name) r)+optimizeE (App (App (Var name'@(UnQual (Symbol _))) l) r)+ = (InfixApp l (QVarOp name') r)+-- eta reduce+optimizeE (Lambda l ps@(_:_) (App e (Var (UnQual v))))+ | free && last ps == PVar v+ = Lambda l (init ps) e+ where free = gcount (mkQ False (== v)) e == 0 -- fail optimizeE x = x ---- Decombinatorization ---- -- fresh name generation. TODO: prettify this+fresh :: StateT (Name, [Name]) Identity Name fresh = do (_, used) <- get modify (\(v,u) -> (until (not . (`elem` used)) succName (succName v), u))- (name, _) <- get- return name+ (name', _) <- get+ return name' -- rename all lambda-bound variables. TODO: rewrite lets as well+rename :: Exp -> StateT (Name, [Name]) Identity Exp rename = do everywhereM (mkM (\e -> case e of- (HsLambda _ ps _) -> do+ (Lambda _ ps _) -> do let pVars = concatMap pVarsIn ps newVars <- mapM (const fresh) pVars let replacements = zip pVars newVars return (everywhere (mkT (\n -> fromMaybe n (lookup n replacements))) e) _ -> return e)) -uncomb' :: HsExp -> State (HsName, [HsName]) HsExp+uncomb' :: Exp -> State (Name, [Name]) Exp +uncomb' (Paren (Paren e)) = return (Paren e)+ -- expand plain combinators-uncomb' (HsVar qname) | isJust maybeDef = rename (fromJust maybeDef)+uncomb' (Var qname) | isJust maybeDef = rename (fromJust maybeDef) where maybeDef = M.lookup qname combinators -- eliminate sections-uncomb' (HsRightSection op arg)+uncomb' (RightSection op' arg) = do a <- fresh- return (HsParen (HsLambda unkLoc [HsPVar a] (HsInfixApp (HsVar (UnQual a)) op arg)))-uncomb' (HsLeftSection arg op)+ return (Paren (Lambda unkLoc [PVar a] (InfixApp (Var (UnQual a)) op' arg)))+uncomb' (LeftSection arg op') = do a <- fresh- return (HsParen (HsLambda unkLoc [HsPVar a] (HsInfixApp arg op (HsVar (UnQual a)))))+ return (Paren (Lambda unkLoc [PVar a] (InfixApp arg op' (Var (UnQual a))))) -- infix to prefix for canonicality-uncomb' (HsInfixApp lf (HsQVarOp name) rf)- = return (HsParen (HsApp (HsApp (HsVar name) (HsParen lf)) (HsParen rf)))+uncomb' (InfixApp lf (QVarOp name') rf)+ = return (Paren (App (App (Var name') (Paren lf)) (Paren rf))) +-- Expand (>>=) when it is obviously the reader monad:++-- rewrite: (>>=) (\x -> e)+-- to: (\ a b -> a ((\ x -> e) b) b)+uncomb' (App (Var (UnQual (Symbol ">>="))) (Paren lam@Lambda{}))+ = do a <- fresh+ b <- fresh+ return (Paren (Lambda unkLoc [PVar a, PVar b]+ (App (App (Var (UnQual a)) (Paren (App lam (Var (UnQual b))))) (Var (UnQual b)))))+-- rewrite: ((>>=) e1) (\x y -> e2)+-- to: (\a -> (\x y -> e2) (e1 a) a)+uncomb' (App (App (Var (UnQual (Symbol ">>="))) e1) (Paren lam@(Lambda _ (_:_:_) _)))+ = do a <- fresh+ return (Paren (Lambda unkLoc [PVar a]+ (App (App lam (App e1 (Var (UnQual a)))) (Var (UnQual a)))))+ -- fail uncomb' expr = return expr ---- Simple combinator definitions ----+combinators :: M.Map QName Exp combinators = M.fromList $ map declToTuple defs where defs = case parseModule combinatorModule of- ParseOk (HsModule _ _ _ _ d) -> d+ ParseOk (Hs.Module _ _ _ _ _ _ d) -> d f@(ParseFailed _ _) -> error ("Combinator loading: " ++ show f)- declToTuple (HsPatBind _ (HsPVar fname) (HsUnGuardedRhs body) [])- = (UnQual fname, HsParen body)+ declToTuple (PatBind _ (PVar fname) (UnGuardedRhs body) (BDecls []))+ = (UnQual fname, Paren body)+ declToTuple _ = error "Pointful Plugin error: can't convert declaration to tuple" -- the names we recognize as combinators, so we don't generate them as temporaries then substitute them. -- TODO: more generally correct would be to not substitute any variable which is bound by a pattern+recognizedNames :: [Name] recognizedNames = map (\(UnQual n) -> n) $ M.keys combinators +combinatorModule :: String combinatorModule = unlines [ "(.) = \\f g x -> f (g x) ", "($) = \\f x -> f x ",@@ -142,6 +182,8 @@ "liftM2 = \\f m1 m2 -> m1 >>= \\x1 -> m2 >>= \\x2 -> return (f x1 x2) ", "join = (>>= id) ", "ap = liftM2 id ",+ "(>=>) = flip (<=<) ",+ "(<=<) = \\f g x -> f >>= g x ", " ", "-- ASSUMED reader monad ", "-- (>>=) = (\\f k r -> k (f r) r) ",@@ -151,7 +193,7 @@ ---- Top level ---- uncombOnce :: (Data a) => a -> a-uncombOnce x = evalState (everywhereM (mkM uncomb') x) (HsIdent "`", namesIn x ++ recognizedNames)+uncombOnce x = evalState (everywhereM (mkM uncomb') x) (Ident "`", namesIn x ++ recognizedNames) uncomb :: (Eq a, Data a) => a -> a uncomb = stabilize uncombOnce @@ -160,14 +202,5 @@ optimize :: (Eq a, Data a) => a -> a optimize = stabilize optimizeOnce -pointful = withParsed (optimize . uncomb)--test s = case parseModule s of- f@(ParseFailed _ _) -> fail (show f)- ParseOk (HsModule _ _ _ _ defs) ->- flip mapM_ defs $ \def -> do- putStrLn . prettyPrintInLine $ def- putStrLn . prettyPrintInLine . uncomb $ def- putStrLn . prettyPrintInLine . optimize . uncomb $ def--main = test "f = tail . head; g = head . tail; h = tail + tail; three = g . h . i; dontSub = (\\x -> x + x) 1; ofHead f = f . head; fm = flip mapM_ xs (\\x -> g x); po = (+1); op = (1+); g = (. f); stabilize = fix (ap . flip (ap . (flip =<< (if' .) . (==))) =<<)"+pointful :: String -> String+pointful = withParsed (stabilize (optimize . uncomb))
pointful.cabal view
@@ -1,5 +1,5 @@ name: pointful-version: 1.0.5+version: 1.0.6 synopsis: Pointful refactoring tool @@ -9,28 +9,28 @@ category: Development license: BSD3 license-file: LICENSE-author: Thomas Jäger+author: Thomas Jäger et al. maintainer: Mikhail Glushenkov <mikhail.glushenkov@gmail.com> homepage: http://github.com/23Skidoo/pointful build-type: Simple extra-source-files: Lambdabot/*.hs cabal-version: >= 1.6 -Flag separateSYB- Description: Data.Generics available in separate package.- Library exposed-modules: Lambdabot.Pointful- build-depends: containers, haskell-src, mtl- if flag(separateSYB)- build-Depends: base >= 4 && < 5, syb- else- build-Depends: base >= 3 && < 4+ other-modules: Lambdabot.Parser+ build-depends: base >= 4.4 && < 5,+ containers >= 0.4,+ haskell-src-exts >= 1.16.0,+ mtl >= 2,+ syb >= 0.3,+ transformers >= 0.2 -Executable pointful+Executable pointful main-is: Pointful.hs- build-depends: containers, haskell-src, mtl- if flag(separateSYB)- build-Depends: base >= 4 && < 5, syb- else- build-Depends: base >= 3 && < 4+ build-depends: base >= 4.4 && < 5,+ containers >= 0.4,+ haskell-src-exts >= 1.16.0,+ mtl >= 2,+ syb >= 0.3,+ transformers >= 0.2