refactor: compile function split up into smaller functions
This commit is contained in:
@@ -4,7 +4,7 @@ import scala.collection.mutable
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
import parsley.{Failure, Success}
|
||||
|
||||
import java.nio.file.{Files, Path, Paths}
|
||||
import java.nio.file.{Files, Path}
|
||||
import cats.syntax.all._
|
||||
|
||||
import cats.effect.IO
|
||||
@@ -83,38 +83,75 @@ def backend(typedProg: microWacc.Program): Chain[asm.AsmLine] =
|
||||
|
||||
// TODO: filename being String seems unnatural due to Path refactor
|
||||
// TODO: this function is doing too much should refactor
|
||||
def compile(filename: String, outputDir: Option[Path], log: Boolean): IO[Int] =
|
||||
// def compile(filename: String, outputDir: Option[Path], log: Boolean): IO[Int] =
|
||||
// val logAction: String => IO[Unit] =
|
||||
// if (log) logger.info(_)
|
||||
// else (_ => IO.unit)
|
||||
// for {
|
||||
// contents <- IO.delay(
|
||||
// os.read(os.Path(filename))
|
||||
// ) // TODO: Is IO as a wrapper ok or do we require .delay - also, should it be .blocking?
|
||||
// _ <- logAction(s"Compiling file: $filename")
|
||||
// result <- frontend(contents)
|
||||
// exitCode <- result.fold(
|
||||
// code => logger.error(s"Compilation failed for $filename\nExit code: $code").as(code),
|
||||
// typedProg =>
|
||||
// val outDir = outputDir.getOrElse(Paths.get(filename).getParent)
|
||||
// IO.delay(
|
||||
// Files.createDirectories(outDir)
|
||||
// ) // TODO: Is IO as a wrapper ok or do we require .delay - also, should it be .blocking?
|
||||
// val outputFile = outDir.resolve(filename.stripSuffix(".wacc") + ".s")
|
||||
// writer.writeTo(
|
||||
// backend(typedProg),
|
||||
// outputFile
|
||||
// ) *> // TODO: I dont think we need IO here if we look at the implementation of writer
|
||||
// logAction(s"Compilation succeeded: $filename").as(0)
|
||||
// )
|
||||
// } yield exitCode
|
||||
|
||||
def compile(filePath: Path, outputDir: Option[Path], log: Boolean): IO[Int] = {
|
||||
val logAction: String => IO[Unit] =
|
||||
if (log) logger.info(_)
|
||||
else (_ => IO.unit)
|
||||
|
||||
def readSourceFile: IO[String] =
|
||||
IO.blocking(os.read(os.Path(filePath)))
|
||||
|
||||
def ensureOutputDir(outDir: Path): IO[Path] =
|
||||
IO.blocking {
|
||||
Files.createDirectories(outDir)
|
||||
outDir
|
||||
}
|
||||
|
||||
// TODO: path, file , the names are confusing (when Path is the type but we are working with files)
|
||||
def writeOutputFile(typedProg: microWacc.Program, outputPath: Path): IO[Unit] =
|
||||
writer.writeTo(backend(typedProg), outputPath) *>
|
||||
logger.info(s"Success: ${outputPath.toAbsolutePath}")
|
||||
|
||||
def processProgram(contents: String, outDir: Path): IO[Int] =
|
||||
frontend(contents).flatMap {
|
||||
case Left(code) =>
|
||||
logger.error(s"Compilation failed for $filePath\nExit code: $code").as(code)
|
||||
|
||||
case Right(typedProg) =>
|
||||
val outputFile = outDir.resolve(filePath.getFileName.toString.stripSuffix(".wacc") + ".s")
|
||||
writeOutputFile(typedProg, outputFile).as(0)
|
||||
}
|
||||
|
||||
for {
|
||||
contents <- IO.delay(
|
||||
os.read(os.Path(filename))
|
||||
) // TODO: Is IO as a wrapper ok or do we require .delay - also, should it be .blocking?
|
||||
_ <- logAction(s"Compiling file: $filename")
|
||||
result <- frontend(contents)
|
||||
exitCode <- result.fold(
|
||||
code => logger.error(s"Compilation failed for $filename\nExit code: $code").as(code),
|
||||
typedProg =>
|
||||
val outDir = outputDir.getOrElse(Paths.get(filename).getParent)
|
||||
IO.delay(
|
||||
Files.createDirectories(outDir)
|
||||
) // TODO: Is IO as a wrapper ok or do we require .delay - also, should it be .blocking?
|
||||
val outputFile = outDir.resolve(filename.stripSuffix(".wacc") + ".s")
|
||||
writer.writeTo(
|
||||
backend(typedProg),
|
||||
outputFile
|
||||
) *> // TODO: I dont think we need IO here if we look at the implementation of writer
|
||||
logAction(s"Compilation succeeded: $filename").as(0)
|
||||
)
|
||||
contents <- readSourceFile
|
||||
_ <- logAction(s"Compiling file: ${filePath.toAbsolutePath}")
|
||||
outDir <- ensureOutputDir(outputDir.getOrElse(filePath.getParent))
|
||||
exitCode <- processProgram(contents, outDir)
|
||||
} yield exitCode
|
||||
}
|
||||
|
||||
// TODO: this is sequential, thus should be what occurs when --greedy is passed in
|
||||
val compileCommand: Opts[IO[ExitCode]] =
|
||||
(filesOpt, logOpt, outputOpt).mapN { (files, log, outDir) =>
|
||||
files
|
||||
.traverse { file =>
|
||||
compile(file.toAbsolutePath.toString, outDir, log)
|
||||
compile(file.toAbsolutePath, outDir, log)
|
||||
}
|
||||
.map { exitCodes =>
|
||||
if (exitCodes.exists(_ != 0))
|
||||
|
||||
@@ -36,7 +36,7 @@ object writer {
|
||||
|
||||
/** Main function to write assembly to a file */
|
||||
def writeTo(asmList: Chain[AsmLine], outputPath: Path)(using logger: Logger[IO]): IO[Unit] =
|
||||
bufferedWriter(outputPath).use { writer =>
|
||||
writeLines(writer, asmList) *> logger.info(s"Success: ${outputPath.toAbsolutePath}")
|
||||
bufferedWriter(outputPath).use {
|
||||
writeLines(_, asmList)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user