packages feed

hat-2.7.0.0: trans/HatTrans.hs

{- ---------------------------------------------------------------------------
This `main' function is basically just the front-end of the nhc98
compiler.  It parses the .hs source file, creates the .hx file, and
then stops immediately after writing a new transformed .hs file.
-} 
module Main where


import System.IO
import System.Environment(getArgs)
import System.Exit(exitWith,ExitCode(..))
import System.IO.Error(isAlreadyExistsError)
import Control.Monad(when)
import Data.List(isPrefixOf,intersperse,inits)
import System.Directory(doesDirectoryExist,createDirectory)

import Error
import Syntax
import Extra (Pos(..),mix,mixSpace,jRight,jLeft,noPos,strPos,showErr,mixLine
             ,pair,fst3,thd3,trace)
import AssocTree(listAT) -- for debugging output
import ParseCore (Parser(..),ParseBad(..),ParseError(..),ParseGood(..)
                 ,ParseResult(..),parseit)
import Flags (Flags(..),processArgs,pF,sUnderscore,sRealFile,sSourceFile,sUnlit
             ,sLex,sParse,sPrelude,sSrcDir,sIBound,sWrap
             ,sHatFileBase,sHatAuxFile,sTraceFns,sHatTransFile,sDbgTrusted)
import PrettyTraceId (prettyPrintTokenId,prettyPrintId,prettyPrintTraceId
                     ,ppModule,ppTopDecls,ppClassCodes)

import TokenId (TokenId(..),getUnqualified
               ,visible,t_Arrow,t_List,tPrelude,tminus,tnegate,tTrue)
import IdKind (IdKind(..))
import Id (Id)
import Lex (Lex,LexAnnot)  -- need show

import Unlit (unlit)
import Lexical (PosToken(..),PosTokenPre(..),LexState(..),lexical)
import Parse (parseProg)

import AuxFile (toAuxFile)
import AuxLabelAST (auxLabelSyntaxTree)
import TraceTrans (traceTrans,maybeStripOffQual)
import Wrapper (prepareWrapping)

import Paths_hat (getDataDir)


--import NonStdProfile
--beginPhase str = profile str (return ())
beginPhase str = return ()

-- some miscellaneous settings
primFlags = (False   -- bool is not the same as Word
	    ,False   -- && || not is not primitive
	    ,False   -- translate into prim only when strict
	    )

-- some nicer error handling
catchError :: Either b a -> String -> (b->String) -> IO a
catchError comp errmsg showErrors = do
    case comp of
        Left errs -> do pF True errmsg (showErrors errs)
                        exit
        Right a   -> return a

-- for Hugs, which cannot read commandline args using System.getArgs:
gmain cml = main' (words cml)

-- for all other compilers:
main = do
  args <- getArgs
  main' args

main' args = do
  let flagsArgs = processArgs args
  preludePath <- getDataDir
  let flags = flagsArgs{sPreludes = preludePath : (sPreludes flagsArgs)}
  let filename = sRealFile flags


  {- lex source code -}
  beginPhase "lex"
  mainChar	-- :: String
           <- catch (readFile filename) (can'tOpen filename) 
  lexdata	-- :: [PosToken]
           <- return (lexical (sUnderscore flags) (sSourceFile flags)
                              (if sUnlit flags 
                                then unlit (sSourceFile flags) mainChar 
                                else mainChar))
  pF (sLex flags) "Lexical" 
       (mixSpace (map (\ (p,l,_,_) -> strPos p ++ ':':show l) lexdata))


  {- parse source code -}
  beginPhase "parse"
  parsedPrg	-- :: Module TokenId
            <- catchError (parseit parseProg lexdata)
                          ("In file: "++sSourceFile flags) showErr
  pF (sParse flags) "Parse" (prettyPrintTokenId flags ppModule parsedPrg) 

  {- Ensure we can write our output files. -}
  let hatDir = sSrcDir flags
  dir <- doesDirectoryExist hatDir
  when (not dir) (createDirectoriesRecursively hatDir)

  {- If wrapping rather than transforming, then prepare the parsed AST.
  -}
  parsedPrg <- if sWrap flags then return (prepareWrapping (sSourceFile flags) parsedPrg)
                              else return parsedPrg
  when (sWrap flags)
       (pF (sParse flags) "Wrap" (prettyPrintTokenId flags ppModule parsedPrg))

  {-
  -- Read and write auxiliary information files (for tracing).
  -- Then relabel the syntax tree with the auxiliary information.
  -- Then the tracing transformation itself is applied.
  -- The result is written to file (no redirection possible yet)
  -}
  let prg = implicitlyImportPrelude flags 
              (maybeStripOffQual "Prelude" parsedPrg)
  pF (sParse flags) "post-Prelude" (prettyPrintTokenId flags ppModule parsedPrg)
  -- hack to make full environment available when writing aux file
  -- (needed for handling partial expansion of type synonyms)
  (env,newprog) <- auxLabelSyntaxTree flags prg
  toAuxFile env flags (sHatAuxFile flags) prg -- waste space: hold on to prg

  when (sIBound flags)
    (print (listAT (fst env)))

  when (sTraceFns flags)
       (putStr (prettyPrintTraceId flags ppModule newprog)) -- debug
  let outputTree = (implicitlyImportPreludeBasic flags 
                     (maybeStripOffQual "Hat.Prelude"
                       (traceTrans (not (sDbgTrusted flags)) (sSourceFile flags) 
                         (sHatFileBase flags) newprog)))
  writeFile (sHatTransFile flags)
      (prettyPrintTokenId flags ppModule outputTree)
  putStrLn ("Wrote " ++ sHatTransFile flags)
  exitWith (ExitSuccess)


-- add implicit Prelude, if it is not imported explicitly and we 
-- are not currently transforming part of the Prelude.
implicitlyImportPrelude :: Flags -> Module TokenId -> Module TokenId
implicitlyImportPrelude flags
  (Module pos modId exports imports fixities decls) =
  Module pos modId exports imports' fixities decls
  where 
  imports' = 
    if (sPrelude flags && "Prelude" `isPrefixOf` getUnqualified modId) ||
       (any ((==) tPrelude . importedModule) imports)
      then imports 
      else Import (noPos,tPrelude) (Hiding []) : imports

-- import implementation of Prelude qualified
-- use this for transformation-introduced Prelude identifiers
-- even some internal identifiers that are not part of the Prelude
-- (e.g. for deriving of instances)
implicitlyImportPreludeBasic :: Flags -> Module TokenId -> Module TokenId
implicitlyImportPreludeBasic flags
  (Module pos modId exports imports fixities decls) =
  Module pos modId exports imports' fixities decls
  where 
  imports' = 
    if sPrelude flags && "Hat.Prelude" `isPrefixOf` getUnqualified modId
      then imports 
      else
        ImportQ (noPos,visible (reverse "Hat.PreludeBasic")) (Hiding []) 
        : imports


createDirectoriesRecursively path = do
    putStrLn ("Creating directories "++unwords paths)
    mapM_ safeCreate paths
  where paths = accum (wordsBy '/' path)
        accum xs = map (concat . intersperse "/") (tail (inits xs))
        wordsBy c s = let (w,s') = break (==c) s
                      in w: case s' of [] -> []; (_:s'') -> wordsBy c s''
        safeCreate path = catch (createDirectory path)
                                (\e-> if isAlreadyExistsError e then return ()
                                      else ioError e)

{- End Module Main ----------------------------------------------------------}