hs2bf-0.6: Main.hs
-- | Create a chain based on given arguments and run it.
--
-- Overall development policy:
--
-- * If you seek /elegant/ abstraction, you will get /elephant/ abstraction.
--
-- * All intermediate-languages should be interpretable in 'IO' monad with exactly same behavior,
-- or at least have such semantics.
--
-- * Interpreters should not try to optimize, use simplest implementation while keeping the order low.
--
-- See the source of 'help' for detailed description\/specification of features.
module Main where
import Control.Monad
import System.Environment
import System.FilePath.Posix
import System.IO
import qualified Paths_hs2bf
import Util
import qualified Front
import qualified Core
import qualified GMachine
import qualified SAM
import qualified SCGR
import qualified Brainfuck
main=execCommand =<< liftM parseArgs getArgs
-- | Complete description of /hs2bf/ behavior
data Command
=ShowMessage String
|Interpret Option String
|Compile Option String
data Language
=LangCore String
|LangGM String
|LangSAM String
|LangBF
deriving(Show,Eq,Ord)
-- | All /global options/
data Option=Option
{addrSpace :: Int
,verbose :: Bool
,debug :: Bool
,tolang :: Language
}
-- | Parse arguments to 'Command'. Note this is a total function.
parseArgs :: [String] -> Command
parseArgs []=ShowMessage $ version++"\n"++help
parseArgs ("-v":_)=ShowMessage version
parseArgs ("--version":_)=ShowMessage version
parseArgs ("-h":_)=ShowMessage $ version++"\n"++help
parseArgs ("--help":_)=ShowMessage $ version++"\n"++help
parseArgs ("--run":n:as)=Interpret (parseOption as) n
parseArgs ("--make":n:as)=Compile (parseOption as) n
parseArgs _=ShowMessage "Invalid command. See 'hs2bf --help' for usage."
parseOption :: [String] -> Option
parseOption []=Option{addrSpace=2,verbose=True,debug=False,tolang=LangBF}
parseOption (term:xs)=case term of
'-':'S':'c':xs -> o{tolang=LangCore xs}
'-':'S':'g':xs -> o{tolang=LangGM xs}
'-':'S':'s':xs -> o{tolang=LangSAM xs}
"-Sb" -> o{tolang=LangBF}
_ -> error $ "unknown option:"++term
where o=parseOption xs
execCommand :: Command -> IO ()
execCommand (ShowMessage x)=putStrLn x
execCommand (Interpret opt from)=partialChain opt from $
(error "Core interpreter is not implemented"
,error "Core interpreter is not implemented"
,f GMachine.interpret
,f GMachine.interpretR
,f SAM.interpret
,f SAM.interpret
,f Brainfuck.interpret
)
where
f g=runProcessWithIO (\x->setio >> g x)
setio=hSetBuffering stdin NoBuffering >> hSetBuffering stdout NoBuffering
execCommand (Compile opt from)=partialChain opt from $
(f Core.pprint
,f Core.pprint
,f GMachine.pprint
,f GMachine.pprint
,f SAM.pprint
,f SAM.pprint
,f Brainfuck.pprint
)
where f g=runProcessWithIO (putStr . g)
partialChain opt from (c0,c1,g0,g1,s0,s1,b)=do
dir<-Paths_hs2bf.getDataDir
let (mod,env)=analyzeName from dir
xs<-Front.collectModules env mod
let cr =xs >>= Front.compile
cr' =cr >>= Core.simplify
gm =cr' >>= Core.compile
gm' =gm >>= GMachine.simplify
sam =gm' >>= GMachine.compile
sam'=sam >>= SAM.simplify
bf =sam' >>= SAM.compile
case tolang opt of
LangCore "" -> c0 cr
LangCore "s" -> c1 cr'
LangGM "" -> g0 gm
LangGM "r" -> g1 gm'
LangSAM "" -> s0 sam
LangSAM "f" -> s1 sam'
LangBF -> b bf
version :: String
version="Haskell to Brainf**k Compiler: version 0.5"
help :: String
help=unlines $
["Usage: hs2bf <command>"
,""
,"command:"
," --help: show help"
," --version: show version"
," --run <module> <option>*: interpret <module>"
," --make <module> <option>*: compile <module>"
,""
,"option:"
," -o <file> : output path (stdout if omitted)"
," -Sc : to Core code"
," -Scs: to Core code (simplified)"
," -Sg : to GMachine"
," -Sgr: to GMachine (simplified)"
," -Ss : to SAM"
," -Ssf: to SAM (most simplified)"
-- ," -Sr : to SCGR" -- not implemented
," -Sb : to BF"
," --addr n : use n byte for pointer arithmetic"
," --debug : include detailed error message (this will make the program a LOT larger)"
,""
,"examples:"
," hs2bf --make path/to/App.hs -o app : compile App.hs to bf"
," hs2bf --run Main -Sm : compile module Main to GMachine code and interpret it"
]
analyzeName :: String -> FilePath -> (String,Front.ModuleEnv)
analyzeName n lib=(takeBaseName n,Front.ModuleEnv [dirPrefix++takeDirectory n,lib])
where dirPrefix=if isAbsolute n then "" else "./"