{- |
    Module      :  $Header$
    Description :  File names for several intermediate file formats.
    Copyright   :  (c) 2009        Holger Siegel
                       2013 - 2014 Björn Peemöller
                       2018        Kai-Oliver Prott
    License     :  BSD-3-clause

    Maintainer  :  fte@informatik.uni-kiel.de
    Stability   :  experimental
    Portability :  portable

    The functions in this module were collected from several compiler modules
    in order to provide a unique accessing point for this functionality.
-}
module Curry.Files.Filenames
  ( -- * Re-exports from 'System.FilePath'
    FilePath, takeBaseName, dropExtension, takeExtension, takeFileName

    -- * Conversion between 'ModuleIdent' and 'FilePath'
  , moduleNameToFile, fileNameToModule, splitModuleFileName, isCurryFilePath

    -- * Curry sub-directory
  , defaultOutDir, hasOutDir, addOutDir, addOutDirModule
  , ensureOutDir

    -- * File name extensions
    -- ** Curry files
  , curryExt, lcurryExt, icurryExt

    -- ** FlatCurry files
  , annotatedFlatExt, typedFlatExt, flatExt, flatIntExt

    -- ** AbstractCurry files
  , acyExt, uacyExt

    -- ** Source and object files
  , sourceRepExt, sourceExts, moduleExts

    -- * Functions for computing file names
  , interfName, typedFlatName, annotatedFlatName, flatName, flatIntName
  , acyName, uacyName, sourceRepName, tokensName, commentsName
  , astName, shortASTName, htmlName
  ) where

import System.FilePath

import Curry.Base.Ident

-- -----------------------------------------------------------------------------
-- Conversion between ModuleIdent and FilePath
-- -----------------------------------------------------------------------------

-- |Create a 'FilePath' from a 'ModuleIdent' using the hierarchical module
-- system
moduleNameToFile :: ModuleIdent -> FilePath
moduleNameToFile :: ModuleIdent -> FilePath
moduleNameToFile = (FilePath -> FilePath -> FilePath) -> [FilePath] -> FilePath
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 FilePath -> FilePath -> FilePath
(</>) ([FilePath] -> FilePath)
-> (ModuleIdent -> [FilePath]) -> ModuleIdent -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleIdent -> [FilePath]
midQualifiers

-- |Extract the 'ModuleIdent' from a 'FilePath'
fileNameToModule :: FilePath -> ModuleIdent
fileNameToModule :: FilePath -> ModuleIdent
fileNameToModule = [FilePath] -> ModuleIdent
mkMIdent ([FilePath] -> ModuleIdent)
-> (FilePath -> [FilePath]) -> FilePath -> ModuleIdent
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> [FilePath]
splitDirectories (FilePath -> [FilePath])
-> (FilePath -> FilePath) -> FilePath -> [FilePath]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
dropExtension (FilePath -> FilePath)
-> (FilePath -> FilePath) -> FilePath -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
dropDrive

-- |Split a 'FilePath' into a prefix directory part and those part that
-- corresponds to the 'ModuleIdent'. This is especially useful for
-- hierarchically module names.
splitModuleFileName :: ModuleIdent -> FilePath -> (FilePath, FilePath)
splitModuleFileName :: ModuleIdent -> FilePath -> (FilePath, FilePath)
splitModuleFileName m :: ModuleIdent
m fn :: FilePath
fn = case ModuleIdent -> [FilePath]
midQualifiers ModuleIdent
m of
  [_] -> FilePath -> (FilePath, FilePath)
splitFileName FilePath
fn
  ms :: [FilePath]
ms  -> let (base :: FilePath
base, ext :: FilePath
ext) = FilePath -> (FilePath, FilePath)
splitExtension FilePath
fn
             dirs :: [FilePath]
dirs        = FilePath -> [FilePath]
splitDirectories FilePath
base
             (pre :: [FilePath]
pre, suf :: [FilePath]
suf)  = Int -> [FilePath] -> ([FilePath], [FilePath])
forall a. Int -> [a] -> ([a], [a])
splitAt ([FilePath] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
dirs Int -> Int -> Int
forall a. Num a => a -> a -> a
- [FilePath] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
ms) [FilePath]
dirs
             path :: FilePath
