added optimizer and got language/VM mostly working
--HG-- branch : mung
This commit is contained in:
parent
fe0ea2a7f9
commit
bae69502bd
13 changed files with 708 additions and 99 deletions
153
builtins.py
153
builtins.py
|
@ -1,104 +1,113 @@
|
|||
from builtins_code import bi
|
||||
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
|
||||
builtin_map.update({
|
||||
'properties': bi_properties,
|
||||
'add_property': bi_add_property,
|
||||
'delete_property': bi_delete_property,
|
||||
'clear_property': bi_clear_property,
|
||||
'set_property_opts': bi_set_property_opts,
|
||||
'get_property_opts': bi_get_property_opts,
|
||||
'set_property_owner': bi_set_property_owner,
|
||||
'get_property_owner': bi_get_property_owner,
|
||||
'properties': bi.properties,
|
||||
'add_property': bi.add_property,
|
||||
'delete_property': bi.delete_property,
|
||||
'clear_property': bi.clear_property,
|
||||
'set_property_opts': bi.set_property_opts,
|
||||
'get_property_opts': bi.get_property_opts,
|
||||
'set_property_owner': bi.set_property_owner,
|
||||
'get_property_owner': bi.get_property_owner,
|
||||
})
|
||||
|
||||
# manage files
|
||||
builtin_map.update({
|
||||
'files': bi_files,
|
||||
'add_file': bi_add_file,
|
||||
'delete_file': bi_delete_file,
|
||||
'set_file_opts': bi_set_file_opts,
|
||||
'get_file_opts': bi_get_file_opts,
|
||||
'set_file_owner': bi_set_file_owner,
|
||||
'get_file_owner': bi_get_file_owner,
|
||||
'files': bi.files,
|
||||
'add_file': bi.add_file,
|
||||
'delete_file': bi.delete_file,
|
||||
'set_file_opts': bi.set_file_opts,
|
||||
'get_file_opts': bi.get_file_opts,
|
||||
'set_file_owner': bi.set_file_owner,
|
||||
'get_file_owner': bi.get_file_owner,
|
||||
})
|
||||
|
||||
# manage functions
|
||||
builtin_map.update({
|
||||
'functions': bi_functions,
|
||||
'add_function': bi_add_function,
|
||||
'delete_function': bi_delete_function,
|
||||
'set_function_code': bi_set_function_code,
|
||||
'get_function_code': bi_get_function_code,
|
||||
'set_function_opts': bi_set_function_opts,
|
||||
'get_function_opts': bi_get_function_opts,
|
||||
'set_function_args': bi_set_function_args,
|
||||
'get_function_args': bi_get_function_args,
|
||||
'set_function_owner': bi_set_function_owner,
|
||||
'get_function_owner': bi_get_function_owner,
|
||||
'functions': bi.functions,
|
||||
'add_function': bi.add_function,
|
||||
'delete_function': bi.delete_function,
|
||||
'set_function_code': bi.set_function_code,
|
||||
'get_function_code': bi.get_function_code,
|
||||
'set_function_opts': bi.set_function_opts,
|
||||
'get_function_opts': bi.get_function_opts,
|
||||
'set_function_args': bi.set_function_args,
|
||||
'get_function_args': bi.get_function_args,
|
||||
'set_function_owner': bi.set_function_owner,
|
||||
'get_function_owner': bi.get_function_owner,
|
||||
})
|
||||
|
||||
# server configuration
|
||||
builtin_map.update({
|
||||
'set_server_var': bi_set_server_var,
|
||||
'get_server_var': bi_get_server_var,
|
||||
'save_database': bi_save_database,
|
||||
'shutdown': bi_shutdown,
|
||||
'server_info': bi_server_info,
|
||||
'time': bi_time,
|
||||
'format_time': bi_format_time,
|
||||
'set_server_var': bi.set_server_var,
|
||||
'get_server_var': bi.get_server_var,
|
||||
'save_database': bi.save_database,
|
||||
'shutdown': bi.shutdown,
|
||||
'server_info': bi.server_info,
|
||||
'time': bi.time,
|
||||
'format_time': bi.format_time,
|
||||
})
|
||||
|
||||
# manage running tasks
|
||||
builtin_map.update({
|
||||
'set_perms': bi_set_perms,
|
||||
'get_perms': bi_get_perms,
|
||||
'task_id': bi_task_id,
|
||||
'kill_task': bi_kill_task,
|
||||
'eval': bi_eval,
|
||||
'fork': bi_fork,
|
||||
'sleep': bi_sleep,
|
||||
'resume': bi_resume,
|
||||
'raise': bi_raise,
|
||||
'stack': bi_stack,
|
||||
'set_perms': bi.set_perms,
|
||||
'get_perms': bi.get_perms,
|
||||
'task_id': bi.task_id,
|
||||
'kill_task': bi.kill_task,
|
||||
'eval': bi.eval,
|
||||
'fork': bi.fork,
|
||||
'sleep': bi.sleep,
|
||||
'resume': bi.resume,
|
||||
'raise': bi.raise,
|
||||
'stack': bi.stack,
|
||||
})
|
||||
|
||||
# manage objects
|
||||
builtin_map.update({
|
||||
'create': bi_create,
|
||||
'destroy': bi_destroy,
|
||||
'set_parent': bi_set_parent,
|
||||
'parent': bi_parent,
|
||||
'children': bi_children,
|
||||
'set_owner': bi_set_owner,
|
||||
'owner': bi_owner,
|
||||
'max_object': bi_max_object,
|
||||
'renumber': bi_renumber,
|
||||
'create': bi.create,
|
||||
'destroy': bi.destroy,
|
||||
'set_parent': bi.set_parent,
|
||||
'parent': bi.parent,
|
||||
'children': bi.children,
|
||||
'set_owner': bi.set_owner,
|
||||
'owner': bi.owner,
|
||||
'max_object': bi.max_object,
|
||||
'renumber': bi.renumber,
|
||||
})
|
||||
|
||||
# manage connections and ports
|
||||
builtin_map.update({
|
||||
'connect': bi_connect,
|
||||
'disconnect': bi_disconnect,
|
||||
'open_connection': bi_open_connection,
|
||||
'close_connection': bi_close_connection,
|
||||
'incoming_connections': bi_incoming_connections,
|
||||
'outgoing_connections': bi_outgoing_connections,
|
||||
'get_connection_info': bi_get_connection_info,
|
||||
'set_connection_opts': bi_set_connection_opts,
|
||||
'send': bi_send,
|
||||
'recv': bi_recv,
|
||||
'listen': bi_listen,
|
||||
'unlisten': bi_unlisten,
|
||||
'setuid': bi_setuid,
|
||||
'connect': bi.connect,
|
||||
'disconnect': bi.disconnect,
|
||||
'open_connection': bi.open_connection,
|
||||
'close_connection': bi.close_connection,
|
||||
'incoming_connections': bi.incoming_connections,
|
||||
'outgoing_connections': bi.outgoing_connections,
|
||||
'get_connection_info': bi.get_connection_info,
|
||||
'set_connection_opts': bi.set_connection_opts,
|
||||
'send': bi.send,
|
||||
'recv': bi.recv,
|
||||
'listen': bi.listen,
|
||||
'unlisten': bi.unlisten,
|
||||
'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
10
builtins_code.py
Executable 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
192
database.py
Executable 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
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
|
||||
class DSDB_Database(object):
|
||||
class DSDB_Database(Database):
|
||||
def __init__(self, dbname):
|
||||
self.dbname = dbname
|
||||
self.fn = "%s.dsdb" % (dbname,)
|
||||
|
|
|
@ -3,14 +3,5 @@ class Fake_Database(object):
|
|||
def __init__(self, dbname):
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
53
language.py
53
language.py
|
@ -1,6 +1,7 @@
|
|||
import sys
|
||||
import time
|
||||
from builtins import builtin_map
|
||||
from database import ObjRef
|
||||
import bisect
|
||||
import optimizer
|
||||
import traceback
|
||||
|
@ -65,6 +66,26 @@ def tokenparser(func):
|
|||
|
||||
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):
|
||||
rv = []
|
||||
for arg in args:
|
||||
|
@ -103,10 +124,6 @@ def uncoerce(value):
|
|||
return value.value
|
||||
|
||||
|
||||
class ObjRef(object):
|
||||
def __init__(self, objnum):
|
||||
self.objnum = objnum
|
||||
|
||||
class VMBaseObject(object):
|
||||
def __init__(self):
|
||||
self.pos = None
|
||||
|
@ -161,7 +178,7 @@ class VMTable(VMType):
|
|||
class VMList(VMType):
|
||||
def __init__(self, value):
|
||||
VMType.__init__(self)
|
||||
self.value = dict(value)
|
||||
self.value = list(value)
|
||||
|
||||
@staticmethod
|
||||
@tokenparser
|
||||
|
@ -222,11 +239,12 @@ class VMIdent(VMRef):
|
|||
self.name = name
|
||||
|
||||
def bytecode(self):
|
||||
return [StackLiteral(self.name)]
|
||||
return [StackLiteral(unicode(self.name))]
|
||||
|
||||
@staticmethod
|
||||
@tokenparser
|
||||
def parse(tokens):
|
||||
disallow_keywords(tokens)
|
||||
return VMIdent(tokens[0])
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -239,7 +257,7 @@ class VMVariable(VMRef):
|
|||
self.name = name
|
||||
|
||||
def ref(self):
|
||||
return [StackLiteral(self.name)]
|
||||
return [StackLiteral(unicode(self.name))]
|
||||
|
||||
def bytecode(self):
|
||||
return codejoin(self.ref(), GetVariable())
|
||||
|
@ -247,6 +265,7 @@ class VMVariable(VMRef):
|
|||
@staticmethod
|
||||
@tokenparser
|
||||
def parse(tokens):
|
||||
disallow_keywords(tokens)
|
||||
return VMVariable(tokens[0])
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -388,15 +407,17 @@ class GetVariable(CodeOp):
|
|||
|
||||
class CallBuiltin(CodeOp):
|
||||
def execute(self, vm):
|
||||
funcname, = vm.pop(1)
|
||||
builtin_map[funcname](vm)
|
||||
funcname, args = vm.pop(2)
|
||||
funcname = funcname.encode('ascii', 'ignore')
|
||||
retval = builtin_map[funcname](vm, args)
|
||||
vm.push(retval)
|
||||
|
||||
@staticmethod
|
||||
@tokenparser
|
||||
def parse(tokens):
|
||||
if not tokens[0] in builtin_map:
|
||||
raise ParseException, 'Attempt to call undefined builtin function: "%s"' % (tokens[0],)
|
||||
return codejoin(StackLiteral(VMString(tokens[0])), tokens[1], CallBuiltin())
|
||||
#if not tokens[0] in builtin_map:
|
||||
# raise ParseException, 'Attempt to call undefined builtin function: "%s"' % (tokens[0],)
|
||||
return codejoin(tokens[0], tokens[1], CallBuiltin())
|
||||
|
||||
class CallFunction(CodeOp):
|
||||
def execute(self, vm):
|
||||
|
@ -555,7 +576,7 @@ class Assignment(CodeOp):
|
|||
|
||||
class StackLiteral(StackCodeOp):
|
||||
def execute(self, vm):
|
||||
vm.push(self.value)
|
||||
vm.push(self.stack_value)
|
||||
|
||||
@staticmethod
|
||||
@tokenparser
|
||||
|
@ -563,6 +584,10 @@ class StackLiteral(StackCodeOp):
|
|||
return StackLiteral(tokens[0])
|
||||
|
||||
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),)
|
||||
|
||||
|
||||
|
@ -585,6 +610,8 @@ class StackToList(CodeOp):
|
|||
@staticmethod
|
||||
@tokenparser
|
||||
def parse(tokens):
|
||||
if not tokens:
|
||||
return codejoin(StackLiteral(0), StackToList())
|
||||
rv = codejoin(tokens[0][0], StackLiteral(len(tokens)), StackToList())
|
||||
return rv
|
||||
|
||||
|
|
75
listener.py
75
listener.py
|
@ -4,6 +4,65 @@ import select
|
|||
import random
|
||||
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):
|
||||
def __init__(self, id, conn, addr):
|
||||
self.id = id
|
||||
|
@ -75,6 +134,7 @@ class Listener(object):
|
|||
self.connections = {}
|
||||
self.connection_list = []
|
||||
self.connection_list_dirty = False
|
||||
self.remove_from_poller = []
|
||||
|
||||
self.MAX_CONN = 3000
|
||||
|
||||
|
@ -108,6 +168,7 @@ class Listener(object):
|
|||
def delete_connection(self, conn):
|
||||
print "%s disconnected." % (conn.id,)
|
||||
del self.connections[conn.id]
|
||||
self.remove_from_poller.append(conn.conn)
|
||||
self.connection_list_dirty = True
|
||||
|
||||
def all_connections(self):
|
||||
|
@ -147,10 +208,22 @@ class Listener(object):
|
|||
else:
|
||||
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):
|
||||
for conn in self.all_connections():
|
||||
if conn.output_waiting():
|
||||
conn.flush_output()
|
||||
conn.flush_buffer()
|
||||
|
||||
def shutdown(self):
|
||||
for x in self.connection_list:
|
||||
|
|
54
optimizer.py
Executable file
54
optimizer.py
Executable 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
|
||||
|
18
parse.py
18
parse.py
|
@ -61,7 +61,7 @@ class Parser(object):
|
|||
propref = (objrefexpr + point + ident).setParseAction(VMPropRef.parse) | coreref
|
||||
fileref = (objrefexpr + excl + ident).setParseAction(VMFileRef.parse)
|
||||
|
||||
argspec = delimitedList(expr)
|
||||
argspec = Optional(delimitedList(expr))
|
||||
argspec.setParseAction(StackToList.parse)
|
||||
funccall = objrefexpr + call + identexpr + lpar + argspec + rpar
|
||||
|
||||
|
@ -174,12 +174,25 @@ class Parser(object):
|
|||
self.parser = block
|
||||
#print argspec.parseString("hello(hi.xyz)", parseAll=True)
|
||||
#print block.parseString(u"hi.xyz + #555.test;", parseAll=True)
|
||||
#print block.parseString("""serverlog();""")
|
||||
|
||||
def parse(self, data):
|
||||
rv = self.parser.parseString(data, parseAll=True)
|
||||
|
||||
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):
|
||||
#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')
|
||||
print self.parse(data)
|
||||
|
||||
|
||||
static_parser = Parser()
|
||||
|
||||
if __name__ == "__main__":
|
||||
p = Parser()
|
||||
p.test()
|
||||
|
|
24
server.py
24
server.py
|
@ -1,7 +1,8 @@
|
|||
import os, sys, time
|
||||
from listener import Listener
|
||||
from listener import Listener, Poller
|
||||
from language import VirtualMachine
|
||||
from database_fake import Fake_Database as Database
|
||||
from parse import static_parser
|
||||
|
||||
class Server(object):
|
||||
def __init__(self, dbname):
|
||||
|
@ -14,16 +15,24 @@ class Server(object):
|
|||
self.server_started = None
|
||||
self.loop_started = None
|
||||
self.listeners = []
|
||||
self.poller = Poller()
|
||||
|
||||
def listen(self, addr, port):
|
||||
l = Listener()
|
||||
l.listen(addr, port)
|
||||
self.listeners.append(l)
|
||||
|
||||
def unlisten(self, addr, port):
|
||||
l = Listener()
|
||||
l.listen(addr, port)
|
||||
self.listeners.append(l)
|
||||
|
||||
def mainloop(self):
|
||||
self.server_started = time.time()
|
||||
while True:
|
||||
self.loop_started = time.time()
|
||||
|
||||
"""
|
||||
for l in self.listeners:
|
||||
l.handle_incoming_connections()
|
||||
l.scan_for_input(self.read_input)
|
||||
|
@ -31,6 +40,9 @@ class Server(object):
|
|||
for l in self.listeners:
|
||||
l.flush_output()
|
||||
self.idlewait()
|
||||
"""
|
||||
self.poller.poll(self.get_sleepytime())
|
||||
|
||||
|
||||
|
||||
def read_input(self, conn, data):
|
||||
|
@ -39,7 +51,17 @@ class Server(object):
|
|||
del ds[-1]
|
||||
for line in ds:
|
||||
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):
|
||||
if self.vm.sleepytime == None:
|
||||
"virtual machine is still busy anyway, no sleeping on the job!"
|
||||
|
|
8
test.moo
Executable file
8
test.moo
Executable 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
9
tt.py
Executable 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
198
virtualmachine.py
Executable 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)
|
Loading…
Add table
Reference in a new issue