mung/virtualmachine.py
cecilkorik dcaeb7145f refactored massive language file, separating into language_types and bytecode
tests for parser and virtualmachine now work!
Running test.moo is now able to successfully call built-in functions

--HG--
branch : mung
2015-09-23 14:38:03 -06:00

214 lines
No EOL
5.1 KiB
Python
Executable file

import random, heapq
from parse import Parser
from language import *
class VMContext(object):
def __init__(self, vars):
self.variables = vars
class VMTask(object):
def __init__(self, id, code, vars):
self.task_id = id
self.code_stack = [x for x in reversed(code)]
self.exc_stack = []
self.stack = []
self.contexts = [VMContext(vars)]
def context(self):
return self.contexts[-1]
class VirtualMachine(object):
def __init__(self, db):
self.db = db
self.active_task_id = None
self.sleepytime = None
self.contexts = []
self.tasks = {}
self.task = None
self.active_task_heap = []
self.next_task_heap = []
self.ticks_used = 0
self.max_ticks = 300000
self.max_tasks = 2**16
def generate_task_id(self):
rv = random.randint(1,self.max_tasks)
if len(self.tasks) >= self.max_tasks:
raise RangeError, "Maximum number of tasks exceeded"
while rv in self.tasks:
rv += 1
if rv > self.max_tasks:
rv = 1
return rv
def spawn_cmd_task(self, bytecode, vars):
task = VMTask(self.generate_task_id(), bytecode, vars)
self.tasks[task.task_id] = task
heapq.heappush(self.active_task_heap, (time.time(), task.task_id, task))
def spawn_forked_task(self, bytecode, vars):
task = VMTask(self.generate_task_id(), bytecode, vars)
self.tasks[task.task_id] = task
heapq.heappush(self.next_task_heap, (time.time(), task.task_id, task))
def code_pop(self):
return self.task.code_stack.pop()
def code_push(self, code):
if isinstance(code, list):
for op in reversed(code):
self.task.code_stack.append(op)
else:
self.task.code_stack.append(code)
def exc_pop(self):
return self.task.exc_stack.pop()
def exc_push(self, exc):
self.task.exc_stack.append(exc)
def pop(self, count=1):
stack = [uncoerce(self.task.stack.pop()) for x in xrange(count)]
return [x for x in reversed(stack)]
def pop_raw(self, count=1):
stack = [self.task.stack.pop() for x in xrange(count)]
return [x for x in reversed(stack)]
def push(self, value):
self.task.stack.append(coerce(value))
def setvar(self, varname, val):
self.task.context().variables[varname] = val
def getvar(self, varname):
return self.task.context().variables[varname]
def push_context(self):
self.task.contexts.append(VMContext())
def pop_context(self):
self.task.contexts.pop()
if not self.task.contexts:
del self.tasks[self.active_task_id]
self.active_task_id = None
self.task = None
self.finished_start_next()
def suspend_start_next(self, delay):
now = time.time()
newtask = heapq.heappop(self.active_task_heap)
heapq.heappush(self.next_task_heap, (now+max(0.0,delay), self.active_task_id, self.task))
return self.activate_task(newtask)
def finished_start_next(self):
if self.active_task_heap:
now = time.time()
newtask = heapq.heappop(self.active_task_heap)
return self.activate_task(newtask)
elif self.next_task_heap:
now = time.time()
newtask = heapq.heappop(self.next_task_heap)
return self.activate_task(newtask)
def activate_task(self, task):
now = time.time()
if now < task[0]:
"task isn't ready to execute yet"
self.sleepytime = task[0] - now
heapq.heappush(self.next_task_heap, task)
return False
self.active_task_id = task[1]
self.task = task[2]
self.ticks_used = 0
def uncaught_exception(self):
for exc in self.task.exc_stack:
print "Unhandled exception: %s" % (exc,)
def run_active_task(self):
task_id = self.active_task_id
while task_id == self.active_task_id and self.task.code_stack:
nextop = self.code_pop()
self.execute(nextop)
if self.task.exc_stack:
self.uncaught_exception()
self.finished_start_next()
if task_id == self.active_task_id:
self.finished_start_next()
def run(self):
self.sleepytime = None
if self.active_task_id == None:
self.finished_start_next()
if self.active_task_id == None:
"vm is idle"
return
self.run_active_task()
def jump(self, target):
while self.task.code_stack:
nextop = self.code_pop()
if isinstance(nextop, EndBlock):
if nextop.label == target:
self.execute(nextop)
break
def jump_code(self, target):
while self.code_stack:
nextop = self.code_pop()
if type(nextop) == target:
if isinstance(nextop, target):
self.execute(nextop)
break
if isinstance(nextop, EndBlock):
if nextop.label == target:
break
def raise_exc(self, exc=None):
if exc != None:
self.exc_push(exc)
while self.code_stack:
nextop = self.code_pop()
if isinstance(nextop, ExcHandlerBlock):
break
elif isinstance(nextop, FinallyBlock):
break
self.execute(nextop)
def execute(self, op):
self.ticks_used += op.ticks()
if self.ticks_used > self.max_ticks:
"ran out of ticks"
self.exc_push("out of ticks exception")
self.uncaught_exception()
self.finished_start_next()
else:
print "executing %s with stack %s" % (op, self.task.stack)
#op.load_stack(self)
op.execute(self)
def test(self):
import parse
testcode = parse.static_parser.test()
self.spawn_forked_task(testcode, {})
self.run()
static_vm = VirtualMachine(None)
if __name__ == "__main__":
static_vm.test()