Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/compiler98/Flags.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


{- ---------------------------------------------------------------------------
Flags are all the choices and information given to the compiler in the
argument list. Here a data type Flags is defined for holding this information,
a function processArgs to obtain a value of type Flags from the argument list,
and a simple function pF for printing information demanded by a flag.
-}
module Flags
  ( FileFlags(..)
  , Flags(..)
  , processArgs
--  , processMoreArgs
--  , splitArgs
  , pF
  , printUsage
  , getFileFlags
  , allOpts
  , calcRootPath
  ) where

import IO
import Util.OsOnly(fixRootDir,fixTypeFile,fixObjectFile)
import Char(isDigit)
import System.FilePath
import Util.Text
import Util.Extra
import Building (Compiler(..),compiler)
import System.Console.GetOpt

{- File flags are things which are specific to each compiled file -}
data FileFlags = FileFF {
      sSourceFile :: String,   -- name of source file as given
      sModuleName :: String,   -- name of the module
      sTypeFile :: String,     -- full path to the .hi file
      sObjectFile :: String,   -- full path to the .hbc file
      sCoreFile :: String,     -- full path to the .ycr file
      sWrapFile :: String      -- full path to the .c wrapper
    }

instance Show FileFlags where
    show ff = unlines [ "sourceFile = "++sSourceFile ff,
                        "moduleName = "++sModuleName ff,
                        "typeFile = "++sTypeFile ff,
                        "objFile = "++sObjectFile ff,
                        "coreFile = "++sCoreFile ff,
                        "wrapFile = "++sWrapFile ff]

getFileFlags :: Flags -> FilePath -> String -> FileFlags
getFileFlags flags sourcefile modname | compiler==Yhc =
    FileFF sourcefile modname typefile objfile corefile wrapfile
  where
    typefile = fileForModule (sTypeDst flags) "hi"
    objfile = fileForModule (sObjectDst flags) "hbc"
    corefile = fileForModule (sObjectDst flags) "ycr"
    wrapfile = fileForModule (sWrapDst flags) "c"
    -- if modname == "Main" that means that the file may not be "Main", but could be something else
    name = if modname == "Main" then takeBaseName sourcefile else modname
    rootPath = calcRootPath sourcefile modname
    fileForModule root ext =
            if sHideObj flags then
                addExtension (combine rootPath (joinPath [ext, name])) ext
            else
                replaceExtension (combine (rootPath </> root)
                                          (joinPath $ splitList "." name)) ext

getFileFlags flags sourcefile modname | compiler==Nhc98 =
    case sFileArgs flags of
      [source] -> FileFF source modname (hiFile source) (hcFile source)
                                       (core source)   undefined
      [source,hi,hc]
                   -> FileFF source modname hi hc (core source) undefined
      [realfile,source,hi,hc]
                   -> FileFF realfile modname hi hc (core source) undefined
      _ -> error (printUsage undefined)
  where
    hiFile f = let (rootdir,file) = fixRootDir (sUnix flags) f
               in fixTypeFile (sUnix flags) rootdir file
    hcFile f = let (rootdir,file) = fixRootDir (sUnix flags) f
               in fixObjectFile (sUnix flags) rootdir file
    core f   = f++".ycr"


-- | Figure out how far up the directory tree you have to go to get the root
calcRootPath :: FilePath -> String -> FilePath
calcRootPath filename modname = joinPath $ take tsize orig
    where
        orig = splitPath filename
        tsize = length orig - depthmod + depthfil - 1
        depthmod = length $ filter (== '.') modname
        depthfil = length $ filter (== '.') $ takeFileName $ dropExtension filename


