From 345c652a5755fd5c453f4c29a6a99d5e254942e2 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 28 Feb 2025 15:18:24 +0000 Subject: [PATCH] feat: introduce cats-effect and io --- project.scala | 21 ++++----- src/main/wacc/Main.scala | 85 ++++++++++++++++++------------------ src/test/wacc/examples.scala | 3 +- 3 files changed, 53 insertions(+), 56 deletions(-) diff --git a/project.scala b/project.scala index 4deaa08..6443ad8 100644 --- a/project.scala +++ b/project.scala @@ -5,23 +5,18 @@ //> using dep com.github.j-mie6::parsley::5.0.0-M10 //> using dep com.github.j-mie6::parsley-cats::1.5.0 //> using dep com.lihaoyi::os-lib::0.11.4 +//> using dep org.typelevel::cats-core::2.13.0 +//> using dep org.typelevel::cats-effect::3.5.7 +//> using dep com.monovore::decline::2.5.0 +//> using dep com.monovore::decline-effect::2.5.0 //> using dep com.github.scopt::scopt::4.1.0 //> using test.dep org.scalatest::scalatest::3.2.19 -// these are all sensible defaults to catch annoying issues +// sensible defaults for warnings and compiler checks //> using options -deprecation -unchecked -feature //> using options -Wimplausible-patterns -Wunused:all //> using options -Yexplicit-nulls -Wsafe-init -Xkind-projector:underscores -// these will help ensure you have access to the latest parsley releases -// even before they land on maven proper, or snapshot versions, if necessary. -// just in case they cause problems, however, keep them turned off unless you -// specifically need them. -// using repositories sonatype-s01:releases -// using repositories sonatype-s01:snapshots - -// these are flags used by Scala native: if you aren't using scala-native, then they do nothing -// lto-thin has decent linking times, and release-fast does not too much optimisation. -// using nativeLto thin -// using nativeGc commix -// using nativeMode release-fast +// repositories for pre-release versions if needed +//> using repositories sonatype-s01:releases +//> using repositories sonatype-s01:snapshots diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index fc9fb45..8bf7d3b 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -6,8 +6,12 @@ import parsley.{Failure, Success} import scopt.OParser import java.io.File import java.io.PrintStream +import cats.implicits._ +import cats.effect.unsafe.implicits.global import assemblyIR as asm +import cats.effect.IO +import cats.effect.IOApp case class CliConfig( file: File = new File(".") @@ -36,30 +40,26 @@ val cliParser = { def frontend( contents: String -)(using stdout: PrintStream): Either[microWacc.Program, Int] = { - parser.parse(contents) match { +)(using stdout: PrintStream): IO[microWacc.Program] = { + IO(parser.parse(contents)).flatMap { + case Failure(msg) => IO.raiseError(new RuntimeException(msg)) case Success(prog) => - given errors: mutable.Builder[Error, List[Error]] = List.newBuilder - val (names, funcs) = renamer.rename(prog) - given ctx: typeChecker.TypeCheckerCtx = typeChecker.TypeCheckerCtx(names, funcs, errors) - val typedProg = typeChecker.check(prog) - if (errors.result.nonEmpty) { - given errorContent: String = contents - Right( - errors.result - .map { error => - printError(error) - error match { - case _: Error.InternalError => 201 - case _ => 200 - } - } - .max() - ) - } else Left(typedProg) - case Failure(msg) => - stdout.println(msg) - Right(100) + given errors: mutable.Builder[Error, List[Error]] = List.newBuilder + given errorContent: String = contents + + val (names, funcs) = renamer.rename(prog) + given ctx: typeChecker.TypeCheckerCtx = typeChecker.TypeCheckerCtx(names, funcs, errors) + + val typedProg = typeChecker.check(prog) + + if (errors.result.isEmpty) IO.pure(typedProg) + else { + errors.result.foreach(printError) + IO.raiseError(new RuntimeException("Compilation failed with code: " + errors.result.view.map { + case _: Error.InternalError => 201 + case _ => 200 + }.max)) + } } } @@ -67,26 +67,27 @@ val s = "enter an integer to echo" def backend(typedProg: microWacc.Program): Chain[asm.AsmLine] = asmGenerator.generateAsm(typedProg) + def compile(filename: String, outFile: Option[File] = None)(using stdout: PrintStream = Console.out -): Int = - frontend(os.read(os.Path(filename))) match { - case Left(typedProg) => - val asmFile = outFile.getOrElse(File(filename.stripSuffix(".wacc") + ".s")) - val asm = backend(typedProg) - writer.writeTo(asm, PrintStream(asmFile)) - 0 - case Right(exitCode) => exitCode - } - -def main(args: Array[String]): Unit = - OParser.parse(cliParser, args, CliConfig()) match { - case Some(config) => - System.exit( - compile( - config.file.getAbsolutePath, - outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) +): IO[Int] = + for { + contents <- IO(os.read(os.Path(filename))) + typedProg <- frontend(contents) + _ <- IO { + writer.writeTo( + backend(typedProg), + PrintStream(outFile.getOrElse(File(filename.stripSuffix(".wacc") + ".s"))) ) - ) - case None => } +} yield 0 + +object Main extends IOApp.Simple { + override def run: IO[Unit] = + OParser.parse(cliParser, sys.env.getOrElse("WACC_ARGS", "").split(" "), CliConfig()).traverse_ { config => + compile( + config.file.getAbsolutePath, + outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) + ) + } +} diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala index 6114afd..4fac462 100644 --- a/src/test/wacc/examples.scala +++ b/src/test/wacc/examples.scala @@ -7,6 +7,7 @@ import java.io.File import sys.process._ import java.io.PrintStream import scala.io.Source +import cats.effect.unsafe.implicits.global class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { val files = @@ -29,7 +30,7 @@ class ParallelExamplesSpec extends AnyFlatSpec with BeforeAndAfterAll { given stdout: PrintStream = PrintStream(File(baseFilename + ".out")) s"$filename" should "be compiled with correct result" in { - val result = compile(filename) + val result = compile(filename).unsafeRunSync() assert(expectedResult.contains(result)) }