module BNFC.Options where import BNFC.CF (CF) import Data.Maybe (fromMaybe) import Data.Version ( showVersion ) import Paths_BNFC ( version ) import System.Console.GetOpt import System.FilePath (takeBaseName) import Text.Printf (printf) -- ~~~ Option data structures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- | To decouple the option parsing from the execution of the program, -- we introduce a data structure that holds the result of the -- parsing of the arguments. data Mode -- An error has been made by the user -- e.g. invalid argument/combination of arguments = UsageError String -- Basic modes: print some info and exits | Help | Version -- Normal mode, specifying the back end to use, -- the option record to be passed to the backend -- and the path of the input grammar file | Target SharedOptions FilePath deriving (Eq,Show,Ord) -- | Target languages data Target = TargetC | TargetCpp | TargetCppNoStl | TargetCSharp | TargetHaskell | TargetHaskellGadt | TargetLatex | TargetJava | TargetOCaml | TargetProfile | TargetPygments deriving (Eq,Bounded, Enum,Ord) -- Create a list of all target using the enum and bounded classes targets :: [Target] targets = [minBound..] instance Show Target where show TargetC = "C" show TargetCpp = "C++" show TargetCppNoStl = "C++ (without STL)" show TargetCSharp = "C#" show TargetHaskell = "Haskell" show TargetHaskellGadt = "Haskell (with GADT)" show TargetLatex = "Latex" show TargetJava = "Java" show TargetOCaml = "OCaml" show TargetProfile = "Haskell (with permutation profiles)" show TargetPygments = "Pygments" -- | Which version of Alex is targeted? data AlexVersion = Alex1 | Alex2 | Alex3 deriving (Show,Eq,Ord,Bounded,Enum) -- | Happy modes data HappyMode = Standard | GLR deriving (Eq,Show,Bounded,Enum,Ord) -- | This is the option record that is passed to the different backends data SharedOptions = Options -- Option shared by at least 2 backends { target :: Target , make :: Maybe String -- ^ The name of the Makefile to generate -- or Nothing for no Makefile. , inPackage :: Maybe String -- ^ The hierarchical package to put -- the modules in, or Nothing. , cnf :: Bool -- ^ Generate CNF-like tables? , lang :: String -- Haskell specific: , alexMode :: AlexVersion , jflex :: Bool , inDir :: Bool , shareStrings :: Bool , byteStrings :: Bool , glr :: HappyMode , xml :: Int , ghcExtensions :: Bool -- C++ specific , linenumbers :: Bool -- ^ Add and set line_number field for syntax classes -- C# specific , visualStudio :: Bool -- ^ Generate Visual Studio solution/project files , wcf :: Bool -- ^ Windows Communication Foundation , functor :: Bool , outDir :: FilePath -- ^ Target directory for generated files } deriving (Eq,Show,Ord) -- | We take this oportunity to define the type of the backend functions type Backend = SharedOptions -- ^ options -> CF -- ^ Grammar -> IO () defaultOptions :: SharedOptions defaultOptions = Options { cnf = False , target = TargetHaskell , inPackage = Nothing , make = Nothing , alexMode = Alex3 , inDir = False , shareStrings = False , byteStrings = False , glr = Standard , xml = 0 , ghcExtensions = False , lang = error "lang not set" , linenumbers = False , visualStudio = False , wcf = False , functor = False , outDir = "." , jflex = False } -- ~~~ Option definition ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- This defines bnfc's "global" options, like --help globalOptions :: [ OptDescr Mode ] globalOptions = [ Option [] ["help"] (NoArg Help) "show help", Option [] ["version","numeric-version"] (NoArg Version) "show version number"] -- | Options for the target languages -- targetOptions :: [ OptDescr Target ] targetOptions :: [ OptDescr (SharedOptions -> SharedOptions)] targetOptions = [ Option "" ["java"] (NoArg (\o -> o {target = TargetJava})) "Output Java code for use with JLex and CUP" , Option "" ["haskell"] (NoArg (\o -> o {target = TargetHaskell})) "Output Haskell code for use with Alex and Happy (default)" , Option "" ["haskell-gadt"] (NoArg (\o -> o {target = TargetHaskellGadt})) "Output Haskell code which uses GADTs" , Option "" ["latex"] (NoArg (\o -> o {target = TargetLatex})) "Output LaTeX code to generate a PDF description of the language" , Option "" ["c"] (NoArg (\o -> o {target = TargetC})) "Output C code for use with FLex and Bison" , Option "" ["cpp"] (NoArg (\o -> o {target = TargetCpp})) "Output C++ code for use with FLex and Bison" , Option "" ["cpp-nostl"] (NoArg (\o -> o {target = TargetCppNoStl})) "Output C++ code (without STL) for use with FLex and Bison" , Option "" ["csharp"] (NoArg (\o -> o {target = TargetCSharp})) "Output C# code for use with GPLEX and GPPG" , Option "" ["ocaml"] (NoArg (\o -> o {target = TargetOCaml})) "Output OCaml code for use with ocamllex and ocamlyacc" , Option "" ["profile"] (NoArg (\o -> o {target = TargetProfile})) "Output Haskell code for rules with permutation profiles" , Option "" ["pygments"] (NoArg (\o -> o {target = TargetPygments})) "Output a Python lexer for Pygments" ] -- | A list of the options and for each of them, the target language -- they apply to. specificOptions :: [(OptDescr (SharedOptions -> SharedOptions), [Target])] specificOptions = [ ( Option ['l'] [] (NoArg (\o -> o {linenumbers = True})) "Add and set line_number field for all syntax classes" , [TargetCpp] ) , ( Option ['p'] [] (ReqArg (\n o -> o {inPackage = Just n}) "") "Prepend to the package/module name" , [TargetCpp, TargetCSharp, TargetHaskell, TargetHaskellGadt, TargetProfile, TargetJava] ) , ( Option [] ["jflex"] (NoArg (\o -> o {jflex = True})) "Use JFlex instead of JLex for lexing" , [TargetJava] ) , ( Option [] ["vs"] (NoArg (\o -> o {visualStudio = True})) "Generate Visual Studio solution/project files" , [TargetCSharp] ) , ( Option [] ["wcf"] (NoArg (\o -> o {wcf = True})) "Add support for Windows Communication Foundation,\n by marking abstract syntax classes as DataContracts" , [TargetCSharp] ) , ( Option ['d'] [] (NoArg (\o -> o {inDir = True})) "Put Haskell code in modules Lang.* instead of Lang*" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["alex1"] (NoArg (\o -> o {alexMode = Alex1})) "Use Alex 1.1 as Haskell lexer tool" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["alex2"] (NoArg (\o -> o {alexMode = Alex2})) "Use Alex 2 as Haskell lexer tool" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["alex3"] (NoArg (\o -> o {alexMode = Alex3})) "Use Alex 3 as Haskell lexer tool (default)" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["sharestrings"] (NoArg (\o -> o {shareStrings = True})) "Use string sharing in Alex 2 lexer" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["bytestrings"] (NoArg (\o -> o {byteStrings = True})) "Use byte string in Alex 2 lexer" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["glr"] (NoArg (\o -> o {glr = GLR})) "Output Happy GLR parser" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["xml"] (NoArg (\o -> o {xml = 1})) "Also generate a DTD and an XML printer" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["xmlt"] (NoArg (\o -> o {xml = 2})) "DTD and an XML printer, another encoding" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["cnf"] (NoArg (\o -> o {cnf = True})) "Use the CNF parser instead of happy" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["ghc"] (NoArg (\o -> o {ghcExtensions = True})) "Use ghc-specific language extensions" , [TargetHaskell, TargetHaskellGadt, TargetProfile] ) , ( Option [] ["functor"] (NoArg (\o -> o {functor = True})) "Make the AST a functor and use it to store the position of the nodes" , [TargetHaskell] ) ] commonOption :: [OptDescr (SharedOptions -> SharedOptions)] commonOption = [ Option "m" ["makefile"] (OptArg (setMakefile . fromMaybe "Makefile") "MAKEFILE") "generate Makefile" , Option "o" ["outputdir"] (ReqArg (\n o -> o {outDir = n}) "DIR") "Redirects all generated files into DIR" ] where setMakefile = \mf -> \o -> o { make = Just mf } allOptions :: [OptDescr (SharedOptions -> SharedOptions)] allOptions = targetOptions ++ commonOption ++ map fst specificOptions -- ~~~ Help strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ title :: String title = unlines [ "The BNF Converter, "++showVersion version, "(c) Jonas Almström Duregård, Krasimir Angelov, Jean-Philippe Bernardy, Björn Bringert, Johan Broberg, Paul Callaghan, ", " Grégoire Détrez, Markus Forsberg, Ola Frid, Peter Gammie, Thomas Hallgren, Patrik Jansson, ", " Kristofer Johannisson, Antti-Juhani Kaijanaho, Ulf Norell, ", " Michael Pellauer and Aarne Ranta 2002 - 2013.", "Free software under GNU General Public License (GPL).", "Bug reports to bnfc-dev@googlegroups.com." ] usage :: String usage = "usage: bnfc [--version] [--help] [] file.cf" help :: String help = unlines $ usage:"" :usageInfo "Global options" globalOptions :usageInfo "Common option" commonOption :usageInfo "Target languages" targetOptions :map targetUsage helpTargets where helpTargets = [TargetHaskell, TargetJava, TargetCpp, TargetCSharp ] targetUsage t = usageInfo (printf "Special options for the %s backend" (show t)) (map fst $ filter(elem t . snd)specificOptions) -- ~~~ Parsing machinery ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- | Main parsing function parseMode :: [String] -> Mode parseMode args = case args' of [] -> Help _ -> case getOpt' Permute globalOptions args' of (mode:_,_,_,_) -> mode _ -> case getOpt Permute allOptions args' of (_,_,e:_) -> UsageError e (_,[],_) -> UsageError "Missing grammar file" (optionsUpdates, [grammar], []) -> let options = foldl (.) id optionsUpdates defaultOptions in Target (options {lang = takeBaseName grammar}) grammar (_,_,_) -> UsageError "Too many arguments" where args' = translateOldOptions args isUsageError :: Mode -> Bool isUsageError (UsageError _) = True isUsageError _ = False -- ~~~ Backward compatibility ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- A translating function to maintain backward compatiblicy -- with the old option syntay translateOldOptions :: [String] -> [String] translateOldOptions = concatMap translateOne where translateOne "-java" = return "--java" translateOne "-java1.5" = return "--java" translateOne "-c" = return "--c" translateOne "-cpp" = return "--cpp" translateOne "-cpp_stl" = return "--cpp" translateOne "-cpp_no_stl" = return "--cpp-nostl" translateOne "-csharp" = return "--csharp" translateOne "-ocaml" = return "--ocaml" translateOne "-fsharp" = return "fsharp" translateOne "-haskell" = return "--haskell" translateOne "-prof" = return "--profile" translateOne "-gadt" = return "--haskell-gadt" translateOne "-alex1" = return "--alex1" translateOne "-alex2" = return "--alex2" translateOne "-alex3" = return "--alex3" translateOne "-sharestrings" = return "--sharestring" translateOne "-bytestrings" = return "--bytestring" translateOne "-glr" = return "--glr" translateOne "-xml" = return "--xml" translateOne "-xmlt" = return "--xmlt" translateOne "-vs" = return "--vs" translateOne "-wcf" = return "--wcf" translateOne other = return other