{- Flags are flags that apply to every file -}
data Flags = FF
  {sRootFile   :: String	-- full path to root source code
  ,sFileArgs   :: [String]	-- all filenames given on commandline
  ,sIncludes   :: [String]
  ,sPreludes   :: [String]
  ,sBasePath   :: String

  ,sTypeDst     :: String --  where generated .hi should go
  ,sObjectDst   :: String --  where generated bcode should be
  ,sWrapDst     :: String -- b-code    where generated wrappers should go
  ,sHideObj     :: Bool -- hide object code

--v Flags to control actions
  ,sViewCore    :: [String]

--v Flags to control compilation
  ,sRedefine   :: Bool   -- allow redefinitions of imported identifiers
  ,sCompileOne :: Bool   -- compile one file only
  ,sUnix       :: Bool   -- either unix or RiscOS
  ,sUnlit      :: Bool   -- unliterate the source code
  ,sCpp        :: Bool   -- preprocess the source code
  ,sHiSuffix   :: String -- set the interface file suffix (usually .hi)
  ,sHat        :: Bool

  ,sProfile    :: Bool	-- turn on heap profiling
  ,sTprof      :: Bool	-- turn on time profiling
  ,sZap        :: Bool	-- zap unused args / stack positions
  ,sPrelude    :: Bool	-- keep prelude defns in interface file
  ,sLib        :: Bool	-- compiling a library
  ,sPart       :: Bool  -- compiling part of a lib
  ,sKeepCase   :: Bool	-- don't lift case, we fix those later

  ,sUnifyHack  :: Bool  -- enables type hackery that's required to make the prelude compile ...
  ,sDotNet     :: Bool  -- generate .NET IL (implies -no-bytecode)
  ,sNoBytecode :: Bool  -- don't generate any bytecode (typically used with -core)

--v Flags for machine architecture / configuration
  ,sAnsiC      :: Bool	-- generate bytecode via ANSI-C
  ,sNplusK     :: Bool	-- allow (n+k) patterns
  ,sUnderscore :: Bool	-- force H'98 underscores
  ,sPuns       :: Bool	-- allow named-field puns
  ,s98         :: Bool  -- Haskell 98 mode

--v debugging flags - show program / import tables (after each compiler phase)
  ,sLex        :: Bool  -- input    after lexing
  ,sILex       :: Bool  -- input    after lexing imported interface files
  ,sParse      :: Bool  -- ast      after parsing
  ,sNeed       :: Bool  -- need table   before imports
  ,sINeed      :: Bool  -- need table   after all imports
  ,sIINeed     :: Bool  -- need table   after each import
  ,sIRename    :: Bool  -- rename table after all imports
  ,sIIRename   :: Bool  -- rename table after each import
  ,sImport     :: Bool  -- imported filenames
  ,sRImport    :: Bool  -- imports  actually used
  ,sDepend     :: Bool  -- imported ids (not currently used)
  ,sRename     :: Bool  -- ast      after rename
  ,sDerive     :: Bool  -- ast      after deriving
  ,sRemove     :: Bool  -- ast      after named-field removal
  ,sScc        :: Bool  -- ast      after strongly-connected-components
  ,sType       :: Bool  -- ast      after type check
  ,sFixSyntax  :: Bool  -- ast      after removing newtypes
  ,sLift       :: Bool  -- stg tree after lambda lifting
  ,sCase       :: Bool  -- stg tree after case pattern simplification
  ,sPrim       :: Bool  -- stg tree after inserting primitives
  ,sArity      :: Bool  -- stg tree after arity analysis
  ,sAtom       :: Bool  -- stg tree after only atoms in applications
  ,sFree       :: Bool  -- stg code with explicit free variables

  -- | Yhc
  ,sBcodeCompile :: Bool -- b-code
  ,sBcodeMem   :: Bool  -- b-code       after NNEDHEAP analysis
  ,sBcodeFlatten :: Bool -- b-code      after flattening
  ,sBcodeWrapper :: Bool -- b-code      generate wrappers
  ,sBcodeRel :: Bool

  -- | nhc98
  ,sGcode      :: Bool	-- g-code
  ,sGcodeFix   :: Bool	-- g-code	after large constant fix
  ,sGcodeMem   :: Bool	-- g-code	after NEEDHEAP analysis
  ,sGcodeOpt1  :: Bool	-- g-code	after optimisation phase 1
  ,sGcodeRel   :: Bool	-- g-code	after relative offset analysis
  ,sGcodeOpt2  :: Bool	-- g-code	after optimisation phase 2

  ,sFunNames   :: Bool	-- insert position and name of functions in the code

--v debugging flags - show symbol table (after each compiler phase)
  ,sIBound     :: Bool  -- after all imports
  ,sIIBound    :: Bool  -- after each import
  ,sRBound     :: Bool  -- after rename
  ,sDBound     :: Bool  -- after deriving
  ,sEBound     :: Bool  -- after extract
  ,sTBound     :: Bool  -- after type checking
  ,sFSBound    :: Bool  -- after fixsyntax
  ,sLBound     :: Bool  -- after lambda-lifting
  ,sCBound     :: Bool  -- after case
  ,sPBound     :: Bool  -- after inserting prims
  ,sABound     :: Bool  -- after only atoms in applications

--v miscellaneous flags
  ,sShowType   :: Bool  -- report type of "main" (for hmake interactive)
  ,sShowWidth  :: Int   -- width for showing intermediate program
  ,sShowIndent :: Int   -- indentation for nesting shown intermediate program
  ,sShowQualified :: Bool -- show qualified ids as far as possible

  ,sShowCore  :: Bool -- show Core
  ,sGenCore :: Bool -- generate a .ycr file
  ,sLinkCore :: Bool -- link all the core files together

--export control flags
  ,sExportAll :: Bool -- ignore what the module decl says, just export the lot

  ,sHelp :: Bool
  ,sVersion :: Bool
  }
  deriving (Show)

