feat: introduction of logger to eliminate printstreams
This commit is contained in:
@@ -9,6 +9,8 @@
|
|||||||
//> 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 org.typelevel::log4cats-slf4j::2.7.0
|
||||||
|
//> using dep org.slf4j:slf4j-simple:2.0.17
|
||||||
//> 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
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import scala.collection.mutable
|
|||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import parsley.{Failure, Success}
|
import parsley.{Failure, Success}
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
|
||||||
import cats.implicits.*
|
import cats.implicits.*
|
||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
@@ -14,6 +13,9 @@ import com.monovore.decline._
|
|||||||
import com.monovore.decline.effect._
|
import com.monovore.decline.effect._
|
||||||
import com.monovore.decline.Argument
|
import com.monovore.decline.Argument
|
||||||
|
|
||||||
|
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
||||||
|
import org.typelevel.log4cats.Logger
|
||||||
|
|
||||||
import assemblyIR as asm
|
import assemblyIR as asm
|
||||||
|
|
||||||
given Argument[File] = Argument.from("file") { str =>
|
given Argument[File] = Argument.from("file") { str =>
|
||||||
@@ -32,30 +34,33 @@ val cliCommand: Command[File] =
|
|||||||
Opts.argument[File]("file")
|
Opts.argument[File]("file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
given logger: Logger[IO] = Slf4jLogger.getLogger[IO]
|
||||||
|
|
||||||
def frontend(
|
def frontend(
|
||||||
contents: String
|
contents: String
|
||||||
)(using stdout: PrintStream): IO[Either[Int, microWacc.Program]] = {
|
): IO[Either[Int, microWacc.Program]] = {
|
||||||
IO(parser.parse(contents)).map {
|
IO(parser.parse(contents)).flatMap {
|
||||||
case Failure(msg) =>
|
case Failure(msg) =>
|
||||||
stdout.println(msg)
|
logger.error(s"Syntax error: $msg").as(Left(100))
|
||||||
Left(100) // Syntax error
|
|
||||||
|
|
||||||
case Success(prog) =>
|
case Success(prog) =>
|
||||||
given errors: mutable.Builder[Error, List[Error]] = List.newBuilder
|
given errors: mutable.Builder[Error, List[Error]] = List.newBuilder
|
||||||
given errorContent: String = contents
|
|
||||||
|
|
||||||
val (names, funcs) = renamer.rename(prog)
|
val (names, funcs) = renamer.rename(prog)
|
||||||
given ctx: typeChecker.TypeCheckerCtx = typeChecker.TypeCheckerCtx(names, funcs, errors)
|
given ctx: typeChecker.TypeCheckerCtx = typeChecker.TypeCheckerCtx(names, funcs, errors)
|
||||||
|
|
||||||
val typedProg = typeChecker.check(prog)
|
val typedProg = typeChecker.check(prog)
|
||||||
|
|
||||||
if (errors.result.isEmpty) Right(typedProg)
|
if (errors.result.isEmpty) IO.pure(Right(typedProg))
|
||||||
else {
|
else {
|
||||||
errors.result.foreach(printError)
|
val exitCode = errors.result.view.map {
|
||||||
Left(errors.result.view.map {
|
|
||||||
case _: Error.InternalError => 201
|
case _: Error.InternalError => 201
|
||||||
case _ => 200
|
case _ => 200
|
||||||
}.max)
|
}.max
|
||||||
|
|
||||||
|
logger.error(s"Semantic errors:\n${errors.result.mkString("\n")}") *> IO.pure(
|
||||||
|
Left(exitCode)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,21 +69,18 @@ val s = "enter an integer to echo"
|
|||||||
def backend(typedProg: microWacc.Program): Chain[asm.AsmLine] =
|
def backend(typedProg: microWacc.Program): Chain[asm.AsmLine] =
|
||||||
asmGenerator.generateAsm(typedProg)
|
asmGenerator.generateAsm(typedProg)
|
||||||
|
|
||||||
def compile(filename: String, outFile: Option[File] = None)(using
|
def compile(filename: String, outFile: Option[File] = None): IO[Int] =
|
||||||
stdout: PrintStream = Console.out
|
|
||||||
): IO[Int] =
|
|
||||||
for {
|
for {
|
||||||
contents <- IO(os.read(os.Path(filename)))
|
contents <- IO(os.read(os.Path(filename)))
|
||||||
|
_ <- logger.info(s"Compiling file: $filename")
|
||||||
result <- frontend(contents)
|
result <- frontend(contents)
|
||||||
exitCode <- result.fold(
|
exitCode <- result.fold(
|
||||||
IO.pure, // Return error code (handles Left case)
|
code => logger.error(s"Compilation failed for $filename\nExit code: $code").as(code),
|
||||||
typedProg =>
|
typedProg =>
|
||||||
IO {
|
val outputFile = outFile.getOrElse(File(filename.stripSuffix(".wacc") + ".s"))
|
||||||
writer.writeTo(
|
writer.writeTo(backend(typedProg), outputFile) *> logger
|
||||||
backend(typedProg),
|
.info(s"Compilation succeeded: $filename")
|
||||||
PrintStream(outFile.getOrElse(File(filename.stripSuffix(".wacc") + ".s")))
|
.as(0)
|
||||||
)
|
|
||||||
}.as(0) // Compilation succeeded
|
|
||||||
)
|
)
|
||||||
} yield exitCode
|
} yield exitCode
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
package wacc
|
package wacc
|
||||||
|
|
||||||
import java.io.PrintStream
|
import cats.effect.Resource
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.io.File
|
||||||
|
import java.io.BufferedWriter
|
||||||
|
import java.io.FileWriter
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
import cats.effect.IO
|
||||||
|
|
||||||
|
import org.typelevel.log4cats.Logger
|
||||||
|
|
||||||
object writer {
|
object writer {
|
||||||
import assemblyIR._
|
import assemblyIR._
|
||||||
|
|
||||||
def writeTo(asmList: Chain[AsmLine], printStream: PrintStream): Unit = {
|
def writeTo(asmList: Chain[AsmLine], outputFile: File)(using logger: Logger[IO]): IO[Unit] =
|
||||||
asmList.iterator.foreach(printStream.println)
|
Resource
|
||||||
}
|
.fromAutoCloseable {
|
||||||
|
IO(BufferedWriter(FileWriter(outputFile, StandardCharsets.UTF_8)))
|
||||||
|
}
|
||||||
|
.use { writer =>
|
||||||
|
IO {
|
||||||
|
asmList.iterator.foreach(line => writer.write(line.toString + "\n"))
|
||||||
|
writer.flush() // TODO: NECESSARY OR NOT?
|
||||||
|
} *> logger.info(s"Wrote assembly to ${outputFile.getAbsolutePath}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.scalatest.freespec.AsyncFreeSpec
|
|||||||
import cats.effect.testing.scalatest.AsyncIOSpec
|
import cats.effect.testing.scalatest.AsyncIOSpec
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import sys.process._
|
import sys.process._
|
||||||
import java.io.PrintStream
|
|
||||||
import scala.io.Source
|
import scala.io.Source
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import wacc.{compile as compileWacc}
|
import wacc.{compile as compileWacc}
|
||||||
@@ -30,7 +29,6 @@ class ParallelExamplesSpec extends AsyncFreeSpec with AsyncIOSpec with BeforeAnd
|
|||||||
|
|
||||||
forEvery(files) { (filename, expectedResult) =>
|
forEvery(files) { (filename, expectedResult) =>
|
||||||
val baseFilename = filename.stripSuffix(".wacc")
|
val baseFilename = filename.stripSuffix(".wacc")
|
||||||
given stdout: PrintStream = PrintStream(File(baseFilename + ".out"))
|
|
||||||
|
|
||||||
s"$filename" - {
|
s"$filename" - {
|
||||||
"should be compiled with correct result" in {
|
"should be compiled with correct result" in {
|
||||||
|
|||||||
Reference in New Issue
Block a user