progress on mung server
--HG-- branch : mung
This commit is contained in:
parent
8a5b25e6b0
commit
d3f845144e
11 changed files with 826 additions and 18 deletions
|
@ -3,7 +3,7 @@
|
|||
CALLBUILTIN eval argstr
|
||||
.
|
||||
:"handle_connection" x *
|
||||
RETURN #0;
|
||||
RETURN #0
|
||||
.
|
||||
|
||||
|
||||
|
|
104
builtins.py
Executable file
104
builtins.py
Executable file
|
@ -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,
|
||||
})
|
||||
"""
|
|
@ -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
|
||||
|
|
16
database_fake.py
Executable file
16
database_fake.py
Executable file
|
@ -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
|
||||
|
||||
|
||||
|
149
ebnf.py
Executable file
149
ebnf.py
Executable file
|
@ -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
|
129
language.py
129
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()
|
54
language_types.py
Executable file
54
language_types.py
Executable file
|
@ -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
|
41
listener.py
41
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:
|
||||
|
|
103
parse.py
Executable file
103
parse.py
Executable file
|
@ -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 ) )
|
||||
|
||||
|
203
ringbuffer.py
Executable file
203
ringbuffer.py
Executable file
|
@ -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)
|
||||
|
||||
|
33
server.py
33
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")
|
||||
|
|
Loading…
Add table
Reference in a new issue