{- Default values for flags -}
defaultFlags = FF
  {sRootFile   = ""
  ,sFileArgs   = []
  ,sIncludes   = []
  ,sPreludes   = []
  ,sBasePath   = ""

  ,sTypeDst    = "."
  ,sObjectDst  = "."
  ,sWrapDst    = "."
  ,sHideObj    = False

--v Flags to control actions
  ,sViewCore   = []

--v Flags to control compilation
  ,sRedefine   = False
  ,sPart       = False
  ,sCompileOne = False
  ,sUnix       = True
  ,sUnlit      = False
  ,sCpp        = False
  ,sHiSuffix   = "hi"
  ,sHat        = False

  ,sProfile    = False
  ,sTprof      = False
  ,sZap        = True
  ,sPrelude    = False
  ,sLib        = False
  ,sKeepCase   = False

  ,sUnifyHack  = False
  ,sDotNet     = False
  ,sNoBytecode = False

--v Flags for machine architecture / configuration
  ,sAnsiC      = True
  ,sNplusK     = True
  ,sUnderscore = False
  ,sPuns       = False
  ,s98         = False

--v debugging flags - show program / import tables (after each compiler phase)
  ,sLex        = False
  ,sILex       = False
  ,sParse      = False
  ,sNeed       = False
  ,sINeed      = False
  ,sIINeed     = False
  ,sIRename    = False
  ,sIIRename   = False
  ,sImport     = False
  ,sRImport    = False
  ,sDepend     = False
  ,sRename     = False
  ,sDerive     = False
  ,sRemove     = False
  ,sScc        = False
  ,sType       = False
  ,sFixSyntax  = False
  ,sLift       = False
  ,sCase       = False
  ,sPrim       = False
  ,sArity      = False
  ,sAtom       = False
  ,sFree       = False

  -- Yhc
  ,sBcodeCompile = False
  ,sBcodeMem     = False
  ,sBcodeFlatten = False
  ,sBcodeWrapper = False
  ,sBcodeRel     = False

  -- nhc98
  ,sGcode      = False
  ,sGcodeFix   = False
  ,sGcodeMem   = False
  ,sGcodeOpt1  = False
  ,sGcodeRel   = False
  ,sGcodeOpt2  = False

  ,sFunNames   = False

--v debugging flags - show symbol table (after each compiler phase)
  ,sIBound     = False
  ,sIIBound    = False
  ,sRBound     = False
  ,sDBound     = False
  ,sEBound     = False
  ,sTBound     = False
  ,sFSBound    = False
  ,sLBound     = False
  ,sCBound     = False
  ,sPBound     = False
  ,sABound     = False

--v miscellaneous flags
  ,sShowType      = False
  ,sShowWidth     = 80
  ,sShowIndent    = 2
  ,sShowQualified = True

  ,sShowCore  = False
  ,sGenCore   = False
  ,sLinkCore  = False

--export control flags
  ,sExportAll = False

  ,sHelp      = False
  ,sVersion   = False
  }

