refactor: introduce decline to integrate command-line parsing with cats-effect

This commit is contained in:
Jonny
2025-02-28 18:00:18 +00:00
parent 1a72decf55
commit d56be9249a
2 changed files with 35 additions and 31 deletions

View File

@@ -9,7 +9,6 @@
//> using dep org.typelevel::cats-effect::3.5.7 //> using dep org.typelevel::cats-effect::3.5.7
//> using dep com.monovore::decline::2.5.0 //> using dep com.monovore::decline::2.5.0
//> using dep com.monovore::decline-effect::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 //> using test.dep org.scalatest::scalatest::3.2.19
//> using dep org.typelevel::cats-effect-testing-scalatest::1.6.0 //> using dep org.typelevel::cats-effect-testing-scalatest::1.6.0

View File

@@ -3,40 +3,40 @@ package wacc
import scala.collection.mutable import scala.collection.mutable
import cats.data.Chain import cats.data.Chain
import parsley.{Failure, Success} import parsley.{Failure, Success}
import scopt.OParser
import java.io.File import java.io.File
import java.io.PrintStream import java.io.PrintStream
import cats.implicits.* import cats.implicits.*
import assemblyIR as asm
import cats.effect.IO import cats.effect.IO
import cats.effect.IOApp import cats.effect.ExitCode
import com.monovore.decline._
import com.monovore.decline.effect._
import com.monovore.decline.Argument
import assemblyIR as asm
case class CliConfig( case class CliConfig(
file: File = new File(".") file: File = new File(".")
) )
val cliBuilder = OParser.builder[CliConfig] given Argument[File] = Argument.from("file") { str =>
val cliParser = { val file = File(str)
import cliBuilder._ (
OParser.sequence( Option.when(file.exists())(file).toValidNel(s"File '${file.getAbsolutePath}' does not exist"),
programName("wacc-compiler"), Option
help('h', "help") .when(file.isFile())(file)
.text("Prints this help message"), .toValidNel(s"File '${file.getAbsolutePath}' must be a regular file"),
arg[File]("<file>") Option.when(file.getName.endsWith(".wacc"))(file).toValidNel("File must have .wacc extension")
.text("Input WACC source file") ).mapN((_, _, _) => file)
.required()
.action((f, c) => c.copy(file = f))
.validate(f =>
if (!f.exists) failure("File does not exist")
else if (!f.isFile) failure("File must be a regular file")
else if (!f.getName.endsWith(".wacc"))
failure("File must have .wacc extension")
else success
)
)
} }
val cliCommand: Command[File] =
Command("wacc-compiler", "Compile WACC programs") {
Opts.argument[File]("file")
}
def frontend( def frontend(
contents: String contents: String
)(using stdout: PrintStream): IO[Either[Int, microWacc.Program]] = { )(using stdout: PrintStream): IO[Either[Int, microWacc.Program]] = {
@@ -87,13 +87,18 @@ def compile(filename: String, outFile: Option[File] = None)(using
) )
} yield exitCode } yield exitCode
object Main extends IOApp.Simple { object Main
override def run: IO[Unit] = extends CommandIOApp(
OParser.parse(cliParser, sys.env.getOrElse("WACC_ARGS", "").split(" "), CliConfig()).traverse_ { name = "wacc-compiler",
config => header = "the ultimate wacc compiler",
compile( version = "1.0"
config.file.getAbsolutePath, ) {
outFile = Some(File(".", config.file.getName.stripSuffix(".wacc") + ".s")) def main: Opts[IO[ExitCode]] =
) Opts.argument[File]("file").map { file =>
compile(
file.getAbsolutePath,
outFile = Some(File(".", file.getName.stripSuffix(".wacc") + ".s"))
).map(ExitCode(_)) // turn the int into exit code for compatibility with commandioapp
// https://ben.kirw.in/decline/effect.html
} }
} }