packages feed

cparsing-0.1.0.0: CTransform.hs

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, NamedFieldPuns, LambdaCase, ViewPatterns
           , FlexibleContexts, TemplateHaskell, RankNTypes #-}

-- | Reimplementation of Remix-C transformation program.
module CTransform where

import MiniC.ParseProgram
import MiniC.Representation
import MiniC.AST
import MiniC.PrettyPrint
import MiniC.GenTemplate
import MiniC.SourceNotation
import MiniC.RangeTree
import MiniC.Semantics
import MiniC.Helpers
import MiniC.SymbolTable
import MiniC.TransformInfo
import MiniC.Instances

import SourceCode.ASTElems
import SourceCode.ToSourceTree
import SourceCode.ASTNode

import Control.Monad
import Control.Lens hiding ((<.>))
import Control.Lens.Plated
import Data.Data.Lens
import Data.Function
import Data.Maybe
import Data.Either.Combinators
import Control.Applicative
import System.Environment
import System.Directory
import System.FilePath
import Debug.Trace

main :: IO ()
main = do (file:trfs) <- getArgs
          wd <- getCurrentDirectory
          transforms <- parseTrfs trfs
          runProgram (ProgramParams file transforms) >>= \case
            Right prog -> writeFile (modifiedFileName transforms file) (prettyPrint prog)
            Left err -> putStrLn err
          
runProgram :: ProgramParams -> IO (Either String TranslationUnitNI)
runProgram (ProgramParams file transforms) =
  readFile file
    >>= parseProgram file
    >>= return 
         . mapBoth show
                   ( flip transformAST transforms 
                      . analyseAST
                      . transformSourceInfo )
                   
       
data ProgramParams 
  = ProgramParams { _inputFile :: String 
                  , _transformations :: [Transformation]
                  }
  deriving (Show)
        
-- | Transformations that the tool can produce
data Transformation = IntroduceIndirection QualifiedName
                    | RemoveIndirection QualifiedName
                    | CreateSkeleton
  deriving Show
        
-- | Parses command line arguments
parseTrfs :: [String] -> IO [Transformation]
parseTrfs ("-ri":qname:rest) 
  = (:) <$> (RemoveIndirection <$> parseQualName qname) <*> parseTrfs rest
parseTrfs ("-ii":qname:rest) 
  = (:) <$> (IntroduceIndirection <$> parseQualName qname) <*> parseTrfs rest
parseTrfs ("-cc":rest) 
  = (CreateSkeleton :) <$> parseTrfs rest
parseTrfs [] = return []
-- handle command line errors
parseTrfs (tr:_) | tr `elem` ["-ri","-ii"] 
  = error ("Not enough parameters for transformation " ++ tr) 
parseTrfs (command:_) = error $ "Unknown command: " ++ command
  
-- | Produces the name of the transformed file
modifiedFileName :: [Transformation] -> FilePath -> FilePath
modifiedFileName [CreateSkeleton]   = addPlusExtension "skeleton"
modifiedFileName _                  = addPlusExtension "configured" 

-- | Adds a new extensions before an existing one
addPlusExtension ext fn 
  = dropExtensions fn <.> ext <.> takeExtensions fn
     
transformAST :: TranslationUnitNI -> [Transformation] -> TranslationUnitNI
transformAST = foldl doTrfAST 
  where doTrfAST :: TranslationUnitNI -> Transformation -> TranslationUnitNI
        doTrfAST tu (RemoveIndirection name) = removeIndirection name tu
        doTrfAST tu (IntroduceIndirection name) = addIndirection name tu
        doTrfAST tu CreateSkeleton = createSkeleton tu
                                                
addIndirection :: QualifiedName -> TranslationUnitNI -> TranslationUnitNI
addIndirection qn tu
  = let declaration :: Simple Traversal TranslationUnitNI VariableDeclarationNI
        declaration = biplate . checkScope qn . filterTrav (has (matchQualItself qn)) simple
        exprs = biplate :: Simple Traversal TranslationUnitNI ExpressionNI
     in case toListOf declaration tu of
          [] -> error "No matching declaration for adding indirection"
          [_] -> over exprs trfExpr $ over declaration trfDecl tu
          _ -> error "Multiple matching declarations for adding indirection"
     
  where -- | Add indirection to a declaration
        trfDecl :: VariableDeclarationNI -> VariableDeclarationNI
        trfDecl vd = vd & matchQualItself qn %~ (addPtrToQual (vd ^? matchParentQual qn))
  
        trfExpr :: ExpressionNI -> ExpressionNI
        trfExpr memb@(Member{}) 
          | toTrf qn (memb ^?! exprBase)
             && (case memb ^?! exprMemberDeref of SimpleMember {} -> True; _ -> False)
          = memb & exprMemberDeref .~ genMemberDeref
        trfExpr name 
          | toTrf qn name
          = genDerefExpr name
        trfExpr other 
          = over uniplate trfExpr other
        
