Add stage options

This commit is contained in:
Eugen Wissner 2024-12-20 15:32:20 +01:00
parent fbd08f2707
commit a1c9910300
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
5 changed files with 133 additions and 70 deletions

View File

@ -16,6 +16,7 @@ common warnings
build-depends: build-depends:
base >=4.7 && <5, base >=4.7 && <5,
bytestring ^>= 0.12.1, bytestring ^>= 0.12.1,
filepath ^>= 1.5.3,
megaparsec ^>= 9.6, megaparsec ^>= 9.6,
optparse-applicative ^>= 0.18.1, optparse-applicative ^>= 0.18.1,
vector ^>= 0.13.1, vector ^>= 0.13.1,
@ -36,7 +37,8 @@ library elna-internal
Language.Elna.Architecture.RiscV Language.Elna.Architecture.RiscV
Language.Elna.Backend.Allocator Language.Elna.Backend.Allocator
Language.Elna.Backend.Intermediate Language.Elna.Backend.Intermediate
Language.Elna.CommandLine Language.Elna.Driver
Language.Elna.Driver.CommandLine
Language.Elna.Frontend.AST Language.Elna.Frontend.AST
Language.Elna.Frontend.NameAnalysis Language.Elna.Frontend.NameAnalysis
Language.Elna.Frontend.Parser Language.Elna.Frontend.Parser
@ -62,8 +64,7 @@ executable elna
import: warnings import: warnings
main-is: Main.hs main-is: Main.hs
build-depends: build-depends:
elna:elna-internal, elna:elna-internal
filepath ^>= 1.5.3
hs-source-dirs: src hs-source-dirs: src
test-suite elna-test test-suite elna-test

View File

@ -1,48 +0,0 @@
{- This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. -}
module Language.Elna.CommandLine
( CommandLine(..)
, commandLine
, execParser
) where
import Options.Applicative
( Parser
, ParserInfo(..)
, argument
, execParser
, fullDesc
, help
, helper
, info
, long
, metavar
, optional
, progDesc
, short
, str
, strOption
)
import Control.Applicative ((<**>))
data CommandLine = CommandLine
{ input :: FilePath
, output :: Maybe FilePath
} deriving (Eq, Show)
parser :: Parser CommandLine
parser = CommandLine
<$> argument str inFile
<*> optional (strOption outFile)
where
inFile = metavar "INFILE" <> help "Input file."
outFile = long "output"
<> short 'o'
<> metavar "OUTFILE"
<> help "Output file."
commandLine :: ParserInfo CommandLine
commandLine = info (parser <**> helper)
$ fullDesc <> progDesc "Elna compiler."

View File

@ -0,0 +1,37 @@
{- This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. -}
module Language.Elna.Driver
( Driver(..)
, IntermediateStage(..)
, drive
, execParser
) where
import Data.Maybe (fromMaybe)
import Language.Elna.Driver.CommandLine
( CommandLine(..)
, IntermediateStage(..)
, commandLine
)
import Options.Applicative (execParser)
import System.FilePath (replaceExtension, takeFileName)
data Driver = Driver
{ input :: FilePath
, output :: FilePath
, intermediateStage :: Maybe IntermediateStage
} deriving (Eq, Show)
drive :: IO Driver
drive = rewrite <$> execParser commandLine
where
rewrite CommandLine{..} =
let defaultOutputName = replaceExtension (takeFileName input) "o"
outputName = fromMaybe defaultOutputName output
in Driver
{ input = input
, output = outputName
, intermediateStage = intermediateStage
}

View File

@ -0,0 +1,69 @@
{- This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. -}
module Language.Elna.Driver.CommandLine
( CommandLine(..)
, IntermediateStage(..)
, commandLine
) where
import Options.Applicative
( Parser
, ParserInfo(..)
, argument
, flag'
, fullDesc
, help
, helper
, info
, long
, metavar
, optional
, progDesc
, short
, str
, strOption
)
import Control.Applicative (Alternative(..), (<**>))
data IntermediateStage
= ParseStage
| ValidateStage
| CodeGenStage
deriving (Eq, Show)
data CommandLine = CommandLine
{ input :: FilePath
, output :: Maybe FilePath
, intermediateStage :: Maybe IntermediateStage
} deriving (Eq, Show)
intermediateStageP :: Parser IntermediateStage
intermediateStageP
= flag' ParseStage parseStageP
<|> flag' ValidateStage validateStageP
<|> flag' CodeGenStage codeGenStageP
where
parseStageP = long "parse"
<> help "Run the lexer and parser, but stop before assembly generation"
validateStageP = long "validate"
<> help "Run through the semantic analysis stage, stopping before TAC generation"
codeGenStageP = long "codegen"
<> help "Perform lexing, parsing, and assembly generation, but stop before code emission"
commandLineP :: Parser CommandLine
commandLineP = CommandLine
<$> argument str inFile
<*> optional (strOption outFile)
<*> optional intermediateStageP
where
inFile = metavar "INFILE" <> help "Input file."
outFile = long "output"
<> short 'o'
<> metavar "OUTFILE"
<> help "Output file."
commandLine :: ParserInfo CommandLine
commandLine = info (commandLineP <**> helper)
$ fullDesc <> progDesc "Elna compiler."

