feat: add option flag, greedy compilation of multiple files, and refactor to... #41

Merged
gc1523 merged 34 commits from master into intelliwacc-ide 2025-03-13 23:28:08 +00:00
15 changed files with 613 additions and 909 deletions
Showing only changes of commit 9a5ccea1f6 - Show all commits

View File

@@ -19,15 +19,17 @@ import org.typelevel.log4cats.Logger
import assemblyIR as asm import assemblyIR as asm
// TODO: IO correctness, --greedy, parallelisable, and probably splitting this file up // TODO: IO correctness, --greedy, parallelisable, and probably splitting this file up
given Argument[Path] = Argument.from("path") { str => given Argument[Path] = Argument.from("path") { str =>
val path = Path.of(str) val path = Path.of(str)
( (
Either.cond(Files.exists(path), path, s"File '${path.toAbsolutePath}' does not exist"), Either.cond(Files.exists(path), path, s"File '${path.toAbsolutePath}' does not exist"),
Either.cond(Files.isRegularFile(path), path, s"File '${path.toAbsolutePath}' must be a regular file"), Either.cond(
Files.isRegularFile(path),
path,
s"File '${path.toAbsolutePath}' must be a regular file"
),
Either.cond(path.toString.endsWith(".wacc"), path, "File must have .wacc extension") Either.cond(path.toString.endsWith(".wacc"), path, "File must have .wacc extension")
).mapN((_, _, _) => path).toValidatedNel ).mapN((_, _, _) => path).toValidatedNel
} }
@@ -42,7 +44,6 @@ val outputOpt: Opts[Option[Path]] =
val filesOpt: Opts[NonEmptyList[Path]] = Opts.arguments[Path]("files") val filesOpt: Opts[NonEmptyList[Path]] = Opts.arguments[Path]("files")
def frontend( def frontend(
contents: String contents: String
): IO[Either[Int, microWacc.Program]] = { ): IO[Either[Int, microWacc.Program]] = {
@@ -64,9 +65,11 @@ def frontend(
else { else {
// TODO: multiple traversal of error content, should be a foldleft or co // TODO: multiple traversal of error content, should be a foldleft or co
given errorContent: String = contents given errorContent: String = contents
val exitCode = errResult.collectFirst { val exitCode = errResult
case _: Error.InternalError => 201 .collectFirst { case _: Error.InternalError =>
}.getOrElse(200) 201
}
.getOrElse(200)
val formattedErrors = errResult.map(formatError).mkString("\n") val formattedErrors = errResult.map(formatError).mkString("\n")
@@ -85,33 +88,37 @@ def compile(filename: String, outputDir: Option[Path], log: Boolean): IO[Int] =
if (log) logger.info(_) if (log) logger.info(_)
else (_ => IO.unit) else (_ => IO.unit)
for { 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? 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") _ <- logAction(s"Compiling file: $filename")
result <- frontend(contents) result <- frontend(contents)
exitCode <- result.fold( exitCode <- result.fold(
code => logger.error(s"Compilation failed for $filename\nExit code: $code").as(code), code => logger.error(s"Compilation failed for $filename\nExit code: $code").as(code),
typedProg => typedProg =>
val outDir = outputDir.getOrElse(Paths.get(filename).getParent) 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? 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") 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 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) logAction(s"Compilation succeeded: $filename").as(0)
) )
} yield exitCode } yield exitCode
// TODO: this is sequential, thus should be what occurs when --greedy is passed in // TODO: this is sequential, thus should be what occurs when --greedy is passed in
val compileCommand: Opts[IO[ExitCode]] = val compileCommand: Opts[IO[ExitCode]] =
(filesOpt, logOpt, outputOpt).mapN{ (filesOpt, logOpt, outputOpt).mapN { (files, log, outDir) =>
(files, log, outDir) =>
files files
.traverse { file => .traverse { file =>
compile( compile(file.toAbsolutePath.toString, outDir, log)
file.toAbsolutePath.toString, }
outDir, .map { exitCodes =>
log) if (exitCodes.exists(_ != 0))
}.map { ExitCode.Error // TODO- it should be the first one to exit when parallelised :)
exitCodes =>
if (exitCodes.exists(_ != 0)) ExitCode.Error // TODO- it should be the first one to exit when parallelised :)
else ExitCode.Success else ExitCode.Success
} }