lvmlib-1.1: Lvm/Core/Main.hs
--------------------------------------------------------------------------------
-- Copyright 2001-2012, Daan Leijen, Bastiaan Heeren, Jurriaan Hage. This file
-- is distributed under the terms of the BSD3 License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
--------------------------------------------------------------------------------
-- $Id: Main.hs 300 2014-08-21 10:43:02Z hage0101 $
module Main (main) where
import Control.Monad
import Lvm.Common.Id
import Lvm.Path
import System.Console.GetOpt
import System.Environment
import System.Exit
import System.FilePath
import Text.PrettyPrint.Leijen
import Lvm.Asm.Inline (asmInline) -- optimize Asm (ie. local inlining)
import Lvm.Asm.ToLvm (asmToLvm) -- translate Asm to Lvm instructions
import Lvm.Core.Module (modulePublic) -- imports
import Lvm.Core.Parsing.Layout (layout) -- apply layout rule
import Lvm.Core.Parsing.Lexer (lexer) -- lexical tokens
import Lvm.Core.Parsing.Parser (parseModuleExport) -- parse text into Core
import Lvm.Core.RemoveDead (coreRemoveDead) -- remove dead declarations
import Lvm.Core.ToAsm (coreToAsm) -- enriched lambda expressions (Core) to Asm
import Lvm.Import (lvmImport) -- resolve import declarations
import Lvm.Write (lvmWriteFile) -- write a binary Lvm file
----------------------------------------------------------------
--
----------------------------------------------------------------
main :: IO ()
main = do
args <- getArgs
case getOpt Permute options args of
(flags, files, errs)
| Help `elem` flags -> do
putStrLn (usageInfo header options)
exitSuccess
| Version `elem` flags -> do
putStrLn ("coreasm, " ++ versionText)
exitSuccess
| null errs && not (null files) -> do
mapM_ (compile flags) files
exitSuccess
| otherwise -> do
putStrLn $ concat errs ++
"Usage: For basic information, try the --help option."
exitFailure
versionText :: String
versionText = "version 1.7" -- $Id: Main.hs 300 2014-08-21 10:43:02Z hage0101 $"
header :: String
header =
"coreasm: The Core Assembler for the Lazy Virtual Machine\n" ++
"Copyright 2001, Daan Leijen\n" ++
"\nUsage: coreasm [OPTION] <core modules>\n" ++
"\nOptions:"
data Flag = Help | Version | Verbosity Verbosity | Dump Dump (Maybe String)
deriving Eq
data Verbosity = Silent | Normal | Verbose
deriving (Eq, Ord)
data Dump = DumpTokens | DumpCore | DumpCoreOpt | DumpAsm
| DumpAsmOpt | DumpInstr
deriving Eq
getVerbosity :: [Flag] -> Verbosity
getVerbosity flags =
case [ a | Verbosity a <- flags ] of
[] -> Normal
xs -> minimum xs
flagVerbose, flagSilent :: Flag
flagVerbose = Verbosity Verbose
flagSilent = Verbosity Silent
options :: [OptDescr Flag]
options =
[ simple [] "version" Version "show version number"
, simple "?" "help" Help "show options"
, simple [] "verbose" flagVerbose "verbose output"
, simple [] "silent" flagSilent "no output"
, dump "dump-tokens" DumpTokens "pretty print tokens"
, dump "dump-core" DumpCore "pretty print core"
, dump "dump-core-opt" DumpCoreOpt "pretty print core (optimized)"
, dump "dump-asm" DumpAsm "pretty print assembler"
, dump "dump-asm-opt" DumpAsmOpt "pretty print assembler (optimized)"
, dump "dump-instr" DumpInstr "pretty print instructions"
]
where
simple xs long = Option xs [long] . NoArg
dump long d = Option [] [long] (OptArg (Dump d) "file")
findModule :: [String] -> Id -> IO String
findModule paths = searchPath paths ".lvm" . stringFromId
findSrc :: [String] -> String -> IO String
findSrc paths = searchPath paths ".core"
compile :: [Flag] -> FilePath -> IO ()
compile flags srcraw = do
-- searching
message flags $ "Compiling " ++ showFile srcraw
let (srcPath, srcFile, srcExt) = splitFilePath srcraw
let src = joinPath [srcFile, srcExt]
let path = [srcPath, "."]
source <- findSrc path src
verbose $ "Source file: " ++ showFile source
-- lexing
verbose "Lexing"
input <- readFile source
let tokens = layout (lexer (1,1) input)
dumpWith DumpTokens flags "Tokens" tokens
-- parsing
verbose "Parsing"
(m, implExps, es) <- parseModuleExport source tokens
-- resolving
verbose "Resolving imports"
chasedMod <- lvmImport (findModule path) m
let publicmod = modulePublic implExps es chasedMod
dumpWith DumpCore flags "Core" publicmod
-- compiling
verbose "Remove dead declarations"
let coremod = coreRemoveDead publicmod
dumpWith DumpCoreOpt flags "Core (dead declarations removed)" coremod
verbose "Generating code"
nameSupply <- newNameSupply
let asmmod = coreToAsm nameSupply coremod
dumpWith DumpAsm flags "Assembler" asmmod
let asmopt = asmInline asmmod
dumpWith DumpAsmOpt flags "Assembler (optimized)" asmopt
let lvmmod = asmToLvm asmopt
dumpWith DumpInstr flags "Instructions" lvmmod
-- writing
let target = reverse (dropWhile (/='.') (reverse source)) ++ "lvm"
message flags $ "Writing " ++ showFile target
lvmWriteFile target lvmmod
where
verbose :: Pretty a => a -> IO ()
verbose = messageFor Verbose flags
---------------------------------------------------------------------
-- Messages
message :: Pretty a => [Flag] -> a -> IO ()
message = messageFor Normal
messageFor :: Pretty a => Verbosity -> [Flag] -> a -> IO ()
messageFor a flags =
when (getVerbosity flags >= a) . messageDoc
messageDoc :: Pretty a => a -> IO ()
messageDoc = print . pretty
dumpWith :: Pretty a => Dump -> [Flag] -> String -> a -> IO ()
dumpWith dump flags s a =
case [ mf | Dump d mf <- flags, d == dump ] of
Just file:_ -> do message flags ("Writing " ++ file)
writeFile file (show (pretty a))
Nothing:_ -> messageDoc nice
[] -> return ()
where
nice = vsep [hline, pretty ("-- " ++ s), empty, pretty a, hline]
hline = pretty (replicate 40 '-')
showFile :: String -> String
showFile = map (\c -> if c == '\\' then '/' else c)