-- All option descriptions
allOpts :: [ OptDescr (Flags->Flags) ]
allOpts =
--  OptGroup "Help Options"
  [ Option "v"  ["version"]
           (NoArg (\f -> f{sVersion=True})) "Show version information"
  , Option "?h" ["help"]
           (NoArg (\f -> f{sHelp=True})) "Show options and usage information"

--  OptGroup "Inputs and Outputs"
  , Option "I" ["includes","imports"]
           (ReqArg (\x f-> f{sIncludes = x:sIncludes f}) "dir")
           "Include/import directories"
  , Option "P" ["preludes"]
           (ReqArg (\x f-> f{sPreludes = x:sPreludes f}) "dir")
           "Prelude directories"
  , Option "d" ["dst","objdir"]
           (ReqArg (\x f-> f{sObjectDst=x}) "dir")
           "destination path for generated bytecode files"
  , Option "i" ["hidst","hidir"]
           (ReqArg (\x f-> f{sTypeDst=x}) "dir")
           "destination path for generated .hi files"
  , Option "w" ["wrapdst","ffidir"]
           (ReqArg (\x f-> f{sWrapDst=x}) "dir")
           "destination path for generated FFI wrapper"
  , Option ""  ["hide"]
           (NoArg (\f -> f{sHideObj=True})) "hide object files"

--  OptGroup "Generation Options"
  , Option ""  ["hat"]
           (NoArg (\f -> f{sHat=True})) "compile with Hat debugging support"
  , Option ""  ["dotnet"]
           (NoArg (\f -> f{sDotNet=True})) "Generate .NET IL code (implies -no-bytecode)"
  , Option ""  ["no-bytecode"]
           (NoArg (\f -> f{sNoBytecode=True})) "Do not generate any bytecode"
  , Option "W" ["genwrapper"]
           (NoArg (\f -> f{sBcodeWrapper=True})) "generate FFI wrapper"
  , Option ""  ["hi-suffix","hisuf"]
           (ReqArg (\x f -> f{sHiSuffix=x}) "SUF")
           "change the default \".hi\" suffix"
  , Option ""  ["exportall"]
           (NoArg (\f -> f{sExportAll=True}))
           "export all identifiers from a module, ignoring export list"

--  OptGroup "Action Flags"
  , Option ""  ["viewcore"]
           (ReqArg (\x f -> f{sViewCore= x:sViewCore f}) "FILE")
           "View named Core file (.ycr)"
  , Option "c" ["compile"]
           (NoArg (\f -> f{sCompileOne=True})) "Compile one file only"

--  OptGroup "Compilation Options"
  , Option ""  ["redefine"]
           (NoArg (\f-> f{sRedefine=True}))
           "Don't complain if redefining an imported identifier"
  , Option ""  ["nounix"]
           (NoArg (\f -> f{sUnix=False})) "Use unix file names"
  , Option ""  ["unlit"]
           (NoArg (\f -> f{sUnlit=True})) "Unliterate the source code"
  , Option ""  ["cpp"]
           (NoArg (\f -> f{sCpp=True})) "Pre-process the file first"
  , Option ""  ["prelude"]
           (NoArg (\f -> f{sPrelude=True}))
           "Keep prelude definitions in interface file"
  , Option ""  ["lib"]
           (NoArg (\f -> f{sLib=True}))
           "Compiling a lib, don't complain if importing modules with \
          \ names that differ from their filename."
  , Option ""  ["part"]
           (NoArg (\f -> f{sPart=True}))
                  "Compiling part of a lib, so don't complain if module \
                  \ name differs from file name and don't create profiling \
                  \ information for this module"
  , Option ""  ["unifyhack"]
           (NoArg (\f -> f{sUnifyHack=True}))
           "Enable nasty type hack needed to make the prelude compile"
  , Option "" ["zap"]
           (NoArg (\f -> f{sZap=True})) "Zap unused args/stack positions"
  , Option "" ["nozap"]
           (NoArg (\f -> f{sZap=False})) "Don't zap unused args/stack positions"

--  OptGroup "Profiling"
  , Option "p" ["profile"]
           (NoArg (\f -> f{sProfile=True})) "Generate code for heap profiling"
  , Option "z" ["tprof"]
           (NoArg (\f -> f{sTprof=True})) "Generate code for time profiling"

--  OptGroup "Compliance Options"
  , Option ""  ["ansiC"]
           (NoArg (\f -> f{sAnsiC=True})) "Generate bytecode via ANSI-C"
  , Option ""  ["noansiC"]
           (NoArg (\f -> f{sAnsiC=False})) "Generate bytecode via assembler"
  , Option ""  ["nplusk","nkpat"]
           (NoArg (\f -> f{sNplusK=True})) "Allow (n+k) patterns"
  , Option ""  ["nonplusk","nonkpat"]
           (NoArg (\f -> f{sNplusK=False})) "Disallow (n+k) patterns"
  , Option ""  ["underscore"]
           (NoArg (\f -> f{sUnderscore=True})) "H98 underscore-is-lower-case"
  , Option ""  ["nounderscore"]
           (NoArg (\f -> f{sUnderscore=False})) "Enable underscore-is-invisible"
  , Option ""  ["puns"]
           (NoArg (\f -> f{sPuns=True})) "Enable pre-98 named-field puns"
  , Option ""  ["nopuns"]
           (NoArg (\f -> f{sPuns=False})) "Disable named-field puns"
  , Option ""  ["98"]
           (NoArg (\f -> f{s98=True})) "Haskell 98 compliance"
  , Option ""  ["no98"]
           (NoArg (\f -> f{s98=False})) "Turn off strict Haskell 98 compliance"

--  OptGroup "Core Options"
  , Option ""  ["core"]
           (NoArg (\f -> f{sGenCore=True})) "generate a .ycr binary file"
  , Option ""  ["showcore"]
           (NoArg (\f -> f{sShowCore=True})) "show the Core language"
  , Option ""  ["linkcore"]
           (NoArg (\f -> f{sLinkCore=True,sGenCore=True}))
                  "generate a linked .yca binary file"

--  OptGroup "Debug Options"
  , Option ""  ["lex"]
           (NoArg (\f -> f{sLex=True})) "show lexical input"
  , Option ""  ["parse"]
           (NoArg (\f -> f{sParse=True})) "show syntax tree after parser"
  , Option ""  ["need"]
           (NoArg (\f -> f{sNeed=True})) "show need table before import"
  , Option ""  ["ineed"]
           (NoArg (\f -> f{sINeed=True})) "show need table after import"
  , Option ""  ["irename"]
           (NoArg (\f -> f{sIRename=True})) "show rename table after import"
  , Option ""  ["iineed"]
           (NoArg (\f -> f{sIINeed=True}))
           "show need table between all import files"
  , Option ""  ["iirename"]
           (NoArg (\f -> f{sIIRename=True}))
           "show rename table between all imports"
  , Option ""  ["rename"]
           (NoArg (\f -> f{sRename=True})) "show syntax tree after rename"
  , Option ""  ["derive"]
           (NoArg (\f -> f{sDerive=True})) "show syntax tree after derive"
  , Option ""  ["remove"]
           (NoArg (\f -> f{sRemove=True}))
            "show syntax tree after fields are translated into selectors"
  , Option ""  ["scc"]
           (NoArg (\f -> f{sScc=True}))
           "show syntax tree after splitting into strongly connected groups"
  , Option ""  ["type"]
           (NoArg (\f -> f{sType=True})) "show syntax tree after type check"
  , Option ""  ["fixsyntax"]
           (NoArg (\f -> f{sFixSyntax=True}))
           "show syntax tree after removing newtype constructors and fixing \
           \ Class.Type.method"
  , Option ""  ["lift"]
           (NoArg (\f -> f{sLift=True})) "show syntax tree after lambda lifting"
  , Option ""  ["case"]
           (NoArg (\f -> f{sCase=True}))
           "show stg tree after simplification of patterns"
  , Option ""  ["prim"]
           (NoArg (\f -> f{sPrim=True}))
           "show stg tree after inserting primitive functions"
  , Option ""  ["bcodecompile"]
           (NoArg (\f -> f{sBcodeCompile=True})) "show B code after compile"
  , Option ""  ["bcodemem"]
           (NoArg (\f -> f{sBcodeMem=True}))
           "show B code after heap usage analysis"
  , Option ""  ["bcodeflatten"]
           (NoArg (\f -> f{sBcodeFlatten=True})) "show B code after flattening"
  , Option ""  ["bcoderel"]
           (NoArg (\f -> f{sBcodeRel=True}))
           "show B code after converting to relative jumps"
  , Option ""  ["keepcase"]
           (NoArg (\f -> f{sKeepCase=True}))
           "Don't lift case, we fix those later"
  , Option ""  ["arity"]
           (NoArg (\f -> f{sArity=True})) "show stg tree after arity"
  , Option ""  ["ibound"]
           (NoArg (\f -> f{sIBound=True})) "show symbol table after import"
  , Option ""  ["iibound"]
           (NoArg (\f -> f{sIIBound=True}))
           "show symbol table between all import files"
  , Option ""  ["rbound"]
           (NoArg (\f -> f{sRBound=True})) "show symbol table after rename"
  , Option ""  ["dbound"]
           (NoArg (\f -> f{sDBound=True})) "show symbol table after derive"
  , Option ""  ["pbound"]
           (NoArg (\f -> f{sPBound=True}))
           "show symbol table after inserting primitive functions"
  , Option ""  ["ebound"]
           (NoArg (\f -> f{sEBound=True})) "show symbol table after extract"
  , Option ""  ["tbound"]
           (NoArg (\f -> f{sTBound=True})) "show symbol table after type check"
  , Option ""  ["fsbound"]
           (NoArg (\f -> f{sFSBound=True}))
           "show symbol table after adding Class.Type.method info"
  , Option ""  ["lbound"]
           (NoArg (\f -> f{sLBound=True}))
           "show symbol table after lambda lifting"
  , Option ""  ["cbound"]
           (NoArg (\f -> f{sCBound=True}))
           "show symbol table after simplification of pattern"
  , Option ""  ["abound"]
           (NoArg (\f -> f{sABound=True}))
           "show symbol table after only atoms in applications"
  , Option ""  ["import"]
           (NoArg (\f -> f{sImport=True})) "print name of imported files"
  , Option ""  ["depend"]
           (NoArg (\f -> f{sDepend=True}))
           "print imported identifiers that are used (alpha quality)"
  , Option ""  ["free"]
           (NoArg (\f -> f{sFree=True}))
           "show stg tree with explicitly free variables"
  , Option ""  ["atom"]
           (NoArg (\f -> f{sAtom=True}))
           "show stg tree after only atoms in applications"
  , Option ""  ["funnames"]
           (NoArg (\f -> f{sFunNames=True}))
           "insert position and name of functions in the code"
  , Option ""  ["ilex"]
           (NoArg ( \f -> f{sILex=True})) "show lexical input"
  , Option ""  ["report-imports"]
           (NoArg (\f -> f{sRImport=True})) "show only imports actually used"
  , Option ""  ["showtype"]
           (NoArg (\f -> f{sShowType=True})) "report type of \"main\""
  , Option ""  ["showwidth"]
           (ReqArg (\x f -> f{sShowWidth=read x}) "NUM")
           "set width for showing intermediate program"
  , Option ""  ["showindent"]
           (ReqArg (\x f -> f{sShowIndent=read x}) "NUM")
           "set indentation for nesting"
  , Option ""  ["showqualified"]
           (NoArg (\f -> f{sShowQualified=True}))
           "show qualified ids as far as possible"
  , Option ""  ["noshowqualified"]
           (NoArg (\f -> f{sShowQualified=False})) "show always unqualified ids"
  ]


