added optimizer and got language/VM mostly working

--HG--
branch : mung
This commit is contained in:
cecilkorik 2011-06-07 19:24:31 +00:00
parent fe0ea2a7f9
commit bae69502bd
13 changed files with 708 additions and 99 deletions

View file

@ -1,104 +1,113 @@
from builtins_code import bi
builtin_map = {} builtin_map = {}
# decorator for builtin function with "n" args
def bi(func, n):
def builtin_varg(vm):
args = vm.pop(n)
rv = func(args)
vm.push(rv)
return builtin_varg
""" """
# manage properties # manage properties
builtin_map.update({ builtin_map.update({
'properties': bi_properties, 'properties': bi.properties,
'add_property': bi_add_property, 'add_property': bi.add_property,
'delete_property': bi_delete_property, 'delete_property': bi.delete_property,
'clear_property': bi_clear_property, 'clear_property': bi.clear_property,
'set_property_opts': bi_set_property_opts, 'set_property_opts': bi.set_property_opts,
'get_property_opts': bi_get_property_opts, 'get_property_opts': bi.get_property_opts,
'set_property_owner': bi_set_property_owner, 'set_property_owner': bi.set_property_owner,
'get_property_owner': bi_get_property_owner, 'get_property_owner': bi.get_property_owner,
}) })
# manage files # manage files
builtin_map.update({ builtin_map.update({
'files': bi_files, 'files': bi.files,
'add_file': bi_add_file, 'add_file': bi.add_file,
'delete_file': bi_delete_file, 'delete_file': bi.delete_file,
'set_file_opts': bi_set_file_opts, 'set_file_opts': bi.set_file_opts,
'get_file_opts': bi_get_file_opts, 'get_file_opts': bi.get_file_opts,
'set_file_owner': bi_set_file_owner, 'set_file_owner': bi.set_file_owner,
'get_file_owner': bi_get_file_owner, 'get_file_owner': bi.get_file_owner,
}) })
# manage functions # manage functions
builtin_map.update({ builtin_map.update({
'functions': bi_functions, 'functions': bi.functions,
'add_function': bi_add_function, 'add_function': bi.add_function,
'delete_function': bi_delete_function, 'delete_function': bi.delete_function,
'set_function_code': bi_set_function_code, 'set_function_code': bi.set_function_code,
'get_function_code': bi_get_function_code, 'get_function_code': bi.get_function_code,
'set_function_opts': bi_set_function_opts, 'set_function_opts': bi.set_function_opts,
'get_function_opts': bi_get_function_opts, 'get_function_opts': bi.get_function_opts,
'set_function_args': bi_set_function_args, 'set_function_args': bi.set_function_args,
'get_function_args': bi_get_function_args, 'get_function_args': bi.get_function_args,
'set_function_owner': bi_set_function_owner, 'set_function_owner': bi.set_function_owner,
'get_function_owner': bi_get_function_owner, 'get_function_owner': bi.get_function_owner,
}) })
# server configuration # server configuration
builtin_map.update({ builtin_map.update({
'set_server_var': bi_set_server_var, 'set_server_var': bi.set_server_var,
'get_server_var': bi_get_server_var, 'get_server_var': bi.get_server_var,
'save_database': bi_save_database, 'save_database': bi.save_database,
'shutdown': bi_shutdown, 'shutdown': bi.shutdown,
'server_info': bi_server_info, 'server_info': bi.server_info,
'time': bi_time, 'time': bi.time,
'format_time': bi_format_time, 'format_time': bi.format_time,
}) })
# manage running tasks # manage running tasks
builtin_map.update({ builtin_map.update({
'set_perms': bi_set_perms, 'set_perms': bi.set_perms,
'get_perms': bi_get_perms, 'get_perms': bi.get_perms,
'task_id': bi_task_id, 'task_id': bi.task_id,
'kill_task': bi_kill_task, 'kill_task': bi.kill_task,
'eval': bi_eval, 'eval': bi.eval,
'fork': bi_fork, 'fork': bi.fork,
'sleep': bi_sleep, 'sleep': bi.sleep,
'resume': bi_resume, 'resume': bi.resume,
'raise': bi_raise, 'raise': bi.raise,
'stack': bi_stack, 'stack': bi.stack,
}) })
# manage objects # manage objects
builtin_map.update({ builtin_map.update({
'create': bi_create, 'create': bi.create,
'destroy': bi_destroy, 'destroy': bi.destroy,
'set_parent': bi_set_parent, 'set_parent': bi.set_parent,
'parent': bi_parent, 'parent': bi.parent,
'children': bi_children, 'children': bi.children,
'set_owner': bi_set_owner, 'set_owner': bi.set_owner,
'owner': bi_owner, 'owner': bi.owner,
'max_object': bi_max_object, 'max_object': bi.max_object,
'renumber': bi_renumber, 'renumber': bi.renumber,
}) })
# manage connections and ports # manage connections and ports
builtin_map.update({ builtin_map.update({
'connect': bi_connect, 'connect': bi.connect,
'disconnect': bi_disconnect, 'disconnect': bi.disconnect,
'open_connection': bi_open_connection, 'open_connection': bi.open_connection,
'close_connection': bi_close_connection, 'close_connection': bi.close_connection,
'incoming_connections': bi_incoming_connections, 'incoming_connections': bi.incoming_connections,
'outgoing_connections': bi_outgoing_connections, 'outgoing_connections': bi.outgoing_connections,
'get_connection_info': bi_get_connection_info, 'get_connection_info': bi.get_connection_info,
'set_connection_opts': bi_set_connection_opts, 'set_connection_opts': bi.set_connection_opts,
'send': bi_send, 'send': bi.send,
'recv': bi_recv, 'recv': bi.recv,
'listen': bi_listen, 'listen': bi.listen,
'unlisten': bi_unlisten, 'unlisten': bi.unlisten,
'setuid': bi_setuid, 'setuid': bi.setuid,
})
"""
builtin_map.update({
'serverlog': bi.serverlog,
})
"""
# builtin functions for list types
listbi_map = {}
listbi_map.update({
'length': bi.list_len,
'len': bi.list_len,
'sort': bi.list_sort,
}) })
""" """