View File

@ -6,7 +6,11 @@ module Main
( main ( main
) where ) where
import Language.Elna.CommandLine (CommandLine(..), commandLine, execParser) import Language.Elna.Driver
( Driver(..)
, IntermediateStage(..)
, drive
)
import Language.Elna.Object.ElfCoder (elfObject) import Language.Elna.Object.ElfCoder (elfObject)
import Language.Elna.Backend.Allocator (allocate) import Language.Elna.Backend.Allocator (allocate)
import Language.Elna.Glue (glue) import Language.Elna.Glue (glue)
@ -15,8 +19,6 @@ import Language.Elna.Frontend.Parser (programP)
import Language.Elna.Frontend.TypeAnalysis (typeAnalysis) import Language.Elna.Frontend.TypeAnalysis (typeAnalysis)
import Language.Elna.RiscV.CodeGenerator (generateRiscV, riscVConfiguration) import Language.Elna.RiscV.CodeGenerator (generateRiscV, riscVConfiguration)
import Language.Elna.RiscV.ElfWriter (riscv32Elf) import Language.Elna.RiscV.ElfWriter (riscv32Elf)
import Data.Maybe (fromMaybe)
import System.FilePath (replaceExtension, takeFileName)
import Text.Megaparsec (runParser, errorBundlePretty) import Text.Megaparsec (runParser, errorBundlePretty)
import qualified Data.Text.IO as Text import qualified Data.Text.IO as Text
import System.Exit (ExitCode(..), exitWith) import System.Exit (ExitCode(..), exitWith)
@ -32,27 +34,29 @@ import Control.Exception (IOException, catch)
-- 6 - Register allocation error. -- 6 - Register allocation error.
main :: IO () main :: IO ()
main = execParser commandLine >>= withCommandLine main = drive >>= withCommandLine
where where
withCommandLine CommandLine{..} = withCommandLine driver@Driver{ input }
let defaultOutputName = replaceExtension (takeFileName input) "o" = catch (Text.readFile input) (printAndExit 2 :: IOException -> IO a)
outputName = fromMaybe defaultOutputName output >>= withParsedInput driver
in catch (Text.readFile input) (printAndExit 2 :: IOException -> IO a) . runParser programP input
>>= withParsedInput outputName withParsedInput driver@Driver{ intermediateStage } (Right program)
. runParser programP input | Just ParseStage <- intermediateStage = pure ()
withParsedInput output (Right program) | otherwise
= either (printAndExit 4) (withSymbolTable output program) = either (printAndExit 4) (withSymbolTable driver program)
$ nameAnalysis program $ nameAnalysis program
withParsedInput _ (Left errorBundle) withParsedInput _ (Left errorBundle)
= putStrLn (errorBundlePretty errorBundle) = putStrLn (errorBundlePretty errorBundle)
>> exitWith (ExitFailure 3) >> exitWith (ExitFailure 3)
withSymbolTable output program symbolTable withSymbolTable driver@Driver{ intermediateStage } program symbolTable
| Just typeError <- typeAnalysis symbolTable program = | Just typeError <- typeAnalysis symbolTable program =
printAndExit 5 typeError printAndExit 5 typeError
| otherwise = | Just ValidateStage <- intermediateStage = pure ()
let makeObject = elfObject output . riscv32Elf . generateRiscV | otherwise = either (printAndExit 6) (withTac driver)
in either (printAndExit 6) makeObject $ allocate riscVConfiguration symbolTable
$ allocate riscVConfiguration symbolTable $ glue symbolTable program
$ glue symbolTable program withTac Driver{ intermediateStage, output } tac
| Just CodeGenStage <- intermediateStage = pure ()
| otherwise = elfObject output $ riscv32Elf $ generateRiscV tac
printAndExit :: Show b => forall a. Int -> b -> IO a printAndExit :: Show b => forall a. Int -> b -> IO a
printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode) printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode)