100 lines
2.7 KiB
Scala
100 lines
2.7 KiB
Scala
package wacc
|
|
|
|
import scala.collection.mutable
|
|
import cats.data.Chain
|
|
import parsley.{Failure, Success}
|
|
import scopt.OParser
|
|
import java.io.File
|
|
import java.io.PrintStream
|
|
import cats.implicits.*
|
|
|
|
import assemblyIR as asm
|
|
import cats.effect.IO
|
|
import cats.effect.IOApp
|
|
|
|
case class CliConfig(
|
|
file: File = new File(".")
|
|
)
|
|
|
|
val cliBuilder = OParser.builder[CliConfig]
|
|
val cliParser = {
|
|
import cliBuilder._
|
|
OParser.sequence(
|
|
programName("wacc-compiler"),
|
|
help('h', "help")
|
|
.text("Prints this help message"),
|
|
arg[File]("<file>")
|
|
.text("Input WACC source 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
|
|
)
|
|
)
|
|
}
|
|
|
|
def frontend(
|
|
contents: String
|
|
)(using stdout: PrintStream): IO[Either[Int, microWacc.Program]] = {
|
|
IO(parser.parse(contents)).map {
|
|
case Failure(msg) =>
|
|
stdout.println(msg)
|
|
Left(100) // Syntax error
|
|
|
|
case Success(prog) =>
|
|
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) Right(typedProg)
|
|
else {
|
|
errors.result.foreach(printError)
|
|
Left(errors.result.view.map {
|
|
case _: Error.InternalError => 201
|
|
case _ => 200
|
|
}.max)
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
): IO[Int] =
|
|
for {
|
|
contents <- IO(os.read(os.Path(filename)))
|
|
result <- frontend(contents)
|
|
exitCode <- result.fold(
|
|
IO.pure, // Return error code (handles Left case)
|
|
typedProg =>
|
|
IO {
|
|
writer.writeTo(
|
|
backend(typedProg),
|
|
PrintStream(outFile.getOrElse(File(filename.stripSuffix(".wacc") + ".s")))
|
|
)
|
|
}.as(0) // Compilation succeeded
|
|
)
|
|
} yield exitCode
|
|
|
|
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"))
|
|
)
|
|
}
|
|
}
|