10
builtins_code.py Executable file
View file

@ -0,0 +1,10 @@
class builtin_functions(object):
def __init__(self):
pass
@staticmethod
def serverlog(vm, args):
print "serverlog: %s" % (args,)
bi = builtin_functions()

192
database.py Executable file
View file

@ -0,0 +1,192 @@
from parse import static_parser
class ObjRef(object):
def __init__(self, objnum):
self.objnum = objnum
def __eq__(self, other):
return self.objnum == other.objnum
class DBObject(object):
def __init__(self, objref):
self.obj = objref
self.parent = ObjRef(-1)
self.children = []
self.props = {}
self.funcs = []
def match_local_func(self, name, flags=""):
for func in self.funcs:
skip = False
for f in flags:
if not f in o.flags:
skip = True
if not skip and func.match(name):
return func
return None
def get_property(self, prop):
if prop in self.props:
rv = self.props[prop]
else:
return None
def add_func(self, name):
self.funcs.append(DBFunc(self.obj, name))
class DBFunc(object):
def __init__(self, objref, name):
self.obj = objref
self.name = name
self.names = name.split(' ')
self.flags = ""
self.bytecode = []
self.code = ""
def compile(self, code):
try:
bytecode = static_parser.parse(code)
except ParseError:
return [0, "Compile error in line <unknown>"]
self.bytecode = bytecode
self.code = code
return [1, "Success"]
def match(self, name):
for test in self.names:
if test[-1] == '*':
if name[:len(test)-1] == test[:-1]:
"partial match"
return True
elif test == name:
return True
return False
def ref(self):
return self.obj
class Database(object):
def __init__(self, dbname):
self.dbname = dbname
self.objects = []
def load(self):
raise NotImplementedError, "This function should be overridden by a derived class"
def checkpoint(self):
raise NotImplementedError, "This function should be overridden by a derived class"
def save(self):
raise NotImplementedError, "This function should be overridden by a derived class"
def bootstrap_minimal(self):
so = DBObject(ObjRef(0))
self.objects = [so]
so.
def set_property(self, obj, prop, val):
o = self.get(obj)
o.set_property(prop, val)
def set_file(self, obj, fn, val):
o = self.get(obj)
o.set_file(fn, val)
def get_property(self, obj, prop):
o = self.get(obj)
return o.get_property(prop)
def get_file(self, obj, fn):
o = self.get(obj)
return o.get_file(fn)
def create(self):
for i in xrange(len(self.objects)):
if self.objects[i] == None:
newref = ObjRef(i)
self.objects[i] = DBObject(newref)
return newref
newref = ObjRef(len(self.objects))
self.objects.append(DBObject(newref))
return newref
def destroy(self, ref):
if ref.objnum >= len(self.objects) or ref.objnum < 0:
raise ValueError, "Invalid object number"
obj = self.objects[ref.objnum]
if obj == None:
raise ValueError, "Object already destroyed"
for child in obj.children:
child.parent = ObjRef(-1)
self.objects[ref.objnum] = None
self.objects_cleanup()
def objects_cleanup(self):
i = len(self.objects)
while i > 0:
i -= 1
if self.objects[i] != None:
i += 1
break
if i < len(self.objects):
self.objects[i:] = []
def get(self, objref):
return self.objects[objref.objnum]
def match_command(self, player, cmd, vars):
vars['player'] = player
vars['caller'] = player
match_order = [player]
for ref in match_order:
match = self.match_func(self, ref, cmd)
if match:
obj, func = match
vars['this'] = ref
vars['funcname'] = func.name
vars['funcobj'] = obj
return [func, vars]
return None
def match_func(self, obj, funcname, flags=""):
o = self.get(obj)
func = o.match_func(funcname, flags)
if func != None:
return [obj, func]
ancestor = o.parent
while ancestor != ObjRef(-1):
o = self.get(ancestor)
func = o.match_func(funcname, flags)
if func != None:
return [ancestor, func]
ancestor = o.parent
return None
def match_property(self, obj, propname):
o = self.get(obj)
prop = o.get_property(propname)
if prop != None:
return [obj, prop]
ancestor = o.parent
while ancestor != ObjRef(-1):
o = self.get(ancestor)
prop = o.get_property(propname)
if prop != None:
return [ancestor, prop]
ancestor = o.parent
return None