path        = if [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
pre then ""
                                       else FilePath -> FilePath
addTrailingPathSeparator ([FilePath] -> FilePath
joinPath [FilePath]
pre)
         in  (FilePath
path, [FilePath] -> FilePath
joinPath [FilePath]
suf FilePath -> FilePath -> FilePath
<.> FilePath
ext)

-- |Checks whether a 'String' represents a 'FilePath' to a Curry module
isCurryFilePath :: String -> Bool
isCurryFilePath :: FilePath -> Bool
isCurryFilePath str :: FilePath
str =  FilePath -> Bool
isValid FilePath
str
                    Bool -> Bool -> Bool
&& FilePath -> FilePath
takeExtension FilePath
str FilePath -> [FilePath] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ("" FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
moduleExts)

-- -----------------------------------------------------------------------------
-- Curry sub-directory
-- -----------------------------------------------------------------------------

-- |The standard hidden subdirectory for curry files
defaultOutDir :: String
defaultOutDir :: FilePath
defaultOutDir = ".curry"

-- |Does the given 'FilePath' contain the 'outDir'
-- as its last directory component?
hasOutDir :: String -> FilePath -> Bool
hasOutDir :: FilePath -> FilePath -> Bool
hasOutDir outDir :: FilePath
outDir f :: FilePath
f = Bool -> Bool
not ([FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
dirs) Bool -> Bool -> Bool
&& [FilePath] -> FilePath
forall a. [a] -> a
last [FilePath]
dirs FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
outDir
  where dirs :: [FilePath]
dirs = FilePath -> [FilePath]
splitDirectories (FilePath -> [FilePath]) -> FilePath -> [FilePath]
forall a b. (a -> b) -> a -> b
$ FilePath -> FilePath
takeDirectory FilePath
f

-- |Add the 'outDir' to the given 'FilePath' if the flag is 'True' and
-- the path does not already contain it, otherwise leave the path untouched.
addOutDir :: Bool -> String -> FilePath -> FilePath
addOutDir :: Bool -> FilePath -> FilePath -> FilePath
addOutDir b :: Bool
b outDir :: FilePath
outDir fn :: FilePath
fn = if Bool
b then FilePath -> FilePath -> FilePath
ensureOutDir FilePath
outDir FilePath
fn else FilePath
fn

-- |Add the 'outDir' to the given 'FilePath' if the flag is 'True' and
-- the path does not already contain it, otherwise leave the path untouched.
addOutDirModule :: Bool -> String -> ModuleIdent -> FilePath -> FilePath
addOutDirModule :: Bool -> FilePath -> ModuleIdent -> FilePath -> FilePath
addOutDirModule b :: Bool
b outDir :: FilePath
outDir m :: ModuleIdent
m fn :: FilePath
fn
  | Bool
b         = let (pre :: FilePath
pre, file :: FilePath
file) = ModuleIdent -> FilePath -> (FilePath, FilePath)
splitModuleFileName ModuleIdent
m FilePath
fn
                in  FilePath -> FilePath -> FilePath
ensureOutDir FilePath
outDir FilePath
pre FilePath -> FilePath -> FilePath
</> FilePath
file
  | Bool
otherwise = FilePath
fn

-- | Ensure that the 'outDir' is the last component of the
-- directory structure of the given 'FilePath'. If the 'FilePath' already
-- contains the sub-directory, it remains unchanged.
ensureOutDir :: String   -- ^ the 'outDir'
             -> FilePath -- ^ original 'FilePath'
             -> FilePath -- ^ new 'FilePath'
ensureOutDir :: FilePath -> FilePath -> FilePath
ensureOutDir outDir :: FilePath
outDir fn :: FilePath
fn = FilePath -> FilePath
normalise (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ [FilePath] -> FilePath
addSub (FilePath -> [FilePath]
splitDirectories FilePath
d) FilePath -> FilePath -> FilePath
</> FilePath
f
  where
  (d :: FilePath
d, f :: FilePath
f) = FilePath -> (FilePath, FilePath)
splitFileName FilePath
fn
  addSub :: [FilePath] -> FilePath
addSub dirs :: [FilePath]
dirs | [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
dirs           = FilePath
outDir
              | [FilePath] -> FilePath
forall a. [a] -> a
last [FilePath]
dirs FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
outDir = [FilePath] -> FilePath
joinPath [FilePath]
dirs
              | Bool
otherwise           = [FilePath] -> FilePath
joinPath [FilePath]
dirs FilePath -> FilePath -> FilePath
</> FilePath
outDir

-- -----------------------------------------------------------------------------
-- File name extensions
-- -----------------------------------------------------------------------------

-- |Filename extension for non-literate curry files
curryExt :: String
curryExt :: FilePath
curryExt = ".curry"

-- |Filename extension for literate curry files
lcurryExt :: String
lcurryExt :: FilePath
lcurryExt = ".lcurry"

-- |Filename extension for curry interface files
icurryExt :: String
icurryExt :: FilePath
icurryExt = ".icurry"

-- |Filename extension for curry source files.
--
-- /Note:/ The order of the extensions defines the order in which source files
-- should be searched for, i.e. given a module name @M@, the search order
-- should be the following:
--
-- 1. @M.curry@
-- 2. @M.lcurry@
--
sourceExts :: [String]
sourceExts :: [FilePath]
sourceExts = [FilePath
curryExt, FilePath
lcurryExt]

-- |Filename extension for curry module files
-- TODO: Is the order correct?
moduleExts :: [String]
moduleExts :: [FilePath]
moduleExts = [FilePath]
sourceExts [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath
icurryExt]

-- |Filename extension for typed flat-curry files
typedFlatExt :: String
typedFlatExt :: FilePath
typedFlatExt = ".tfcy"

-- |Filename extension for type-annotated flat-curry files
annotatedFlatExt :: String
annotatedFlatExt :: FilePath
annotatedFlatExt = ".tafcy"

-- |Filename extension for flat-curry files
flatExt :: String
flatExt :: FilePath
flatExt = ".fcy"

-- |Filename extension for extended-flat-curry interface files
flatIntExt :: String
flatIntExt :: FilePath
flatIntExt = ".fint"

-- |Filename extension for abstract-curry files
acyExt :: String
acyExt :: FilePath
acyExt = ".acy"

-- |Filename extension for untyped-abstract-curry files
uacyExt :: String
uacyExt :: FilePath
uacyExt = ".uacy"

-- |Filename extension for curry source representation files
sourceRepExt :: String
sourceRepExt :: FilePath
sourceRepExt = ".cy"

-- |Filename extension for token files
tokensExt :: String
tokensExt :: FilePath
tokensExt = ".tokens"

-- |Filename extension for comment token files
commentsExt :: String
commentsExt :: FilePath
commentsExt = ".cycom"

-- |Filename extension for AST files
astExt :: String
astExt :: FilePath
astExt = ".ast"

-- |Filename extension for shortened AST files
shortASTExt :: String
shortASTExt :: FilePath
shortASTExt = ".sast"

-- ---------------------------------------------------------------------------
-- Computation of file names for a given source file
-- ---------------------------------------------------------------------------

-- |Compute the filename of the interface file for a source file
interfName :: FilePath -> FilePath
interfName :: FilePath -> FilePath
interfName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
icurryExt

-- |Compute the filename of the typed flat curry file for a source file
typedFlatName :: FilePath -> FilePath
typedFlatName :: FilePath -> FilePath
typedFlatName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
typedFlatExt

-- |Compute the filename of the typed flat curry file for a source file
annotatedFlatName :: FilePath -> FilePath
annotatedFlatName :: FilePath -> FilePath
annotatedFlatName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
annotatedFlatExt

-- |Compute the filename of the flat curry file for a source file
flatName :: FilePath -> FilePath
flatName :: FilePath -> FilePath
flatName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
flatExt

-- |Compute the filename of the flat curry interface file for a source file
flatIntName :: FilePath -> FilePath
flatIntName :: FilePath -> FilePath
flatIntName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
flatIntExt

-- |Compute the filename of the abstract curry file for a source file
acyName :: FilePath -> FilePath
acyName :: FilePath -> FilePath
acyName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
acyExt

-- |Compute the filename of the untyped abstract curry file for a source file
uacyName :: FilePath -> FilePath
uacyName :: FilePath -> FilePath
uacyName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
uacyExt

-- |Compute the filename of the source representation file for a source file
sourceRepName :: FilePath -> FilePath
sourceRepName :: FilePath -> FilePath
sourceRepName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
sourceRepExt

-- |Compute the filename of the tokens file for a source file
tokensName :: FilePath -> FilePath
tokensName :: FilePath -> FilePath
tokensName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
tokensExt

-- |Compute the filename of the comment tokens file for a source file
commentsName :: FilePath -> FilePath
commentsName :: FilePath -> FilePath
commentsName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
commentsExt

-- |Compute the filename of the ast file for a source file
astName :: FilePath -> FilePath
astName :: FilePath -> FilePath
astName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
astExt

-- |Compute the filename of the ast file for a source file
shortASTName :: FilePath -> FilePath
shortASTName :: FilePath -> FilePath
shortASTName = FilePath -> FilePath -> FilePath
replaceExtensionWith FilePath
shortASTExt

-- |Compute the filename of the HTML file for a source file
htmlName :: ModuleIdent -> String
htmlName :: ModuleIdent -> FilePath
htmlName m :: ModuleIdent
m = ModuleIdent -> FilePath
moduleName ModuleIdent
m FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ "_curry.html"

-- |Replace a filename extension with a new extension
replaceExtensionWith :: String -> FilePath -> FilePath
replaceExtensionWith :: FilePath -> FilePath -> FilePath
replaceExtensionWith = (FilePath -> FilePath -> FilePath)
-> FilePath -> FilePath -> FilePath
forall a b c. (a -> b -> c) -> b -> a -> c
flip FilePath -> FilePath -> FilePath
replaceExtension