RosettaCodeData/Task/Compiler-AST-interpreter/Kotlin/compiler-ast-interpreter.kts
2026-04-30 12:34:36 -04:00

155 lines
5.1 KiB
Kotlin

import java.io.File
import java.util.*
internal object Interpreter {
private val globals = mutableMapOf<String, Int>()
private lateinit var s: Scanner
private val strToNodes = NodeType.entries.associateBy(NodeType::value)
private fun str(s: String): String = buildString {
var i = 0
val s = s.replace("\"", "")
while (i < s.length) {
if (s[i] == '\\' && i + 1 < s.length) {
if (s[i + 1] == 'n') {
append('\n')
i += 2
} else if (s[i] == '\\') {
append('\\')
i += 2
}
} else {
append(s[i])
i++
}
}
}
private fun itob(i: Int): Boolean = i != 0
private fun btoi(b: Boolean): Int = if (b) 1 else 0
private fun fetchVar(name: String): Int = globals.computeIfAbsent(name) { 0 }
@Throws(Exception::class)
fun interpret(n: Node?): Int = when (n?.nt) {
null -> 0
NodeType.Integer -> n.value!!.toInt()
NodeType.Ident -> fetchVar(n.value!!)
NodeType.String -> n.value!!.toInt()
NodeType.Assign -> {
globals[n.left!!.value!!] = interpret(n.right)
0
}
NodeType.Add -> interpret(n.left) + interpret(n.right)
NodeType.Sub -> interpret(n.left) - interpret(n.right)
NodeType.Mul -> interpret(n.left) * interpret(n.right)
NodeType.Div -> interpret(n.left) / interpret(n.right)
NodeType.Mod -> interpret(n.left) % interpret(n.right)
NodeType.Lss -> btoi(interpret(n.left) < interpret(n.right))
NodeType.Leq -> btoi(interpret(n.left) <= interpret(n.right))
NodeType.Gtr -> btoi(interpret(n.left) > interpret(n.right))
NodeType.Geq -> btoi(interpret(n.left) >= interpret(n.right))
NodeType.Eql -> btoi(interpret(n.left) == interpret(n.right))
NodeType.Neq -> btoi(interpret(n.left) != interpret(n.right))
NodeType.And -> btoi(itob(interpret(n.left)) && itob(interpret(n.right)))
NodeType.Or -> btoi(itob(interpret(n.left)) || itob(interpret(n.right)))
NodeType.Not -> if (interpret(n.left) == 0) 1 else 0
NodeType.Negate -> -interpret(n.left)
NodeType.If -> {
if (interpret(n.left) != 0) interpret(n.right!!.left) else interpret(n.right!!.right)
0
}
NodeType.While -> {
while (interpret(n.left) != 0) {
interpret(n.right)
}
0
}
NodeType.Prtc -> {
print(interpret(n.left).toChar().toString())
0
}
NodeType.Prti -> {
print(interpret(n.left).toString())
0
}
NodeType.Prts -> {
print(str(n.left!!.value!!)) //interpret(n.left));
0
}
NodeType.Sequence -> {
interpret(n.left)
interpret(n.right)
0
}
else -> throw Exception("Error: '${n.nt}' found, expecting operator")
}
private fun loadAst(): Node? {
while (s.hasNext()) {
val line = s.nextLine()
val (command, value) = if (line.length > 15) {
line.substring(0, 15).trim { it <= ' ' } to line.substring(15).trim { it <= ' ' }
} else {
line.trim { it <= ' ' } to null
}
if (command == ";") return null
if (value != null) return Node.makeLeaf(strToNodes.getValue(command), value)
return Node.makeNode(
nodeType = strToNodes[command] ?: throw Exception("Command not found: '$command'"),
left = loadAst(),
right = loadAst(),
)
}
return null // for the compiler, not needed
}
@JvmStatic
fun main(args: Array<String>) {
if (args.isNotEmpty()) {
try {
s = Scanner(File(args[0]))
val n = loadAst()
interpret(n)
} catch (e: Exception) {
println("Ex: " + e.message)
}
}
}
internal class Node(
val nt: NodeType,
var left: Node? = null,
var right: Node? = null,
var value: String? = null,
) {
companion object {
fun makeNode(nodeType: NodeType, left: Node?, right: Node?): Node = Node(nodeType, left, right, "")
fun makeLeaf(nodeType: NodeType, value: String?): Node = Node(nodeType, null, null, value)
}
}
internal enum class NodeType(val value: String) {
None(";"), Ident("Identifier"), String("String"), Integer("Integer"),
Sequence("Sequence"), If("If"),
Prtc("Prtc"), Prts("Prts"), Prti("Prti"), While("While"),
Assign("Assign"), Negate("Negate"), Not("Not"), Mul("Multiply"), Div("Divide"),
Mod("Mod"), Add("Add"),
Sub("Subtract"), Lss("Less"), Leq("LessEqual"),
Gtr("Greater"), Geq("GreaterEqual"), Eql("Equal"), Neq("NotEqual"), And("And"), Or("Or");
override fun toString() = value
}
}