View file

@ -1,6 +1,6 @@
class DSDB_Database(object): class DSDB_Database(Database):
def __init__(self, dbname): def __init__(self, dbname):
self.dbname = dbname self.dbname = dbname
self.fn = "%s.dsdb" % (dbname,) self.fn = "%s.dsdb" % (dbname,)

View file

@ -3,14 +3,5 @@ class Fake_Database(object):
def __init__(self, dbname): def __init__(self, dbname):
pass pass
def set_property(self, obj, prop, val):
pass
def set_file(self, obj, fn, val):
pass
def get_property(self, obj, prop):
pass
def get_file(self, obj, fn):
pass

View file

@ -1,6 +1,7 @@
import sys import sys
import time import time
from builtins import builtin_map from builtins import builtin_map
from database import ObjRef
import bisect import bisect
import optimizer import optimizer
import traceback import traceback
@ -65,6 +66,26 @@ def tokenparser(func):
return newfunc return newfunc
def disallow_keywords(tokens,keywords=None):
if keywords == None:
keywords = disallow_keywords.keywords
else:
keywords = set(keywords)
for t in tokens:
if isinstance(t, VMIdent):
if t.name in keywords:
raise ParseException, "Restricted keyword: %s" % (t.name,)
elif isinstance(t, unicode):
tstr = t.encode('ascii', 'ignore')
if tstr in keywords:
raise ParseException, "Restricted keyword: %s" % (tstr,)
elif isinstance(t, str):
if t in keywords:
raise ParseException, "Restricted keyword: %s" % (t,)
disallow_keywords.keywords = set('if,elseif,else,endif,try,except,finally,endtry,while,endwhile,continue,break,for,foreach,endfor'.split(','))
def codejoin(*args): def codejoin(*args):
rv = [] rv = []
for arg in args: for arg in args:
@ -103,10 +124,6 @@ def uncoerce(value):
return value.value return value.value
class ObjRef(object):
def __init__(self, objnum):
self.objnum = objnum
class VMBaseObject(object): class VMBaseObject(object):
def __init__(self): def __init__(self):
self.pos = None self.pos = None
@ -161,7 +178,7 @@ class VMTable(VMType):
class VMList(VMType): class VMList(VMType):
def __init__(self, value): def __init__(self, value):
VMType.__init__(self) VMType.__init__(self)
self.value = dict(value) self.value = list(value)
@staticmethod @staticmethod
@tokenparser @tokenparser
@ -222,11 +239,12 @@ class VMIdent(VMRef):
self.name = name self.name = name
def bytecode(self): def bytecode(self):
return [StackLiteral(self.name)] return [StackLiteral(unicode(self.name))]
@staticmethod @staticmethod
@tokenparser @tokenparser
def parse(tokens): def parse(tokens):
disallow_keywords(tokens)
return VMIdent(tokens[0]) return VMIdent(tokens[0])
def __repr__(self): def __repr__(self):
@ -239,7 +257,7 @@ class VMVariable(VMRef):
self.name = name self.name = name
def ref(self): def ref(self):
return [StackLiteral(self.name)] return [StackLiteral(unicode(self.name))]
def bytecode(self): def bytecode(self):
return codejoin(self.ref(), GetVariable()) return codejoin(self.ref(), GetVariable())
@ -247,6 +265,7 @@ class VMVariable(VMRef):
@staticmethod @staticmethod
@tokenparser @tokenparser
def parse(tokens): def parse(tokens):
disallow_keywords(tokens)
return VMVariable(tokens[0]) return VMVariable(tokens[0])
def __repr__(self): def __repr__(self):
@ -388,15 +407,17 @@ class GetVariable(CodeOp):
class CallBuiltin(CodeOp): class CallBuiltin(CodeOp):
def execute(self, vm): def execute(self, vm):
funcname, = vm.pop(1) funcname, args = vm.pop(2)
builtin_map[funcname](vm) funcname = funcname.encode('ascii', 'ignore')
retval = builtin_map[funcname](vm, args)
vm.push(retval)
@staticmethod @staticmethod
@tokenparser @tokenparser
def parse(tokens): def parse(tokens):
if not tokens[0] in builtin_map: #if not tokens[0] in builtin_map:
raise ParseException, 'Attempt to call undefined builtin function: "%s"' % (tokens[0],) # raise ParseException, 'Attempt to call undefined builtin function: "%s"' % (tokens[0],)
return codejoin(StackLiteral(VMString(tokens[0])), tokens[1], CallBuiltin()) return codejoin(tokens[0], tokens[1], CallBuiltin())
class CallFunction(CodeOp): class CallFunction(CodeOp):
def execute(self, vm): def execute(self, vm):
@ -555,7 +576,7 @@ class Assignment(CodeOp):
class StackLiteral(StackCodeOp): class StackLiteral(StackCodeOp):
def execute(self, vm): def execute(self, vm):
vm.push(self.value) vm.push(self.stack_value)
@staticmethod @staticmethod
@tokenparser @tokenparser
@ -563,6 +584,10 @@ class StackLiteral(StackCodeOp):
return StackLiteral(tokens[0]) return StackLiteral(tokens[0])
def __repr__(self): def __repr__(self):
try:
nv = coerce(self.stack_value)
except TypeError:
return "<** INVALID STACKLITERAL: %r **>" % (self.stack_value,)
return "<StackLiteral %s>" % (coerce(self.stack_value),) return "<StackLiteral %s>" % (coerce(self.stack_value),)
@ -585,6 +610,8 @@ class StackToList(CodeOp):
@staticmethod @staticmethod
@tokenparser @tokenparser
def parse(tokens): def parse(tokens):
if not tokens:
return codejoin(StackLiteral(0), StackToList())
rv = codejoin(tokens[0][0], StackLiteral(len(tokens)), StackToList()) rv = codejoin(tokens[0][0], StackLiteral(len(tokens)), StackToList())
return rv return rv