-- | Match the parent or the qualifier itself where changes need to be done
matchParentQual, matchQualItself :: QualifiedName -> Simple Traversal VariableDeclarationNI TypeQualifierNI
matchParentQual qn = varDeclQualTyp . qualTypQual . matchQualParent (qn ^. qnTypeQual)
matchQualItself qn = varDeclQualTyp . qualTypQual . matchQual (qn ^. qnTypeQual)
  
        
createSizeOfQual :: QualifiedTypeNI -> ExpressionNI
createSizeOfQual = undefined
        
-- | Removes indirection 
removeIndirection :: QualifiedName -> TranslationUnitNI -> TranslationUnitNI
removeIndirection qn tu
  = let declaration :: Simple Traversal TranslationUnitNI VariableDeclarationNI
        declaration = biplate . checkScope qn . filterTrav (has (matchQualItself qn)) simple
        exprs = biplate :: Simple Traversal TranslationUnitNI ExpressionNI
     in case toListOf declaration tu of
          [] -> error "No matching declaration for removing indirection"
          [_] -> over exprs trfExpr $ over declaration trfDecl tu
          _ -> error "Multiple matching declarations for removing indirection"
      
  where trfDecl :: VariableDeclarationNI -> VariableDeclarationNI
        trfDecl vd = vd & matchQualItself qn %~ (removePtrFromQual (vd ^? matchParentQual qn))
        
        trfExpr :: ExpressionNI -> ExpressionNI
        trfExpr memb@(Member{})
          | case memb ^?! exprMemberDeref of 
              DerefMember {} -> fmap (view qnTypeQual) (getQualName (memb ^?! exprBase))
                                  == Just (removePtrFromQual Nothing (qn ^. qnTypeQual))
              SimpleMember {} -> False
          = memb & exprMemberDeref .~ genMemberSimple
        trfExpr un 
          | Just (DereferenceOp {}) <- un ^? exprUnaryOp
          , Just name <- un ^? exprOperand
          , toTrf qn un
          = name
        trfExpr other 
          = over uniplate trfExpr other
      

-- | Check that the element is in the scope we search for
checkScope qn = filterTrav (((==) `on` view qnScope . simplifyQualName) qn) 
                           (info.semanticInfo.declQualName._Just) 
    
-- | Check that the elem is the element we want to transform
toTrf qn elem 
  | Just qualName <- getQualName elem
    = simplifyQualName qualName == simplifyQualName qn
  | otherwise 
    = False
            
getQualName elem = (join (elem ^? info.semanticInfo.referenceQualName)
                       <|> join (elem ^? info.semanticInfo.declQualName))
        
-- | Creates a skeleton file, a file where only declarations are kept.
createSkeleton :: TranslationUnitNI -> TranslationUnitNI
createSkeleton = transformOn (biplate :: Simple Traversal TranslationUnitNI StatementNI) 
                             removeStatements
  where removeStatements :: StatementNI -> StatementNI
        removeStatements 
          = compoundStmts %~ astFilter hasDeclaration
          
        hasDeclaration :: ASTEitherNI Declaration Statement 
                            -> Maybe (ASTEitherNI Declaration Statement)
        hasDeclaration (ASTLeft d) = Just (ASTLeft d)
        hasDeclaration (ASTRight stmt) 
          = if null (universeOn (biplate :: Simple Traversal StatementNI DeclarationNI) stmt) 
              then Nothing 
              else Just (ASTRight (transform removeStatements stmt)) 
        

-- | Propagates additional information in the syntax tree
analyseAST :: TranslationUnitNI -> TranslationUnitNI
analyseAST = transformOn (biplate :: Simple Traversal TranslationUnitNI ExpressionNI)
                         additionalScopes
  where additionalScopes expr@(Unary {}) 
          | Just (DereferenceOp {}) <- expr ^? exprUnaryOp
          , Just (Just qn) <- expr ^? exprOperand.refName
            = expr & refName %~ (<|> Just (qn & qnTypeQual %~ addPtrToQual Nothing))
        additionalScopes expr@(Indexing {})
          | Just (Just qn) <- expr ^? exprBase.refName
            = expr & refName %~ (<|> Just (qn & qnTypeQual %~ genArrayQual ))
        additionalScopes expr@(NameExpr {})
          | Just qn <- expr ^? exprIdent.refName
            = expr & refName %~ (<|> qn)
        -- additionalScopes expr@(Member {})
          -- | Just qn <- expr ^? exprBase.refName
          -- , Just deref <- expr ^? exprMemberDeref
            -- = expr & refName %~ (<|> Just (qn & qnTypeQual %~ case deref of DerefMember {} -> addPtrToQual Nothing
                                                                            -- SimpleMember {} -> id))
        additionalScopes expr@(ParenExpr {})
          | Just qn <- expr ^? parenExpr.refName
            = expr & refName %~ (<|> qn)
        additionalScopes expr = expr
          
        refName :: ASTNode node NI => Lens' (node NI) (Maybe QualifiedName)
        refName = info.semanticInfo.referenceQualName
        
$(makeLenses ''ProgramParams)