module Main (main) where import Control.Monad (forM_) import Data.Bits (testBit) import Data.Foldable (foldl', foldr) import qualified Data.Map.Strict as M import qualified Data.Text as T import qualified Data.Text.IO as TIO import Nand2Tetris.Hack (AsmLine (LineInstruction), addLabels, addVars, compile, defaultVars, hackParser) import Options.Applicative (execParser, fullDesc, header, help, helper, info, long, metavar, progDesc, short, strOption, (<**>)) import System.IO (IOMode (WriteMode), hPutStrLn, withFile) import Text.Megaparsec (runParser) import Text.Megaparsec.Error (ParseErrorBundle, ShowErrorComponent (showErrorComponent), errorBundlePretty) data Options = Options { inputFile :: FilePath, outputFile :: FilePath } main :: IO () main = do options <- execParser opts programText <- TIO.readFile (inputFile options) let result = runParser hackParser (inputFile options) programText case result of Left err -> putStrLn $ errorBundlePretty err Right parsed -> do let labels = addLabels M.empty parsed let vars = addVars defaultVars labels parsed print vars let compiled = compile labels vars $ foldr addInstr [] parsed case compiled of Left err -> putStrLn $ "Error: " <> T.unpack err Right program -> withFile (outputFile options) WriteMode $ \h -> forM_ program (\x -> hPutStrLn h (printInstruction x)) where printInstruction w = concatMap (\x -> if testBit w x then "1" else "0") $ reverse [0..15] regVars = defaultVars addInstr (LineInstruction instr) is = instr : is addInstr _ is = is opts = info (hackAsmOptParser <**> helper) ( fullDesc <> progDesc "Compiles a file with Hack assembly instructions to Hask binary code" <> header "hackasm - assembler for hack CPU (nand2tetris)" ) hackAsmOptParser = Options <$> strOption ( long "input" <> short 'i' <> metavar "FILENAME" <> help "Input file" ) <*> strOption ( long "output" <> short 'o' <> metavar "FILENAME" <> help "Output file" )