View file

@ -4,6 +4,65 @@ import select
import random import random
from ringbuffer import RingBuffer from ringbuffer import RingBuffer
class Poller(object):
POLLIN = 1
POLLERR = 2
POLLRD = 3 # POLLIN | POLLERR
POLLWR = 4
def __init__(self):
try:
self.mode = select.poll()
Poller.POLLIN = select
Poller.POLLRD = select.POLLIN|select.POLLPRI|select.POLLHUP|select.POLLERR|select.POLLNVAL
Poller.POLLWR = select.POLLOUT
except:
self.mode = select.select
Poller.POLLIN = 1
Poller.POLLERR = 2
Poller.POLLRD = Poller.POLLIN|Poller.POLLERR
Poller.POLLWR = 4
self.registered = {}
def register_read(self, listener, fd):
if not fd in self.registered:
self.registered[fd.fileno()] = [0, listener, fd]
self.registered[fd.fileno()][0] |= Poller.POLLRD
if self.mode != select.select:
self.mode.register(fd.fileno(), Poller.POLLRD)
def register_write(self, listener, fd):
if not fd in self.registered:
self.registered[fd.fileno()] = [0, listener, fd]
self.registered[fd.fileno()][0] |= Poller.POLLWR
if self.mode != select.select:
self.mode.register(fd.fileno(), Poller.POLLRD|Poller.POLLWR)
def unregister(self, fd):
if self.mode == select.select and fd in self.registered:
del self.registered[fd.fileno()]
else:
self.mode.unregister(fd.fileno())
def get_fd(self, fd, flags):
return (self.registered[fd][1], self.registered[fd][2], flags)
def poll(self, timeout):
if self.mode == select.select:
rdfd = []
wrfd = []
for fd, flags in self.registered.items():
if flags & Poller.POLLRD:
rdfd.append(fd)
if flags & Poller.POLLWR:
wrfd.append(fd)
r, w, x = select.select(rdfd, wrfd, rdfd, timeout)
return [self.get_fd(fd, Poller.POLLIN) for fd in r] + [self.get_fd(fd, Poller.POLLWR) for fd in w] + [self.get_fd(fd, Poller.POLLERR) for fd in x]
else:
timeout = int(round(timeout * 1000.0, 0))
return [self.get_fd(fd, flags) for fd, flags in self.mode.poll(timeout)]
class Connection(object): class Connection(object):
def __init__(self, id, conn, addr): def __init__(self, id, conn, addr):
self.id = id self.id = id
@ -75,6 +134,7 @@ class Listener(object):
self.connections = {} self.connections = {}
self.connection_list = [] self.connection_list = []
self.connection_list_dirty = False self.connection_list_dirty = False
self.remove_from_poller = []
self.MAX_CONN = 3000 self.MAX_CONN = 3000
@ -108,6 +168,7 @@ class Listener(object):
def delete_connection(self, conn): def delete_connection(self, conn):
print "%s disconnected." % (conn.id,) print "%s disconnected." % (conn.id,)
del self.connections[conn.id] del self.connections[conn.id]
self.remove_from_poller.append(conn.conn)
self.connection_list_dirty = True self.connection_list_dirty = True
def all_connections(self): def all_connections(self):
@ -147,10 +208,22 @@ class Listener(object):
else: else:
callback(conn, input) callback(conn, input)
def update_poller(self, poll):
for dconn in self.delete_from_poller:
poll.unregister(dconn)
self.delete_from_poller = []
for conn in self.all_connections():
if conn.output_waiting():
poll.register_write(self, conn.conn)
else:
poll.register_read(self, conn.conn)
def flush_output(self): def flush_output(self):
for conn in self.all_connections(): for conn in self.all_connections():
if conn.output_waiting(): if conn.output_waiting():
conn.flush_output() conn.flush_buffer()
def shutdown(self): def shutdown(self):
for x in self.connection_list: for x in self.connection_list:

54
optimizer.py Executable file
View file

@ -0,0 +1,54 @@
from pyparsing import ParseResults
def flatten_orig(l, ltypes=(list, tuple)):
ltype = type(l)
l = list(l)
i = 0
while i < len(l):
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
i -= 1
break
else:
l[i:i + 1] = l[i]
i += 1
return ltype(l)
def bytecode_flatten(l, ltypes=(list, tuple, ParseResults)):
l = list(l)
i = 0
while i < len(l):
broken = False
while isinstance(l[i], ltypes):
if not l[i]:
l.pop(i)
broken = True
break
else:
l[i:i + 1] = list(l[i])
if broken:
continue
assert not isinstance(l[i], ltypes)
assert not isinstance(l[i], (str, unicode, dict))
bc = l[i].bytecode()
if len(bc) > 1 or bc[0] != l[i]:
l[i:i+1] = bc
continue
i += 1
return l
def optimize(data):
"""
yeah, this is some pretty intense optimiziation, I know...
it will hopefully be expanded on later...
"""
data = bytecode_flatten(data)
for x in data:
if not hasattr(x, "pos"):
print "Missing position on element %s" % (x,)
return data

View file

@ -61,7 +61,7 @@ class Parser(object):
propref = (objrefexpr + point + ident).setParseAction(VMPropRef.parse) | coreref propref = (objrefexpr + point + ident).setParseAction(VMPropRef.parse) | coreref
fileref = (objrefexpr + excl + ident).setParseAction(VMFileRef.parse) fileref = (objrefexpr + excl + ident).setParseAction(VMFileRef.parse)
argspec = delimitedList(expr) argspec = Optional(delimitedList(expr))
argspec.setParseAction(StackToList.parse) argspec.setParseAction(StackToList.parse)
funccall = objrefexpr + call + identexpr + lpar + argspec + rpar funccall = objrefexpr + call + identexpr + lpar + argspec + rpar
@ -174,12 +174,25 @@ class Parser(object):
self.parser = block self.parser = block
#print argspec.parseString("hello(hi.xyz)", parseAll=True) #print argspec.parseString("hello(hi.xyz)", parseAll=True)
#print block.parseString(u"hi.xyz + #555.test;", parseAll=True) #print block.parseString(u"hi.xyz + #555.test;", parseAll=True)
#print block.parseString("""serverlog();""")
def parse(self, data): def parse(self, data):
rv = self.parser.parseString(data, parseAll=True) rv = self.parser.parseString(data, parseAll=True)
return optimizer.optimize(rv) return optimizer.optimize(rv)
def parse_command(self, line):
ls = line.split(' ')
cmd = ls[0]
argstr = ' '.join(ls[1:])
vars = {
'cmdstr': line,
'cmd': cmd,
'argstr': argstr,
'args': [x.strip() for x in ls[1:] if x.strip() != '']
}
return [cmd, vars]
def test(self): def test(self):
#print self.parse(u"if (1) #740.xyz + -hello.world; endif") #print self.parse(u"if (1) #740.xyz + -hello.world; endif")
@ -187,6 +200,9 @@ class Parser(object):
data = unicode(open("test.moo", "r").read(), 'utf-8') data = unicode(open("test.moo", "r").read(), 'utf-8')
print self.parse(data) print self.parse(data)
static_parser = Parser()
if __name__ == "__main__": if __name__ == "__main__":
p = Parser() p = Parser()
p.test() p.test()

