216 lines
6.8 KiB
Scala
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
|
|
}
|
|
|
|
}
|