-
Notifications
You must be signed in to change notification settings - Fork 1
/
INTERPRETER.scala
73 lines (66 loc) · 2.22 KB
/
INTERPRETER.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import cats.effect.*
import cats.effect.std.*
import fs2.io.file.*
import cats.parse.*
object INTERPRETER extends IOApp:
def run(args: List[String]) =
Files[IO]
.readAll(Path(args.head))
.through(fs2.text.utf8.decode)
.compile
.string
.flatMap { content =>
QuickmaffsParser.parse(content) match
case Left(err) =>
printErrors(content, List(err.caret -> "Parsing error"))
case Right(prg) =>
QuickmaffsCompiler.compile(prg) match
case Left(errs) =>
printErrors(
content,
errs.map(ce => ce.position.from -> ce.message)
)
case Right(idx) =>
QuickmaffsEvaluator.evaluate(prg) match
case Left(errs) =>
printErrors(
content,
List(Caret(0, 0, 0) -> errs.message)
)
case Right(st) =>
printErrors(
content,
List.empty,
st.map { case (name, value) =>
idx.variables(name).definedAt.from.line -> value
}
)
}
.as(ExitCode.Success)
end INTERPRETER
def printErrors(
program: String,
errors: Seq[(Caret, String)],
results: Map[Int, Int] = Map.empty
): IO[Unit] =
val byLine = errors.groupBy(_._1.line)
val sb = StringBuilder()
import Colors.*
val size = program.linesIterator.length
val round = math.log10(size).toInt + 1
program.linesIterator.zipWithIndex.foreach { case (l, i) =>
val lineNumber = i.toString.reverse.padTo(round, '0').reverse
val result = results.get(i).map(v => green(s" // $v")).getOrElse("")
val pref = s"[$lineNumber]: "
sb.append(yellow(pref) + l + s"$result\n")
inline def shift(c: Caret) = " " * (pref.length + c.col)
byLine.get(i).foreach { errs =>
errors.headOption.foreach { (caret, msg) =>
sb.append(shift(caret) + red("^") + "\n")
sb.append(shift(caret) + red("|") + "\n")
sb.append(shift(caret) + red(msg) + "\n")
}
}
}
IO.println(sb.result())
end printErrors