View file

@ -1,7 +1,8 @@
import os, sys, time import os, sys, time
from listener import Listener from listener import Listener, Poller
from language import VirtualMachine from language import VirtualMachine
from database_fake import Fake_Database as Database from database_fake import Fake_Database as Database
from parse import static_parser
class Server(object): class Server(object):
def __init__(self, dbname): def __init__(self, dbname):
@ -14,16 +15,24 @@ class Server(object):
self.server_started = None self.server_started = None
self.loop_started = None self.loop_started = None
self.listeners = [] self.listeners = []
self.poller = Poller()
def listen(self, addr, port): def listen(self, addr, port):
l = Listener() l = Listener()
l.listen(addr, port) l.listen(addr, port)
self.listeners.append(l) self.listeners.append(l)
def unlisten(self, addr, port):
l = Listener()
l.listen(addr, port)
self.listeners.append(l)
def mainloop(self): def mainloop(self):
self.server_started = time.time() self.server_started = time.time()
while True: while True:
self.loop_started = time.time() self.loop_started = time.time()
"""
for l in self.listeners: for l in self.listeners:
l.handle_incoming_connections() l.handle_incoming_connections()
l.scan_for_input(self.read_input) l.scan_for_input(self.read_input)
@ -31,6 +40,9 @@ class Server(object):
for l in self.listeners: for l in self.listeners:
l.flush_output() l.flush_output()
self.idlewait() self.idlewait()
"""
self.poller.poll(self.get_sleepytime())
def read_input(self, conn, data): def read_input(self, conn, data):
@ -39,7 +51,17 @@ class Server(object):
del ds[-1] del ds[-1]
for line in ds: for line in ds:
print "%s: %s" % (conn.id, line) print "%s: %s" % (conn.id, line)
cmd, vars = static_parser.parse_command(line)
self.db.match_command(cmd, vars)
def get_sleepytime(self):
if self.vm.sleepytime == None:
"virtual machine is still busy, no sleeping on the job!"
return 0.0
return self.vm.sleepytime
def idlewait(self): def idlewait(self):
if self.vm.sleepytime == None: if self.vm.sleepytime == None:
"virtual machine is still busy anyway, no sleeping on the job!" "virtual machine is still busy anyway, no sleeping on the job!"

8
test.moo Executable file
View file

@ -0,0 +1,8 @@
serverlog("hello");
part = 1;
if (part)
vvv = 3;
vxx = "abc";
else
vvv = #-1; vvx = $nothing;
endif

9
tt.py Executable file
View file

@ -0,0 +1,9 @@
from virtualmachine import VirtualMachine
from parse import Parser
p = Parser()
vm = VirtualMachine(None)
bytecode = p.parse("""serverlog("hello"); var1 = var2;""")
vm.spawn_cmd_task(bytecode, {})
vm.run()

198
virtualmachine.py Executable file
View file

@ -0,0 +1,198 @@
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, CodeOpSequence):
for op in reverse(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)
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.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)