From d3f845144ea7db33186b0ef89dcb82ab939541be Mon Sep 17 00:00:00 2001 From: cecilkorik Date: Sat, 13 Nov 2010 07:55:35 +0000 Subject: [PATCH] progress on mung server --HG-- branch : mung --- Test.dsdb | 2 +- builtins.py | 104 ++++++++++++++++++++++++ database_dsdb.py | 10 ++- database_fake.py | 16 ++++ ebnf.py | 149 ++++++++++++++++++++++++++++++++++ language.py | 129 +++++++++++++++++++++++++---- language_types.py | 54 ++++++++++++ listener.py | 41 +++++++++- parse.py | 103 +++++++++++++++++++++++ ringbuffer.py | 203 ++++++++++++++++++++++++++++++++++++++++++++++ server.py | 33 +++++++- 11 files changed, 826 insertions(+), 18 deletions(-) create mode 100755 builtins.py create mode 100755 database_fake.py create mode 100755 ebnf.py create mode 100755 language_types.py create mode 100755 parse.py create mode 100755 ringbuffer.py diff --git a/Test.dsdb b/Test.dsdb index b945536..bea2b9e 100644 --- a/Test.dsdb +++ b/Test.dsdb @@ -3,7 +3,7 @@ CALLBUILTIN eval argstr . :"handle_connection" x * -RETURN #0; +RETURN #0 . diff --git a/builtins.py b/builtins.py new file mode 100755 index 0000000..ed6c750 --- /dev/null +++ b/builtins.py @@ -0,0 +1,104 @@ +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, + }) + +# 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, + }) + +# 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, + }) + +# 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, + }) + +# 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, + }) + +# 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, + }) + +# 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, + }) +""" \ No newline at end of file diff --git a/database_dsdb.py b/database_dsdb.py index b28b04f..80ca453 100644 --- a/database_dsdb.py +++ b/database_dsdb.py @@ -1,3 +1,11 @@ - +class DSDB_Database(object): + def __init__(self, dbname): + self.dbname = dbname + self.fn = "%s.dsdb" % (dbname,) + self.load_from_file() + + def load_from_file(self): + fd = open(self.fn, 'rb') + fd diff --git a/database_fake.py b/database_fake.py new file mode 100755 index 0000000..e92cb6f --- /dev/null +++ b/database_fake.py @@ -0,0 +1,16 @@ + +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 + + + \ No newline at end of file diff --git a/ebnf.py b/ebnf.py new file mode 100755 index 0000000..8dfcaea --- /dev/null +++ b/ebnf.py @@ -0,0 +1,149 @@ +# This module tries to implement ISO 14977 standard with pyparsing. +# pyparsing version 1.1 or greater is required. + +# ISO 14977 standardize The Extended Backus-Naur Form(EBNF) syntax. +# You can read a final draft version here: +# http://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html + + +from pyparsing import * + + +all_names = ''' +integer +meta_identifier +terminal_string +optional_sequence +repeated_sequence +grouped_sequence +syntactic_primary +syntactic_factor +syntactic_term +single_definition +definitions_list +syntax_rule +syntax +'''.split() + + +integer = Word(nums) +meta_identifier = Word(alphas, alphanums + '_') +terminal_string = Suppress("'") + CharsNotIn("'") + Suppress("'") ^ \ + Suppress('"') + CharsNotIn('"') + Suppress('"') +definitions_list = Forward() +optional_sequence = Suppress('[') + definitions_list + Suppress(']') +repeated_sequence = Suppress('{') + definitions_list + Suppress('}') +grouped_sequence = Suppress('(') + definitions_list + Suppress(')') +syntactic_primary = optional_sequence ^ repeated_sequence ^ \ + grouped_sequence ^ meta_identifier ^ terminal_string +syntactic_factor = Optional(integer + Suppress('*')) + syntactic_primary +syntactic_term = syntactic_factor + Optional(Suppress('-') + syntactic_factor) +single_definition = delimitedList(syntactic_term, ',') +definitions_list << delimitedList(single_definition, '|') +syntax_rule = meta_identifier + Suppress('=') + definitions_list + \ + Suppress(';') + +ebnfComment = ( "(*" + + ZeroOrMore( CharsNotIn("*") | ( "*" + ~Literal(")") ) ) + + "*)" ).streamline().setName("ebnfComment") + +syntax = OneOrMore(syntax_rule) +syntax.ignore(ebnfComment) + + +def do_integer(str, loc, toks): + return int(toks[0]) + +def do_meta_identifier(str, loc, toks): + if toks[0] in symbol_table: + return symbol_table[toks[0]] + else: + forward_count.value += 1 + symbol_table[toks[0]] = Forward() + return symbol_table[toks[0]] + +def do_terminal_string(str, loc, toks): + return Literal(toks[0]) + +def do_optional_sequence(str, loc, toks): + return Optional(toks[0]) + +def do_repeated_sequence(str, loc, toks): + return ZeroOrMore(toks[0]) + +def do_grouped_sequence(str, loc, toks): + return Group(toks[0]) + +def do_syntactic_primary(str, loc, toks): + return toks[0] + +def do_syntactic_factor(str, loc, toks): + if len(toks) == 2: + # integer * syntactic_primary + return And([toks[1]] * toks[0]) + else: + # syntactic_primary + return [ toks[0] ] + +def do_syntactic_term(str, loc, toks): + if len(toks) == 2: + # syntactic_factor - syntactic_factor + return NotAny(toks[1]) + toks[0] + else: + # syntactic_factor + return [ toks[0] ] + +def do_single_definition(str, loc, toks): + toks = toks.asList() + if len(toks) > 1: + # syntactic_term , syntactic_term , ... + return And(toks) + else: + # syntactic_term + return [ toks[0] ] + +def do_definitions_list(str, loc, toks): + toks = toks.asList() + if len(toks) > 1: + # single_definition | single_definition | ... + return Or(toks) + else: + # single_definition + return [ toks[0] ] + +def do_syntax_rule(str, loc, toks): + # meta_identifier = definitions_list ; + assert toks[0].expr is None, "Duplicate definition" + forward_count.value -= 1 + toks[0] << toks[1] + return [ toks[0] ] + +def do_syntax(str, loc, toks): + # syntax_rule syntax_rule ... + return symbol_table + + + +symbol_table = {} +class forward_count: + pass +forward_count.value = 0 +for name in all_names: + expr = vars()[name] + action = vars()['do_' + name] + expr.setName(name) + expr.setParseAction(action) + #~ expr.setDebug() + + +def parse(ebnf, given_table={}): + symbol_table.clear() + symbol_table.update(given_table) + forward_count.value = 0 + table = syntax.parseString(ebnf)[0] + assert forward_count.value == 0, "Missing definition" + for name in table: + expr = table[name] + expr.setName(name) + #~ expr.setDebug() + return table diff --git a/language.py b/language.py index 259a7ae..b7d8212 100644 --- a/language.py +++ b/language.py @@ -1,20 +1,121 @@ +import time +from builtins import builtin_map +from language_types import coerce, uncoerce -from ply import lex +class VirtualMachine(object): + def __init__(self, db): + self.db = db + self.active_task_id = None + self.sleepytime = None + self.stack = [] + self.contexts = [] + self.tasks = {} + self.task_sequence = [] + self.ticks_used = 0 + + def pop(self, count=1): + return [uncoerce(self.stack.pop()) for x in range(count)] + + def push(self, value): + self.stack.append(coerce(value)) + + def setvar(self, varname, val): + self.contexts[-1][varname] = val + + def getvar(self, varname): + return self.contexts[-1][varname] + + def push_context(self): + self.contexts.append({}) + + def pop_context(self): + self.contexts.pop() + + def suspend_start_next(self, delay): + now = time.time() + newtask = heapq.heappushpop(self.task_sequence, (now+max(0.0,delay), self.stack, self.contexts, self.ticks_used)) + return activate_task(newtask) + + def finished_start_next(self): + now = time.time() + newtask = heapq.heappop(self.task_sequence) + return activate_task(newtask) + + def activate_task(self, task): + if now < task[0]: + "task isn't ready to execute yet" + self.sleepytime = task[0] - now + heapq.heappush(self.task_sequence, task[0]) + return False + + self.active_task_id = task[0] + self.stack = task[1] + self.contexts = task[2] + self.ticks_used = task[3] + + def get_next_task(self): + heapq.heappop(self.task_sequence) + + def run_active_task + + def run(self): + self.sleepytime = None + if self.active_task_id == None: + finished_start_next() + if self.active_task_id == None: + return + + + + + -tokens = ( - "OPERATION", - "LITERAL", - "VARIABLE" -) +class CodeOp(object): + def __init__(self): + pass + +class GetProperty(CodeOp): + def execute(self, vm): + prop, obj = vm.pop(2) + vm.stack.append(vm.db.get_property(obj, prop)) -t_OPERATION = ( - r"CALLBUILTIN|CALLFUNC|SETVAR|RETURN" -) +class SetProperty(CodeOp): + def execute(self, vm): + val, prop, obj = vm.pop(3) + vm.db.set_property(obj, prop, val) -t_LITERAL = ( - r'"i"' -) +class GetFile(CodeOp): + def execute(self, vm): + prop, obj = vm.pop(2) + vm.stack.append(vm.db.get_file(obj, prop)) -lex.lex() -print lex +class SetFile(CodeOp): + def execute(self, vm): + val, prop, obj = vm.pop(3) + vm.db.set_file(obj, prop, val) + +class SetVariable(CodeOp): + def execute(self, vm): + val, varname = vm.pop(2) + vm.setvar(varname, val) + +class GetVariable(CodeOp): + def execute(self, vm): + varname, = vm.pop(1) + vm.push(vm.getvar(varname)) + +class CallBuiltin(CodeOp): + def __init__(self): + pass + + def execute(self, vm): + funcname, = vm.pop(1) + builtin_map[funcname](vm) + +class StartContext(CodeOp): + def execute(self, vm): + vm.push_context() +class EndContext(CodeOp): + def execute(self, vm): + vm.pop_context() \ No newline at end of file diff --git a/language_types.py b/language_types.py new file mode 100755 index 0000000..b1ef428 --- /dev/null +++ b/language_types.py @@ -0,0 +1,54 @@ + + +class VMType(object): + pass + +class VMInteger(VMType): + def __init__(self, value): + self.value = int(value) +class VMFloat(VMType): + def __init__(self, value): + self.value = float(value) +class VMTable(VMType): + def __init__(self, value): + self.value = dict(value) + +class VMString(VMType): + def __init__(self, value): + if isinstance(value, unicode): + self.value = value + else: + self.value = unicode(str(value), 'ascii', 'ignore') + +class VMObjRef(VMType): + def __init__(self, value): + if isinstance(value, ObjRef): + self.value = value + elif isinstance(value, (float, int)): + self.value = ObjRef(int(value)) + else: + raise TypeError, "Attempted to create VMObjRef with invalid object reference: %r" % (value,) + + +def coerce(value): + if isinstance(value, int): + return VMInteger(value) + elif isinstance(value, (tuple, list)): + return VMList(list(value)) + elif isinstance(value, unicode): + return VMString(value) + elif isinstance(value, dict): + return VMTable(value) + elif isinstance(value, ObjRef): + return VMObjRef(value) + elif isinstance(value, float): + return VMFloat(value) + elif value == None: + return VMInteger(0) + else: + raise TypeError("Unknown type %s cannot be coerced to VMType" % (type(value),)) + + +def uncoerce(value): + assert isinstance(value, VMType) + return value.value \ No newline at end of file diff --git a/listener.py b/listener.py index 3619711..1c92ae2 100644 --- a/listener.py +++ b/listener.py @@ -2,6 +2,7 @@ import os, sys, time import socket import select import random +from ringbuffer import RingBuffer class Connection(object): def __init__(self, id, conn, addr): @@ -11,6 +12,7 @@ class Connection(object): self.input_encoding = 'raw' self.output_encoding = 'ascii' self.linebuffer = True + self.output_buffer = RingBuffer(65536, True) def fileno(self): return self.conn.fileno() @@ -34,6 +36,35 @@ class Connection(object): return unicode(self.escape(data), 'ascii') else: return unicode(data, self.input_encoding, 'ignore') + + def send(self, data): + assert isinstance(data, unicode) + try: + encoded = data.encode(self.output_encoding, 'replace') + except UnicodeEncodeError: + try: + encoded = data.encode(self.output_encoding, 'ignore') + except UnicodeEncodeError: + encoded = data.encode('ascii', 'ignore') + + if len(encoded) > self.output_buffer.sizeleft(): + return False + + self.output_buffer.write(encoded) + return True + + def output_waiting(self): + return self.output_buffer.bytes_waiting() > 0 + + def flush_buffer(self): + if not self.output_waiting(): + return + + data = self.output_buffer.read() + count = self.conn.send(data) + if count < len(data): + self.output_buffer.rewind(len(data) - count) + class Listener(object): @@ -102,7 +133,10 @@ class Listener(object): else: conn.close() - + def send(connid, data): + conn = self.connections[connid] + conn.send(data) + def scan_for_input(self, callback): r, w, x = select.select(self.all_connections(), [], [], 0) @@ -112,6 +146,11 @@ class Listener(object): self.delete_connection(conn) else: callback(conn, input) + + def flush_output(self): + for conn in self.all_connections(): + if conn.output_waiting(): + conn.flush_output() def shutdown(self): for x in self.connection_list: diff --git a/parse.py b/parse.py new file mode 100755 index 0000000..10fba0d --- /dev/null +++ b/parse.py @@ -0,0 +1,103 @@ +from pyparsing import * + +def enum_parse(): + fd = open('test.enum', 'r') + + # syntax we don't want to see in the final parse tree + _lcurl = Suppress('{') + _rcurl = Suppress('}') + _equal = Suppress('=') + _comma = Suppress(',') + _semi = Suppress(';') + _enum = Suppress('enum') + + identifier = Word(alphas,alphanums+'_') + integer = Word(nums) + enumValue = Group(identifier('name') + Optional(_equal + integer('value'))) + enumList = Group(enumValue + ZeroOrMore(_comma + enumValue)) + enum = _enum + identifier('enum') + _lcurl + enumList('list') + _rcurl + Optional(_semi) + + enumlist = ZeroOrMore(enum) + + #print enumlist.parseString(fd.read(), parseAll=True) + # find instances of enums ignoring other syntax + for item in enumlist.parseString(fd.read(), parseAll=True): + id = 0 + for entry in item: + if entry.value != '': + id = int(entry.value) + print '%s_%s = %d' % (item.enum.upper(),entry.name.upper(),id) + id += 1 + + fd.close() + +def parse_fourfn(): + global bnf + if not bnf: + point = Literal( "." ) + e = CaselessLiteral( "E" ) + fnumber = Combine( Word( "+-"+nums, nums ) + + Optional( point + Optional( Word( nums ) ) ) + + Optional( e + Word( "+-"+nums, nums ) ) ) + ident = Word(alphas, alphas+nums+"_$") + + plus = Literal( "+" ) + minus = Literal( "-" ) + mult = Literal( "*" ) + div = Literal( "/" ) + lpar = Literal( "(" ).suppress() + rpar = Literal( ")" ).suppress() + addop = plus | minus + multop = mult | div + expop = Literal( "^" ) + pi = CaselessLiteral( "PI" ) + + expr = Forward() + atom = (Optional("-") + ( pi | e | fnumber | ident + lpar + expr + rpar ).setParseAction( pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(pushUMinus) + + # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ + # that is, 2^3^2 = 2^(3^2), not (2^3)^2. + factor = Forward() + factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) + + term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) + expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) + bnf = expr + return bnf + + +def moo_parse(): + fd = open('test.moo', 'r') + data = fd.read() + fd.close() + + point = Literal( "." ) + + integer = Word( "+-"+nums, nums ) + fnumber = Combine( integer + + Optional( point + Optional( Word( nums ) ) ) + + Optional( CaselessLiteral('e') + Word( "+-"+nums, nums ) ) ) + ident = Word(alphas, alphas+nums+"_") + + plus = Literal( "+" ) + minus = Literal( "-" ) + mult = Literal( "*" ) + div = Literal( "/" ) + lpar = Literal( "(" ).suppress() + rpar = Literal( ")" ).suppress() + addop = plus | minus + multop = mult | div + expop = Literal( "^" ) + + expr = Forward() + atom = (Optional("-") + ( fnumber | ident + lpar + expr + rpar ).setParseAction( pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(pushUMinus) + + # by defining exponentiation as "atom [ ^ factor ]..." instead of "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-righ + # that is, 2^3^2 = 2^(3^2), not (2^3)^2. + factor = Forward() + factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) + + term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) + expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) + + diff --git a/ringbuffer.py b/ringbuffer.py new file mode 100755 index 0000000..6bf5319 --- /dev/null +++ b/ringbuffer.py @@ -0,0 +1,203 @@ +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +class FullBufferError(Exception): + +class RingBuffer(object): + def __init__(self, size, overflow=True): + self.size = size + self.buffer = StringIO() + self.buffer_pos = 0 + self.read_pos = 0 + self.bytes_written = 0 + self.first_read = True + self.allow_overflow = overflow + self.emptyclass = RingBuffer + self.fullclass = RingBufferFull + self.empty = True + + def sizeleft(self): + return self.size - self.buffer_pos + self.read_pos + + def bytes_waiting(self): + return self.size - self.sizeleft() + + def write(self, data): + if data: + self.empty = False + ld = len(data) + if ld > self.sizeleft() and not self.allow_overflow: + raise FullBufferError("Data would overflow the buffer") + if self.buffer_pos + ld >= self.size: + self.__class__ = self.fullclass + split = self.size - self.buffer_pos + self.buffer.write(data[:split]) + self.buffer.seek(0, 0) + self.buffer_pos = 0 + self.bytes_written += split + self.write(data[split:]) + else: + self.buffer.write(data) + self.buffer_pos += len(data) + self.bytes_written += len(data) + + def read(self, bytes=0): + self.buffer.seek(self.read_pos, 0) + maxbytes = self.bytes_waiting() + if bytes > 0 and bytes < maxbytes: + maxbytes = bytes + + rb = self.buffer.read(maxbytes) + self.read_pos = self.buffer.tell() + self.buffer.seek(self.buffer_pos, 0) + + if self.read_pos == self.buffer_pos: + self.empty = True + return rb + + def rewind(self, bytes): + if bytes > self.read_pos: + raise ValueError, "Buffer is not big enough to rewind that far" + + self.read_pos -= bytes + if bytes > 0: + self.empty = False + + + +class SplitRingBuffer(RingBuffer): + def __init__(self, size, split, overflow=True): + RingBuffer.__init__(self, size, overflow) + self.emptyclass = SplitRingBuffer + self.fullclass = SplitRingBufferFull + self.splitpos = split + self.read_pos = split + + def read_head(): + self.buffer.seek(0, 0) + rb = self.buffer.read() + self.buffer.seek(self.buffer_pos, 0) + return (True, rb) + + +class RingBufferFull(object): + def __init__(self, size): + raise NotImplementedError, "You should not create this class manually, use RingBuffer() instead" + + def overflow_buffer(): + self.buffer_pos = 0 + self.seek_to_start() + + def seek_to_start(): + self.buffer.seek(0, 0) + + def sizeleft(self): + if self.read_pos == None: + return self.size + elif self.read_pos == self.buffer_pos and self.empty: + return self.size + elif self.read_pos == self.buffer_pos: + return 0 + elif self.read_pos < self.buffer_pos: + return self.size - self.buffer_pos + self.read_pos + else: + return self.read_pos - self.buffer_pos + + def write(self, data): + if data: + self.empty = False + + di = 0 + ld = len(data) + + # check for overflow before we start + if ld > self.sizeleft() and not self.allow_overflow: + # overflow will happen, raise an exception + raise FullBufferError("Data would overflow the buffer") + + while (ld - di) + self.buffer_pos >= self.size: + # write data from the current buffer_pos all the way to the end of the ringbuffer + self.buffer.write(data[di:di + (self.size - self.buffer_pos)]) + + if self.read_pos != None and self.read_pos > self.buffer_pos: + # our read pos was between the buffer_pos and the end. since we just overwrote + # all that, it has been overwritten and we've lost our place, doh! + self.read_pos = None + self.overflow_buffer() + di += (self.size - self.buffer_pos) + + # no more writing past the end of the buffer, now we can just do a simple write + self.buffer.write(data[di:]) + if self.read_pos != None and self.buffer_pos <= self.read_pos and (self.buffer_pos + ld - di) > self.read_pos: + self.read_pos = None + self.buffer_pos += ld - di + self.bytes_written += ld + + def read(self, bytes=0): + pos = self.read_pos + fullread = False + if pos == None: + pos = self.buffer_pos + + maxlen = self.bytes_waiting() + self.buffer.seek(pos, 0) + if bytes > 0 and maxlen > bytes: + maxlen = bytes + + if maxlen == 0: + return '' + + split = self.size - pos + if split >= maxlen: + self.buffer.seek(pos, 0) + rb = self.buffer.read(maxlen) + self.read_pos = self.buffer.tell() + self.buffer.seek(self.buffer_pos, 0) + else: + self.buffer.seek(pos, 0) + rb = self.buffer.read(split) + self.seek_to_start() + rb += self.buffer.read(maxlen - split) + self.read_pos = self.buffer.tell() + self.buffer.seek(self.buffer_pos, 0) + + if self.read_pos == self.buffer_pos and bytes > 0: + "we've read everything out of the buffer" + self.empty = True + + return rb + + def rewind(self, bytes): + if bytes > self.sizeleft(): + raise ValueError, "Buffer is not big enough to rewind that far" + + self.read_pos -= bytes + if self.read_pos < 0: + self.read_pos += self.size + + if bytes > 0: + self.empty = False + + + + +class SplitRingBufferFull(RingBufferFull): + def read(self, bytes=0): + pass + + def overflow_buffer(): + self.buffer_pos = self.split_pos + + def seek_to_start(): + self.buffer.seek(self.split_pos, 0) + + def read_head(): + self.buffer.seek(0, 0) + rb = self.buffer.read(self.split_pos) + self.buffer.seek(self.buffer_pos, 0) + return (False, rb) + + \ No newline at end of file diff --git a/server.py b/server.py index 95af5e1..70097d6 100644 --- a/server.py +++ b/server.py @@ -1,9 +1,18 @@ import os, sys, time from listener import Listener +from language import VirtualMachine +from database_fake import Fake_Database as Database class Server(object): def __init__(self, dbname): self.dbname = dbname + self.db = Database(dbname) + self.vm = VirtualMachine(self.db) + self.max_sleep = 0.1 + self.min_sleep = 0.005 + self.vm_sleep_fudge = 0.85 + self.server_started = None + self.loop_started = None self.listeners = [] def listen(self, addr, port): @@ -12,11 +21,16 @@ class Server(object): 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) - time.sleep(2) + self.vm.run() + for l in self.listeners: + l.flush_output() + self.idlewait() def read_input(self, conn, data): @@ -25,6 +39,23 @@ class Server(object): del ds[-1] for line in ds: print "%s: %s" % (conn.id, line) + + def idlewait(self): + if self.vm.sleepytime == None: + "virtual machine is still busy anyway, no sleeping on the job!" + return + + "also check how much time we have spent already. if it's already been our polling" + "time (or longer!), there's no sense waiting even more!" + time_already_spent = time.time() - self.loop_started + + sleepytime = min(self.max_sleep - time_already_spent, self.vm.sleepytime * self.vm_sleep_fudge) + if sleepytime < self.min_sleep or sleepytime < 0.0: + return + + time.sleep(sleepytime) + + if __name__ == "__main__": srv = Server("Test")