-
Notifications
You must be signed in to change notification settings - Fork 0
/
VMTranslator.hs
76 lines (70 loc) · 2.6 KB
/
VMTranslator.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{-# LANGUAGE LambdaCase #-}
module VMTranslator (main) where
import Assembler.Render (renderAssemblyLine)
import Control.Monad (forM)
import Data.List (intercalate)
import System.Directory (doesFileExist, getCurrentDirectory, listDirectory)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import System.FilePath
( dropTrailingPathSeparator
, splitExtension
, takeBaseName
, takeExtension
, takeFileName
, (<.>)
, (</>)
)
import System.IO (readFile')
import VMTranslator.CodeGen (generateAssembly)
import VMTranslator.Parser (runParser)
-- | Valiate args & run
main :: IO ()
main =
getArgs >>= \case
[vmFileOrDir] ->
runTranslator vmFileOrDir
[] ->
getCurrentDirectory >>= runTranslator
args -> do
putStrLn $
unlines
[ "ERROR: Expected 0 or 1 arguments, got: "
, ""
, "\t" <> intercalate ", " (map show args)
, ""
, "VMTranslator.hs [<FILE_NAME>.vm|<DIRECTORY>]"
, ""
, "Reads input file or `.vm` files in directory."
, "Translates to Hack assembly code."
, "Write results to <FILE_NAME>.asm in same directory as input file."
]
exitFailure
-- | Get input file text, parse it, generate the machine code, & write to
-- output file.
runTranslator :: FilePath -> IO ()
runTranslator vmPath = do
(outputPath, files) <- getFiles
parsedFiles <- forM files $ \file -> do
inputText <- readFile' file
let parsedCommands = runParser inputText
return (takeBaseName file, parsedCommands)
let generatedAssembly = generateAssembly parsedFiles
writeFile outputPath $ unlines $ map renderAssemblyLine generatedAssembly
where
-- Get files to process & make the output file path
getFiles :: IO (FilePath, [FilePath])
getFiles = do
isFile <- doesFileExist vmPath
if isFile
then case splitExtension vmPath of
(basePath, ".vm") ->
return (basePath <.> "asm", [vmPath])
_ ->
error "ERROR: Expected input file with extension '.vm"
else do
let folderName = takeFileName $ dropTrailingPathSeparator vmPath
outputPath = vmPath </> folderName <.> "asm"
allFiles <- map (vmPath </>) <$> listDirectory vmPath
let vmFiles = filter ((== ".vm") . takeExtension) allFiles
return (outputPath, vmFiles)