-- Parse etc.
processArgs :: [String] -> Flags
processArgs ss =
    let (funs, nonopts, errors) = getOpt Permute allOpts ss
    in (if not (null errors) then
           error ("Could not parse cmd-line options: "++unlines errors)
        else if compiler==Yhc && length nonopts > 1 then
           warning ("ignoring extra options or files:\n"
                    ++unlines (tail nonopts))
        else if compiler==Nhc98 && length nonopts > 4 then
           warning ("ignoring extra options or files:\n"
                    ++unlines (drop 4 nonopts))
        else if compiler==Yhc then
           (\f-> f{sRootFile=head nonopts})
        else
           (\f-> f{sFileArgs=nonopts})
       ) (foldr ($) defaultFlags funs)


printUsage :: Bool -> String
printUsage _ | compiler==Yhc =
    flip usageInfo allOpts (unlines $
        [ "yhc - York Haskell Compiler"
        , "A cross platform Haskell compiler"
        , ""
        , "  yhc [options] file"
        , ""
        , "file: Name of the source file to compile, i.e. main.hs"
        , "options: "
        ])
printUsage _ | compiler==Nhc98 =
    usageInfo "nhc98comp [options] file.hs" allOpts

{- If first argument is True, then print second and third with formatting -}
pF :: Bool -> [Char] -> [Char] -> IO ()
pF flag title text =
  if flag
    then hPutStr stderr ( "======\t"++title++":\n"++text++"\n")
    else return ()

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.