Skip to content

Instantly share code, notes, and snippets.

@prepor
Created December 11, 2017 10:00
Show Gist options
  • Save prepor/3d814d88f52188de4cef20ac50c1124b to your computer and use it in GitHub Desktop.
Save prepor/3d814d88f52188de4cef20ac50c1124b to your computer and use it in GitHub Desktop.
package com.d360.oam
class Inter(val dependencies: Code, val code: Code, val prims: Prims) {
constructor(code: Code, prims: Prims) : this(listOf(), code, prims)
private var instance: Instance = Instance(0, mutableListOf())
private var values = mutableListOf<Value>()
private var coeffects = mutableListOf<Coeffect>()
private var queue = mutableListOf<Token>()
sealed class Realized {
data class Values(val vals: List<Value>) : Realized()
data class Pend(val pending: Pending) : Realized()
object Stopped : Realized()
}
private fun getCode(pc: Int) : CodeFun {
return if (pc < 0) {
code[Math.abs(pc) - 1]
} else {
dependencies[pc]
}
}
private tailrec fun tick(pc: Pc, stack: Stack, env: Env) {
val op = getCode(pc.pc).code[pc.epc]
fun realized(args: List<Int>): Realized {
val values = mutableListOf<Value>()
for (arg in args) {
val argV = env[arg]
when (argV) {
is EnvPending -> {
val v = argV.pend
v.value.let {
when (it) {
is Pend -> {
return Realized.Pend(argV.pend)
}
is PendStopped -> {
return Realized.Stopped
}
is PendVal -> values.add(it.v)
}
}
}
is EnvVal -> {
values.add(argV.v)
}
}
}
return Realized.Values(values)
}
when (op) {
is OpConst -> {
publish(stack, env, VConst(op.const))
halt(stack, env)
}
is OpLabel -> {
publish(stack, env, VLabel(op.pc))
halt(stack, env)
}
is OpStop -> halt(stack, env)
is OpParallel -> {
incrementInstances(stack)
tick(Pc(pc.pc, op.left), stack, env.toMutableList())
tick(Pc(pc.pc, op.right), stack, env)
}
is OpOtherwise -> {
val frame = FOtherwise(false, 1, Pc(pc.pc, op.right))
tick(Pc(pc.pc, op.left), Cons(frame, stack), env)
}
is OpPruning -> {
val pending = Pending(Pend, mutableListOf())
val frame = FPruning(1, pending)
op.index?.let { env.set(it, EnvPending(pending)) }
tick(Pc(pc.pc, op.right), Cons(frame, stack), env)
tick(Pc(pc.pc, op.left), stack, env)
}
is OpSequential -> {
val frame = FSequential(op.index, Pc(pc.pc, op.right))
tick(Pc(pc.pc, op.left), Cons(frame, stack), env)
}
is OpClosure -> {
publish(stack, env, VClosure(op.pc, op.toCopy, env))
halt(stack, env)
}
is OpTailCall -> {
val t = op.target as? TFun ?: throw RuntimeException()
val f = getCode(t.pc)
var i = 0
for (arg in op.args) {
env[i] = env[arg]
i++
}
queue.add(Token(Pc(t.pc, f.code.lastIndex), env, stack))
}
is OpCall -> {
val t = op.target
when (t) {
is TPrim -> {
val impl = prims[t.impl]
val args = realized(op.args)
// System.err.println("----CALL PRIM ${op.target} ${args}")
when (args) {
is Realized.Values -> {
val res = impl(args.vals)
when (res) {
is PrimVal -> {
publish(stack, env, res.v)
halt(stack, env)
}
is PrimHalt -> halt(stack, env)
is PrimUnsupported -> throw UnsupportedOperationException()
}
}
is Realized.Stopped -> halt(stack, env)
is Realized.Pend ->
args.pending.waiters.add(Token(pc, env, stack))
}
}
is TFun -> {
val f = getCode(t.pc)
val newEnv = MutableList(f.envSize) { index ->
if (index < op.args.size) {
env[op.args[index]]
} else {
EnvVal(VConst(ConstNull))
}
}
val frame = FCall(env)
tick(Pc(t.pc, f.code.lastIndex), Cons(frame, stack), newEnv)
}
is TDynamic -> {
val realized = realized(listOf(t.index))
when (realized) {
is Realized.Values -> {
val v = realized.vals[0]
when (v) {
is VClosure -> {
val f = getCode(v.pc)
val newEnv = MutableList(f.envSize) { index ->
if (index < v.toCopy) {
v.env[index]
} else if (index < op.args.size + v.toCopy) {
env[op.args[index - v.toCopy]]
} else {
EnvVal(VConst(ConstNull))
}
}
val frame = FCall(env)
tick(Pc(v.pc, f.code.lastIndex), Cons(frame, stack), newEnv)
}
is VLabel -> {
val f = getCode(v.pc)
val newEnv = MutableList(f.envSize) { index ->
if (index < op.args.size) {
env[op.args[index]]
} else {
EnvVal(VConst(ConstNull))
}
}
val frame = FCall(env)
tick(Pc(v.pc, f.code.lastIndex), Cons(frame, stack), newEnv)
}
is VTuple -> {
val index = realized(listOf(op.args[0]))
when (index) {
is Realized.Values -> {
val indexV = index.vals[0]
// System.err.println("---TUPLE ACCESS ${v} ${indexV}")
when (indexV) {
is VConst -> {
if (indexV.const is ConstInt) {
publish(stack, env, v.v[indexV.const.v])
halt(stack, env)
} else {
throw RuntimeException()
}
}
else -> throw RuntimeException()
}
}
is Realized.Stopped -> halt(stack, env)
is Realized.Pend ->
index.pending.waiters.add(Token(pc, env, stack))
}
}
}
}
is Realized.Stopped -> halt(stack, env)
is Realized.Pend ->
realized.pending.waiters.add(Token(pc, env, stack))
}
}
}
}
is OpCoeffect -> {
val realized = realized(listOf(op.index))
when (realized) {
is Realized.Values -> {
val v = realized.vals[0]
val id = instance.currentCoeffect
instance.currentCoeffect += 1
instance.blocks.add(Block(id, Token(pc, env.toMutableList(), stack)))
coeffects.add(Coeffect(id, v))
}
is Realized.Stopped -> halt(stack, env)
is Realized.Pend ->
realized.pending.waiters.add(Token(pc, env, stack))
}
}
}
}
private tailrec fun publish(stack: Stack, env: Env, v: Value) {
val s = stack.value
when (s) {
is FResult -> values.add(v)
is FSequential -> {
incrementInstances(stack)
s.index?.let { env.set(it, EnvVal(v)) }
tick(s.right, stack.next!!, env)
}
is FOtherwise -> {
s.first_value = true
publish(stack.next!!, env, v)
}
is FPruning -> {
if (s.pending.value is PendVal) {
return
}
s.pending.value = PendVal(v)
for (w in s.pending.waiters) {
tick(w.pc, w.stack, w.env)
}
}
is FCall -> {
publish(stack.next!!, s.env, v)
}
}
}
private fun halt(stack: Stack, env: Env) {
val s = stack.value
when (s) {
is FResult -> {
}
is FOtherwise -> {
if (!s.first_value && s.instances == 1) {
tick(s.otherwise, stack.next!!, env)
} else if (s.instances == 1) {
halt(stack.next!!, env)
} else {
s.instances -= 1
}
}
is FPruning -> {
if (s.pending.value is Pend && s.instances == 1) {
s.pending.value = PendStopped
} else {
s.instances -= 1
}
}
is FSequential, is FCall -> {
halt(stack.next!!, env)
}
}
}
private fun incrementInstances(stack: Stack) {
loop@ for (s in stack) {
when (s) {
is FOtherwise -> {
s.instances += 1; break@loop
}
is FPruning -> {
s.instances += 1; break@loop
}
}
}
}
fun isAlive(stack: Stack) : Boolean {
for (s in stack) {
if (s is FPruning && !(s.pending.value is Pend)) {
return false
}
}
return true
}
fun checkKilled(justUnblocked: Int) : List<Int> {
val killed = mutableListOf<Int>()
val newBlocks = mutableListOf<Block>()
for (block in instance.blocks) {
when {
block.id == justUnblocked -> {}
!isAlive(block.token.stack) -> killed.add(block.id)
else -> newBlocks.add(block)
}
}
instance.blocks.clear()
instance.blocks.addAll(newBlocks)
return killed
}
fun runLoop() {
while (queue.isNotEmpty()) {
val token = queue.removeAt(queue.size - 1)
tick(token.pc, token.stack, token.env)
}
}
fun run(): Snapshot {
val l = code.last()
val pc = Pc(-code.lastIndex - 1, l.code.lastIndex)
val stack = Cons(FResult, null)
val env: MutableList<EnvValue> = MutableList(l.envSize, { EnvVal(VConst(ConstNull)) })
values = mutableListOf<Value>()
coeffects = mutableListOf<Coeffect>()
queue.add(Token(pc, env, stack))
runLoop()
return Snapshot(values, coeffects, listOf(), instance)
}
fun unblock(instance: Instance, id: Int, value: Value): Snapshot {
this.instance = instance
values = mutableListOf<Value>()
coeffects = mutableListOf<Coeffect>()
val block = instance.blocks.find { it.id == id }
if (block == null) {
throw RuntimeException("Unknown coeffect")
} else {
publish(block.token.stack, block.token.env, value)
runLoop()
val killed = checkKilled(id)
return Snapshot(values, coeffects, killed, instance)
}
}
fun unblock(id: Int, value: Value): Snapshot {
return unblock(instance, id, value)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment