packages feed

descript-lang-0.2.0.0: src/Descript/Build/Refactor.hs

module Descript.Build.Refactor
  ( RefactorAction (..)
  , parseRefactor
  , parseRefactorAction
  , refactor
  ) where

import Descript.Build.Read
import qualified Descript.BasicInj as BasicInj
import Descript.Misc
import qualified Data.Text as Text
import Control.Monad
import Core.Control.Monad.Trans
import Control.Monad.Trans.Class

data RefactorAction
  = RenameRecord String String
  | RenameProp String String String
  | RefactorReduce String String
  deriving (Eq, Ord, Read, Show)

-- | Parses a refactor action from command-line arguments,
-- then runs the action.
parseRefactor :: (Monad u)
              => String
              -> [String]
              -> DFile u
              -> RefactorResultT u Patch
parseRefactor label args file
    = (`refactor` file)
  =<< hoist (parseRefactorAction label args)

-- | Parses a refactor action from command-line arguments.
-- The string is the action label, the list contains the args.
parseRefactorAction :: String -> [String] -> Result RefactorError RefactorAction
parseRefactorAction "rename-record" [old, new]
  = Success $ RenameRecord old new
parseRefactorAction "rename-record" args
  = Failure $ BadRefactorArgs "rename-record" 2 args
parseRefactorAction "rename-property" [head', old, new]
  = Success $ RenameProp head' old new
parseRefactorAction "rename-property" args
  = Failure $ BadRefactorArgs "rename-property" 3 args
parseRefactorAction "reduce" [old, new]
  = Success $ RefactorReduce old new
parseRefactorAction "reduce" args
  = Failure $ BadRefactorArgs "reduce" 2 args
parseRefactorAction label args
  = Failure $ UnsupportedRefactorAction label args

-- | Parses a source file, refactors it, and returns a patch to update
-- the original file.
refactor :: (Monad u) => RefactorAction -> DFile u -> RefactorResultT u Patch
refactor (RenameRecord old new) file
  = refactorViaBasic (BasicInj.renameRecord old new) file
refactor (RenameProp head' old new) file
  = refactorViaBasic (BasicInj.renameProp head' old new) file
refactor (RefactorReduce old new) file = do
  Depd ddep src <- presToRRes Nothing $ readSrc file
  let dep = dirtyVal ddep
      dsrc = Depd dep src
      scope = BasicInj.sourceScope src
      ctx = BasicInj.recordCtx $ BasicInj.dsourceAModule dsrc
  old' <- refIRead (readInputValIn scope ctx) $ ifile $ Text.pack old
  let old_ = remAnns old'
  new' <- refIRead (readOutputValIn scope ctx old_) $ ifile $ Text.pack new
  refactorViaBasic (BasicInj.refactorReduce ddep old' new') file

refactorViaBasic :: (Monad u)
                 => RefactorFunc BasicInj.Source
                 -> DFile u
                 -> RefactorResultT u Patch
refactorViaBasic f = refactorViaBasicT $ rehoist . f

refactorViaBasicT :: (Monad u)
                  => RefactorFuncT u BasicInj.Source
                  -> DFile u
                  -> RefactorResultT u Patch
refactorViaBasicT f = fmap ppatch . f . depdVal <=< refRead

refRead :: (Monad u) => DFile u -> RefactorResultT u (DirtyDepd BasicInj.Source SrcAnn)
refRead = presToRRes Nothing . readSrc

refIRead :: (Monad u)
         => (SFile -> ParseResult a)
         -> SFile
         -> RefactorResultT u a
refIRead readSrc' isrc = presToRRes (Just isrc) $ hoist $ readSrc' isrc

presToRRes :: (Monad u) => Maybe SFile -> ParseResultT u a -> RefactorResultT u a
presToRRes isrc = mapInner lift . mapErrorT (RefactorParseError isrc)