267 lines
7.5 KiB
Text
267 lines
7.5 KiB
Text
import "./dynamic" for Enum
|
|
import "./crypto" for Bytes
|
|
import "./fmt" for Conv
|
|
import "./ioutil" for FileUtil
|
|
|
|
var codes = [
|
|
"fetch",
|
|
"store",
|
|
"push",
|
|
"add",
|
|
"sub",
|
|
"mul",
|
|
"div",
|
|
"mod",
|
|
"lt",
|
|
"gt",
|
|
"le",
|
|
"ge",
|
|
"eq",
|
|
"ne",
|
|
"and",
|
|
"or",
|
|
"neg",
|
|
"not",
|
|
"jmp",
|
|
"jz",
|
|
"prtc",
|
|
"prts",
|
|
"prti",
|
|
"halt"
|
|
]
|
|
|
|
var Code = Enum.create("Code", codes)
|
|
|
|
var codeMap = {
|
|
"fetch": Code.fetch,
|
|
"store": Code.store,
|
|
"push": Code.push,
|
|
"add": Code.add,
|
|
"sub": Code.sub,
|
|
"mul": Code.mul,
|
|
"div": Code.div,
|
|
"mod": Code.mod,
|
|
"lt": Code.lt,
|
|
"gt": Code.gt,
|
|
"le": Code.le,
|
|
"ge": Code.ge,
|
|
"eq": Code.eq,
|
|
"ne": Code.ne,
|
|
"and": Code.and,
|
|
"or": Code.or,
|
|
"neg": Code.neg,
|
|
"not": Code.not,
|
|
"jmp": Code.jmp,
|
|
"jz": Code.jz,
|
|
"prtc": Code.prtc,
|
|
"prts": Code.prts,
|
|
"prti": Code.prti,
|
|
"halt": Code.halt
|
|
}
|
|
|
|
var object = []
|
|
var stringPool = []
|
|
|
|
var reportError = Fn.new { |msg| Fiber.abort("error : %(msg)") }
|
|
|
|
var emitByte = Fn.new { |c| object.add(c) }
|
|
|
|
var emitWord = Fn.new { |n|
|
|
var bs = Bytes.fromIntLE(n)
|
|
for (b in bs) emitByte.call(b)
|
|
}
|
|
|
|
// Converts the 4 bytes starting at object[pc] to an unsigned 32 bit integer
|
|
// and thence to a signed 32 bit integer
|
|
var toInt32LE = Fn.new { |pc|
|
|
var x = Bytes.toIntLE(object[pc...pc+4])
|
|
if (x >= 2.pow(31)) x = x - 2.pow(32)
|
|
return x
|
|
}
|
|
|
|
/* Virtual Machine interpreter */
|
|
|
|
var runVM = Fn.new { |dataSize|
|
|
var stack = List.filled(dataSize + 1, 0)
|
|
var pc = 0
|
|
while (true) {
|
|
var op = object[pc]
|
|
pc = pc + 1
|
|
if (op == Code.fetch) {
|
|
var x = toInt32LE.call(pc)
|
|
stack.add(stack[x])
|
|
pc = pc + 4
|
|
} else if (op == Code.store) {
|
|
var x = toInt32LE.call(pc)
|
|
var ln = stack.count
|
|
stack[x] = stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
pc = pc + 4
|
|
} else if (op == Code.push) {
|
|
var x = toInt32LE.call(pc)
|
|
stack.add(x)
|
|
pc = pc + 4
|
|
} else if (op == Code.add) {
|
|
var ln = stack.count
|
|
stack[ln-2] = stack[ln-2] + stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.sub) {
|
|
var ln = stack.count
|
|
stack[ln-2] = stack[ln-2] - stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.mul) {
|
|
var ln = stack.count
|
|
stack[ln-2] = stack[ln-2] * stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.div) {
|
|
var ln = stack.count
|
|
stack[ln-2] = (stack[ln-2] / stack[ln-1]).truncate
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.mod) {
|
|
var ln = stack.count
|
|
stack[ln-2] = stack[ln-2] % stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.lt) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] < stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.gt) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] > stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.le) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] <= stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.ge) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] >= stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.eq) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] == stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.ne) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(stack[ln-2] != stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.and) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(Conv.itob(stack[ln-2]) && Conv.itob(stack[ln-1]))
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.or) {
|
|
var ln = stack.count
|
|
stack[ln-2] = Conv.btoi(Conv.itob(stack[ln-2]) || Conv.itob(stack[ln-1]))
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.neg) {
|
|
var ln = stack.count
|
|
stack[ln-1] = -stack[ln-1]
|
|
} else if (op == Code.not) {
|
|
var ln = stack.count
|
|
stack[ln-1] = Conv.btoi(!Conv.itob(stack[ln-1]))
|
|
} else if (op == Code.jmp) {
|
|
var x = toInt32LE.call(pc)
|
|
pc = pc + x
|
|
} else if (op == Code.jz) {
|
|
var ln = stack.count
|
|
var v = stack[ln-1]
|
|
stack = stack[0...ln-1]
|
|
if (v != 0) {
|
|
pc = pc + 4
|
|
} else {
|
|
var x = toInt32LE.call(pc)
|
|
pc = pc + x
|
|
}
|
|
} else if (op == Code.prtc) {
|
|
var ln = stack.count
|
|
System.write(String.fromByte(stack[ln-1]))
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.prts) {
|
|
var ln = stack.count
|
|
System.write(stringPool[stack[ln-1]])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.prti) {
|
|
var ln = stack.count
|
|
System.write(stack[ln-1])
|
|
stack = stack[0...ln-1]
|
|
} else if (op == Code.halt) {
|
|
return
|
|
} else {
|
|
reportError.call("Unknown opcode %(op)")
|
|
}
|
|
}
|
|
}
|
|
|
|
var translate = Fn.new { |s|
|
|
var d = ""
|
|
var i = 0
|
|
while (i < s.count) {
|
|
if (s[i] == "\\" && (i+1) < s.count) {
|
|
if (s[i+1] == "n") {
|
|
d = d + "\n"
|
|
i = i + 1
|
|
} else if (s[i+1] == "\\") {
|
|
d = d + "\\"
|
|
i = i + 1
|
|
}
|
|
} else {
|
|
d = d + s[i]
|
|
}
|
|
i = i + 1
|
|
}
|
|
return d
|
|
}
|
|
|
|
var lines = []
|
|
var lineCount = 0
|
|
var lineNum = 0
|
|
|
|
var loadCode = Fn.new {
|
|
var dataSize
|
|
var firstLine = true
|
|
while (lineNum < lineCount) {
|
|
var line = lines[lineNum].trimEnd(" \t")
|
|
lineNum = lineNum + 1
|
|
if (line.count == 0) {
|
|
if (firstLine) {
|
|
reportError.call("empty line")
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
var lineList = line.split(" ").where { |s| s != "" }.toList
|
|
if (firstLine) {
|
|
dataSize = Num.fromString(lineList[1])
|
|
var nStrings = Num.fromString(lineList[3])
|
|
for (i in 0...nStrings) {
|
|
var s = lines[lineNum].trim("\"\n")
|
|
lineNum = lineNum + 1
|
|
stringPool.add(translate.call(s))
|
|
}
|
|
firstLine = false
|
|
continue
|
|
}
|
|
var offset = Num.fromString(lineList[0])
|
|
var instr = lineList[1]
|
|
var opCode = codeMap[instr]
|
|
if (!opCode) {
|
|
reportError.call("Unknown instruction %(instr) at %(opCode)")
|
|
}
|
|
emitByte.call(opCode)
|
|
if (opCode == Code.jmp || opCode == Code.jz) {
|
|
var p = Num.fromString(lineList[3])
|
|
emitWord.call(p - offset - 1)
|
|
} else if (opCode == Code.push) {
|
|
var value = Num.fromString(lineList[2])
|
|
emitWord.call(value)
|
|
} else if (opCode == Code.fetch || opCode == Code.store) {
|
|
var value = Num.fromString(lineList[2].trim("[]"))
|
|
emitWord.call(value)
|
|
}
|
|
}
|
|
return dataSize
|
|
}
|
|
|
|
lines = FileUtil.readLines("codegen.txt")
|
|
lineCount = lines.count
|
|
runVM.call(loadCode.call())
|