RosettaCodeData/Task/Compiler-virtual-machine-interpreter/Scala/compiler-virtual-machine-interpreter-1.scala
2023-07-01 13:44:08 -04:00

216 lines
6.8 KiB
Scala

package xyz.hyperreal.rosettacodeCompiler
import java.io.{BufferedReader, FileReader, Reader, StringReader}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
object VirtualMachine {
private object Opcodes {
val FETCH: Byte = 0
val STORE: Byte = 1
val PUSH: Byte = 2
val JMP: Byte = 3
val JZ: Byte = 4
val ADD: Byte = 5
val SUB: Byte = 6
val MUL: Byte = 7
val DIV: Byte = 8
val MOD: Byte = 9
val LT: Byte = 10
val GT: Byte = 11
val LE: Byte = 12
val GE: Byte = 13
val EQ: Byte = 14
val NE: Byte = 15
val AND: Byte = 16
val OR: Byte = 17
val NEG: Byte = 18
val NOT: Byte = 19
val PRTC: Byte = 20
val PRTI: Byte = 21
val PRTS: Byte = 22
val HALT: Byte = 23
}
import Opcodes._
private val HEADER_REGEX = "Datasize: ([0-9]+) Strings: ([0-9]+)" r
private val STRING_REGEX = "\"([^\"]*)\"" r
private val PUSH_REGEX = " *[0-9]+ push +([0-9]+|'(?:[^'\\n]|\\\\n|\\\\\\\\)')" r
private val PRTS_REGEX = " *[0-9]+ prts" r
private val PRTI_REGEX = " *[0-9]+ prti" r
private val PRTC_REGEX = " *[0-9]+ prtc" r
private val HALT_REGEX = " *[0-9]+ halt" r
private val STORE_REGEX = " *[0-9]+ store +\\[([0-9]+)\\]" r
private val FETCH_REGEX = " *[0-9]+ fetch +\\[([0-9]+)\\]" r
private val LT_REGEX = " *[0-9]+ lt" r
private val GT_REGEX = " *[0-9]+ gt" r
private val LE_REGEX = " *[0-9]+ le" r
private val GE_REGEX = " *[0-9]+ ge" r
private val NE_REGEX = " *[0-9]+ ne" r
private val EQ_REGEX = " *[0-9]+ eq" r
private val JZ_REGEX = " *[0-9]+ jz +\\((-?[0-9]+)\\) [0-9]+" r
private val ADD_REGEX = " *[0-9]+ add" r
private val SUB_REGEX = " *[0-9]+ sub" r
private val MUL_REGEX = " *[0-9]+ mul" r
private val DIV_REGEX = " *[0-9]+ div" r
private val MOD_REGEX = " *[0-9]+ mod" r
private val AND_REGEX = " *[0-9]+ and" r
private val OR_REGEX = " *[0-9]+ or" r
private val NOT_REGEX = " *[0-9]+ not" r
private val NEG_REGEX = " *[0-9]+ neg" r
private val JMP_REGEX = " *[0-9]+ jmp +\\((-?[0-9]+)\\) [0-9]+" r
def fromStdin = fromReader(Console.in)
def fromFile(file: String) = fromReader(new FileReader(file))
def fromString(src: String) = fromReader(new StringReader(src))
def fromReader(r: Reader) = {
val in = new BufferedReader(r)
val vm =
in.readLine match {
case HEADER_REGEX(datasize, stringsize) =>
val strings =
for (_ <- 1 to stringsize.toInt)
yield
in.readLine match {
case STRING_REGEX(s) => unescape(s)
case null => sys.error("expected string constant but encountered end of input")
case s => sys.error(s"expected string constant: $s")
}
var line: String = null
val code = new ArrayBuffer[Byte]
def addShort(a: Int) = {
code += (a >> 8).toByte
code += a.toByte
}
def addInstIntOperand(opcode: Byte, operand: Int) = {
code += opcode
addShort(operand >> 16)
addShort(operand)
}
def addInst(opcode: Byte, operand: String) = addInstIntOperand(opcode, operand.toInt)
while ({ line = in.readLine; line ne null }) line match {
case PUSH_REGEX(n) if n startsWith "'" =>
addInstIntOperand(PUSH, unescape(n.substring(1, n.length - 1)).head)
case PUSH_REGEX(n) => addInst(PUSH, n)
case PRTS_REGEX() => code += PRTS
case PRTI_REGEX() => code += PRTI
case PRTC_REGEX() => code += PRTC
case HALT_REGEX() => code += HALT
case STORE_REGEX(idx) => addInst(STORE, idx)
case FETCH_REGEX(idx) => addInst(FETCH, idx)
case LT_REGEX() => code += LT
case GT_REGEX() => code += GT
case LE_REGEX() => code += LE
case GE_REGEX() => code += GE
case NE_REGEX() => code += NE
case EQ_REGEX() => code += EQ
case JZ_REGEX(disp) => addInst(JZ, disp)
case ADD_REGEX() => code += ADD
case SUB_REGEX() => code += SUB
case MUL_REGEX() => code += MUL
case DIV_REGEX() => code += DIV
case MOD_REGEX() => code += MOD
case AND_REGEX() => code += AND
case OR_REGEX() => code += OR
case NOT_REGEX() => code += NOT
case NEG_REGEX() => code += NEG
case JMP_REGEX(disp) => addInst(JMP, disp)
}
new VirtualMachine(code, datasize.toInt, strings)
case _ => sys.error("expected header")
}
in.close
vm
}
}
class VirtualMachine(code: IndexedSeq[Byte], datasize: Int, strings: IndexedSeq[String]) {
import VirtualMachine.Opcodes._
var pc = 0
val stack = new mutable.ArrayStack[Int]
val data = new Array[Int](datasize)
var running = false
def getByte = {
val byte = code(pc) & 0xFF
pc += 1
byte
}
def getShort = getByte << 8 | getByte
def getInt = getShort << 16 | getShort
def pushBoolean(b: Boolean) = stack push (if (b) 1 else 0)
def popBoolean = if (stack.pop != 0) true else false
def operator(f: (Int, Int) => Int) = {
val y = stack.pop
stack.push(f(stack.pop, y))
}
def relation(r: (Int, Int) => Boolean) = {
val y = stack.pop
pushBoolean(r(stack.pop, y))
}
def connective(c: (Boolean, Boolean) => Boolean) = pushBoolean(c(popBoolean, popBoolean))
def execute: Unit =
getByte match {
case FETCH => stack push data(getInt)
case STORE => data(getInt) = stack.pop
case PUSH => stack push getInt
case JMP => pc = pc + getInt
case JZ => if (stack.pop == 0) pc = pc + getInt else pc += 4
case ADD => operator(_ + _)
case SUB => operator(_ - _)
case MUL => operator(_ * _)
case DIV => operator(_ / _)
case MOD => operator(_ % _)
case LT => relation(_ < _)
case GT => relation(_ > _)
case LE => relation(_ <= _)
case GE => relation(_ >= _)
case EQ => relation(_ == _)
case NE => relation(_ != _)
case AND => connective(_ && _)
case OR => connective(_ || _)
case NEG => stack push -stack.pop
case NOT => pushBoolean(!popBoolean)
case PRTC => print(stack.pop.toChar)
case PRTI => print(stack.pop)
case PRTS => print(strings(stack.pop))
case HALT => running = false
}
def run = {
pc = 0
stack.clear
running = true
for (i <- data.indices) data(i) = 0
while (running) execute
}
}